summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorMark Logan <Mark.Logan@Sun.COM>2009-05-19 18:34:13 -0700
committerMark Logan <Mark.Logan@Sun.COM>2009-05-19 18:34:13 -0700
commit7e7bd3dccbfe8f79e25e5c1554b5bc3a9aaca321 (patch)
treec8c0f683033b602ab4ba21e0fe88db62e04ac8df /usr/src/lib
parent8326c110821caa2450812958945e3631e8ad696e (diff)
downloadillumos-joyent-7e7bd3dccbfe8f79e25e5c1554b5bc3a9aaca321.tar.gz
PSARC 2009/145 Parted - GNU Partition Editor
6819750 Port GNU Parted to Solaris and include it on the OpenSolaris Live CD 6819757 Port ntfsprogs to Solaris and include it on the OpenSolaris Live CD
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile4
-rw-r--r--usr/src/lib/libntfs/AUTHORS9
-rw-r--r--usr/src/lib/libntfs/COPYING340
-rw-r--r--usr/src/lib/libntfs/CREDITS40
-rw-r--r--usr/src/lib/libntfs/Makefile116
-rw-r--r--usr/src/lib/libntfs/Makefile.com123
-rw-r--r--usr/src/lib/libntfs/README16
-rw-r--r--usr/src/lib/libntfs/THIRDPARTYLICENSE340
-rw-r--r--usr/src/lib/libntfs/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/lib/libntfs/THIRDPARTYLICENSE.readme7
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/attrib.h382
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/attrlist.h51
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/bitmap.h134
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/bootsect.h47
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/collate.h38
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/compat.h58
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/compress.h33
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/config.h313
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/crypto.h46
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/debug.h63
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/device.h128
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/device_io.h77
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/dir.h112
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/endians.h248
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-method.h43
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-module.h42
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/index.h131
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/inode.h215
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/layout.h3063
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h50
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/list.h192
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/logfile.h441
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/logging.h143
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/mft.h116
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/mst.h34
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/ntfstime.h69
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/runlist.h90
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/security.h55
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/support.h121
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/types.h142
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/unistr.h69
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/version.h29
-rw-r--r--usr/src/lib/libntfs/common/include/ntfs/volume.h247
-rw-r--r--usr/src/lib/libntfs/common/libntfs/attrib.c5234
-rw-r--r--usr/src/lib/libntfs/common/libntfs/attrlist.c320
-rw-r--r--usr/src/lib/libntfs/common/libntfs/bitmap.c248
-rw-r--r--usr/src/lib/libntfs/common/libntfs/bootsect.c273
-rw-r--r--usr/src/lib/libntfs/common/libntfs/collate.c219
-rw-r--r--usr/src/lib/libntfs/common/libntfs/compat.c73
-rw-r--r--usr/src/lib/libntfs/common/libntfs/compress.c552
-rw-r--r--usr/src/lib/libntfs/common/libntfs/crypto.c1518
-rw-r--r--usr/src/lib/libntfs/common/libntfs/debug.c73
-rw-r--r--usr/src/lib/libntfs/common/libntfs/device.c795
-rw-r--r--usr/src/lib/libntfs/common/libntfs/device_io.c46
-rw-r--r--usr/src/lib/libntfs/common/libntfs/dir.c1773
-rw-r--r--usr/src/lib/libntfs/common/libntfs/gnome-vfs-method.c915
-rw-r--r--usr/src/lib/libntfs/common/libntfs/gnome-vfs-module.c74
-rw-r--r--usr/src/lib/libntfs/common/libntfs/index.c1862
-rw-r--r--usr/src/lib/libntfs/common/libntfs/inode.c1200
-rw-r--r--usr/src/lib/libntfs/common/libntfs/lcnalloc.c857
-rw-r--r--usr/src/lib/libntfs/common/libntfs/logfile.c769
-rw-r--r--usr/src/lib/libntfs/common/libntfs/logging.c643
-rw-r--r--usr/src/lib/libntfs/common/libntfs/mft.c1583
-rw-r--r--usr/src/lib/libntfs/common/libntfs/misc.c64
-rw-r--r--usr/src/lib/libntfs/common/libntfs/mst.c216
-rw-r--r--usr/src/lib/libntfs/common/libntfs/runlist.c2154
-rw-r--r--usr/src/lib/libntfs/common/libntfs/security.c271
-rw-r--r--usr/src/lib/libntfs/common/libntfs/unistr.c775
-rw-r--r--usr/src/lib/libntfs/common/libntfs/unix_io.c320
-rw-r--r--usr/src/lib/libntfs/common/libntfs/version.c45
-rw-r--r--usr/src/lib/libntfs/common/libntfs/volume.c1688
-rw-r--r--usr/src/lib/libntfs/common/mapfile-vers138
-rw-r--r--usr/src/lib/libntfs/i386/Makefile37
-rw-r--r--usr/src/lib/libparted/AUTHORS233
-rw-r--r--usr/src/lib/libparted/COPYING676
-rw-r--r--usr/src/lib/libparted/Makefile102
-rw-r--r--usr/src/lib/libparted/Makefile.com128
-rw-r--r--usr/src/lib/libparted/README22
-rw-r--r--usr/src/lib/libparted/THANKS28
-rw-r--r--usr/src/lib/libparted/THIRDPARTYLICENSE676
-rw-r--r--usr/src/lib/libparted/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/lib/libparted/THIRDPARTYLICENSE.readme7
-rw-r--r--usr/src/lib/libparted/common/include/parted/constraint.h96
-rw-r--r--usr/src/lib/libparted/common/include/parted/crc32.h34
-rw-r--r--usr/src/lib/libparted/common/include/parted/debug.h89
-rw-r--r--usr/src/lib/libparted/common/include/parted/device.h150
-rw-r--r--usr/src/lib/libparted/common/include/parted/disk.h348
-rw-r--r--usr/src/lib/libparted/common/include/parted/endian.h85
-rw-r--r--usr/src/lib/libparted/common/include/parted/exception.h116
-rw-r--r--usr/src/lib/libparted/common/include/parted/fdasd.h230
-rw-r--r--usr/src/lib/libparted/common/include/parted/filesys.h116
-rw-r--r--usr/src/lib/libparted/common/include/parted/geom.h83
-rw-r--r--usr/src/lib/libparted/common/include/parted/gnu.h44
-rw-r--r--usr/src/lib/libparted/common/include/parted/linux.h45
-rw-r--r--usr/src/lib/libparted/common/include/parted/natmath.h108
-rw-r--r--usr/src/lib/libparted/common/include/parted/parted.h63
-rw-r--r--usr/src/lib/libparted/common/include/parted/solaris.h42
-rw-r--r--usr/src/lib/libparted/common/include/parted/timer.h65
-rw-r--r--usr/src/lib/libparted/common/include/parted/unit.h93
-rw-r--r--usr/src/lib/libparted/common/include/parted/vtoc.h293
-rw-r--r--usr/src/lib/libparted/common/lib/__fpending.h34
-rw-r--r--usr/src/lib/libparted/common/lib/basename.c129
-rw-r--r--usr/src/lib/libparted/common/lib/close-stream.c76
-rw-r--r--usr/src/lib/libparted/common/lib/close-stream.h2
-rw-r--r--usr/src/lib/libparted/common/lib/closeout.c86
-rw-r--r--usr/src/lib/libparted/common/lib/closeout.h33
-rw-r--r--usr/src/lib/libparted/common/lib/config.h597
-rw-r--r--usr/src/lib/libparted/common/lib/configmake.h25
-rw-r--r--usr/src/lib/libparted/common/lib/dirname.c85
-rw-r--r--usr/src/lib/libparted/common/lib/dirname.h70
-rw-r--r--usr/src/lib/libparted/common/lib/error.c339
-rw-r--r--usr/src/lib/libparted/common/lib/error.h66
-rw-r--r--usr/src/lib/libparted/common/lib/exitfail.c26
-rw-r--r--usr/src/lib/libparted/common/lib/exitfail.h20
-rw-r--r--usr/src/lib/libparted/common/lib/full-write.c81
-rw-r--r--usr/src/lib/libparted/common/lib/full-write.h35
-rw-r--r--usr/src/lib/libparted/common/lib/getopt.c1191
-rw-r--r--usr/src/lib/libparted/common/lib/getopt_int.h131
-rw-r--r--usr/src/lib/libparted/common/lib/gettext.h270
-rw-r--r--usr/src/lib/libparted/common/lib/localcharset.c460
-rw-r--r--usr/src/lib/libparted/common/lib/localcharset.h41
-rw-r--r--usr/src/lib/libparted/common/lib/long-options.c89
-rw-r--r--usr/src/lib/libparted/common/lib/long-options.h26
-rw-r--r--usr/src/lib/libparted/common/lib/memcpy.c36
-rw-r--r--usr/src/lib/libparted/common/lib/memmove.c26
-rw-r--r--usr/src/lib/libparted/common/lib/memset.c28
-rw-r--r--usr/src/lib/libparted/common/lib/quotearg.c697
-rw-r--r--usr/src/lib/libparted/common/lib/quotearg.h140
-rw-r--r--usr/src/lib/libparted/common/lib/regcomp.c3832
-rw-r--r--usr/src/lib/libparted/common/lib/regex.c71
-rw-r--r--usr/src/lib/libparted/common/lib/regex.h675
-rw-r--r--usr/src/lib/libparted/common/lib/regex_internal.c1741
-rw-r--r--usr/src/lib/libparted/common/lib/regex_internal.h857
-rw-r--r--usr/src/lib/libparted/common/lib/regexec.c4399
-rw-r--r--usr/src/lib/libparted/common/lib/rpmatch.c79
-rw-r--r--usr/src/lib/libparted/common/lib/safe-read.c78
-rw-r--r--usr/src/lib/libparted/common/lib/safe-read.h35
-rw-r--r--usr/src/lib/libparted/common/lib/safe-write.c19
-rw-r--r--usr/src/lib/libparted/common/lib/safe-write.h25
-rw-r--r--usr/src/lib/libparted/common/lib/strcspn.c41
-rw-r--r--usr/src/lib/libparted/common/lib/stripslash.c45
-rw-r--r--usr/src/lib/libparted/common/lib/strndup.c37
-rw-r--r--usr/src/lib/libparted/common/lib/version-etc-fsf.c31
-rw-r--r--usr/src/lib/libparted/common/lib/version-etc.c173
-rw-r--r--usr/src/lib/libparted/common/lib/version-etc.h37
-rw-r--r--usr/src/lib/libparted/common/lib/xalloc-die.c42
-rw-r--r--usr/src/lib/libparted/common/lib/xalloc.h271
-rw-r--r--usr/src/lib/libparted/common/lib/xmalloc.c123
-rw-r--r--usr/src/lib/libparted/common/lib/xstrndup.c37
-rw-r--r--usr/src/lib/libparted/common/lib/xstrndup.h25
-rw-r--r--usr/src/lib/libparted/common/libparted/arch/solaris.c1397
-rw-r--r--usr/src/lib/libparted/common/libparted/cs/constraint.c529
-rw-r--r--usr/src/lib/libparted/common/libparted/cs/geom.c474
-rw-r--r--usr/src/lib/libparted/common/libparted/cs/natmath.c503
-rw-r--r--usr/src/lib/libparted/common/libparted/debug.c120
-rw-r--r--usr/src/lib/libparted/common/libparted/device.c445
-rw-r--r--usr/src/lib/libparted/common/libparted/disk.c2265
-rw-r--r--usr/src/lib/libparted/common/libparted/exception.c312
-rw-r--r--usr/src/lib/libparted/common/libparted/filesys.c782
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/affs.c462
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/affs.h19
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/amiga.c349
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/amiga.h70
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/apfs.c150
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/apfs.h17
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/asfs.c142
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/asfs.h17
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/amiga/interface.c87
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2.c792
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2.h249
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_block_relocator.c927
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_buffer.c446
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_fs.h323
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_inode_relocator.c599
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_meta.c145
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_mkfs.c632
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/ext2_resize.c730
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/interface.c352
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.c135
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.h27
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/tune.c39
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ext2/tune.h29
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/bootsector.c452
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/bootsector.h142
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/calc.c435
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/calc.h77
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/clstdup.c424
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/clstdup.h28
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/context.c260
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/context.h69
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/count.c403
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/count.h55
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/fat.c887
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/fat.h168
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/fatio.c151
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/fatio.h48
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/resize.c877
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/table.c481
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/table.h74
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/traverse.c365
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/fat/traverse.h73
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/advfs.c328
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/advfs.h48
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.c383
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.h51
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/cache.c238
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/cache.h117
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/file.c228
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/file.h41
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.c273
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.h60
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/hfs.c1353
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/hfs.h830
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/journal.c389
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/journal.h44
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/probe.c230
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/probe.h43
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/reloc.c670
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/reloc.h35
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.c945
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.h36
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/jfs/jfs.c109
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/jfs/jfs_superblock.h144
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/jfs/jfs_types.h530
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/linux_swap/linux_swap.c522
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ntfs/ntfs.c599
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.c138
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.h60
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.c866
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.h108
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/solaris_x86/solaris_x86.c171
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/ufs/ufs.c324
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/xfs/platform_defs.h117
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/xfs/xfs.c119
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/xfs/xfs_sb.h489
-rw-r--r--usr/src/lib/libparted/common/libparted/fs/xfs/xfs_types.h302
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/aix.c295
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/bsd.c633
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/dos.c2280
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/dvh.c911
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/dvh.h178
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/efi_crc32.c126
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/gpt.c1538
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/loop.c333
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/mac.c1629
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/pc98.c896
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/rdb.c1183
-rw-r--r--usr/src/lib/libparted/common/libparted/labels/sun.c868
-rw-r--r--usr/src/lib/libparted/common/libparted/libparted.c351
-rw-r--r--usr/src/lib/libparted/common/libparted/timer.c245
-rw-r--r--usr/src/lib/libparted/common/libparted/unit.c565
-rw-r--r--usr/src/lib/libparted/common/mapfile-vers150
-rw-r--r--usr/src/lib/libparted/i386/Makefile52
253 files changed, 96445 insertions, 0 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 04f4be9712..5d7a30377b 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -259,6 +259,10 @@ SUBDIRS += \
mpapi \
$($(MACH)_SUBDIRS)
+i386_SUBDIRS= \
+ libntfs \
+ libparted
+
sparc_SUBDIRS= .WAIT \
efcode \
libc_psr .WAIT \
diff --git a/usr/src/lib/libntfs/AUTHORS b/usr/src/lib/libntfs/AUTHORS
new file mode 100644
index 0000000000..86a3cf8304
--- /dev/null
+++ b/usr/src/lib/libntfs/AUTHORS
@@ -0,0 +1,9 @@
+
+ntfsprogs is written by the Linux-NTFS project (www.linux-ntfs.org) and
+maintained by Anton Altaparmakov <aia21 at cantab.net>.
+
+Current active project members are (in alphabetical order):
+
+Anton Altaparmakov <aia21 at cantab.net>
+Mario Emmenlauer <mario at emmenlauer.de>
+Yura Pakhuchiy <pakhuchiy at gmail.com>
diff --git a/usr/src/lib/libntfs/COPYING b/usr/src/lib/libntfs/COPYING
new file mode 100644
index 0000000000..d60c31a97a
--- /dev/null
+++ b/usr/src/lib/libntfs/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) <year> <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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/usr/src/lib/libntfs/CREDITS b/usr/src/lib/libntfs/CREDITS
new file mode 100644
index 0000000000..29bd3e4c85
--- /dev/null
+++ b/usr/src/lib/libntfs/CREDITS
@@ -0,0 +1,40 @@
+The following people have contributed directly or indirectly to the Linux-NTFS
+project.
+
+The list is sorted alphabetically, so please keep it this way!
+
+Please contact <linux-ntfs-dev at lists.sf.net> if you believe someone is
+missing or if you prefer not to be listed.
+
+Alexei Alexandrov <alex_alexandrov at hotmail.com>
+Anton Altaparmakov <aia21 at cantab.net>
+Albert D. Cahalan <acahalan at cs.uml.edu>
+Russ Christensen <rchriste at cs.utah.edu>
+Pete Curran <curran at rpi.edu>
+Mario Emmenlauer <mario at emmenlauer.de>
+Andras Erdei <ccg at freemail.hu>
+Matthew J. Fanto <mattjf at uncompiled.com>
+Yuval Fledel <yuvalfl at gmail.com>
+Marcin Gibuła <m.gibula at conecto.pl>
+Christophe Grenier <grenier at cgsecurity.org>
+Csaba Henk <csaba.henk at creo.hu>
+Ian Jackson <ian at davenant.greenend.org.uk>
+Max Khon <fjoe at samodelkin.net>
+Carmelo Kintana <kintana at berkeley.edu>
+Jan Kratochvil <project-captive at jankratochvil.net>
+Lode Leroy <lode_leroy at hotmail.com>
+David Martínez Moreno <ender at debian.org>
+Giang Nguyen <cauthu at hotmail.com>
+Leonard Norrgård <vinsci at nic.funet.fi>
+Holger Ohmacht <holger.ohmacht at web.de>
+Per Olofsson <pelle at dsv.su.se>
+Yura Pakhuchiy <pakhuchiy at gmail.com>
+Yuri Per <yuri at acronis.com>
+Richard Russon <ntfs at flatcap.org>
+Erik Sørnes <erso1970 at yahoo.no>
+Szabolcs Szakacsits <szaka at sienet.hu>
+zhanglinbao <zhanglinbao2000 at 163.com>
+
+Configuration, compilation and installation system are originally based on
+numerous different GNU and Gnome utilities and libraries so "Many thanks!"
+to all the people who have participated in their creation!
diff --git a/usr/src/lib/libntfs/Makefile b/usr/src/lib/libntfs/Makefile
new file mode 100644
index 0000000000..0e4a739780
--- /dev/null
+++ b/usr/src/lib/libntfs/Makefile
@@ -0,0 +1,116 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS = $(MACH)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+delete := TARGET= delete
+install := TARGET= install
+_msg := TARGET= _msg
+package := TARGET= package
+
+LIBRARY= libntfs.a
+TEXT_DOMAIN= SUNW_OST_OSLIB
+XGETFLAGS= -a
+POFILE= $(LIBRARY:.a=.po)
+POFILES= generic.po
+
+SED= sed
+GREP= grep
+
+.KEEP_STATE:
+
+all clean clobber delete install package: $(SUBDIRS)
+
+# definitions for install_h target
+HDRS= ../common/include/ntfs/attrib.h \
+ ../common/include/ntfs/attrlist.h \
+ ../common/include/ntfs/bitmap.h \
+ ../common/include/ntfs/bootsect.h \
+ ../common/include/ntfs/collate.h \
+ ../common/include/ntfs/compat.h \
+ ../common/include/ntfs/compress.h \
+ ../common/include/ntfs/crypto.h \
+ ../common/include/ntfs/debug.h \
+ ../common/include/ntfs/device.h \
+ ../common/include/ntfs/device_io.h \
+ ../common/include/ntfs/dir.h \
+ ../common/include/ntfs/endians.h \
+ ../common/include/ntfs/gnome-vfs-method.h \
+ ../common/include/ntfs/gnome-vfs-module.h \
+ ../common/include/ntfs/index.h \
+ ../common/include/ntfs/inode.h \
+ ../common/include/ntfs/layout.h \
+ ../common/include/ntfs/lcnalloc.h \
+ ../common/include/ntfs/list.h \
+ ../common/include/ntfs/logfile.h \
+ ../common/include/ntfs/logging.h \
+ ../common/include/ntfs/mft.h \
+ ../common/include/ntfs/mst.h \
+ ../common/include/ntfs/ntfstime.h \
+ ../common/include/ntfs/runlist.h \
+ ../common/include/ntfs/security.h \
+ ../common/include/ntfs/support.h \
+ ../common/include/ntfs/types.h \
+ ../common/include/ntfs/unistr.h \
+ ../common/include/ntfs/version.h \
+ ../common/include/ntfs/volume.h
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+# install rule for install_h target
+$(ROOTHDRDIR)/%: %
+ $(INS.file)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) *.[ch]* */*.[ch]*
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+$(MSGDOMAIN):
+ $(INS.dir)
+
+FRC:
diff --git a/usr/src/lib/libntfs/Makefile.com b/usr/src/lib/libntfs/Makefile.com
new file mode 100644
index 0000000000..3efd822887
--- /dev/null
+++ b/usr/src/lib/libntfs/Makefile.com
@@ -0,0 +1,123 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= libntfs.a
+VERS= .10
+
+#
+# All relative to SRCDIR
+#
+
+LIBNTFSDIR= libntfs
+
+OBJECTS= $(LIBNTFSDIR)/attrib.o \
+ $(LIBNTFSDIR)/attrlist.o \
+ $(LIBNTFSDIR)/bitmap.o \
+ $(LIBNTFSDIR)/bootsect.o \
+ $(LIBNTFSDIR)/collate.o \
+ $(LIBNTFSDIR)/compat.o \
+ $(LIBNTFSDIR)/compress.o \
+ $(LIBNTFSDIR)/crypto.o \
+ $(LIBNTFSDIR)/debug.o \
+ $(LIBNTFSDIR)/device.o \
+ $(LIBNTFSDIR)/device_io.o \
+ $(LIBNTFSDIR)/dir.o \
+ $(LIBNTFSDIR)/gnome-vfs-method.o \
+ $(LIBNTFSDIR)/gnome-vfs-module.o \
+ $(LIBNTFSDIR)/index.o \
+ $(LIBNTFSDIR)/inode.o \
+ $(LIBNTFSDIR)/lcnalloc.o \
+ $(LIBNTFSDIR)/logfile.o \
+ $(LIBNTFSDIR)/logging.o \
+ $(LIBNTFSDIR)/mft.o \
+ $(LIBNTFSDIR)/misc.o \
+ $(LIBNTFSDIR)/mst.o \
+ $(LIBNTFSDIR)/runlist.o \
+ $(LIBNTFSDIR)/security.o \
+ $(LIBNTFSDIR)/unistr.o \
+ $(LIBNTFSDIR)/version.o \
+ $(LIBNTFSDIR)/volume.o
+
+# include library definitions
+include ../../Makefile.lib
+
+SRCDIR = ../common
+
+C99MODE= $(C99_ENABLE)
+CERRWARN += -erroff=E_ENUM_VAL_OVERFLOWS_INT_MAX
+CERRWARN += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
+CERRWARN += -erroff=E_END_OF_LOOP_CODE_NOT_REACHED
+CERRWARN += -erroff=E_LOOP_NOT_ENTERED_AT_TOP
+
+LIBS = $(DYNLIB)
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -DHAVE_CONFIG_H \
+ -DLTVERSION_LIBNTFS=\"10:0:0\" \
+ -I$(SRCDIR)/include/ntfs
+DYNFLAGS += $(ZINTERPOSE)
+LDLIBS += -lc -lgnomevfs-2 -lglib-2.0
+
+.KEEP_STATE:
+
+#
+# This open source is exempted from lint
+#
+lint:
+
+# include library targets
+include ../../Makefile.targ
+
+pics/$(LIBNTFSDIR)/gnome-vfs-method.o: ../common/$(LIBNTFSDIR)/gnome-vfs-method.c
+ $(CC) $(CFLAGS) \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/gnome-vfs-2.0 \
+ -I/usr/include/gnome-vfs-module-2.0 \
+ -I/usr/lib/gnome-vfs-2.0/include \
+ -I/usr/include/gconf/2 \
+ -I/usr/include/orbit-2.0 \
+ -I/usr/include/dbus-1.0 \
+ -I/usr/lib/dbus-1.0/include \
+ -D_PTHREADS -DORBIT2=1 \
+ $(CPPFLAGS) -c -o $@ \
+ ../common/$(LIBNTFSDIR)/gnome-vfs-method.c
+ $(POST_PROCESS_O)
+
+pics/$(LIBNTFSDIR)/gnome-vfs-module.o: ../common/$(LIBNTFSDIR)/gnome-vfs-module.c
+ $(CC) $(CFLAGS) \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -I/usr/include/gnome-vfs-2.0 \
+ -I/usr/include/gnome-vfs-module-2.0 \
+ -I/usr/lib/gnome-vfs-2.0/include \
+ -I/usr/include/gconf/2 \
+ -I/usr/include/orbit-2.0 \
+ -I/usr/include/dbus-1.0 \
+ -I/usr/lib/dbus-1.0/include \
+ -D_PTHREADS -DORBIT2=1 \
+ $(CPPFLAGS) -c -o $@ \
+ ../common/$(LIBNTFSDIR)/gnome-vfs-module.c
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/libntfs/README b/usr/src/lib/libntfs/README
new file mode 100644
index 0000000000..80efa88af0
--- /dev/null
+++ b/usr/src/lib/libntfs/README
@@ -0,0 +1,16 @@
+This is the Solaris ON port of ntfsprogs v2.0.0
+Please see http://www.linux-ntfs.org/ for more information.
+
+ntfsprogs has been broken into two pieces: src/lib/libntfs and src/cmd/ntfsprogs
+
+The Makefiles have all been replaced by ON Makefiles.
+
+The common directory contains these subdirectories from ntfsprogs-2.0.0:
+include and libntfs
+
+$(SUBDIR)/config.status: $(SUBDIR)/configure
+ cd src; \
+ MAKE=gmake ./configure CFLAGS=-I$(ROOT)/usr/include \
+ LDFLAGS="-L$(ROOT)/lib -L$(ROOT)/usr/lib -Wl,-Bdirect -Wl,-M$(MAPFILE.NE
+S) -Wl,-zignore" \
+ --disable-dependency-tracking
diff --git a/usr/src/lib/libntfs/THIRDPARTYLICENSE b/usr/src/lib/libntfs/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..d60c31a97a
--- /dev/null
+++ b/usr/src/lib/libntfs/THIRDPARTYLICENSE
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) <year> <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) year 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/usr/src/lib/libntfs/THIRDPARTYLICENSE.descrip b/usr/src/lib/libntfs/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..6d2fc7f55a
--- /dev/null
+++ b/usr/src/lib/libntfs/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+ntfsprogs - NTFS utilities
diff --git a/usr/src/lib/libntfs/THIRDPARTYLICENSE.readme b/usr/src/lib/libntfs/THIRDPARTYLICENSE.readme
new file mode 100644
index 0000000000..9b47b6e8e9
--- /dev/null
+++ b/usr/src/lib/libntfs/THIRDPARTYLICENSE.readme
@@ -0,0 +1,7 @@
+"GPL Disclaimer
+For the avoidance of doubt, except that if any license choice other than GPL or
+LGPL is available it will apply instead, Sun elects to use only the General
+Public License version 2 (GPLv2) at this time for any software where a choice of
+GPL license versions is made available with the language indicating that GPLv2
+or any later version may be used, or where a choice of which version of the GPL
+is applied is otherwise unspecified."
diff --git a/usr/src/lib/libntfs/common/include/ntfs/attrib.h b/usr/src/lib/libntfs/common/include/ntfs/attrib.h
new file mode 100644
index 0000000000..dcca5427ea
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/attrib.h
@@ -0,0 +1,382 @@
+/*
+ * attrib.h - Exports for attribute handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_ATTRIB_H
+#define _NTFS_ATTRIB_H
+
+/* Forward declarations */
+typedef struct _ntfs_attr ntfs_attr;
+typedef struct _ntfs_attr_search_ctx ntfs_attr_search_ctx;
+
+#include "list.h"
+#include "types.h"
+#include "inode.h"
+#include "unistr.h"
+#include "runlist.h"
+#include "volume.h"
+#include "debug.h"
+#include "logging.h"
+#include "crypto.h"
+
+extern ntfschar AT_UNNAMED[];
+
+/**
+ * enum ntfs_lcn_special_values - special return values for ntfs_*_vcn_to_lcn()
+ *
+ * Special return values for ntfs_rl_vcn_to_lcn() and ntfs_attr_vcn_to_lcn().
+ *
+ * TODO: Describe them.
+ */
+typedef enum {
+ LCN_HOLE = -1, /* Keep this as highest value or die! */
+ LCN_RL_NOT_MAPPED = -2,
+ LCN_ENOENT = -3,
+ LCN_EINVAL = -4,
+ LCN_EIO = -5,
+} ntfs_lcn_special_values;
+
+/**
+ * struct ntfs_attr_search_ctx - search context used in attribute search functions
+ * @mrec: buffer containing mft record to search
+ * @attr: attribute record in @mrec where to begin/continue search
+ * @is_first: if true lookup_attr() begins search with @attr, else after @attr
+ *
+ * Structure must be initialized to zero before the first call to one of the
+ * attribute search functions. Initialize @mrec to point to the mft record to
+ * search, and @attr to point to the first attribute within @mrec (not necessary
+ * if calling the _first() functions), and set @is_first to TRUE (not necessary
+ * if calling the _first() functions).
+ *
+ * If @is_first is TRUE, the search begins with @attr. If @is_first is FALSE,
+ * the search begins after @attr. This is so that, after the first call to one
+ * of the search attribute functions, we can call the function again, without
+ * any modification of the search context, to automagically get the next
+ * matching attribute.
+ */
+struct _ntfs_attr_search_ctx {
+ MFT_RECORD *mrec;
+ ATTR_RECORD *attr;
+ BOOL is_first;
+ ntfs_inode *ntfs_ino;
+ ATTR_LIST_ENTRY *al_entry;
+ ntfs_inode *base_ntfs_ino;
+ MFT_RECORD *base_mrec;
+ ATTR_RECORD *base_attr;
+};
+
+extern void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx);
+extern ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni,
+ MFT_RECORD *mrec);
+extern void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx);
+
+extern int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
+ const u32 name_len, const IGNORE_CASE_BOOL ic,
+ const VCN lowest_vcn, const u8 *val, const u32 val_len,
+ ntfs_attr_search_ctx *ctx);
+
+extern ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
+ const ATTR_TYPES type);
+
+/**
+ * ntfs_attrs_walk - syntactic sugar for walking all attributes in an inode
+ * @ctx: initialised attribute search context
+ *
+ * Syntactic sugar for walking attributes in an inode.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code from
+ * ntfs_attr_lookup().
+ *
+ * Example: When you want to enumerate all attributes in an open ntfs inode
+ * @ni, you can simply do:
+ *
+ * int err;
+ * ntfs_attr_search_ctx *ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ * if (!ctx)
+ * // Error code is in errno. Handle this case.
+ * while (!(err = ntfs_attrs_walk(ctx))) {
+ * ATTR_RECORD *attr = ctx->attr;
+ * // attr now contains the next attribute. Do whatever you want
+ * // with it and then just continue with the while loop.
+ * }
+ * if (err && errno != ENOENT)
+ * // Ooops. An error occurred! You should handle this case.
+ * // Now finished with all attributes in the inode.
+ */
+static __inline__ int ntfs_attrs_walk(ntfs_attr_search_ctx *ctx)
+{
+ return ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE, 0,
+ NULL, 0, ctx);
+}
+
+/**
+ * struct ntfs_attr - ntfs in memory non-resident attribute structure
+ * @rl: if not NULL, the decompressed runlist
+ * @ni: base ntfs inode to which this attribute belongs
+ * @type: attribute type
+ * @name: Unicode name of the attribute
+ * @name_len: length of @name in Unicode characters
+ * @state: NTFS attribute specific flags describing this attribute
+ * @allocated_size: copy from the attribute record
+ * @data_size: copy from the attribute record
+ * @initialized_size: copy from the attribute record
+ * @compressed_size: copy from the attribute record
+ * @compression_block_size: size of a compression block (cb)
+ * @compression_block_size_bits: log2 of the size of a cb
+ * @compression_block_clusters: number of clusters per cb
+ * @crypto: (valid only for encrypted) see description below
+ *
+ * This structure exists purely to provide a mechanism of caching the runlist
+ * of an attribute. If you want to operate on a particular attribute extent,
+ * you should not be using this structure at all. If you want to work with a
+ * resident attribute, you should not be using this structure at all. As a
+ * fail-safe check make sure to test NAttrNonResident() and if it is false, you
+ * know you shouldn't be using this structure.
+ *
+ * If you want to work on a resident attribute or on a specific attribute
+ * extent, you should use ntfs_lookup_attr() to retrieve the attribute (extent)
+ * record, edit that, and then write back the mft record (or set the
+ * corresponding ntfs inode dirty for delayed write back).
+ *
+ * @rl is the decompressed runlist of the attribute described by this
+ * structure. Obviously this only makes sense if the attribute is not resident,
+ * i.e. NAttrNonResident() is true. If the runlist hasn't been decompressed yet
+ * @rl is NULL, so be prepared to cope with @rl == NULL.
+ *
+ * @ni is the base ntfs inode of the attribute described by this structure.
+ *
+ * @type is the attribute type (see layout.h for the definition of ATTR_TYPES),
+ * @name and @name_len are the little endian Unicode name and the name length
+ * in Unicode characters of the attribute, respectively.
+ *
+ * @state contains NTFS attribute specific flags describing this attribute
+ * structure. See ntfs_attr_state_bits above.
+ *
+ * @crypto points to private structure of crypto code. You should not access
+ * fields of this structure, but you can check whether it is NULL or not. If it
+ * is not NULL, then we successfully obtained FEK (File Encryption Key) and
+ * ntfs_attr_p{read,write} calls probably would succeed. If it is NULL, then we
+ * failed to obtain FEK (do not have corresponding PFX file, wrong password,
+ * etc..) or library was compiled without crypto support. Attribute size can be
+ * changed without knowledge of FEK, so you can use ntfs_attr_truncate in any
+ * case.
+ * NOTE: This field valid only if attribute encrypted (eg., NAttrEncrypted
+ * returns non-zero).
+ */
+struct _ntfs_attr {
+ runlist_element *rl;
+ ntfs_inode *ni;
+ ATTR_TYPES type;
+ ntfschar *name;
+ u32 name_len;
+ unsigned long state;
+ s64 allocated_size;
+ s64 data_size;
+ s64 initialized_size;
+ s64 compressed_size;
+ u32 compression_block_size;
+ u8 compression_block_size_bits;
+ u8 compression_block_clusters;
+ ntfs_crypto_attr *crypto;
+ struct list_head list_entry;
+ int nr_references;
+};
+
+/**
+ * enum ntfs_attr_state_bits - bits for the state field in the ntfs_attr
+ * structure
+ */
+typedef enum {
+ NA_Initialized, /* 1: structure is initialized. */
+ NA_NonResident, /* 1: Attribute is not resident. */
+} ntfs_attr_state_bits;
+
+#define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state)
+#define set_nattr_flag(na, flag) set_bit(NA_##flag, (na)->state)
+#define clear_nattr_flag(na, flag) clear_bit(NA_##flag, (na)->state)
+
+#define NAttrInitialized(na) test_nattr_flag(na, Initialized)
+#define NAttrSetInitialized(na) set_nattr_flag(na, Initialized)
+#define NAttrClearInitialized(na) clear_nattr_flag(na, Initialized)
+
+#define NAttrNonResident(na) test_nattr_flag(na, NonResident)
+#define NAttrSetNonResident(na) set_nattr_flag(na, NonResident)
+#define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident)
+
+#define GenNAttrIno(func_name,flag) \
+static inline int NAttr##func_name(ntfs_attr *na) \
+{ \
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) \
+ return (na->ni->flags & FILE_ATTR_##flag) ? 1 : 0; \
+ return 0; \
+} \
+static inline void NAttrSet##func_name(ntfs_attr *na) \
+{ \
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) \
+ na->ni->flags |= FILE_ATTR_##flag; \
+ else \
+ ntfs_log_trace("BUG! Should be called only for " \
+ "unnamed data attribute.\n"); \
+} \
+static inline void NAttrClear##func_name(ntfs_attr *na) \
+{ \
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) \
+ na->ni->flags &= ~FILE_ATTR_##flag; \
+}
+
+GenNAttrIno(Compressed, COMPRESSED)
+GenNAttrIno(Encrypted, ENCRYPTED)
+GenNAttrIno(Sparse, SPARSE_FILE)
+
+#ifndef __sun
+/**
+ * union attr_val - Union of all known attribute values
+ *
+ * For convenience. Used in the attr structure.
+ */
+typedef union {
+ u8 _default; /* Unnamed u8 to serve as default when just using
+ a_val without specifying any of the below. */
+ STANDARD_INFORMATION std_inf;
+ ATTR_LIST_ENTRY al_entry;
+ FILE_NAME_ATTR filename;
+ OBJECT_ID_ATTR obj_id;
+ SECURITY_DESCRIPTOR_ATTR sec_desc;
+ VOLUME_NAME vol_name;
+ VOLUME_INFORMATION vol_inf;
+ DATA_ATTR data;
+ INDEX_ROOT index_root;
+ INDEX_BLOCK index_blk;
+ BITMAP_ATTR bmp;
+ REPARSE_POINT reparse;
+ EA_INFORMATION ea_inf;
+ EA_ATTR ea;
+ PROPERTY_SET property_set;
+ LOGGED_UTILITY_STREAM logged_util_stream;
+ EFS_ATTR_HEADER efs;
+} attr_val;
+#endif /* __sun */
+
+extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
+ const BOOL compressed, const BOOL encrypted, const BOOL sparse,
+ const s64 allocated_size, const s64 data_size,
+ const s64 initialized_size, const s64 compressed_size,
+ const u8 compression_unit);
+
+extern ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
+ ntfschar *name, u32 name_len);
+extern void ntfs_attr_close(ntfs_attr *na);
+
+extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
+ void *b);
+extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
+ const void *b);
+
+extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
+ ntfschar *name, u32 name_len, s64 *data_size);
+
+extern s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos,
+ const s64 bk_cnt, const u32 bk_size, void *dst);
+extern s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos,
+ s64 bk_cnt, const u32 bk_size, void *src);
+
+extern int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn);
+extern int ntfs_attr_map_runlist_range(ntfs_attr *na, VCN from_vcn, VCN to_vcn);
+extern int ntfs_attr_map_whole_runlist(ntfs_attr *na);
+
+extern LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn);
+extern runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn);
+
+extern int ntfs_attr_size_bounds_check(const ntfs_volume *vol,
+ const ATTR_TYPES type, const s64 size);
+extern int ntfs_attr_can_be_non_resident(const ntfs_volume *vol,
+ const ATTR_TYPES type);
+extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
+ const ATTR_TYPES type);
+
+extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size);
+
+extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, u8 *val, u32 size,
+ ATTR_FLAGS flags);
+extern int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
+ ATTR_FLAGS flags);
+extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
+
+extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, u8 *val, s64 size);
+extern int ntfs_attr_rm(ntfs_attr *na);
+
+extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
+
+extern int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
+ const u32 new_size);
+
+extern int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni);
+extern int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra);
+
+extern int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn);
+
+extern int __ntfs_attr_truncate(ntfs_attr *na, const s64 newsize, BOOL sparse);
+extern int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize);
+
+extern int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type,
+ ntfschar *name, u32 name_len);
+
+static __inline__ ntfschar *ntfs_attr_get_name(ATTR_RECORD *attr)
+{
+ return (ntfschar*)((u8*)attr + le16_to_cpu(attr->name_offset));
+}
+
+// FIXME / TODO: Above here the file is cleaned up. (AIA)
+/**
+ * get_attribute_value_length - return the length of the value of an attribute
+ * @a: pointer to a buffer containing the attribute record
+ *
+ * Return the byte size of the attribute value of the attribute @a (as it
+ * would be after eventual decompression and filling in of holes if sparse).
+ * If we return 0, check errno. If errno is 0 the actual length was 0,
+ * otherwise errno describes the error.
+ *
+ * FIXME: Describe possible errnos.
+ */
+s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a);
+
+/**
+ * get_attribute_value - return the attribute value of an attribute
+ * @vol: volume on which the attribute is present
+ * @a: attribute to get the value of
+ * @b: destination buffer for the attribute value
+ *
+ * Make a copy of the attribute value of the attribute @a into the destination
+ * buffer @b. Note, that the size of @b has to be at least equal to the value
+ * returned by get_attribute_value_length(@a).
+ *
+ * Return number of bytes copied. If this is zero check errno. If errno is 0
+ * then nothing was read due to a zero-length attribute value, otherwise
+ * errno describes the error.
+ */
+s64 ntfs_get_attribute_value(const ntfs_volume *vol, const ATTR_RECORD *a,
+ u8 *b);
+
+#endif /* defined _NTFS_ATTRIB_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/attrlist.h b/usr/src/lib/libntfs/common/include/ntfs/attrlist.h
new file mode 100644
index 0000000000..ff450d09bf
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/attrlist.h
@@ -0,0 +1,51 @@
+/*
+ * attrlist.h - Exports for attribute list attribute handling. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ * Copyright (c) 2004 Yura Pakhuchiy
+ *
+ * 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 _NTFS_ATTRLIST_H
+#define _NTFS_ATTRLIST_H
+
+#include "attrib.h"
+
+extern int ntfs_attrlist_need(ntfs_inode *ni);
+
+extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr);
+extern int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx);
+
+/**
+ * ntfs_attrlist_mark_dirty - set the attribute list dirty
+ * @ni: ntfs inode which base inode contain dirty attribute list
+ *
+ * Set the attribute list dirty so it is written out later (at the latest at
+ * ntfs_inode_close() time).
+ *
+ * This function cannot fail.
+ */
+static __inline__ void ntfs_attrlist_mark_dirty(ntfs_inode *ni)
+{
+ if (ni->nr_extents == -1)
+ NInoAttrListSetDirty(ni->u.base_ni);
+ else
+ NInoAttrListSetDirty(ni);
+}
+
+#endif /* defined _NTFS_ATTRLIST_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/bitmap.h b/usr/src/lib/libntfs/common/include/ntfs/bitmap.h
new file mode 100644
index 0000000000..f6d16f1923
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/bitmap.h
@@ -0,0 +1,134 @@
+/*
+ * bitmap.h - Exports for bitmap handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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 _NTFS_BITMAP_H
+#define _NTFS_BITMAP_H
+
+#include "types.h"
+#include "attrib.h"
+
+/*
+ * NOTES:
+ *
+ * - Operations are 8-bit only to ensure the functions work both on little
+ * and big endian machines! So don't make them 32-bit ops!
+ * - bitmap starts at bit = 0 and ends at bit = bitmap size - 1.
+ * - _Caller_ has to make sure that the bit to operate on is less than the
+ * size of the bitmap.
+ */
+
+/**
+ * ntfs_bit_set - set a bit in a field of bits
+ * @bitmap: field of bits
+ * @bit: bit to set
+ * @new_value: value to set bit to (0 or 1)
+ *
+ * Set the bit @bit in the @bitmap to @new_value. Ignore all errors.
+ */
+static __inline__ void ntfs_bit_set(u8 *bitmap, const u64 bit,
+ const u8 new_value)
+{
+ if (!bitmap || new_value > 1)
+ return;
+ if (!new_value)
+ bitmap[bit >> 3] &= ~(1 << (bit & 7));
+ else
+ bitmap[bit >> 3] |= (1 << (bit & 7));
+}
+
+/**
+ * ntfs_bit_get - get value of a bit in a field of bits
+ * @bitmap: field of bits
+ * @bit: bit to get
+ *
+ * Get and return the value of the bit @bit in @bitmap (0 or 1).
+ * Return -1 on error.
+ */
+static __inline__ char ntfs_bit_get(const u8 *bitmap, const u64 bit)
+{
+ if (!bitmap)
+ return -1;
+ return (bitmap[bit >> 3] >> (bit & 7)) & 1;
+}
+
+static __inline__ void ntfs_bit_change(u8 *bitmap, const u64 bit)
+{
+ if (!bitmap)
+ return;
+ bitmap[bit >> 3] ^= 1 << (bit & 7);
+}
+
+/**
+ * ntfs_bit_get_and_set - get value of a bit in a field of bits and set it
+ * @bitmap: field of bits
+ * @bit: bit to get/set
+ * @new_value: value to set bit to (0 or 1)
+ *
+ * Return the value of the bit @bit and set it to @new_value (0 or 1).
+ * Return -1 on error.
+ */
+static __inline__ char ntfs_bit_get_and_set(u8 *bitmap, const u64 bit,
+ const u8 new_value)
+{
+ register u8 old_bit, shift;
+
+ if (!bitmap || new_value > 1)
+ return -1;
+ shift = bit & 7;
+ old_bit = (bitmap[bit >> 3] >> shift) & 1;
+ if (new_value != old_bit)
+ bitmap[bit >> 3] ^= 1 << shift;
+ return old_bit;
+}
+
+extern int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count);
+extern int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count);
+
+/**
+ * ntfs_bitmap_set_bit - set a bit in a bitmap
+ * @na: attribute containing the bitmap
+ * @bit: bit to set
+ *
+ * Set the @bit in the bitmap described by the attribute @na.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+static __inline__ int ntfs_bitmap_set_bit(ntfs_attr *na, s64 bit)
+{
+ return ntfs_bitmap_set_run(na, bit, 1);
+}
+
+/**
+ * ntfs_bitmap_clear_bit - clear a bit in a bitmap
+ * @na: attribute containing the bitmap
+ * @bit: bit to clear
+ *
+ * Clear @bit in the bitmap described by the attribute @na.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+static __inline__ int ntfs_bitmap_clear_bit(ntfs_attr *na, s64 bit)
+{
+ return ntfs_bitmap_clear_run(na, bit, 1);
+}
+
+#endif /* defined _NTFS_BITMAP_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/bootsect.h b/usr/src/lib/libntfs/common/include/ntfs/bootsect.h
new file mode 100644
index 0000000000..af0da7a945
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/bootsect.h
@@ -0,0 +1,47 @@
+/*
+ * bootsect.h - Exports for bootsector record handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2000-2002 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 _NTFS_BOOTSECT_H
+#define _NTFS_BOOTSECT_H
+
+#include "types.h"
+#include "volume.h"
+#include "layout.h"
+
+/**
+ * is_boot_sector_ntfs - check a boot sector for describing an ntfs volume
+ * @b: buffer containing the boot sector
+ * @silent: if 1 don't display progress information
+ *
+ * This function checks the boot sector in @b for describing a valid ntfs
+ * volume. Return TRUE if @b is a valid NTFS boot sector or FALSE otherwise.
+ * If silent is FALSE, progress output will be output to stdout. If silent is
+ * TRUE no output to stdout will occur. Errors/warnings to stderr will occur
+ * disregarding the value of silent (but only if configure was run with
+ * --enable-debug).
+ */
+extern BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b, BOOL silent);
+extern int ntfs_boot_sector_parse(ntfs_volume *vol,
+ const NTFS_BOOT_SECTOR *bs);
+
+#endif /* defined _NTFS_BOOTSECT_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/collate.h b/usr/src/lib/libntfs/common/include/ntfs/collate.h
new file mode 100644
index 0000000000..1c00ebd77e
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/collate.h
@@ -0,0 +1,38 @@
+/*
+ * collate.h - Defines for NTFS collation handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ * Copyright (c) 2005 Yura Pakhuchiy
+ *
+ * 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 _NTFS_COLLATE_H
+#define _NTFS_COLLATE_H
+
+#include "types.h"
+#include "volume.h"
+
+#define NTFS_COLLATION_ERROR (-2)
+
+extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr);
+
+extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
+ const void *data1, size_t data1_len,
+ const void *data2, size_t data2_len);
+
+#endif /* _NTFS_COLLATE_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/compat.h b/usr/src/lib/libntfs/common/include/ntfs/compat.h
new file mode 100644
index 0000000000..7c1f5f11fe
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/compat.h
@@ -0,0 +1,58 @@
+/*
+ * compat.h - Tweaks for Windows compatibility.
+ *
+ * Copyright (c) 2002 Richard Russon
+ * Copyright (c) 2002-2004 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 _NTFS_COMPAT_H
+#define _NTFS_COMPAT_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WINDOWS
+
+#ifndef HAVE_FFS
+#define HAVE_FFS
+extern int ffs(int i);
+#endif /* HAVE_FFS */
+
+#define HAVE_STDIO_H /* mimic config.h */
+#define HAVE_STDARG_H
+
+#define atoll _atoi64
+#define fdatasync commit
+#define __inline__ inline
+#define __attribute__(X) /*nothing*/
+
+#else /* !defined WINDOWS */
+
+#ifndef O_BINARY
+#define O_BINARY 0 /* unix is binary by default */
+#endif
+
+#endif /* defined WINDOWS */
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#endif /* defined _NTFS_COMPAT_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/compress.h b/usr/src/lib/libntfs/common/include/ntfs/compress.h
new file mode 100644
index 0000000000..93df37afc8
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/compress.h
@@ -0,0 +1,33 @@
+/*
+ * compress.h - Exports for compressed attribute handling. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2004 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 _NTFS_COMPRESS_H
+#define _NTFS_COMPRESS_H
+
+#include "types.h"
+#include "attrib.h"
+
+extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
+ void *b);
+
+#endif /* defined _NTFS_COMPRESS_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/config.h b/usr/src/lib/libntfs/common/include/ntfs/config.h
new file mode 100644
index 0000000000..4c6c18efff
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/config.h
@@ -0,0 +1,313 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define this to 1 if you want to enable support of encrypted files in
+ libntfs and utilities. */
+/* #undef ENABLE_CRYPTO */
+
+/* Define this to 1 if you want to enable generation of DCE compliant UUIDs.
+ */
+#define ENABLE_UUID 1
+
+/* Define to 1 if you have the `atexit' function. */
+#define HAVE_ATEXIT 1
+
+/* Define to 1 if you have the `basename' function. */
+#define HAVE_BASENAME 1
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+/* #undef HAVE_BYTESWAP_H */
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if you have the `dup2' function. */
+#define HAVE_DUP2 1
+
+/* Define to 1 if you have the <endian.h> header file. */
+/* #undef HAVE_ENDIAN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdatasync' function. */
+#define HAVE_FDATASYNC 1
+
+/* Define to 1 if you have the <features.h> header file. */
+/* #undef HAVE_FEATURES_H */
+
+/* Define to 1 if you have the `getmntent' function. */
+#define HAVE_GETMNTENT
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long' function. */
+#define HAVE_GETOPT_LONG 1
+
+/* Define to 1 if you have the `hasmntopt' function. */
+#define HAVE_HASMNTOPT 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the <libintl.h> header file. */
+#define HAVE_LIBINTL_H 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <linux/fd.h> header file. */
+/* #undef HAVE_LINUX_FD_H */
+
+/* Define to 1 if you have the <linux/hdreg.h> header file. */
+/* #undef HAVE_LINUX_HDREG_H */
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+/* #undef HAVE_LINUX_MAJOR_H */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the <machine/endian.h> header file. */
+/* #undef HAVE_MACHINE_ENDIAN_H */
+
+/* Define to 1 if mbrtowc and mbstate_t are properly declared. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the `mbsinit' function. */
+#define HAVE_MBSINIT 1
+
+/* Define to 1 if you have the `memmove' function. */
+/* #undef HAVE_MEMMOVE */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+/* #undef HAVE_MEMSET */
+
+/* Define to 1 if you have the <mntent.h> header file. */
+/* #undef HAVE_MNTENT_H */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `realpath' function. */
+#define HAVE_REALPATH 1
+
+/* Define to 1 if you have the `regcomp' function. */
+#define HAVE_REGCOMP 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setxattr' function. */
+/* #undef HAVE_SETXATTR */
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+ zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strchr' function. */
+/* #undef HAVE_STRCHR */
+
+/* Define to 1 if you have the `strdup' function. */
+/* #undef HAVE_STRDUP */
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strftime' function. */
+/* #undef HAVE_STRFTIME */
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if you have the `strtol' function. */
+#define HAVE_STRTOL 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if `st_blocks' is member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLOCKS 1
+
+/* Define to 1 if `st_rdev' is member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_RDEV 1
+
+/* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use
+ `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */
+#define HAVE_ST_BLOCKS 1
+
+/* Define to 1 if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/byteorder.h> header file. */
+#define HAVE_SYS_BYTEORDER_H 1
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#define HAVE_SYS_VFS_H 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
+#define HAVE_UTIME_NULL 1
+
+/* Define to 1 if you have the `vprintf' function. */
+/* #undef HAVE_VPRINTF */
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if the system has the type `_Bool'. */
+#define HAVE__BOOL 1
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+ slash. */
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define this if you do not want the NTFS library to provide default device
+ io operations. This means that you cannot use ntfs_mount() but have to use
+ ntfs_device_mount() and provide your own device operations. */
+/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */
+
+/* Name of package */
+#define PACKAGE "ntfsprogs"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "linux-ntfs-dev@lists.sourceforge.net"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "ntfsprogs"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "ntfsprogs 2.0.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "ntfsprogs"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.0.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "2.0.0"
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to 1 if your processor stores words with the least significant byte
+ first (like Intel and VAX, unlike Motorola and SPARC). */
+#define WORDS_LITTLEENDIAN 1
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/crypto.h b/usr/src/lib/libntfs/common/include/ntfs/crypto.h
new file mode 100644
index 0000000000..a4b72435c1
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/crypto.h
@@ -0,0 +1,46 @@
+/**
+ * crypto.h - Exports for dealing with encrypted files. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2007 Yura Pakhuchiy
+ *
+ * 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
+ * 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 _NTFS_CRYPTO_H
+#define _NTFS_CRYPTO_H
+
+extern ntfschar NTFS_EFS[5];
+
+/*
+ * This is our Big Secret (TM) structure, so do not allow anyone even read it
+ * values. ;-) In fact, it is private because exist only in libntfs version
+ * compiled with cryptography support, so users can not depend on it.
+ */
+typedef struct _ntfs_crypto_attr ntfs_crypto_attr;
+
+/*
+ * These functions should not be used directly. They are called for encrypted
+ * attributes from corresponding functions without _crypto_ part.
+ */
+
+extern int ntfs_crypto_attr_open(ntfs_attr *na);
+extern void ntfs_crypto_attr_close(ntfs_attr *na);
+
+extern s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
+ void *b);
+
+#endif /* _NTFS_CRYPTO_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/debug.h b/usr/src/lib/libntfs/common/include/ntfs/debug.h
new file mode 100644
index 0000000000..0dd411420b
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/debug.h
@@ -0,0 +1,63 @@
+/*
+ * debug.h - Debugging output functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2004 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 _NTFS_DEBUG_H
+#define _NTFS_DEBUG_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "logging.h"
+
+struct _runlist_element;
+
+#ifndef DEBUG
+static __inline__ void ntfs_debug_runlist_dump(const struct _runlist_element *rl __attribute__((unused))) {}
+#define NTFS_ON_DEBUG(x)
+#else
+extern void ntfs_debug_runlist_dump(const struct _runlist_element *rl);
+#define NTFS_ON_DEBUG(x) (x)
+#endif
+
+#if defined(__GNUC__)
+
+#define NTFS_BUG(msg) \
+{ \
+ int ___i; \
+ ntfs_log_critical("Bug in %s(): %s\n", __FUNCTION__, msg); \
+ ntfs_log_debug("Forcing segmentation fault!"); \
+ ___i = ((int*)NULL)[1]; \
+}
+
+#else /* not __GNUC__ */
+
+#define NTFS_BUG(msg) \
+{ \
+ int ___i; \
+ ntfs_log_critical("Bug in %s(): %s\n", "unknown", msg); \
+ ntfs_log_debug("Forcing segmentation fault!"); \
+ ___i = ((int*)NULL)[1]; \
+}
+
+#endif /* __GNUC__ */
+
+#endif /* defined _NTFS_DEBUG_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/device.h b/usr/src/lib/libntfs/common/include/ntfs/device.h
new file mode 100644
index 0000000000..eeadf13e7a
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/device.h
@@ -0,0 +1,128 @@
+/*
+ * device.h - Exports for low level device io. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 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 _NTFS_DEVICE_H
+#define _NTFS_DEVICE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "device_io.h"
+#include "types.h"
+#include "support.h"
+#include "volume.h"
+
+/**
+ * enum ntfs_device_state_bits -
+ *
+ * Defined bits for the state field in the ntfs_device structure.
+ */
+typedef enum {
+ ND_Open, /* 1: Device is open. */
+ ND_ReadOnly, /* 1: Device is read-only. */
+ ND_Dirty, /* 1: Device is dirty, needs sync. */
+ ND_Block, /* 1: Device is a block device. */
+} ntfs_device_state_bits;
+
+#define test_ndev_flag(nd, flag) test_bit(ND_##flag, (nd)->d_state)
+#define set_ndev_flag(nd, flag) set_bit(ND_##flag, (nd)->d_state)
+#define clear_ndev_flag(nd, flag) clear_bit(ND_##flag, (nd)->d_state)
+
+#define NDevOpen(nd) test_ndev_flag(nd, Open)
+#define NDevSetOpen(nd) set_ndev_flag(nd, Open)
+#define NDevClearOpen(nd) clear_ndev_flag(nd, Open)
+
+#define NDevReadOnly(nd) test_ndev_flag(nd, ReadOnly)
+#define NDevSetReadOnly(nd) set_ndev_flag(nd, ReadOnly)
+#define NDevClearReadOnly(nd) clear_ndev_flag(nd, ReadOnly)
+
+#define NDevDirty(nd) test_ndev_flag(nd, Dirty)
+#define NDevSetDirty(nd) set_ndev_flag(nd, Dirty)
+#define NDevClearDirty(nd) clear_ndev_flag(nd, Dirty)
+
+#define NDevBlock(nd) test_ndev_flag(nd, Block)
+#define NDevSetBlock(nd) set_ndev_flag(nd, Block)
+#define NDevClearBlock(nd) clear_ndev_flag(nd, Block)
+
+/**
+ * struct ntfs_device -
+ *
+ * The ntfs device structure defining all operations needed to access the low
+ * level device underlying the ntfs volume.
+ */
+struct ntfs_device {
+ struct ntfs_device_operations *d_ops; /* Device operations. */
+ unsigned long d_state; /* State of the device. */
+ char *d_name; /* Name of device. */
+ void *d_private; /* Private data used by the
+ device operations. */
+};
+
+struct stat;
+
+/**
+ * struct ntfs_device_operations -
+ *
+ * The ntfs device operations defining all operations that can be performed on
+ * the low level device described by an ntfs device structure.
+ */
+struct ntfs_device_operations {
+ int (*open)(struct ntfs_device *dev, int flags);
+ int (*close)(struct ntfs_device *dev);
+ s64 (*seek)(struct ntfs_device *dev, s64 offset, int whence);
+ s64 (*read)(struct ntfs_device *dev, void *buf, s64 count);
+ s64 (*write)(struct ntfs_device *dev, const void *buf, s64 count);
+ s64 (*pread)(struct ntfs_device *dev, void *buf, s64 count, s64 offset);
+ s64 (*pwrite)(struct ntfs_device *dev, const void *buf, s64 count,
+ s64 offset);
+ int (*sync)(struct ntfs_device *dev);
+ int (*stat)(struct ntfs_device *dev, struct stat *buf);
+ int (*ioctl)(struct ntfs_device *dev, int request, void *argp);
+};
+
+extern struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
+ struct ntfs_device_operations *dops, void *priv_data);
+extern int ntfs_device_free(struct ntfs_device *dev);
+
+extern s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count,
+ void *b);
+extern s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
+ const void *b);
+
+extern s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
+ const u32 bksize, void *b);
+extern s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
+ const u32 bksize, void *b);
+
+extern s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn,
+ const s64 count, void *b);
+extern s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
+ const s64 count, const void *b);
+
+extern s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size);
+extern s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev);
+extern int ntfs_device_heads_get(struct ntfs_device *dev);
+extern int ntfs_device_sectors_per_track_get(struct ntfs_device *dev);
+extern int ntfs_device_sector_size_get(struct ntfs_device *dev);
+extern int ntfs_device_block_size_set(struct ntfs_device *dev, int block_size);
+
+#endif /* defined _NTFS_DEVICE_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/device_io.h b/usr/src/lib/libntfs/common/include/ntfs/device_io.h
new file mode 100644
index 0000000000..6665b68050
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/device_io.h
@@ -0,0 +1,77 @@
+/*
+ * device_io.h - Exports for default device io. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 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 _NTFS_DEVICE_IO_H
+#define _NTFS_DEVICE_IO_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
+
+#ifndef __CYGWIN32__
+
+/* Not on Cygwin; use standard Unix style low level device operations. */
+#define ntfs_device_default_io_ops ntfs_device_unix_io_ops
+
+#else /* __CYGWIN32__ */
+
+#ifndef HDIO_GETGEO
+# define HDIO_GETGEO 0x301
+/**
+ * struct hd_geometry -
+ */
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+#endif
+#ifndef BLKGETSIZE
+# define BLKGETSIZE 0x1260
+#endif
+#ifndef BLKSSZGET
+# define BLKSSZGET 0x1268
+#endif
+#ifndef BLKGETSIZE64
+# define BLKGETSIZE64 0x80041272
+#endif
+#ifndef BLKBSZSET
+# define BLKBSZSET 0x40041271
+#endif
+
+/* On Cygwin; use Win32 low level device operations. */
+#define ntfs_device_default_io_ops ntfs_device_win32_io_ops
+
+#endif /* __CYGWIN32__ */
+
+
+/* Forward declaration. */
+struct ntfs_device_operations;
+
+extern struct ntfs_device_operations ntfs_device_default_io_ops;
+
+#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
+
+#endif /* defined _NTFS_DEVICE_IO_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/dir.h b/usr/src/lib/libntfs/common/include/ntfs/dir.h
new file mode 100644
index 0000000000..5299861b81
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/dir.h
@@ -0,0 +1,112 @@
+/*
+ * dir.h - Exports for directory handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002 Anton Altaparmakov
+ * Copyright (c) 2005-2006 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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 _NTFS_DIR_H
+#define _NTFS_DIR_H
+
+#include "types.h"
+
+#define PATH_SEP '/'
+
+#ifndef MAX_PATH
+#define MAX_PATH 1024
+#endif
+
+/*
+ * We do not have these under DJGPP, so define our version that do not conflict
+ * with other S_IFs defined under DJGPP.
+ */
+#ifdef DJGPP
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#endif
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#endif
+#ifndef S_IFSOCK
+#define S_IFSOCK 0140000
+#endif
+#ifndef S_ISSOCK
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+#endif
+#endif
+
+/*
+ * The little endian Unicode strings $I30, $SII, $SDH, $O, $Q, $R
+ * as a global constant.
+ */
+extern ntfschar NTFS_INDEX_I30[5];
+extern ntfschar NTFS_INDEX_SII[5];
+extern ntfschar NTFS_INDEX_SDH[5];
+extern ntfschar NTFS_INDEX_O[3];
+extern ntfschar NTFS_INDEX_Q[3];
+extern ntfschar NTFS_INDEX_R[3];
+
+extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
+ const ntfschar *uname, const int uname_len);
+
+extern u64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent,
+ const char *pathname);
+extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
+ const char *pathname);
+
+extern ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
+ dev_t type);
+extern ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni,
+ ntfschar *name, u8 name_len, dev_t type, dev_t dev);
+extern ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni,
+ ntfschar *name, u8 name_len, ntfschar *target, u8 target_len);
+
+extern int ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name,
+ u8 name_len);
+
+extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name,
+ u8 name_len);
+
+/*
+ * File types (adapted from include <linux/fs.h>)
+ */
+#define NTFS_DT_UNKNOWN 0
+#define NTFS_DT_FIFO 1
+#define NTFS_DT_CHR 2
+#define NTFS_DT_DIR 4
+#define NTFS_DT_BLK 6
+#define NTFS_DT_REG 8
+#define NTFS_DT_LNK 10
+#define NTFS_DT_SOCK 12
+#define NTFS_DT_WHT 14
+
+/*
+ * This is the "ntfs_filldir" function type, used by ntfs_readdir() to let
+ * the caller specify what kind of dirent layout it wants to have.
+ * This allows the caller to read directories into their application or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name,
+ const int name_len, const int name_type, const s64 pos,
+ const MFT_REF mref, const unsigned dt_type);
+
+extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
+ void *dirent, ntfs_filldir_t filldir);
+
+#endif /* defined _NTFS_DIR_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/endians.h b/usr/src/lib/libntfs/common/include/ntfs/endians.h
new file mode 100644
index 0000000000..b3426df30e
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/endians.h
@@ -0,0 +1,248 @@
+/*
+ * endians.h - Definitions related to handling of byte ordering. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2005 Anton Altaparmakov
+ * Copyright (c) 2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_ENDIANS_H
+#define _NTFS_ENDIANS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+ * Notes:
+ * We define the conversion functions including typecasts since the
+ * defaults don't necessarily perform appropriate typecasts.
+ * Also, using our own functions means that we can change them if it
+ * turns out that we do need to use the unaligned access macros on
+ * architectures requiring aligned memory accesses...
+ */
+
+#ifdef HAVE_ENDIAN_H
+#include <endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/endian.h>
+#endif
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#ifdef HAVE_SYS_BYTEORDER_H
+#include <sys/byteorder.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifndef __BYTE_ORDER
+# if defined(_BYTE_ORDER)
+# define __BYTE_ORDER _BYTE_ORDER
+# define __LITTLE_ENDIAN _LITTLE_ENDIAN
+# define __BIG_ENDIAN _BIG_ENDIAN
+# elif defined(BYTE_ORDER)
+# define __BYTE_ORDER BYTE_ORDER
+# define __LITTLE_ENDIAN LITTLE_ENDIAN
+# define __BIG_ENDIAN BIG_ENDIAN
+# elif defined(__BYTE_ORDER__)
+# define __BYTE_ORDER __BYTE_ORDER__
+# define __LITTLE_ENDIAN __LITTLE_ENDIAN__
+# define __BIG_ENDIAN __BIG_ENDIAN__
+# elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) || \
+ defined(WORDS_LITTLEENDIAN)
+# define __BYTE_ORDER 1
+# define __LITTLE_ENDIAN 1
+# define __BIG_ENDIAN 0
+# elif (!defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) || \
+ defined(WORDS_BIGENDIAN)
+# define __BYTE_ORDER 0
+# define __LITTLE_ENDIAN 1
+# define __BIG_ENDIAN 0
+# else
+# error "__BYTE_ORDER is not defined."
+# endif
+#endif
+
+#define __ntfs_bswap_constant_16(x) \
+ (u16)((((u16)(x) & 0xff00) >> 8) | \
+ (((u16)(x) & 0x00ff) << 8))
+
+#define __ntfs_bswap_constant_32(x) \
+ (u32)((((u32)(x) & 0xff000000u) >> 24) | \
+ (((u32)(x) & 0x00ff0000u) >> 8) | \
+ (((u32)(x) & 0x0000ff00u) << 8) | \
+ (((u32)(x) & 0x000000ffu) << 24))
+
+#define __ntfs_bswap_constant_64(x) \
+ (u64)((((u64)(x) & 0xff00000000000000ull) >> 56) | \
+ (((u64)(x) & 0x00ff000000000000ull) >> 40) | \
+ (((u64)(x) & 0x0000ff0000000000ull) >> 24) | \
+ (((u64)(x) & 0x000000ff00000000ull) >> 8) | \
+ (((u64)(x) & 0x00000000ff000000ull) << 8) | \
+ (((u64)(x) & 0x0000000000ff0000ull) << 24) | \
+ (((u64)(x) & 0x000000000000ff00ull) << 40) | \
+ (((u64)(x) & 0x00000000000000ffull) << 56))
+
+#ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+#else
+# define bswap_16(x) __ntfs_bswap_constant_16(x)
+# define bswap_32(x) __ntfs_bswap_constant_32(x)
+# define bswap_64(x) __ntfs_bswap_constant_64(x)
+#endif
+
+#if defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN)
+
+#define __le16_to_cpu(x) ((__force u16)(x))
+#define __le32_to_cpu(x) ((__force u32)(x))
+#define __le64_to_cpu(x) ((__force u64)(x))
+
+#define __cpu_to_le16(x) ((__force le16)(x))
+#define __cpu_to_le32(x) ((__force le32)(x))
+#define __cpu_to_le64(x) ((__force le64)(x))
+
+#define __constant_le16_to_cpu(x) ((__force u16)(x))
+#define __constant_le32_to_cpu(x) ((__force u32)(x))
+#define __constant_le64_to_cpu(x) ((__force u64)(x))
+
+#define __constant_cpu_to_le16(x) ((__force le16)(x))
+#define __constant_cpu_to_le32(x) ((__force le32)(x))
+#define __constant_cpu_to_le64(x) ((__force le64)(x))
+
+#elif defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN)
+
+#define __le16_to_cpu(x) bswap_16((__force u16)(x))
+#define __le32_to_cpu(x) bswap_32((__force u16)(x))
+#define __le64_to_cpu(x) bswap_64((__force u16)(x))
+
+#define __cpu_to_le16(x) (__force le16)bswap_16((__force u16)(x))
+#define __cpu_to_le32(x) (__force le32)bswap_32((__force u32)(x))
+#define __cpu_to_le64(x) (__force le64)bswap_64((__force u64)(x))
+
+#define __constant_le16_to_cpu(x) __ntfs_bswap_constant_16((__force u16)(x))
+#define __constant_le32_to_cpu(x) __ntfs_bswap_constant_32((__force u32)(x))
+#define __constant_le64_to_cpu(x) __ntfs_bswap_constant_64((__force u64)(x))
+
+#define __constant_cpu_to_le16(x) \
+ (__force le16)__ntfs_bswap_constant_16((__force u16)(x))
+#define __constant_cpu_to_le32(x) \
+ (__force le32)__ntfs_bswap_constant_32((__force u32)(x))
+#define __constant_cpu_to_le64(x) \
+ (__force le64)__ntfs_bswap_constant_64((__force u64)(x))
+
+#else
+
+#error "You must define __BYTE_ORDER to be __LITTLE_ENDIAN or __BIG_ENDIAN."
+
+#endif
+
+/* Unsigned from LE to CPU conversion. */
+
+#define le16_to_cpu(x) (u16)__le16_to_cpu((le16)(x))
+#define le32_to_cpu(x) (u32)__le32_to_cpu((le32)(x))
+#define le64_to_cpu(x) (u64)__le64_to_cpu((le64)(x))
+
+#define le16_to_cpup(x) (u16)__le16_to_cpu(*(const le16*)(x))
+#define le32_to_cpup(x) (u32)__le32_to_cpu(*(const le32*)(x))
+#define le64_to_cpup(x) (u64)__le64_to_cpu(*(const le64*)(x))
+
+/* Signed from LE to CPU conversion. */
+
+#define sle16_to_cpu(x) (s16)__le16_to_cpu((sle16)(x))
+#define sle32_to_cpu(x) (s32)__le32_to_cpu((sle32)(x))
+#define sle64_to_cpu(x) (s64)__le64_to_cpu((sle64)(x))
+
+#define sle16_to_cpup(x) (s16)__le16_to_cpu(*(const sle16*)(x))
+#define sle32_to_cpup(x) (s32)__le32_to_cpu(*(const sle32*)(x))
+#define sle64_to_cpup(x) (s64)__le64_to_cpu(*(const sle64*)(x))
+
+/* Unsigned from CPU to LE conversion. */
+
+#define cpu_to_le16(x) (le16)__cpu_to_le16((u16)(x))
+#define cpu_to_le32(x) (le32)__cpu_to_le32((u32)(x))
+#define cpu_to_le64(x) (le64)__cpu_to_le64((u64)(x))
+
+#define cpu_to_le16p(x) (le16)__cpu_to_le16(*(const u16*)(x))
+#define cpu_to_le32p(x) (le32)__cpu_to_le32(*(const u32*)(x))
+#define cpu_to_le64p(x) (le64)__cpu_to_le64(*(const u64*)(x))
+
+/* Signed from CPU to LE conversion. */
+
+#define cpu_to_sle16(x) (__force sle16)__cpu_to_le16((s16)(x))
+#define cpu_to_sle32(x) (__force sle32)__cpu_to_le32((s32)(x))
+#define cpu_to_sle64(x) (__force sle64)__cpu_to_le64((s64)(x))
+
+#define cpu_to_sle16p(x) (__force sle16)__cpu_to_le16(*(const s16*)(x))
+#define cpu_to_sle32p(x) (__force sle32)__cpu_to_le32(*(const s32*)(x))
+#define cpu_to_sle64p(x) (__force sle64)__cpu_to_le64(*(const s64*)(x))
+
+/* Constant endianness conversion defines. */
+
+#define const_le16_to_cpu(x) (u16)__constant_le16_to_cpu((le16)(x))
+#define const_le32_to_cpu(x) (u32)__constant_le32_to_cpu((le32)(x))
+#define const_le64_to_cpu(x) (u64)__constant_le64_to_cpu((le64)(x))
+
+#define const_cpu_to_le16(x) (le16)__constant_cpu_to_le16((u16)(x))
+#define const_cpu_to_le32(x) (le32)__constant_cpu_to_le32((u32)(x))
+#define const_cpu_to_le64(x) (le64)__constant_cpu_to_le64((u64)(x))
+
+#ifdef __CHECKER__
+static void ntfs_endian_self_test(void)
+{
+ /* Should not generate warnings. */
+ (le16)cpu_to_le16((u16)1);
+ (le32)cpu_to_le32((u32)1);
+ (le64)cpu_to_le64((u64)1);
+ (sle16)cpu_to_sle16((s16)1);
+ (sle32)cpu_to_sle32((s32)1);
+ (sle64)cpu_to_sle64((s64)1);
+ (u16)le16_to_cpu((__force le16)1);
+ (u32)le32_to_cpu((__force le32)1);
+ (u64)le64_to_cpu((__force le64)1);
+ (s16)sle16_to_cpu((__force sle16)1);
+ (s32)sle32_to_cpu((__force sle32)1);
+ (s64)sle64_to_cpu((__force sle64)1);
+ (le16)const_cpu_to_le16((u16)1);
+ (le32)const_cpu_to_le32((u32)1);
+ (le64)const_cpu_to_le64((u64)1);
+ (u16)const_le16_to_cpu((__force le16)1);
+ (u32)const_le32_to_cpu((__force le32)1);
+ (u64)const_le64_to_cpu((__force le64)1);
+
+ /*
+ * TODO: Need some how to test that warnings are actually generated,
+ * but without flooding output with them and vice-versa print warning
+ * in case if some one warning is not triggered, but should. (Yura)
+ *
+ * I think it can only be done in a ./configure like script / shell
+ * script that will compile known good and known bad code and pipe the
+ * output from sparse to a file, then grep the file for the wanted
+ * warnings/lack thereof and then it would say "Tests: PASS " or
+ * "Tests: FAILED" or whatever. And you can then hook that into a
+ * "make test" make target or similar so it is only done when one
+ * wants to do it... (Anton)
+ *
+ * Also we can look on sparse self test script. (Yura)
+ */
+}
+#endif
+
+#endif /* defined _NTFS_ENDIANS_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-method.h b/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-method.h
new file mode 100644
index 0000000000..d83b86ff4a
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-method.h
@@ -0,0 +1,43 @@
+/*
+ * gnome-vfs-method.h - Export for Gnome-VFS init/shutdown implementation of
+ * interface to libntfs. Par of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * Copyright (c) 2000-2004 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 _NTFS_GNOME_VFS_METHOD_H
+#define _NTFS_GNOME_VFS_METHOD_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libgnomevfs/gnome-vfs-method.h>
+
+G_BEGIN_DECLS
+
+GnomeVFSMethod *libntfs_gnomevfs_method_init(const gchar *method_name,
+ const gchar *args);
+
+void libntfs_gnomevfs_method_shutdown(void);
+
+G_END_DECLS
+
+#endif /* _NTFS_GNOME_VFS_METHOD_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-module.h b/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-module.h
new file mode 100644
index 0000000000..316710445f
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/gnome-vfs-module.h
@@ -0,0 +1,42 @@
+/*
+ * gnome-vfs-module.h - Exports for Gnome-VFS init/shutdown implementation of
+ * interface to libntfs. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * Copyright (c) 2000-2004 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 _NTFS_GNOME_VFS_MODULE_H
+#define _NTFS_GNOME_VFS_MODULE_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+G_BEGIN_DECLS
+
+G_LOCK_EXTERN(libntfs);
+
+#define libntfs_newn(objp, n) ((objp) = (typeof(objp))g_new(typeof(*(objp)), (n)))
+#define libntfs_new(objp) (libntfs_newn((objp), 1))
+#define LIBNTFS_MEMZERO(objp) (memset((objp), 0, sizeof(*(objp))))
+
+G_END_DECLS
+
+#endif /* _NTFS_GNOME_VFS_MODULE_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/index.h b/usr/src/lib/libntfs/common/include/ntfs/index.h
new file mode 100644
index 0000000000..75e23e2a4e
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/index.h
@@ -0,0 +1,131 @@
+/*
+ * index.h - Defines for NTFS index handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ * Copyright (c) 2005-2006 Yura Pakhuchiy
+ * Copyright (c) 2006 Szabolcs Szakacsits
+ *
+ * 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 _NTFS_INDEX_H
+#define _NTFS_INDEX_H
+
+#include "attrib.h"
+#include "types.h"
+#include "layout.h"
+#include "inode.h"
+#include "mft.h"
+
+#define VCN_INDEX_ROOT_PARENT ((VCN)-2)
+
+#define MAX_PARENT_VCN 32
+
+/**
+ * struct ntfs_index_context -
+ * @ni: inode containing the @entry described by this context
+ * @name: name of the index described by this context
+ * @name_len: length of the index name
+ * @entry: index entry (points into @ir or @ib)
+ * @data: index entry data (points into @entry)
+ * @data_len: length in bytes of @data
+ * @cr:
+ * @is_in_root: TRUE if @entry is in @ir or FALSE if it is in @ib
+ * @ir: index root if @is_in_root or NULL otherwise
+ * @actx: attribute search context if in root or NULL otherwise
+ * @ia_na: opened INDEX_ALLOCATION attribute
+ * @ib: index block if @is_in_root is FALSE or NULL otherwise
+ * @ib_vcn: VCN from which @ib where read from
+ * @ib_dirty: TRUE if index block was changed
+ * @parent_pos: parent entries' positions in the index block
+ * @parent_vcn: entry's parent nodes or VCN_INDEX_ROOT_PARENT for root
+ * @max_depth: number of the parent nodes
+ * @pindex: maximum it's the number of the parent nodes
+ * @block_size: index block size
+ * @vcn_size_bits: VCN size bits for this index block
+ *
+ * @ni is the inode this context belongs to.
+ *
+ * @entry is the index entry described by this context. @data and @data_len
+ * are the index entry data and its length in bytes, respectively. @data
+ * simply points into @entry. This is probably what the user is interested in.
+ *
+ * If @is_in_root is TRUE, @entry is in the index root attribute @ir described
+ * by the attribute search context @actx and inode @ni. @ib, @ib_vcn and
+ * @ib_dirty are undefined in this case.
+ *
+ * If @is_in_root is FALSE, @entry is in the index allocation attribute and @ib
+ * and @ib_vcn point to the index allocation block and VCN where it's placed,
+ * respectively. @ir and @actx are NULL in this case. @ia_na is opened
+ * INDEX_ALLOCATION attribute. @ib_dirty is TRUE if index block was changed and
+ * FALSE otherwise.
+ *
+ * To obtain a context call ntfs_index_ctx_get().
+ *
+ * When finished with the @entry and its @data, call ntfs_index_ctx_put() to
+ * free the context and other associated resources.
+ *
+ * If the index entry was modified, call ntfs_index_entry_mark_dirty() before
+ * the call to ntfs_index_ctx_put() to ensure that the changes are written
+ * to disk.
+ */
+typedef struct {
+ ntfs_inode *ni;
+ ntfschar *name;
+ u32 name_len;
+ INDEX_ENTRY *entry;
+ void *data;
+ u16 data_len;
+ COLLATION_RULES cr;
+ BOOL is_in_root;
+ INDEX_ROOT *ir;
+ ntfs_attr_search_ctx *actx;
+ ntfs_attr *ia_na;
+ INDEX_BLOCK *ib;
+ VCN ib_vcn;
+ BOOL ib_dirty;
+ int parent_pos[MAX_PARENT_VCN];
+ VCN parent_vcn[MAX_PARENT_VCN];
+ int max_depth;
+ int pindex;
+ u32 block_size;
+ u8 vcn_size_bits;
+} ntfs_index_context;
+
+extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
+ ntfschar *name, u32 name_len);
+extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
+extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
+
+extern int ntfs_index_lookup(const void *key, const int key_len,
+ ntfs_index_context *ictx);
+
+extern int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn,
+ MFT_REF mref);
+extern int ntfs_index_rm(ntfs_index_context *ictx);
+
+extern INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr);
+
+extern VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie);
+
+extern char *ntfs_ie_filename_get(INDEX_ENTRY *ie);
+extern void ntfs_ie_filename_dump(INDEX_ENTRY *ie);
+extern void ntfs_ih_filename_dump(INDEX_HEADER *ih);
+
+extern void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx);
+
+#endif /* _NTFS_INDEX_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/inode.h b/usr/src/lib/libntfs/common/include/ntfs/inode.h
new file mode 100644
index 0000000000..90c2113116
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/inode.h
@@ -0,0 +1,215 @@
+/*
+ * inode.h - Defines for NTFS inode handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2001,2002 Anton Altaparmakov
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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 _NTFS_INODE_H
+#define _NTFS_INODE_H
+
+/* Forward declaration */
+typedef struct _ntfs_inode ntfs_inode;
+
+#include "list.h"
+#include "types.h"
+#include "layout.h"
+#include "support.h"
+#include "volume.h"
+
+/**
+ * enum ntfs_inode_state_bits -
+ *
+ * Defined bits for the state field in the ntfs_inode structure.
+ * (f) = files only, (d) = directories only
+ */
+typedef enum {
+ NI_Dirty, /* 1: Mft record needs to be written to disk. */
+
+ /* Below fields only make sense for base inodes. */
+ NI_AttrList, /* 1: Mft record contains an attribute list. */
+ NI_AttrListDirty, /* 1: Attribute list needs to be written to the
+ mft record and then to disk. */
+ NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated
+ in the index. */
+} ntfs_inode_state_bits;
+
+#define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state)
+#define set_nino_flag(ni, flag) set_bit(NI_##flag, (ni)->state)
+#define clear_nino_flag(ni, flag) clear_bit(NI_##flag, (ni)->state)
+
+#define test_and_set_nino_flag(ni, flag) \
+ test_and_set_bit(NI_##flag, (ni)->state)
+#define test_and_clear_nino_flag(ni, flag) \
+ test_and_clear_bit(NI_##flag, (ni)->state)
+
+#define NInoDirty(ni) test_nino_flag(ni, Dirty)
+#define NInoSetDirty(ni) set_nino_flag(ni, Dirty)
+#define NInoClearDirty(ni) clear_nino_flag(ni, Dirty)
+#define NInoTestAndSetDirty(ni) test_and_set_nino_flag(ni, Dirty)
+#define NInoTestAndClearDirty(ni) test_and_clear_nino_flag(ni, Dirty)
+
+#define NInoAttrList(ni) test_nino_flag(ni, AttrList)
+#define NInoSetAttrList(ni) set_nino_flag(ni, AttrList)
+#define NInoClearAttrList(ni) clear_nino_flag(ni, AttrList)
+
+
+#define test_nino_al_flag(ni, flag) test_nino_flag(ni, AttrList##flag)
+#define set_nino_al_flag(ni, flag) set_nino_flag(ni, AttrList##flag)
+#define clear_nino_al_flag(ni, flag) clear_nino_flag(ni, AttrList##flag)
+
+#define test_and_set_nino_al_flag(ni, flag) \
+ test_and_set_nino_flag(ni, AttrList##flag)
+#define test_and_clear_nino_al_flag(ni, flag) \
+ test_and_clear_nino_flag(ni, AttrList##flag)
+
+#define NInoAttrListDirty(ni) test_nino_al_flag(ni, Dirty)
+#define NInoAttrListSetDirty(ni) set_nino_al_flag(ni, Dirty)
+#define NInoAttrListClearDirty(ni) clear_nino_al_flag(ni, Dirty)
+#define NInoAttrListTestAndSetDirty(ni) test_and_set_nino_al_flag(ni, Dirty)
+#define NInoAttrListTestAndClearDirty(ni) test_and_clear_nino_al_flag(ni, Dirty)
+
+#define NInoFileNameDirty(ni) \
+ test_nino_flag(ni, FileNameDirty)
+#define NInoFileNameSetDirty(ni) \
+ set_nino_flag(ni, FileNameDirty)
+#define NInoFileNameClearDirty(ni) \
+ clear_nino_flag(ni, FileNameDirty)
+#define NInoFileNameTestAndSetDirty(ni) \
+ test_and_set_nino_flag(ni, FileNameDirty)
+#define NInoFileNameTestAndClearDirty(ni) \
+ test_and_clear_nino_flag(ni, FileNameDirty)
+
+/**
+ * struct _ntfs_inode - The NTFS in-memory inode structure.
+ *
+ * It is just used as an extension to the fields already provided in the VFS
+ * inode.
+ */
+struct _ntfs_inode {
+ u64 mft_no; /* Inode / mft record number. */
+ MFT_RECORD *mrec; /* The actual mft record of the inode. */
+ ntfs_volume *vol; /* Pointer to the ntfs volume of this inode. */
+ unsigned long state; /* NTFS specific flags describing this inode.
+ See ntfs_inode_state_bits above. */
+ FILE_ATTR_FLAGS flags; /* Flags describing the file.
+ (Copy from STANDARD_INFORMATION) */
+ /*
+ * Attribute list support (for use by the attribute lookup functions).
+ * Setup during ntfs_open_inode() for all inodes with attribute lists.
+ * Only valid if NI_AttrList is set in state.
+ */
+ u32 attr_list_size; /* Length of attribute list value in bytes. */
+ u8 *attr_list; /* Attribute list value itself. */
+ /* Below fields are always valid. */
+ s32 nr_extents; /* For a base mft record, the number of
+ attached extent inodes (0 if none), for
+ extent records this is -1. */
+ union { /* This union is only used if nr_extents != 0. */
+ ntfs_inode **extent_nis;/* For nr_extents > 0, array of the
+ ntfs inodes of the extent mft
+ records belonging to this base
+ inode which have been loaded. */
+ ntfs_inode *base_ni; /* For nr_extents == -1, the ntfs
+ inode of the base mft record. */
+ } u;
+
+ /* Below fields are valid only for base inode. */
+
+ /*
+ * These two fields are used to sync filename index and guaranteed to be
+ * correct, however value in index itself maybe wrong (windows itself
+ * do not update them properly).
+ */
+ s64 data_size; /* Data size of unnamed DATA attribute. */
+ s64 allocated_size; /* Allocated size stored in the filename
+ index. (NOTE: Equal to allocated size of
+ the unnamed data attribute for normal or
+ encrypted files and to compressed size
+ of the unnamed data attribute for sparse or
+ compressed files.) */
+
+ /*
+ * These four fields are copy of relevant fields from
+ * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME
+ * attribute in the index.
+ */
+ time_t creation_time;
+ time_t last_data_change_time;
+ time_t last_mft_change_time;
+ time_t last_access_time;
+
+ /* These 2 fields are used to keep track of opened inodes. */
+ struct list_head list_entry; /* Keep pointers to the next/prev list
+ entry. */
+ int nr_references; /* How many times this inode was
+ opened. We really close inode only
+ when this reaches zero. */
+
+ struct list_head attr_cache; /* List of opened attributes. */
+};
+
+extern void __ntfs_inode_add_to_cache(ntfs_inode *ni);
+
+extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol);
+
+extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref);
+
+extern int ntfs_inode_close(ntfs_inode *ni);
+
+extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni,
+ const leMFT_REF mref);
+
+extern int ntfs_inode_attach_all_extents(ntfs_inode *ni);
+
+/**
+ * ntfs_inode_mark_dirty - set the inode (and its base inode if it exists) dirty
+ * @ni: ntfs inode to set dirty
+ *
+ * Set the inode @ni dirty so it is written out later (at the latest at
+ * ntfs_inode_close() time). If @ni is an extent inode, set the base inode
+ * dirty, too.
+ *
+ * This function cannot fail.
+ */
+static __inline__ void ntfs_inode_mark_dirty(ntfs_inode *ni)
+{
+ NInoSetDirty(ni);
+ if (ni->nr_extents == -1)
+ NInoSetDirty(ni->u.base_ni);
+}
+
+typedef enum {
+ NTFS_UPDATE_ATIME = 1 << 0,
+ NTFS_UPDATE_MTIME = 1 << 1,
+ NTFS_UPDATE_CTIME = 1 << 2,
+} ntfs_time_update_flags;
+
+extern void ntfs_inode_update_times(ntfs_inode *ni,
+ ntfs_time_update_flags mask);
+
+extern int ntfs_inode_sync(ntfs_inode *ni);
+
+extern int ntfs_inode_add_attrlist(ntfs_inode *ni);
+
+extern int ntfs_inode_free_space(ntfs_inode *ni, int size);
+
+extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a);
+
+#endif /* defined _NTFS_INODE_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/layout.h b/usr/src/lib/libntfs/common/include/ntfs/layout.h
new file mode 100644
index 0000000000..7ae239cccd
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/layout.h
@@ -0,0 +1,3063 @@
+/*
+ * layout.h - Ntfs on-disk layout structures. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2005 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_LAYOUT_H
+#define _NTFS_LAYOUT_H
+
+#include "types.h"
+#include "endians.h"
+#include "support.h"
+
+/* The NTFS oem_id "NTFS " */
+#define NTFS_SB_MAGIC const_cpu_to_le64(0x202020205346544eULL)
+
+/*
+ * Location of boot sector on partition:
+ * The standard NTFS_BOOT_SECTOR is on sector 0 of the partition.
+ * On NT4 and above there is one backup copy of the boot sector to
+ * be found on the last sector of the partition (not normally accessible
+ * from within Windows as the boot sector contained number of sectors
+ * value is one less than the actual value!).
+ * On versions of NT 3.51 and earlier, the backup copy was located at
+ * number of sectors/2 (integer divide), i.e. in the middle of the volume.
+ */
+
+/**
+ * struct BIOS_PARAMETER_BLOCK - BIOS parameter block (BPB) structure.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le16 bytes_per_sector; /* Size of a sector in bytes. */
+ u8 sectors_per_cluster; /* Size of a cluster in sectors. */
+ le16 reserved_sectors; /* zero */
+ u8 fats; /* zero */
+ le16 root_entries; /* zero */
+ le16 sectors; /* zero */
+ u8 media_type; /* 0xf8 = hard disk */
+ le16 sectors_per_fat; /* zero */
+/*0x0d*/le16 sectors_per_track; /* Required to boot Windows. */
+/*0x0f*/le16 heads; /* Required to boot Windows. */
+/*0x11*/le32 hidden_sectors; /* Offset to the start of the partition
+ relative to the disk in sectors.
+ Required to boot Windows. */
+/*0x15*/le32 large_sectors; /* zero */
+/* sizeof() = 25 (0x19) bytes */
+} __attribute__((__packed__)) BIOS_PARAMETER_BLOCK;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct NTFS_BOOT_SECTOR - NTFS boot sector structure.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u8 jump[3]; /* Irrelevant (jump to boot up code).*/
+ le64 oem_id; /* Magic "NTFS ". */
+/*0x0b*/BIOS_PARAMETER_BLOCK bpb; /* See BIOS_PARAMETER_BLOCK. */
+ u8 physical_drive; /* 0x00 floppy, 0x80 hard disk */
+ u8 current_head; /* zero */
+ u8 extended_boot_signature; /* 0x80 */
+ u8 reserved2; /* zero */
+/*0x28*/sle64 number_of_sectors; /* Number of sectors in volume. Gives
+ maximum volume size of 2^63 sectors.
+ Assuming standard sector size of 512
+ bytes, the maximum byte size is
+ approx. 4.7x10^21 bytes. (-; */
+ sle64 mft_lcn; /* Cluster location of mft data. */
+ sle64 mftmirr_lcn; /* Cluster location of copy of mft. */
+ s8 clusters_per_mft_record; /* Mft record size in clusters. */
+ u8 reserved0[3]; /* zero */
+ s8 clusters_per_index_record; /* Index block size in clusters. */
+ u8 reserved1[3]; /* zero */
+ le64 volume_serial_number; /* Irrelevant (serial number). */
+ le32 checksum; /* Boot sector checksum. */
+/*0x54*/u8 bootstrap[426]; /* Irrelevant (boot up code). */
+ le16 end_of_sector_marker; /* End of boot sector magic. Always is
+ 0xaa55 in little endian. */
+/* sizeof() = 512 (0x200) bytes */
+} __attribute__((__packed__)) NTFS_BOOT_SECTOR;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum NTFS_RECORD_TYPES -
+ *
+ * Magic identifiers present at the beginning of all ntfs record containing
+ * records (like mft records for example).
+ */
+typedef enum {
+ /* Found in $MFT/$DATA. */
+ magic_FILE = const_cpu_to_le32(0x454c4946), /* Mft entry. */
+ magic_INDX = const_cpu_to_le32(0x58444e49), /* Index buffer. */
+ magic_HOLE = const_cpu_to_le32(0x454c4f48), /* ? (NTFS 3.0+?) */
+
+ /* Found in $LogFile/$DATA. */
+ magic_RSTR = const_cpu_to_le32(0x52545352), /* Restart page. */
+ magic_RCRD = const_cpu_to_le32(0x44524352), /* Log record page. */
+
+ /* Found in $LogFile/$DATA. (May be found in $MFT/$DATA, also?) */
+ magic_CHKD = const_cpu_to_le32(0x444b4843), /* Modified by chkdsk. */
+
+ /* Found in all ntfs record containing records. */
+ magic_BAAD = const_cpu_to_le32(0x44414142), /* Failed multi sector
+ transfer was detected. */
+
+ /*
+ * Found in $LogFile/$DATA when a page is full or 0xff bytes and is
+ * thus not initialized. User has to initialize the page before using
+ * it.
+ */
+ magic_empty = const_cpu_to_le32(0xffffffff),/* Record is empty and has
+ to be initialized before
+ it can be used. */
+} NTFS_RECORD_TYPES;
+
+/*
+ * Generic magic comparison macros. Finally found a use for the ## preprocessor
+ * operator! (-8
+ */
+
+static inline BOOL __ntfs_is_magic(le32 x, NTFS_RECORD_TYPES r)
+{
+ return (x == (__force le32)r);
+}
+#define ntfs_is_magic(x, m) __ntfs_is_magic(x, magic_##m)
+
+static inline BOOL __ntfs_is_magicp(le32 *p, NTFS_RECORD_TYPES r)
+{
+ return (*p == (__force le32)r);
+}
+#define ntfs_is_magicp(p, m) __ntfs_is_magicp(p, magic_##m)
+
+/*
+ * Specialised magic comparison macros for the NTFS_RECORD_TYPES defined above.
+ */
+#define ntfs_is_file_record(x) ( ntfs_is_magic (x, FILE) )
+#define ntfs_is_file_recordp(p) ( ntfs_is_magicp(p, FILE) )
+#define ntfs_is_mft_record(x) ( ntfs_is_file_record(x) )
+#define ntfs_is_mft_recordp(p) ( ntfs_is_file_recordp(p) )
+#define ntfs_is_indx_record(x) ( ntfs_is_magic (x, INDX) )
+#define ntfs_is_indx_recordp(p) ( ntfs_is_magicp(p, INDX) )
+#define ntfs_is_hole_record(x) ( ntfs_is_magic (x, HOLE) )
+#define ntfs_is_hole_recordp(p) ( ntfs_is_magicp(p, HOLE) )
+
+#define ntfs_is_rstr_record(x) ( ntfs_is_magic (x, RSTR) )
+#define ntfs_is_rstr_recordp(p) ( ntfs_is_magicp(p, RSTR) )
+#define ntfs_is_rcrd_record(x) ( ntfs_is_magic (x, RCRD) )
+#define ntfs_is_rcrd_recordp(p) ( ntfs_is_magicp(p, RCRD) )
+
+#define ntfs_is_chkd_record(x) ( ntfs_is_magic (x, CHKD) )
+#define ntfs_is_chkd_recordp(p) ( ntfs_is_magicp(p, CHKD) )
+
+#define ntfs_is_baad_record(x) ( ntfs_is_magic (x, BAAD) )
+#define ntfs_is_baad_recordp(p) ( ntfs_is_magicp(p, BAAD) )
+
+#define ntfs_is_empty_record(x) ( ntfs_is_magic (x, empty) )
+#define ntfs_is_empty_recordp(p) ( ntfs_is_magicp(p, empty) )
+
+
+#define NTFS_BLOCK_SIZE 512
+#define NTFS_BLOCK_SIZE_BITS 9
+
+/**
+ * struct NTFS_RECORD -
+ *
+ * The Update Sequence Array (USA) is an array of the le16 values which belong
+ * to the end of each sector protected by the update sequence record in which
+ * this array is contained. Note that the first entry is the Update Sequence
+ * Number (USN), a cyclic counter of how many times the protected record has
+ * been written to disk. The values 0 and -1 (ie. 0xffff) are not used. All
+ * last le16's of each sector have to be equal to the USN (during reading) or
+ * are set to it (during writing). If they are not, an incomplete multi sector
+ * transfer has occurred when the data was written.
+ * The maximum size for the update sequence array is fixed to:
+ * maximum size = usa_ofs + (usa_count * 2) = 510 bytes
+ * The 510 bytes comes from the fact that the last le16 in the array has to
+ * (obviously) finish before the last le16 of the first 512-byte sector.
+ * This formula can be used as a consistency check in that usa_ofs +
+ * (usa_count * 2) has to be less than or equal to 510.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ NTFS_RECORD_TYPES magic;/* A four-byte magic identifying the
+ record type and/or status. */
+ le16 usa_ofs; /* Offset to the Update Sequence Array (USA)
+ from the start of the ntfs record. */
+ le16 usa_count; /* Number of u16 sized entries in the USA
+ including the Update Sequence Number (USN),
+ thus the number of fixups is the usa_count
+ minus 1. */
+} __attribute__((__packed__)) NTFS_RECORD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum NTFS_SYSTEM_FILES - System files mft record numbers.
+ *
+ * All these files are always marked as used in the bitmap attribute of the
+ * mft; presumably in order to avoid accidental allocation for random other
+ * mft records. Also, the sequence number for each of the system files is
+ * always equal to their mft record number and it is never modified.
+ */
+typedef enum {
+ FILE_MFT = 0, /* Master file table (mft). Data attribute
+ contains the entries and bitmap attribute
+ records which ones are in use (bit==1). */
+ FILE_MFTMirr = 1, /* Mft mirror: copy of first four mft records
+ in data attribute. If cluster size > 4kiB,
+ copy of first N mft records, with
+ N = cluster_size / mft_record_size. */
+ FILE_LogFile = 2, /* Journalling log in data attribute. */
+ FILE_Volume = 3, /* Volume name attribute and volume information
+ attribute (flags and ntfs version). Windows
+ refers to this file as volume DASD (Direct
+ Access Storage Device). */
+ FILE_AttrDef = 4, /* Array of attribute definitions in data
+ attribute. */
+ FILE_root = 5, /* Root directory. */
+ FILE_Bitmap = 6, /* Allocation bitmap of all clusters (LCNs) in
+ data attribute. */
+ FILE_Boot = 7, /* Boot sector (always at cluster 0) in data
+ attribute. */
+ FILE_BadClus = 8, /* Contains all bad clusters in the non-resident
+ data attribute. */
+ FILE_Secure = 9, /* Shared security descriptors in data attribute
+ and two indexes into the descriptors.
+ Appeared in Windows 2000. Before that, this
+ file was named $Quota but was unused. */
+ FILE_UpCase = 10, /* Uppercase equivalents of all 65536 Unicode
+ characters in data attribute. */
+ FILE_Extend = 11, /* Directory containing other system files (eg.
+ $ObjId, $Quota, $Reparse and $UsnJrnl). This
+ is new to NTFS 3.0. */
+ FILE_reserved12 = 12, /* Reserved for future use (records 12-15). */
+ FILE_reserved13 = 13,
+ FILE_reserved14 = 14,
+ FILE_reserved15 = 15,
+ FILE_first_user = 16, /* First user file, used as test limit for
+ whether to allow opening a file or not. */
+} NTFS_SYSTEM_FILES;
+
+/**
+ * enum MFT_RECORD_FLAGS -
+ *
+ * These are the so far known MFT_RECORD_* flags (16-bit) which contain
+ * information about the mft record in which they are present.
+ *
+ * MFT_RECORD_IS_4 exists on all $Extend sub-files.
+ * It seems that it marks it is a metadata file with MFT record >24, however,
+ * it is unknown if it is limited to metadata files only.
+ *
+ * MFT_RECORD_IS_VIEW_INDEX exists on every metafile with a non directory
+ * index, that means an INDEX_ROOT and an INDEX_ALLOCATION with a name other
+ * than "$I30". It is unknown if it is limited to metadata files only.
+ */
+#ifdef __sun
+typedef uint16_t MFT_RECORD_FLAGS;
+#define MFT_RECORD_IN_USE (const_cpu_to_le16(0x0001))
+#define MFT_RECORD_IS_DIRECTORY (const_cpu_to_le16(0x0002))
+#define MFT_RECORD_IS_4 (const_cpu_to_le16(0x0004))
+#define MFT_RECORD_IS_VIEW_INDEX (const_cpu_to_le16(0x0008))
+#else /* not __sun */
+typedef enum {
+ MFT_RECORD_IN_USE = const_cpu_to_le16(0x0001),
+ MFT_RECORD_IS_DIRECTORY = const_cpu_to_le16(0x0002),
+ MFT_RECORD_IS_4 = const_cpu_to_le16(0x0004),
+ MFT_RECORD_IS_VIEW_INDEX = const_cpu_to_le16(0x0008),
+ MFT_REC_SPACE_FILLER = const_cpu_to_le16(0xffff),
+ /* Just to make flags 16-bit. */
+} __attribute__((__packed__)) MFT_RECORD_FLAGS;
+#endif /* __sun */
+
+/*
+ * mft references (aka file references or file record segment references) are
+ * used whenever a structure needs to refer to a record in the mft.
+ *
+ * A reference consists of a 48-bit index into the mft and a 16-bit sequence
+ * number used to detect stale references.
+ *
+ * For error reporting purposes we treat the 48-bit index as a signed quantity.
+ *
+ * The sequence number is a circular counter (skipping 0) describing how many
+ * times the referenced mft record has been (re)used. This has to match the
+ * sequence number of the mft record being referenced, otherwise the reference
+ * is considered stale and removed (FIXME: only ntfsck or the driver itself?).
+ *
+ * If the sequence number is zero it is assumed that no sequence number
+ * consistency checking should be performed.
+ *
+ * FIXME: Since inodes are 32-bit as of now, the driver needs to always check
+ * for high_part being 0 and if not either BUG(), cause a panic() or handle
+ * the situation in some other way. This shouldn't be a problem as a volume has
+ * to become HUGE in order to need more than 32-bits worth of mft records.
+ * Assuming the standard mft record size of 1kb only the records (never mind
+ * the non-resident attributes, etc.) would require 4Tb of space on their own
+ * for the first 32 bits worth of records. This is only if some strange person
+ * doesn't decide to foul play and make the mft sparse which would be a really
+ * horrible thing to do as it would trash our current driver implementation. )-:
+ * Do I hear screams "we want 64-bit inodes!" ?!? (-;
+ *
+ * FIXME: The mft zone is defined as the first 12% of the volume. This space is
+ * reserved so that the mft can grow contiguously and hence doesn't become
+ * fragmented. Volume free space includes the empty part of the mft zone and
+ * when the volume's free 88% are used up, the mft zone is shrunk by a factor
+ * of 2, thus making more space available for more files/data. This process is
+ * repeated every time there is no more free space except for the mft zone until
+ * there really is no more free space.
+ */
+
+/*
+ * Typedef the MFT_REF as a 64-bit value for easier handling.
+ * Also define two unpacking macros to get to the reference (MREF) and
+ * sequence number (MSEQNO) respectively.
+ * The _LE versions are to be applied on little endian MFT_REFs.
+ * Note: The _LE versions will return a CPU endian formatted value!
+ */
+#define MFT_REF_MASK_CPU 0x0000ffffffffffffULL
+#define MFT_REF_MASK_LE const_cpu_to_le64(MFT_REF_MASK_CPU)
+
+typedef u64 MFT_REF;
+typedef le64 leMFT_REF;
+
+#define MK_MREF(m, s) ((MFT_REF)(((MFT_REF)(s) << 48) | \
+ ((MFT_REF)(m) & MFT_REF_MASK_CPU)))
+#define MK_LE_MREF(m, s) const_cpu_to_le64(((MFT_REF)(((MFT_REF)(s) << 48) | \
+ ((MFT_REF)(m) & MFT_REF_MASK_CPU))))
+
+#define MREF(x) ((u64)((x) & MFT_REF_MASK_CPU))
+#define MSEQNO(x) ((u16)(((x) >> 48) & 0xffff))
+#define MREF_LE(x) ((u64)(const_le64_to_cpu(x) & MFT_REF_MASK_CPU))
+#define MSEQNO_LE(x) ((u16)((const_le64_to_cpu(x) >> 48) & 0xffff))
+
+#define IS_ERR_MREF(x) (((x) & 0x0000800000000000ULL) ? 1 : 0)
+#define ERR_MREF(x) ((u64)((s64)(x)))
+#define MREF_ERR(x) ((int)((s64)(x)))
+
+/**
+ * struct MFT_RECORD - An MFT record layout (NTFS 3.1+)
+ *
+ * The mft record header present at the beginning of every record in the mft.
+ * This is followed by a sequence of variable length attribute records which
+ * is terminated by an attribute of type AT_END which is a truncated attribute
+ * in that it only consists of the attribute type code AT_END and none of the
+ * other members of the attribute structure are present.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
+ NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */
+ le16 usa_ofs; /* See NTFS_RECORD definition above. */
+ le16 usa_count; /* See NTFS_RECORD definition above. */
+
+/* 8*/ leLSN lsn; /* $LogFile sequence number for this record.
+ Changed every time the record is modified. */
+/* 16*/ le16 sequence_number; /* Number of times this mft record has been
+ reused. (See description for MFT_REF
+ above.) NOTE: The increment (skipping zero)
+ is done when the file is deleted. NOTE: If
+ this is zero it is left zero. */
+/* 18*/ le16 link_count; /* Number of hard links, i.e. the number of
+ directory entries referencing this record.
+ NOTE: Only used in mft base records.
+ NOTE: When deleting a directory entry we
+ check the link_count and if it is 1 we
+ delete the file. Otherwise we delete the
+ FILE_NAME_ATTR being referenced by the
+ directory entry from the mft record and
+ decrement the link_count.
+ FIXME: Careful with Win32 + DOS names! */
+/* 20*/ le16 attrs_offset; /* Byte offset to the first attribute in this
+ mft record from the start of the mft record.
+ NOTE: Must be aligned to 8-byte boundary. */
+/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file
+ is deleted, the MFT_RECORD_IN_USE flag is
+ set to zero. */
+/* 24*/ le32 bytes_in_use; /* Number of bytes used in this mft record.
+ NOTE: Must be aligned to 8-byte boundary. */
+/* 28*/ le32 bytes_allocated; /* Number of bytes allocated for this mft
+ record. This should be equal to the mft
+ record size. */
+/* 32*/ leMFT_REF base_mft_record;/* This is zero for base mft records.
+ When it is not zero it is a mft reference
+ pointing to the base mft record to which
+ this record belongs (this is then used to
+ locate the attribute list attribute present
+ in the base record which describes this
+ extension record and hence might need
+ modification when the extension record
+ itself is modified, also locating the
+ attribute list also means finding the other
+ potential extents, belonging to the non-base
+ mft record). */
+/* 40*/ le16 next_attr_instance; /* The instance number that will be
+ assigned to the next attribute added to this
+ mft record. NOTE: Incremented each time
+ after it is used. NOTE: Every time the mft
+ record is reused this number is set to zero.
+ NOTE: The first instance number is always 0.
+ */
+/* The below fields are specific to NTFS 3.1+ (Windows XP and above): */
+/* 42*/ le16 reserved; /* Reserved/alignment. */
+/* 44*/ le32 mft_record_number; /* Number of this mft record. */
+/* sizeof() = 48 bytes */
+/*
+ * When (re)using the mft record, we place the update sequence array at this
+ * offset, i.e. before we start with the attributes. This also makes sense,
+ * otherwise we could run into problems with the update sequence array
+ * containing in itself the last two bytes of a sector which would mean that
+ * multi sector transfer protection wouldn't work. As you can't protect data
+ * by overwriting it since you then can't get it back...
+ * When reading we obviously use the data from the ntfs record header.
+ */
+} __attribute__((__packed__)) MFT_RECORD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct MFT_RECORD_OLD - An MFT record layout (NTFS <=3.0)
+ *
+ * This is the version without the NTFS 3.1+ specific fields.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
+ NTFS_RECORD_TYPES magic;/* Usually the magic is "FILE". */
+ le16 usa_ofs; /* See NTFS_RECORD definition above. */
+ le16 usa_count; /* See NTFS_RECORD definition above. */
+
+/* 8*/ leLSN lsn; /* $LogFile sequence number for this record.
+ Changed every time the record is modified. */
+/* 16*/ le16 sequence_number; /* Number of times this mft record has been
+ reused. (See description for MFT_REF
+ above.) NOTE: The increment (skipping zero)
+ is done when the file is deleted. NOTE: If
+ this is zero it is left zero. */
+/* 18*/ le16 link_count; /* Number of hard links, i.e. the number of
+ directory entries referencing this record.
+ NOTE: Only used in mft base records.
+ NOTE: When deleting a directory entry we
+ check the link_count and if it is 1 we
+ delete the file. Otherwise we delete the
+ FILE_NAME_ATTR being referenced by the
+ directory entry from the mft record and
+ decrement the link_count.
+ FIXME: Careful with Win32 + DOS names! */
+/* 20*/ le16 attrs_offset; /* Byte offset to the first attribute in this
+ mft record from the start of the mft record.
+ NOTE: Must be aligned to 8-byte boundary. */
+/* 22*/ MFT_RECORD_FLAGS flags; /* Bit array of MFT_RECORD_FLAGS. When a file
+ is deleted, the MFT_RECORD_IN_USE flag is
+ set to zero. */
+/* 24*/ le32 bytes_in_use; /* Number of bytes used in this mft record.
+ NOTE: Must be aligned to 8-byte boundary. */
+/* 28*/ le32 bytes_allocated; /* Number of bytes allocated for this mft
+ record. This should be equal to the mft
+ record size. */
+/* 32*/ MFT_REF base_mft_record; /* This is zero for base mft records.
+ When it is not zero it is a mft reference
+ pointing to the base mft record to which
+ this record belongs (this is then used to
+ locate the attribute list attribute present
+ in the base record which describes this
+ extension record and hence might need
+ modification when the extension record
+ itself is modified, also locating the
+ attribute list also means finding the other
+ potential extents, belonging to the non-base
+ mft record). */
+/* 40*/ le16 next_attr_instance; /* The instance number that will be
+ assigned to the next attribute added to this
+ mft record. NOTE: Incremented each time
+ after it is used. NOTE: Every time the mft
+ record is reused this number is set to zero.
+ NOTE: The first instance number is always 0.
+ */
+/* sizeof() = 42 bytes */
+/*
+ * When (re)using the mft record, we place the update sequence array at this
+ * offset, i.e. before we start with the attributes. This also makes sense,
+ * otherwise we could run into problems with the update sequence array
+ * containing in itself the last two bytes of a sector which would mean that
+ * multi sector transfer protection wouldn't work. As you can't protect data
+ * by overwriting it since you then can't get it back...
+ * When reading we obviously use the data from the ntfs record header.
+ */
+} __attribute__((__packed__)) MFT_RECORD_OLD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum ATTR_TYPES - System defined attributes (32-bit).
+ *
+ * Each attribute type has a corresponding attribute name (Unicode string of
+ * maximum 64 character length) as described by the attribute definitions
+ * present in the data attribute of the $AttrDef system file.
+ *
+ * On NTFS 3.0 volumes the names are just as the types are named in the below
+ * enum exchanging AT_ for the dollar sign ($). If that isn't a revealing
+ * choice of symbol... (-;
+ */
+typedef enum {
+ AT_UNUSED = const_cpu_to_le32( 0),
+ AT_STANDARD_INFORMATION = const_cpu_to_le32( 0x10),
+ AT_ATTRIBUTE_LIST = const_cpu_to_le32( 0x20),
+ AT_FILE_NAME = const_cpu_to_le32( 0x30),
+ AT_OBJECT_ID = const_cpu_to_le32( 0x40),
+ AT_SECURITY_DESCRIPTOR = const_cpu_to_le32( 0x50),
+ AT_VOLUME_NAME = const_cpu_to_le32( 0x60),
+ AT_VOLUME_INFORMATION = const_cpu_to_le32( 0x70),
+ AT_DATA = const_cpu_to_le32( 0x80),
+ AT_INDEX_ROOT = const_cpu_to_le32( 0x90),
+ AT_INDEX_ALLOCATION = const_cpu_to_le32( 0xa0),
+ AT_BITMAP = const_cpu_to_le32( 0xb0),
+ AT_REPARSE_POINT = const_cpu_to_le32( 0xc0),
+ AT_EA_INFORMATION = const_cpu_to_le32( 0xd0),
+ AT_EA = const_cpu_to_le32( 0xe0),
+ AT_PROPERTY_SET = const_cpu_to_le32( 0xf0),
+ AT_LOGGED_UTILITY_STREAM = const_cpu_to_le32( 0x100),
+ AT_FIRST_USER_DEFINED_ATTRIBUTE = const_cpu_to_le32( 0x1000),
+ AT_END = const_cpu_to_le32(0xffffffff),
+} ATTR_TYPES;
+
+/**
+ * enum COLLATION_RULES - The collation rules for sorting views/indexes/etc
+ * (32-bit).
+ *
+ * COLLATION_UNICODE_STRING - Collate Unicode strings by comparing their binary
+ * Unicode values, except that when a character can be uppercased, the
+ * upper case value collates before the lower case one.
+ * COLLATION_FILE_NAME - Collate file names as Unicode strings. The collation
+ * is done very much like COLLATION_UNICODE_STRING. In fact I have no idea
+ * what the difference is. Perhaps the difference is that file names
+ * would treat some special characters in an odd way (see
+ * unistr.c::ntfs_collate_names() and unistr.c::legal_ansi_char_array[]
+ * for what I mean but COLLATION_UNICODE_STRING would not give any special
+ * treatment to any characters at all, but this is speculation.
+ * COLLATION_NTOFS_ULONG - Sorting is done according to ascending le32 key
+ * values. E.g. used for $SII index in FILE_Secure, which sorts by
+ * security_id (le32).
+ * COLLATION_NTOFS_SID - Sorting is done according to ascending SID values.
+ * E.g. used for $O index in FILE_Extend/$Quota.
+ * COLLATION_NTOFS_SECURITY_HASH - Sorting is done first by ascending hash
+ * values and second by ascending security_id values. E.g. used for $SDH
+ * index in FILE_Secure.
+ * COLLATION_NTOFS_ULONGS - Sorting is done according to a sequence of ascending
+ * le32 key values. E.g. used for $O index in FILE_Extend/$ObjId, which
+ * sorts by object_id (16-byte), by splitting up the object_id in four
+ * le32 values and using them as individual keys. E.g. take the following
+ * two security_ids, stored as follows on disk:
+ * 1st: a1 61 65 b7 65 7b d4 11 9e 3d 00 e0 81 10 42 59
+ * 2nd: 38 14 37 d2 d2 f3 d4 11 a5 21 c8 6b 79 b1 97 45
+ * To compare them, they are split into four le32 values each, like so:
+ * 1st: 0xb76561a1 0x11d47b65 0xe0003d9e 0x59421081
+ * 2nd: 0xd2371438 0x11d4f3d2 0x6bc821a5 0x4597b179
+ * Now, it is apparent why the 2nd object_id collates after the 1st: the
+ * first le32 value of the 1st object_id is less than the first le32 of
+ * the 2nd object_id. If the first le32 values of both object_ids were
+ * equal then the second le32 values would be compared, etc.
+ */
+typedef enum {
+ COLLATION_BINARY = const_cpu_to_le32(0), /* Collate by binary
+ compare where the first byte is most
+ significant. */
+ COLLATION_FILE_NAME = const_cpu_to_le32(1), /* Collate file names
+ as Unicode strings. */
+ COLLATION_UNICODE_STRING = const_cpu_to_le32(2), /* Collate Unicode
+ strings by comparing their binary
+ Unicode values, except that when a
+ character can be uppercased, the upper
+ case value collates before the lower
+ case one. */
+ COLLATION_NTOFS_ULONG = const_cpu_to_le32(16),
+ COLLATION_NTOFS_SID = const_cpu_to_le32(17),
+ COLLATION_NTOFS_SECURITY_HASH = const_cpu_to_le32(18),
+ COLLATION_NTOFS_ULONGS = const_cpu_to_le32(19),
+} COLLATION_RULES;
+
+/**
+ * enum ATTR_DEF_FLAGS -
+ *
+ * The flags (32-bit) describing attribute properties in the attribute
+ * definition structure. FIXME: This information is based on Regis's
+ * information and, according to him, it is not certain and probably
+ * incomplete. The INDEXABLE flag is fairly certainly correct as only the file
+ * name attribute has this flag set and this is the only attribute indexed in
+ * NT4.
+ */
+typedef enum {
+ ATTR_DEF_INDEXABLE = const_cpu_to_le32(0x02), /* Attribute can be
+ indexed. */
+ ATTR_DEF_MULTIPLE = const_cpu_to_le32(0x04), /* Attribute type
+ can be present multiple times in the
+ mft records of an inode. */
+ ATTR_DEF_NOT_ZERO = const_cpu_to_le32(0x08), /* Attribute value
+ must contain at least one non-zero
+ byte. */
+ ATTR_DEF_INDEXED_UNIQUE = const_cpu_to_le32(0x10), /* Attribute must be
+ indexed and the attribute value must be
+ unique for the attribute type in all of
+ the mft records of an inode. */
+ ATTR_DEF_NAMED_UNIQUE = const_cpu_to_le32(0x20), /* Attribute must be
+ named and the name must be unique for
+ the attribute type in all of the mft
+ records of an inode. */
+ ATTR_DEF_RESIDENT = const_cpu_to_le32(0x40), /* Attribute must be
+ resident. */
+ ATTR_DEF_ALWAYS_LOG = const_cpu_to_le32(0x80), /* Always log
+ modifications to this attribute,
+ regardless of whether it is resident or
+ non-resident. Without this, only log
+ modifications if the attribute is
+ resident. */
+} ATTR_DEF_FLAGS;
+
+/**
+ * struct ATTR_DEF -
+ *
+ * The data attribute of FILE_AttrDef contains a sequence of attribute
+ * definitions for the NTFS volume. With this, it is supposed to be safe for an
+ * older NTFS driver to mount a volume containing a newer NTFS version without
+ * damaging it (that's the theory. In practice it's: not damaging it too much).
+ * Entries are sorted by attribute type. The flags describe whether the
+ * attribute can be resident/non-resident and possibly other things, but the
+ * actual bits are unknown.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*hex ofs*/
+/* 0*/ ntfschar name[0x40]; /* Unicode name of the attribute. Zero
+ terminated. */
+/* 80*/ ATTR_TYPES type; /* Type of the attribute. */
+/* 84*/ le32 display_rule; /* Default display rule.
+ FIXME: What does it mean? (AIA) */
+/* 88*/ COLLATION_RULES collation_rule; /* Default collation rule. */
+/* 8c*/ ATTR_DEF_FLAGS flags; /* Flags describing the attribute. */
+/* 90*/ sle64 min_size; /* Optional minimum attribute size. */
+/* 98*/ sle64 max_size; /* Maximum size of attribute. */
+/* sizeof() = 0xa0 or 160 bytes */
+} __attribute__((__packed__)) ATTR_DEF;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum ATTR_FLAGS - Attribute flags (16-bit).
+ */
+#ifdef __sun
+typedef uint16_t ATTR_FLAGS;
+#define ATTR_IS_COMPRESSED (const_cpu_to_le16(0x0001))
+#define ATTR_COMPRESSION_MASK (const_cpu_to_le16(0x00ff))
+#define ATTR_IS_ENCRYPTED (const_cpu_to_le16(0x4000))
+#define ATTR_IS_SPARSE (const_cpu_to_le16(0x8000))
+#else /* not __sun */
+typedef enum {
+ ATTR_IS_COMPRESSED = const_cpu_to_le16(0x0001),
+ ATTR_COMPRESSION_MASK = const_cpu_to_le16(0x00ff), /* Compression
+ method mask. Also, first
+ illegal value. */
+ ATTR_IS_ENCRYPTED = const_cpu_to_le16(0x4000),
+ ATTR_IS_SPARSE = const_cpu_to_le16(0x8000),
+} __attribute__((__packed__)) ATTR_FLAGS;
+#endif /* __sun */
+
+/*
+ * Attribute compression.
+ *
+ * Only the data attribute is ever compressed in the current ntfs driver in
+ * Windows. Further, compression is only applied when the data attribute is
+ * non-resident. Finally, to use compression, the maximum allowed cluster size
+ * on a volume is 4kib.
+ *
+ * The compression method is based on independently compressing blocks of X
+ * clusters, where X is determined from the compression_unit value found in the
+ * non-resident attribute record header (more precisely: X = 2^compression_unit
+ * clusters). On Windows NT/2k, X always is 16 clusters (compression_unit = 4).
+ *
+ * There are three different cases of how a compression block of X clusters
+ * can be stored:
+ *
+ * 1) The data in the block is all zero (a sparse block):
+ * This is stored as a sparse block in the runlist, i.e. the runlist
+ * entry has length = X and lcn = -1. The mapping pairs array actually
+ * uses a delta_lcn value length of 0, i.e. delta_lcn is not present at
+ * all, which is then interpreted by the driver as lcn = -1.
+ * NOTE: Even uncompressed files can be sparse on NTFS 3.0 volumes, then
+ * the same principles apply as above, except that the length is not
+ * restricted to being any particular value.
+ *
+ * 2) The data in the block is not compressed:
+ * This happens when compression doesn't reduce the size of the block
+ * in clusters. I.e. if compression has a small effect so that the
+ * compressed data still occupies X clusters, then the uncompressed data
+ * is stored in the block.
+ * This case is recognised by the fact that the runlist entry has
+ * length = X and lcn >= 0. The mapping pairs array stores this as
+ * normal with a run length of X and some specific delta_lcn, i.e.
+ * delta_lcn has to be present.
+ *
+ * 3) The data in the block is compressed:
+ * The common case. This case is recognised by the fact that the run
+ * list entry has length L < X and lcn >= 0. The mapping pairs array
+ * stores this as normal with a run length of X and some specific
+ * delta_lcn, i.e. delta_lcn has to be present. This runlist entry is
+ * immediately followed by a sparse entry with length = X - L and
+ * lcn = -1. The latter entry is to make up the vcn counting to the
+ * full compression block size X.
+ *
+ * In fact, life is more complicated because adjacent entries of the same type
+ * can be coalesced. This means that one has to keep track of the number of
+ * clusters handled and work on a basis of X clusters at a time being one
+ * block. An example: if length L > X this means that this particular runlist
+ * entry contains a block of length X and part of one or more blocks of length
+ * L - X. Another example: if length L < X, this does not necessarily mean that
+ * the block is compressed as it might be that the lcn changes inside the block
+ * and hence the following runlist entry describes the continuation of the
+ * potentially compressed block. The block would be compressed if the
+ * following runlist entry describes at least X - L sparse clusters, thus
+ * making up the compression block length as described in point 3 above. (Of
+ * course, there can be several runlist entries with small lengths so that the
+ * sparse entry does not follow the first data containing entry with
+ * length < X.)
+ *
+ * NOTE: At the end of the compressed attribute value, there most likely is not
+ * just the right amount of data to make up a compression block, thus this data
+ * is not even attempted to be compressed. It is just stored as is, unless
+ * the number of clusters it occupies is reduced when compressed in which case
+ * it is stored as a compressed compression block, complete with sparse
+ * clusters at the end.
+ */
+
+/**
+ * enum RESIDENT_ATTR_FLAGS - Flags of resident attributes (8-bit).
+ */
+#ifdef __sun
+typedef uint8_t RESIDENT_ATTR_FLAGS;
+#define RESIDENT_ATTR_IS_INDEXED (0x01)
+#else /* not __sun */
+typedef enum {
+ RESIDENT_ATTR_IS_INDEXED = 0x01, /* Attribute is referenced in an index
+ (has implications for deleting and
+ modifying the attribute). */
+} __attribute__((__packed__)) RESIDENT_ATTR_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct ATTR_RECORD - Attribute record header.
+ *
+ * Always aligned to 8-byte boundary.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0*/ ATTR_TYPES type; /* The (32-bit) type of the attribute. */
+/* 4*/ le32 length; /* Byte size of the resident part of the
+ attribute (aligned to 8-byte boundary).
+ Used to get to the next attribute. */
+/* 8*/ u8 non_resident; /* If 0, attribute is resident.
+ If 1, attribute is non-resident. */
+/* 9*/ u8 name_length; /* Unicode character size of name of attribute.
+ 0 if unnamed. */
+/* 10*/ le16 name_offset; /* If name_length != 0, the byte offset to the
+ beginning of the name from the attribute
+ record. Note that the name is stored as a
+ Unicode string. When creating, place offset
+ just at the end of the record header. Then,
+ follow with attribute value or mapping pairs
+ array, resident and non-resident attributes
+ respectively, aligning to an 8-byte
+ boundary. */
+/* 12*/ ATTR_FLAGS flags; /* Flags describing the attribute. */
+/* 14*/ le16 instance; /* The instance of this attribute record. This
+ number is unique within this mft record (see
+ MFT_RECORD/next_attribute_instance notes
+ above for more details). */
+/* 16*/ union {
+ /* Resident attributes. */
+ struct {
+/* 16 */ le32 value_length; /* Byte size of attribute value. */
+/* 20 */ le16 value_offset; /* Byte offset of the attribute
+ value from the start of the
+ attribute record. When creating,
+ align to 8-byte boundary if we
+ have a name present as this might
+ not have a length of a multiple
+ of 8-bytes. */
+/* 22 */ RESIDENT_ATTR_FLAGS resident_flags; /* See above. */
+/* 23 */ s8 reservedR; /* Reserved/alignment to 8-byte
+ boundary. */
+/* 24 */ void *resident_end[]; /* Use offsetof(ATTR_RECORD,
+ resident_end) to get size of
+ a resident attribute. */
+ } __attribute__((__packed__)) res;
+
+ /* Non-resident attributes. */
+ struct {
+/* 16*/ leVCN lowest_vcn;/* Lowest valid virtual cluster number
+ for this portion of the attribute value or
+ 0 if this is the only extent (usually the
+ case). - Only when an attribute list is used
+ does lowest_vcn != 0 ever occur. */
+/* 24*/ leVCN highest_vcn;/* Highest valid vcn of this extent of
+ the attribute value. - Usually there is only one
+ portion, so this usually equals the attribute
+ value size in clusters minus 1. Can be -1 for
+ zero length files. Can be 0 for "single extent"
+ attributes. */
+/* 32*/ le16 mapping_pairs_offset; /* Byte offset from the
+ beginning of the structure to the mapping pairs
+ array which contains the mappings between the
+ VCNs and the logical cluster numbers (LCNs).
+ When creating, place this at the end of this
+ record header aligned to 8-byte boundary. */
+/* 34*/ u8 compression_unit; /* The compression unit expressed
+ as the log to the base 2 of the number of
+ clusters in a compression unit. 0 means not
+ compressed. (This effectively limits the
+ compression unit size to be a power of two
+ clusters.) WinNT4 only uses a value of 4. */
+/* 35*/ u8 reserved1[5]; /* Align to 8-byte boundary. */
+/* The sizes below are only used when lowest_vcn is zero, as otherwise it would
+ be difficult to keep them up-to-date.*/
+/* 40*/ sle64 allocated_size; /* Byte size of disk space
+ allocated to hold the attribute value. Always
+ is a multiple of the cluster size. When a file
+ is compressed, this field is a multiple of the
+ compression block size (2^compression_unit) and
+ it represents the logically allocated space
+ rather than the actual on disk usage. For this
+ use the compressed_size (see below). */
+/* 48*/ sle64 data_size; /* Byte size of the attribute
+ value. Can be larger than allocated_size if
+ attribute value is compressed or sparse. */
+/* 56*/ sle64 initialized_size; /* Byte size of initialized
+ portion of the attribute value. Usually equals
+ data_size. */
+#ifdef __sun
+/* 64 */
+#define non_resident_end compressed_size
+#else /* not __sun */
+/* 64 */ void *non_resident_end[0]; /* Use offsetof(ATTR_RECORD,
+ non_resident_end) to get
+ size of a non resident
+ attribute. */
+#endif /* __sun */
+/* sizeof(uncompressed attr) = 64*/
+/* 64*/ sle64 compressed_size; /* Byte size of the attribute
+ value after compression. Only present when
+ compressed. Always is a multiple of the
+ cluster size. Represents the actual amount of
+ disk space being used on the disk. */
+/* 72 */ void *compressed_end[];
+ /* Use offsetof(ATTR_RECORD, compressed_end) to
+ get size of a compressed attribute. */
+/* sizeof(compressed attr) = 72*/
+ } __attribute__((__packed__)) nonres;
+ } __attribute__((__packed__)) u;
+} __attribute__((__packed__)) ATTR_RECORD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef ATTR_RECORD ATTR_REC;
+
+/**
+ * enum FILE_ATTR_FLAGS - File attribute flags (32-bit).
+ */
+typedef enum {
+ /*
+ * These flags are only present in the STANDARD_INFORMATION attribute
+ * (in the field file_attributes).
+ */
+ FILE_ATTR_READONLY = const_cpu_to_le32(0x00000001),
+ FILE_ATTR_HIDDEN = const_cpu_to_le32(0x00000002),
+ FILE_ATTR_SYSTEM = const_cpu_to_le32(0x00000004),
+ /* Old DOS valid. Unused in NT. = cpu_to_le32(0x00000008), */
+
+ FILE_ATTR_DIRECTORY = const_cpu_to_le32(0x00000010),
+ /* FILE_ATTR_DIRECTORY is not considered valid in NT. It is reserved
+ for the DOS SUBDIRECTORY flag. */
+ FILE_ATTR_ARCHIVE = const_cpu_to_le32(0x00000020),
+ FILE_ATTR_DEVICE = const_cpu_to_le32(0x00000040),
+ FILE_ATTR_NORMAL = const_cpu_to_le32(0x00000080),
+
+ FILE_ATTR_TEMPORARY = const_cpu_to_le32(0x00000100),
+ FILE_ATTR_SPARSE_FILE = const_cpu_to_le32(0x00000200),
+ FILE_ATTR_REPARSE_POINT = const_cpu_to_le32(0x00000400),
+ FILE_ATTR_COMPRESSED = const_cpu_to_le32(0x00000800),
+
+ FILE_ATTR_OFFLINE = const_cpu_to_le32(0x00001000),
+ FILE_ATTR_NOT_CONTENT_INDEXED = const_cpu_to_le32(0x00002000),
+ FILE_ATTR_ENCRYPTED = const_cpu_to_le32(0x00004000),
+
+ FILE_ATTR_VALID_FLAGS = const_cpu_to_le32(0x00007fb7),
+ /* FILE_ATTR_VALID_FLAGS masks out the old DOS VolId and the
+ FILE_ATTR_DEVICE and preserves everything else. This mask
+ is used to obtain all flags that are valid for reading. */
+ FILE_ATTR_VALID_SET_FLAGS = const_cpu_to_le32(0x000031a7),
+ /* FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the
+ FILE_ATTR_DEVICE, FILE_ATTR_DIRECTORY, FILE_ATTR_SPARSE_FILE,
+ FILE_ATTR_REPARSE_POINT, FILE_ATRE_COMPRESSED and FILE_ATTR_ENCRYPTED
+ and preserves the rest. This mask is used to to obtain all flags that
+ are valid for setting. */
+
+ /**
+ * FILE_ATTR_I30_INDEX_PRESENT - Is it a directory?
+ *
+ * This is a copy of the MFT_RECORD_IS_DIRECTORY bit from the mft
+ * record, telling us whether this is a directory or not, i.e. whether
+ * it has an index root attribute named "$I30" or not.
+ *
+ * This flag is only present in the FILE_NAME attribute (in the
+ * file_attributes field).
+ */
+ FILE_ATTR_I30_INDEX_PRESENT = const_cpu_to_le32(0x10000000),
+
+ /**
+ * FILE_ATTR_VIEW_INDEX_PRESENT - Does have a non-directory index?
+ *
+ * This is a copy of the MFT_RECORD_IS_VIEW_INDEX bit from the mft
+ * record, telling us whether this file has a view index present (eg.
+ * object id index, quota index, one of the security indexes and the
+ * reparse points index).
+ *
+ * This flag is only present in the $STANDARD_INFORMATION and
+ * $FILE_NAME attributes.
+ */
+ FILE_ATTR_VIEW_INDEX_PRESENT = const_cpu_to_le32(0x20000000),
+} __attribute__((__packed__)) FILE_ATTR_FLAGS;
+
+/*
+ * NOTE on times in NTFS: All times are in MS standard time format, i.e. they
+ * are the number of 100-nanosecond intervals since 1st January 1601, 00:00:00
+ * universal coordinated time (UTC). (In Linux time starts 1st January 1970,
+ * 00:00:00 UTC and is stored as the number of 1-second intervals since then.)
+ */
+
+/**
+ * struct STANDARD_INFORMATION - Attribute: Standard information (0x10).
+ *
+ * NOTE: Always resident.
+ * NOTE: Present in all base file records on a volume.
+ * NOTE: There is conflicting information about the meaning of each of the time
+ * fields but the meaning as defined below has been verified to be
+ * correct by practical experimentation on Windows NT4 SP6a and is hence
+ * assumed to be the one and only correct interpretation.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0*/ sle64 creation_time; /* Time file was created. Updated when
+ a filename is changed(?). */
+/* 8*/ sle64 last_data_change_time; /* Time the data attribute was last
+ modified. */
+/* 16*/ sle64 last_mft_change_time; /* Time this mft record was last
+ modified. */
+/* 24*/ sle64 last_access_time; /* Approximate time when the file was
+ last accessed (obviously this is not
+ updated on read-only volumes). In
+ Windows this is only updated when
+ accessed if some time delta has
+ passed since the last update. Also,
+ last access times updates can be
+ disabled altogether for speed. */
+/* 32*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */
+/* 36*/ union {
+ /* NTFS 1.2 (and previous, presumably) */
+ struct {
+ /* 36 */ u8 reserved12[12]; /* Reserved/alignment to 8-byte
+ boundary. */
+ /* 48 */ void *v1_end[]; /* Marker for offsetof(). */
+ } __attribute__((__packed__)) v12;
+/* sizeof() = 48 bytes */
+ /* NTFS 3.0 */
+ struct {
+/*
+ * If a volume has been upgraded from a previous NTFS version, then these
+ * fields are present only if the file has been accessed since the upgrade.
+ * Recognize the difference by comparing the length of the resident attribute
+ * value. If it is 48, then the following fields are missing. If it is 72 then
+ * the fields are present. Maybe just check like this:
+ * if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) {
+ * Assume NTFS 1.2- format.
+ * If (volume version is 3.0+)
+ * Upgrade attribute to NTFS 3.0 format.
+ * else
+ * Use NTFS 1.2- format for access.
+ * } else
+ * Use NTFS 3.0 format for access.
+ * Only problem is that it might be legal to set the length of the value to
+ * arbitrarily large values thus spoiling this check. - But chkdsk probably
+ * views that as a corruption, assuming that it behaves like this for all
+ * attributes.
+ */
+ /* 36*/ le32 maximum_versions; /* Maximum allowed versions for
+ file. Zero if version numbering is disabled. */
+ /* 40*/ le32 version_number; /* This file's version (if any).
+ Set to zero if maximum_versions is zero. */
+ /* 44*/ le32 class_id; /* Class id from bidirectional
+ class id index (?). */
+ /* 48*/ le32 owner_id; /* Owner_id of the user owning
+ the file. Translate via $Q index in FILE_Extend
+ /$Quota to the quota control entry for the user
+ owning the file. Zero if quotas are disabled. */
+ /* 52*/ le32 security_id; /* Security_id for the file.
+ Translate via $SII index and $SDS data stream
+ in FILE_Secure to the security descriptor. */
+ /* 56*/ le64 quota_charged; /* Byte size of the charge to
+ the quota for all streams of the file. Note: Is
+ zero if quotas are disabled. */
+ /* 64*/ le64 usn; /* Last update sequence number
+ of the file. This is a direct index into the
+ change (aka USN) journal file. It is zero if
+ the USN journal is disabled.
+ NOTE: To disable the journal need to delete
+ the journal file itself and to then walk the
+ whole mft and set all USN entries in all mft
+ records to zero! (This can take a while!)
+ The journal is FILE_Extend/$UsnJrnl. Win2k
+ will recreate the journal and initiate
+ logging if necessary when mounting the
+ partition. This, in contrast to disabling the
+ journal is a very fast process, so the user
+ won't even notice it. */
+ /* 72*/ void *v3_end[]; /* Marker for offsetof(). */
+ } __attribute__((__packed__)) v30;
+ } __attribute__((__packed__)) u;
+/* sizeof() = 72 bytes (NTFS 3.0) */
+} __attribute__((__packed__)) STANDARD_INFORMATION;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct ATTR_LIST_ENTRY - Attribute: Attribute list (0x20).
+ *
+ * - Can be either resident or non-resident.
+ * - Value consists of a sequence of variable length, 8-byte aligned,
+ * ATTR_LIST_ENTRY records.
+ * - The attribute list attribute contains one entry for each attribute of
+ * the file in which the list is located, except for the list attribute
+ * itself. The list is sorted: first by attribute type, second by attribute
+ * name (if present), third by instance number. The extents of one
+ * non-resident attribute (if present) immediately follow after the initial
+ * extent. They are ordered by lowest_vcn and have their instance set to zero.
+ * It is not allowed to have two attributes with all sorting keys equal.
+ * - Further restrictions:
+ * - If not resident, the vcn to lcn mapping array has to fit inside the
+ * base mft record.
+ * - The attribute list attribute value has a maximum size of 256kb. This
+ * is imposed by the Windows cache manager.
+ * - Attribute lists are only used when the attributes of mft record do not
+ * fit inside the mft record despite all attributes (that can be made
+ * non-resident) having been made non-resident. This can happen e.g. when:
+ * - File has a large number of hard links (lots of file name
+ * attributes present).
+ * - The mapping pairs array of some non-resident attribute becomes so
+ * large due to fragmentation that it overflows the mft record.
+ * - The security descriptor is very complex (not applicable to
+ * NTFS 3.0 volumes).
+ * - There are many named streams.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0*/ ATTR_TYPES type; /* Type of referenced attribute. */
+/* 4*/ le16 length; /* Byte size of this entry. */
+/* 6*/ u8 name_length; /* Size in Unicode chars of the name of the
+ attribute or 0 if unnamed. */
+/* 7*/ u8 name_offset; /* Byte offset to beginning of attribute name
+ (always set this to where the name would
+ start even if unnamed). */
+/* 8*/ leVCN lowest_vcn; /* Lowest virtual cluster number of this portion
+ of the attribute value. This is usually 0. It
+ is non-zero for the case where one attribute
+ does not fit into one mft record and thus
+ several mft records are allocated to hold
+ this attribute. In the latter case, each mft
+ record holds one extent of the attribute and
+ there is one attribute list entry for each
+ extent. NOTE: This is DEFINITELY a signed
+ value! The windows driver uses cmp, followed
+ by jg when comparing this, thus it treats it
+ as signed. */
+/* 16*/ leMFT_REF mft_reference;/* The reference of the mft record holding
+ the ATTR_RECORD for this portion of the
+ attribute value. */
+/* 24*/ le16 instance; /* If lowest_vcn = 0, the instance of the
+ attribute being referenced; otherwise 0. */
+/* 26*/ ntfschar name[]; /* Use when creating only. When reading use
+ name_offset to determine the location of the
+ name. */
+/* sizeof() = 26 + (attribute_name_length * 2) bytes */
+} __attribute__((__packed__)) ATTR_LIST_ENTRY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/*
+ * The maximum allowed length for a file name.
+ */
+#define NTFS_MAX_NAME_LEN 255
+
+/**
+ * enum FILE_NAME_TYPE_FLAGS - Possible namespaces for filenames in ntfs.
+ * (8-bit).
+ */
+#ifdef __sun
+typedef uint8_t FILE_NAME_TYPE_FLAGS;
+#define FILE_NAME_POSIX (0x00)
+#define FILE_NAME_WIN32 (0x01)
+#define FILE_NAME_DOS (0x02)
+#define FILE_NAME_WIN32_AND_DOS (0x03)
+#else /* not __sun */
+typedef enum {
+ FILE_NAME_POSIX = 0x00,
+ /* This is the largest namespace. It is case sensitive and
+ allows all Unicode characters except for: '\0' and '/'.
+ Beware that in WinNT/2k files which eg have the same name
+ except for their case will not be distinguished by the
+ standard utilities and thus a "del filename" will delete
+ both "filename" and "fileName" without warning. */
+ FILE_NAME_WIN32 = 0x01,
+ /* The standard WinNT/2k NTFS long filenames. Case insensitive.
+ All Unicode chars except: '\0', '"', '*', '/', ':', '<',
+ '>', '?', '\' and '|'. Further, names cannot end with a '.'
+ or a space. */
+ FILE_NAME_DOS = 0x02,
+ /* The standard DOS filenames (8.3 format). Uppercase only.
+ All 8-bit characters greater space, except: '"', '*', '+',
+ ',', '/', ':', ';', '<', '=', '>', '?' and '\'. */
+ FILE_NAME_WIN32_AND_DOS = 0x03,
+ /* 3 means that both the Win32 and the DOS filenames are
+ identical and hence have been saved in this single filename
+ record. */
+} __attribute__((__packed__)) FILE_NAME_TYPE_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct FILE_NAME_ATTR - Attribute: Filename (0x30).
+ *
+ * NOTE: Always resident.
+ * NOTE: All fields, except the parent_directory, are only updated when the
+ * filename is changed. Until then, they just become out of sync with
+ * reality and the more up to date values are present in the standard
+ * information attribute.
+ * NOTE: There is conflicting information about the meaning of each of the time
+ * fields but the meaning as defined below has been verified to be
+ * correct by practical experimentation on Windows NT4 SP6a and is hence
+ * assumed to be the one and only correct interpretation.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*hex ofs*/
+/* 0*/ leMFT_REF parent_directory; /* Directory this filename is
+ referenced from. */
+/* 8*/ sle64 creation_time; /* Time file was created. */
+/* 10*/ sle64 last_data_change_time; /* Time the data attribute was last
+ modified. */
+/* 18*/ sle64 last_mft_change_time; /* Time this mft record was last
+ modified. */
+/* 20*/ sle64 last_access_time; /* Last time this mft record was
+ accessed. */
+/* 28*/ sle64 allocated_size; /* Byte size of on-disk allocated space
+ for the data attribute. So for
+ normal $DATA, this is the
+ allocated_size from the unnamed
+ $DATA attribute and for compressed
+ and/or sparse $DATA, this is the
+ compressed_size from the unnamed
+ $DATA attribute. NOTE: This is a
+ multiple of the cluster size. */
+/* 30*/ sle64 data_size; /* Byte size of actual data in data
+ attribute. */
+/* 38*/ FILE_ATTR_FLAGS file_attributes; /* Flags describing the file. */
+/* 3c*/ union {
+ /* 3c*/ struct {
+ /* 3c*/ le16 packed_ea_size; /* Size of the buffer needed to
+ pack the extended attributes
+ (EAs), if such are present.*/
+ /* 3e*/ le16 reserved; /* Reserved for alignment. */
+ } __attribute__((__packed__)) s;
+ /* 3c*/ le32 reparse_point_tag; /* Type of reparse point,
+ present only in reparse
+ points and only if there are
+ no EAs. */
+ } __attribute__((__packed__)) u;
+/* 40*/ u8 file_name_length; /* Length of file name in
+ (Unicode) characters. */
+/* 41*/ FILE_NAME_TYPE_FLAGS file_name_type; /* Namespace of the file name.*/
+/* 42*/ ntfschar file_name[]; /* File name in Unicode. */
+} __attribute__((__packed__)) FILE_NAME_ATTR;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct GUID - GUID structures store globally unique identifiers (GUID).
+ *
+ * A GUID is a 128-bit value consisting of one group of eight hexadecimal
+ * digits, followed by three groups of four hexadecimal digits each, followed
+ * by one group of twelve hexadecimal digits. GUIDs are Microsoft's
+ * implementation of the distributed computing environment (DCE) universally
+ * unique identifier (UUID).
+ *
+ * Example of a GUID in string format:
+ * 1F010768-5A73-BC91-0010-A52216A7227B
+ * And the same in binary:
+ * 1F0107685A73BC910010A52216A7227B
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef union {
+ struct {
+ le32 data1; /* The first eight hexadecimal digits of the
+ GUID. */
+ le16 data2; /* The first group of four hexadecimal
+ digits. */
+ le16 data3; /* The second group of four hexadecimal
+ digits. */
+ u8 data4[8]; /* The first two bytes are the third group of
+ four hexadecimal digits. The remaining six
+ bytes are the final 12 hexadecimal digits. */
+ } __attribute__((__packed__)) s;
+ u8 raw[16]; /* Raw binary for ease of access. */
+} __attribute__((__packed__)) GUID;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct OBJ_ID_INDEX_DATA - FILE_Extend/$ObjId contains an index named $O.
+ *
+ * This index contains all object_ids present on the volume as the index keys
+ * and the corresponding mft_record numbers as the index entry data parts.
+ *
+ * The data part (defined below) also contains three other object_ids:
+ * birth_volume_id - object_id of FILE_Volume on which the file was first
+ * created. Optional (i.e. can be zero).
+ * birth_object_id - object_id of file when it was first created. Usually
+ * equals the object_id. Optional (i.e. can be zero).
+ * domain_id - Reserved (always zero).
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ leMFT_REF mft_reference;/* Mft record containing the object_id in
+ the index entry key. */
+ union {
+ struct {
+ GUID birth_volume_id;
+ GUID birth_object_id;
+ GUID domain_id;
+ } __attribute__((__packed__)) s;
+ u8 extended_info[48];
+ } __attribute__((__packed__)) u;
+} __attribute__((__packed__)) OBJ_ID_INDEX_DATA;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct OBJECT_ID_ATTR - Attribute: Object id (NTFS 3.0+) (0x40).
+ *
+ * NOTE: Always resident.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ GUID object_id; /* Unique id assigned to the
+ file.*/
+ /* The following fields are optional. The attribute value size is 16
+ bytes, i.e. sizeof(GUID), if these are not present at all. Note,
+ the entries can be present but one or more (or all) can be zero
+ meaning that that particular value(s) is(are) not defined. Note,
+ when the fields are missing here, it is well possible that they are
+ to be found within the $Extend/$ObjId system file indexed under the
+ above object_id. */
+ union {
+ struct {
+ GUID birth_volume_id; /* Unique id of volume on which
+ the file was first created.*/
+ GUID birth_object_id; /* Unique id of file when it was
+ first created. */
+ GUID domain_id; /* Reserved, zero. */
+ } __attribute__((__packed__)) s;
+ u8 extended_info[48];
+ } __attribute__((__packed__)) u;
+} __attribute__((__packed__)) OBJECT_ID_ATTR;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#if 0
+/**
+ * enum IDENTIFIER_AUTHORITIES -
+ *
+ * The pre-defined IDENTIFIER_AUTHORITIES used as SID_IDENTIFIER_AUTHORITY in
+ * the SID structure (see below).
+ */
+typedef enum { /* SID string prefix. */
+ SECURITY_NULL_SID_AUTHORITY = {0, 0, 0, 0, 0, 0}, /* S-1-0 */
+ SECURITY_WORLD_SID_AUTHORITY = {0, 0, 0, 0, 0, 1}, /* S-1-1 */
+ SECURITY_LOCAL_SID_AUTHORITY = {0, 0, 0, 0, 0, 2}, /* S-1-2 */
+ SECURITY_CREATOR_SID_AUTHORITY = {0, 0, 0, 0, 0, 3}, /* S-1-3 */
+ SECURITY_NON_UNIQUE_AUTHORITY = {0, 0, 0, 0, 0, 4}, /* S-1-4 */
+ SECURITY_NT_SID_AUTHORITY = {0, 0, 0, 0, 0, 5}, /* S-1-5 */
+} IDENTIFIER_AUTHORITIES;
+#endif
+
+/**
+ * enum RELATIVE_IDENTIFIERS -
+ *
+ * These relative identifiers (RIDs) are used with the above identifier
+ * authorities to make up universal well-known SIDs.
+ *
+ * Note: The relative identifier (RID) refers to the portion of a SID, which
+ * identifies a user or group in relation to the authority that issued the SID.
+ * For example, the universal well-known SID Creator Owner ID (S-1-3-0) is
+ * made up of the identifier authority SECURITY_CREATOR_SID_AUTHORITY (3) and
+ * the relative identifier SECURITY_CREATOR_OWNER_RID (0).
+ */
+typedef enum { /* Identifier authority. */
+ SECURITY_NULL_RID = 0, /* S-1-0 */
+ SECURITY_WORLD_RID = 0, /* S-1-1 */
+ SECURITY_LOCAL_RID = 0, /* S-1-2 */
+
+ SECURITY_CREATOR_OWNER_RID = 0, /* S-1-3 */
+ SECURITY_CREATOR_GROUP_RID = 1, /* S-1-3 */
+
+ SECURITY_CREATOR_OWNER_SERVER_RID = 2, /* S-1-3 */
+ SECURITY_CREATOR_GROUP_SERVER_RID = 3, /* S-1-3 */
+
+ SECURITY_DIALUP_RID = 1,
+ SECURITY_NETWORK_RID = 2,
+ SECURITY_BATCH_RID = 3,
+ SECURITY_INTERACTIVE_RID = 4,
+ SECURITY_SERVICE_RID = 6,
+ SECURITY_ANONYMOUS_LOGON_RID = 7,
+ SECURITY_PROXY_RID = 8,
+ SECURITY_ENTERPRISE_CONTROLLERS_RID=9,
+ SECURITY_SERVER_LOGON_RID = 9,
+ SECURITY_PRINCIPAL_SELF_RID = 0xa,
+ SECURITY_AUTHENTICATED_USER_RID = 0xb,
+ SECURITY_RESTRICTED_CODE_RID = 0xc,
+ SECURITY_TERMINAL_SERVER_RID = 0xd,
+
+ SECURITY_LOGON_IDS_RID = 5,
+ SECURITY_LOGON_IDS_RID_COUNT = 3,
+
+ SECURITY_LOCAL_SYSTEM_RID = 0x12,
+
+ SECURITY_NT_NON_UNIQUE = 0x15,
+
+ SECURITY_BUILTIN_DOMAIN_RID = 0x20,
+
+ /*
+ * Well-known domain relative sub-authority values (RIDs).
+ */
+
+ /* Users. */
+ DOMAIN_USER_RID_ADMIN = 0x1f4,
+ DOMAIN_USER_RID_GUEST = 0x1f5,
+ DOMAIN_USER_RID_KRBTGT = 0x1f6,
+
+ /* Groups. */
+ DOMAIN_GROUP_RID_ADMINS = 0x200,
+ DOMAIN_GROUP_RID_USERS = 0x201,
+ DOMAIN_GROUP_RID_GUESTS = 0x202,
+ DOMAIN_GROUP_RID_COMPUTERS = 0x203,
+ DOMAIN_GROUP_RID_CONTROLLERS = 0x204,
+ DOMAIN_GROUP_RID_CERT_ADMINS = 0x205,
+ DOMAIN_GROUP_RID_SCHEMA_ADMINS = 0x206,
+ DOMAIN_GROUP_RID_ENTERPRISE_ADMINS= 0x207,
+ DOMAIN_GROUP_RID_POLICY_ADMINS = 0x208,
+
+ /* Aliases. */
+ DOMAIN_ALIAS_RID_ADMINS = 0x220,
+ DOMAIN_ALIAS_RID_USERS = 0x221,
+ DOMAIN_ALIAS_RID_GUESTS = 0x222,
+ DOMAIN_ALIAS_RID_POWER_USERS = 0x223,
+
+ DOMAIN_ALIAS_RID_ACCOUNT_OPS = 0x224,
+ DOMAIN_ALIAS_RID_SYSTEM_OPS = 0x225,
+ DOMAIN_ALIAS_RID_PRINT_OPS = 0x226,
+ DOMAIN_ALIAS_RID_BACKUP_OPS = 0x227,
+
+ DOMAIN_ALIAS_RID_REPLICATOR = 0x228,
+ DOMAIN_ALIAS_RID_RAS_SERVERS = 0x229,
+ DOMAIN_ALIAS_RID_PREW2KCOMPACCESS = 0x22a,
+} RELATIVE_IDENTIFIERS;
+
+/*
+ * The universal well-known SIDs:
+ *
+ * NULL_SID S-1-0-0
+ * WORLD_SID S-1-1-0
+ * LOCAL_SID S-1-2-0
+ * CREATOR_OWNER_SID S-1-3-0
+ * CREATOR_GROUP_SID S-1-3-1
+ * CREATOR_OWNER_SERVER_SID S-1-3-2
+ * CREATOR_GROUP_SERVER_SID S-1-3-3
+ *
+ * (Non-unique IDs) S-1-4
+ *
+ * NT well-known SIDs:
+ *
+ * NT_AUTHORITY_SID S-1-5
+ * DIALUP_SID S-1-5-1
+ *
+ * NETWORK_SID S-1-5-2
+ * BATCH_SID S-1-5-3
+ * INTERACTIVE_SID S-1-5-4
+ * SERVICE_SID S-1-5-6
+ * ANONYMOUS_LOGON_SID S-1-5-7 (aka null logon session)
+ * PROXY_SID S-1-5-8
+ * SERVER_LOGON_SID S-1-5-9 (aka domain controller account)
+ * SELF_SID S-1-5-10 (self RID)
+ * AUTHENTICATED_USER_SID S-1-5-11
+ * RESTRICTED_CODE_SID S-1-5-12 (running restricted code)
+ * TERMINAL_SERVER_SID S-1-5-13 (running on terminal server)
+ *
+ * (Logon IDs) S-1-5-5-X-Y
+ *
+ * (NT non-unique IDs) S-1-5-0x15-...
+ *
+ * (Built-in domain) S-1-5-0x20
+ */
+
+/**
+ * union SID_IDENTIFIER_AUTHORITY - A 48-bit value used in the SID structure
+ *
+ * NOTE: This is stored as a big endian number.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef union {
+ struct {
+ be16 high_part; /* High 16-bits. */
+ be32 low_part; /* Low 32-bits. */
+ } __attribute__((__packed__)) s;
+ u8 value[6]; /* Value as individual bytes. */
+} __attribute__((__packed__)) SID_IDENTIFIER_AUTHORITY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SID -
+ *
+ * The SID structure is a variable-length structure used to uniquely identify
+ * users or groups. SID stands for security identifier.
+ *
+ * The standard textual representation of the SID is of the form:
+ * S-R-I-S-S...
+ * Where:
+ * - The first "S" is the literal character 'S' identifying the following
+ * digits as a SID.
+ * - R is the revision level of the SID expressed as a sequence of digits
+ * in decimal.
+ * - I is the 48-bit identifier_authority, expressed as digits in decimal,
+ * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
+ * - S... is one or more sub_authority values, expressed as digits in
+ * decimal.
+ *
+ * Example SID; the domain-relative SID of the local Administrators group on
+ * Windows NT/2k:
+ * S-1-5-32-544
+ * This translates to a SID with:
+ * revision = 1,
+ * sub_authority_count = 2,
+ * identifier_authority = {0,0,0,0,0,5}, // SECURITY_NT_AUTHORITY
+ * sub_authority[0] = 32, // SECURITY_BUILTIN_DOMAIN_RID
+ * sub_authority[1] = 544 // DOMAIN_ALIAS_RID_ADMINS
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u8 revision;
+ u8 sub_authority_count;
+ SID_IDENTIFIER_AUTHORITY identifier_authority;
+ le32 sub_authority[1]; /* At least one sub_authority. */
+} __attribute__((__packed__)) SID;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum SID_CONSTANTS - Current constants for SIDs.
+ */
+typedef enum {
+ SID_REVISION = 1, /* Current revision level. */
+ SID_MAX_SUB_AUTHORITIES = 15, /* Maximum number of those. */
+ SID_RECOMMENDED_SUB_AUTHORITIES = 1, /* Will change to around 6 in
+ a future revision. */
+} SID_CONSTANTS;
+
+/**
+ * enum ACE_TYPES - The predefined ACE types (8-bit, see below).
+ */
+#ifdef __sun
+typedef uint8_t ACE_TYPES;
+#define ACCESS_ALLOWED_ACE_TYPE (0)
+#define ACCESS_DENIED_ACE_TYPE (1)
+#define SYSTEM_AUDIT_ACE_TYPE (2)
+#else /* not __sun */
+typedef enum {
+ ACCESS_MIN_MS_ACE_TYPE = 0,
+ ACCESS_ALLOWED_ACE_TYPE = 0,
+ ACCESS_DENIED_ACE_TYPE = 1,
+ SYSTEM_AUDIT_ACE_TYPE = 2,
+ SYSTEM_ALARM_ACE_TYPE = 3, /* Not implemented as of Win2k. */
+ ACCESS_MAX_MS_V2_ACE_TYPE = 3,
+
+ ACCESS_ALLOWED_COMPOUND_ACE_TYPE= 4,
+ ACCESS_MAX_MS_V3_ACE_TYPE = 4,
+
+ /* The following are Win2k only. */
+ ACCESS_MIN_MS_OBJECT_ACE_TYPE = 5,
+ ACCESS_ALLOWED_OBJECT_ACE_TYPE = 5,
+ ACCESS_DENIED_OBJECT_ACE_TYPE = 6,
+ SYSTEM_AUDIT_OBJECT_ACE_TYPE = 7,
+ SYSTEM_ALARM_OBJECT_ACE_TYPE = 8,
+ ACCESS_MAX_MS_OBJECT_ACE_TYPE = 8,
+
+ ACCESS_MAX_MS_V4_ACE_TYPE = 8,
+
+ /* This one is for WinNT&2k. */
+ ACCESS_MAX_MS_ACE_TYPE = 8,
+} __attribute__((__packed__)) ACE_TYPES;
+#endif /* __sun */
+
+/**
+ * enum ACE_FLAGS - The ACE flags (8-bit) for audit and inheritance.
+ *
+ * SUCCESSFUL_ACCESS_ACE_FLAG is only used with system audit and alarm ACE
+ * types to indicate that a message is generated (in Windows!) for successful
+ * accesses.
+ *
+ * FAILED_ACCESS_ACE_FLAG is only used with system audit and alarm ACE types
+ * to indicate that a message is generated (in Windows!) for failed accesses.
+ */
+#ifdef __sun
+typedef uint8_t ACE_FLAGS;
+#define OBJECT_INHERIT_ACE (0x01)
+#define CONTAINER_INHERIT_ACE (0x02)
+#define INHERIT_ONLY_ACE (0x08)
+#else /* not __sun */
+typedef enum {
+ /* The inheritance flags. */
+ OBJECT_INHERIT_ACE = 0x01,
+ CONTAINER_INHERIT_ACE = 0x02,
+ NO_PROPAGATE_INHERIT_ACE = 0x04,
+ INHERIT_ONLY_ACE = 0x08,
+ INHERITED_ACE = 0x10, /* Win2k only. */
+ VALID_INHERIT_FLAGS = 0x1f,
+
+ /* The audit flags. */
+ SUCCESSFUL_ACCESS_ACE_FLAG = 0x40,
+ FAILED_ACCESS_ACE_FLAG = 0x80,
+} __attribute__((__packed__)) ACE_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct ACE_HEADER -
+ *
+ * An ACE is an access-control entry in an access-control list (ACL).
+ * An ACE defines access to an object for a specific user or group or defines
+ * the types of access that generate system-administration messages or alarms
+ * for a specific user or group. The user or group is identified by a security
+ * identifier (SID).
+ *
+ * Each ACE starts with an ACE_HEADER structure (aligned on 4-byte boundary),
+ * which specifies the type and size of the ACE. The format of the subsequent
+ * data depends on the ACE type.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ ACE_TYPES type; /* Type of the ACE. */
+ ACE_FLAGS flags; /* Flags describing the ACE. */
+ le16 size; /* Size in bytes of the ACE. */
+} __attribute__((__packed__)) ACE_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum ACCESS_MASK - The access mask (32-bit).
+ *
+ * Defines the access rights.
+ */
+typedef enum {
+ /*
+ * The specific rights (bits 0 to 15). Depend on the type of the
+ * object being secured by the ACE.
+ */
+
+ /* Specific rights for files and directories are as follows: */
+
+ /* Right to read data from the file. (FILE) */
+ FILE_READ_DATA = const_cpu_to_le32(0x00000001),
+ /* Right to list contents of a directory. (DIRECTORY) */
+ FILE_LIST_DIRECTORY = const_cpu_to_le32(0x00000001),
+
+ /* Right to write data to the file. (FILE) */
+ FILE_WRITE_DATA = const_cpu_to_le32(0x00000002),
+ /* Right to create a file in the directory. (DIRECTORY) */
+ FILE_ADD_FILE = const_cpu_to_le32(0x00000002),
+
+ /* Right to append data to the file. (FILE) */
+ FILE_APPEND_DATA = const_cpu_to_le32(0x00000004),
+ /* Right to create a subdirectory. (DIRECTORY) */
+ FILE_ADD_SUBDIRECTORY = const_cpu_to_le32(0x00000004),
+
+ /* Right to read extended attributes. (FILE/DIRECTORY) */
+ FILE_READ_EA = const_cpu_to_le32(0x00000008),
+
+ /* Right to write extended attributes. (FILE/DIRECTORY) */
+ FILE_WRITE_EA = const_cpu_to_le32(0x00000010),
+
+ /* Right to execute a file. (FILE) */
+ FILE_EXECUTE = const_cpu_to_le32(0x00000020),
+ /* Right to traverse the directory. (DIRECTORY) */
+ FILE_TRAVERSE = const_cpu_to_le32(0x00000020),
+
+ /*
+ * Right to delete a directory and all the files it contains (its
+ * children), even if the files are read-only. (DIRECTORY)
+ */
+ FILE_DELETE_CHILD = const_cpu_to_le32(0x00000040),
+
+ /* Right to read file attributes. (FILE/DIRECTORY) */
+ FILE_READ_ATTRIBUTES = const_cpu_to_le32(0x00000080),
+
+ /* Right to change file attributes. (FILE/DIRECTORY) */
+ FILE_WRITE_ATTRIBUTES = const_cpu_to_le32(0x00000100),
+
+ /*
+ * The standard rights (bits 16 to 23). Are independent of the type of
+ * object being secured.
+ */
+
+ /* Right to delete the object. */
+ DELETE = const_cpu_to_le32(0x00010000),
+
+ /*
+ * Right to read the information in the object's security descriptor,
+ * not including the information in the SACL. I.e. right to read the
+ * security descriptor and owner.
+ */
+ READ_CONTROL = const_cpu_to_le32(0x00020000),
+
+ /* Right to modify the DACL in the object's security descriptor. */
+ WRITE_DAC = const_cpu_to_le32(0x00040000),
+
+ /* Right to change the owner in the object's security descriptor. */
+ WRITE_OWNER = const_cpu_to_le32(0x00080000),
+
+ /*
+ * Right to use the object for synchronization. Enables a process to
+ * wait until the object is in the signalled state. Some object types
+ * do not support this access right.
+ */
+ SYNCHRONIZE = const_cpu_to_le32(0x00100000),
+
+ /*
+ * The following STANDARD_RIGHTS_* are combinations of the above for
+ * convenience and are defined by the Win32 API.
+ */
+
+ /* These are currently defined to READ_CONTROL. */
+ STANDARD_RIGHTS_READ = const_cpu_to_le32(0x00020000),
+ STANDARD_RIGHTS_WRITE = const_cpu_to_le32(0x00020000),
+ STANDARD_RIGHTS_EXECUTE = const_cpu_to_le32(0x00020000),
+
+ /* Combines DELETE, READ_CONTROL, WRITE_DAC, and WRITE_OWNER access. */
+ STANDARD_RIGHTS_REQUIRED = const_cpu_to_le32(0x000f0000),
+
+ /*
+ * Combines DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER, and
+ * SYNCHRONIZE access.
+ */
+ STANDARD_RIGHTS_ALL = const_cpu_to_le32(0x001f0000),
+
+ /*
+ * The access system ACL and maximum allowed access types (bits 24 to
+ * 25, bits 26 to 27 are reserved).
+ */
+ ACCESS_SYSTEM_SECURITY = const_cpu_to_le32(0x01000000),
+ MAXIMUM_ALLOWED = const_cpu_to_le32(0x02000000),
+
+ /*
+ * The generic rights (bits 28 to 31). These map onto the standard and
+ * specific rights.
+ */
+
+ /* Read, write, and execute access. */
+ GENERIC_ALL = const_cpu_to_le32(0x10000000),
+
+ /* Execute access. */
+ GENERIC_EXECUTE = const_cpu_to_le32(0x20000000),
+
+ /*
+ * Write access. For files, this maps onto:
+ * FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA |
+ * FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE
+ * For directories, the mapping has the same numerical value. See
+ * above for the descriptions of the rights granted.
+ */
+ GENERIC_WRITE = const_cpu_to_le32(0x40000000),
+
+ /*
+ * Read access. For files, this maps onto:
+ * FILE_READ_ATTRIBUTES | FILE_READ_DATA | FILE_READ_EA |
+ * STANDARD_RIGHTS_READ | SYNCHRONIZE
+ * For directories, the mapping has the same numerical value. See
+ * above for the descriptions of the rights granted.
+ */
+ GENERIC_READ = const_cpu_to_le32(0x80000000),
+} ACCESS_MASK;
+
+/**
+ * struct GENERIC_MAPPING -
+ *
+ * The generic mapping array. Used to denote the mapping of each generic
+ * access right to a specific access mask.
+ *
+ * FIXME: What exactly is this and what is it for? (AIA)
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ ACCESS_MASK generic_read;
+ ACCESS_MASK generic_write;
+ ACCESS_MASK generic_execute;
+ ACCESS_MASK generic_all;
+} __attribute__((__packed__)) GENERIC_MAPPING;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/*
+ * The predefined ACE type structures are as defined below.
+ */
+
+/**
+ * struct ACCESS_DENIED_ACE -
+ *
+ * ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE, SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */
+ ACE_TYPES type; /* Type of the ACE. */
+ ACE_FLAGS flags; /* Flags describing the ACE. */
+ le16 size; /* Size in bytes of the ACE. */
+
+/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */
+/* 8*/ SID sid; /* The SID associated with the ACE. */
+} __attribute__((__packed__)) ACCESS_ALLOWED_ACE, ACCESS_DENIED_ACE,
+ SYSTEM_AUDIT_ACE, SYSTEM_ALARM_ACE;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum OBJECT_ACE_FLAGS - The object ACE flags (32-bit).
+ */
+typedef enum {
+ ACE_OBJECT_TYPE_PRESENT = const_cpu_to_le32(1),
+ ACE_INHERITED_OBJECT_TYPE_PRESENT = const_cpu_to_le32(2),
+} OBJECT_ACE_FLAGS;
+
+/**
+ * struct ACCESS_ALLOWED_OBJECT_ACE -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 ACE_HEADER; -- Unfolded here as gcc doesn't like unnamed structs. */
+ ACE_TYPES type; /* Type of the ACE. */
+ ACE_FLAGS flags; /* Flags describing the ACE. */
+ le16 size; /* Size in bytes of the ACE. */
+
+/* 4*/ ACCESS_MASK mask; /* Access mask associated with the ACE. */
+/* 8*/ OBJECT_ACE_FLAGS object_flags; /* Flags describing the object ACE. */
+/* 12*/ GUID object_type;
+/* 28*/ GUID inherited_object_type;
+/* 44*/ SID sid; /* The SID associated with the ACE. */
+} __attribute__((__packed__)) ACCESS_ALLOWED_OBJECT_ACE,
+ ACCESS_DENIED_OBJECT_ACE,
+ SYSTEM_AUDIT_OBJECT_ACE,
+ SYSTEM_ALARM_OBJECT_ACE;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct ACL - An ACL is an access-control list (ACL).
+ *
+ * An ACL starts with an ACL header structure, which specifies the size of
+ * the ACL and the number of ACEs it contains. The ACL header is followed by
+ * zero or more access control entries (ACEs). The ACL as well as each ACE
+ * are aligned on 4-byte boundaries.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u8 revision; /* Revision of this ACL. */
+ u8 alignment1;
+ le16 size; /* Allocated space in bytes for ACL. Includes this
+ header, the ACEs and the remaining free space. */
+ le16 ace_count; /* Number of ACEs in the ACL. */
+ le16 alignment2;
+/* sizeof() = 8 bytes */
+} __attribute__((__packed__)) ACL;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum ACL_CONSTANTS - Current constants for ACLs.
+ */
+typedef enum {
+ /* Current revision. */
+ ACL_REVISION = 2,
+ ACL_REVISION_DS = 4,
+
+ /* History of revisions. */
+ ACL_REVISION1 = 1,
+ MIN_ACL_REVISION = 2,
+ ACL_REVISION2 = 2,
+ ACL_REVISION3 = 3,
+ ACL_REVISION4 = 4,
+ MAX_ACL_REVISION = 4,
+} ACL_CONSTANTS;
+
+/**
+ * enum SECURITY_DESCRIPTOR_CONTROL -
+ *
+ * The security descriptor control flags (16-bit).
+ *
+ * SE_OWNER_DEFAULTED - This boolean flag, when set, indicates that the
+ * SID pointed to by the Owner field was provided by a
+ * defaulting mechanism rather than explicitly provided by the
+ * original provider of the security descriptor. This may
+ * affect the treatment of the SID with respect to inheritance
+ * of an owner.
+ *
+ * SE_GROUP_DEFAULTED - This boolean flag, when set, indicates that the
+ * SID in the Group field was provided by a defaulting mechanism
+ * rather than explicitly provided by the original provider of
+ * the security descriptor. This may affect the treatment of
+ * the SID with respect to inheritance of a primary group.
+ *
+ * SE_DACL_PRESENT - This boolean flag, when set, indicates that the
+ * security descriptor contains a discretionary ACL. If this
+ * flag is set and the Dacl field of the SECURITY_DESCRIPTOR is
+ * null, then a null ACL is explicitly being specified.
+ *
+ * SE_DACL_DEFAULTED - This boolean flag, when set, indicates that the
+ * ACL pointed to by the Dacl field was provided by a defaulting
+ * mechanism rather than explicitly provided by the original
+ * provider of the security descriptor. This may affect the
+ * treatment of the ACL with respect to inheritance of an ACL.
+ * This flag is ignored if the DaclPresent flag is not set.
+ *
+ * SE_SACL_PRESENT - This boolean flag, when set, indicates that the
+ * security descriptor contains a system ACL pointed to by the
+ * Sacl field. If this flag is set and the Sacl field of the
+ * SECURITY_DESCRIPTOR is null, then an empty (but present)
+ * ACL is being specified.
+ *
+ * SE_SACL_DEFAULTED - This boolean flag, when set, indicates that the
+ * ACL pointed to by the Sacl field was provided by a defaulting
+ * mechanism rather than explicitly provided by the original
+ * provider of the security descriptor. This may affect the
+ * treatment of the ACL with respect to inheritance of an ACL.
+ * This flag is ignored if the SaclPresent flag is not set.
+ *
+ * SE_SELF_RELATIVE - This boolean flag, when set, indicates that the
+ * security descriptor is in self-relative form. In this form,
+ * all fields of the security descriptor are contiguous in memory
+ * and all pointer fields are expressed as offsets from the
+ * beginning of the security descriptor.
+ */
+#ifdef __sun
+typedef uint16_t SECURITY_DESCRIPTOR_CONTROL;
+#define SE_DACL_PRESENT (const_cpu_to_le16(0x0004))
+#define SE_DACL_DEFAULTED (const_cpu_to_le16(0x0008))
+#define SE_SACL_PRESENT (const_cpu_to_le16(0x0010))
+#define SE_SACL_DEFAULTED (const_cpu_to_le16(0x0020))
+#define SE_SELF_RELATIVE (const_cpu_to_le16(0x8000))
+#else /* not __sun */
+typedef enum {
+ SE_OWNER_DEFAULTED = const_cpu_to_le16(0x0001),
+ SE_GROUP_DEFAULTED = const_cpu_to_le16(0x0002),
+ SE_DACL_PRESENT = const_cpu_to_le16(0x0004),
+ SE_DACL_DEFAULTED = const_cpu_to_le16(0x0008),
+ SE_SACL_PRESENT = const_cpu_to_le16(0x0010),
+ SE_SACL_DEFAULTED = const_cpu_to_le16(0x0020),
+ SE_DACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0100),
+ SE_SACL_AUTO_INHERIT_REQ = const_cpu_to_le16(0x0200),
+ SE_DACL_AUTO_INHERITED = const_cpu_to_le16(0x0400),
+ SE_SACL_AUTO_INHERITED = const_cpu_to_le16(0x0800),
+ SE_DACL_PROTECTED = const_cpu_to_le16(0x1000),
+ SE_SACL_PROTECTED = const_cpu_to_le16(0x2000),
+ SE_RM_CONTROL_VALID = const_cpu_to_le16(0x4000),
+ SE_SELF_RELATIVE = const_cpu_to_le16(0x8000),
+} __attribute__((__packed__)) SECURITY_DESCRIPTOR_CONTROL;
+#endif /* __sun */
+
+/**
+ * struct SECURITY_DESCRIPTOR_RELATIVE -
+ *
+ * Self-relative security descriptor. Contains the owner and group SIDs as well
+ * as the sacl and dacl ACLs inside the security descriptor itself.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u8 revision; /* Revision level of the security descriptor. */
+ u8 alignment;
+ SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of
+ the descriptor as well as the following fields. */
+ le32 owner; /* Byte offset to a SID representing an object's
+ owner. If this is NULL, no owner SID is present in
+ the descriptor. */
+ le32 group; /* Byte offset to a SID representing an object's
+ primary group. If this is NULL, no primary group
+ SID is present in the descriptor. */
+ le32 sacl; /* Byte offset to a system ACL. Only valid, if
+ SE_SACL_PRESENT is set in the control field. If
+ SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
+ is specified. */
+ le32 dacl; /* Byte offset to a discretionary ACL. Only valid, if
+ SE_DACL_PRESENT is set in the control field. If
+ SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
+ (unconditionally granting access) is specified. */
+/* sizeof() = 0x14 bytes */
+} __attribute__((__packed__)) SECURITY_DESCRIPTOR_RELATIVE;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SECURITY_DESCRIPTOR - Absolute security descriptor.
+ *
+ * Does not contain the owner and group SIDs, nor the sacl and dacl ACLs inside
+ * the security descriptor. Instead, it contains pointers to these structures
+ * in memory. Obviously, absolute security descriptors are only useful for in
+ * memory representations of security descriptors.
+ *
+ * On disk, a self-relative security descriptor is used.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u8 revision; /* Revision level of the security descriptor. */
+ u8 alignment;
+ SECURITY_DESCRIPTOR_CONTROL control; /* Flags qualifying the type of
+ the descriptor as well as the following fields. */
+ SID *owner; /* Points to a SID representing an object's owner. If
+ this is NULL, no owner SID is present in the
+ descriptor. */
+ SID *group; /* Points to a SID representing an object's primary
+ group. If this is NULL, no primary group SID is
+ present in the descriptor. */
+ ACL *sacl; /* Points to a system ACL. Only valid, if
+ SE_SACL_PRESENT is set in the control field. If
+ SE_SACL_PRESENT is set but sacl is NULL, a NULL ACL
+ is specified. */
+ ACL *dacl; /* Points to a discretionary ACL. Only valid, if
+ SE_DACL_PRESENT is set in the control field. If
+ SE_DACL_PRESENT is set but dacl is NULL, a NULL ACL
+ (unconditionally granting access) is specified. */
+} __attribute__((__packed__)) SECURITY_DESCRIPTOR;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum SECURITY_DESCRIPTOR_CONSTANTS -
+ *
+ * Current constants for security descriptors.
+ */
+typedef enum {
+ /* Current revision. */
+ SECURITY_DESCRIPTOR_REVISION = 1,
+ SECURITY_DESCRIPTOR_REVISION1 = 1,
+
+ /* The sizes of both the absolute and relative security descriptors is
+ the same as pointers, at least on ia32 architecture are 32-bit. */
+ SECURITY_DESCRIPTOR_MIN_LENGTH = sizeof(SECURITY_DESCRIPTOR),
+} SECURITY_DESCRIPTOR_CONSTANTS;
+
+/*
+ * Attribute: Security descriptor (0x50).
+ *
+ * A standard self-relative security descriptor.
+ *
+ * NOTE: Can be resident or non-resident.
+ * NOTE: Not used in NTFS 3.0+, as security descriptors are stored centrally
+ * in FILE_Secure and the correct descriptor is found using the security_id
+ * from the standard information attribute.
+ */
+typedef SECURITY_DESCRIPTOR_RELATIVE SECURITY_DESCRIPTOR_ATTR;
+
+/*
+ * On NTFS 3.0+, all security descriptors are stored in FILE_Secure. Only one
+ * referenced instance of each unique security descriptor is stored.
+ *
+ * FILE_Secure contains no unnamed data attribute, i.e. it has zero length. It
+ * does, however, contain two indexes ($SDH and $SII) as well as a named data
+ * stream ($SDS).
+ *
+ * Every unique security descriptor is assigned a unique security identifier
+ * (security_id, not to be confused with a SID). The security_id is unique for
+ * the NTFS volume and is used as an index into the $SII index, which maps
+ * security_ids to the security descriptor's storage location within the $SDS
+ * data attribute. The $SII index is sorted by ascending security_id.
+ *
+ * A simple hash is computed from each security descriptor. This hash is used
+ * as an index into the $SDH index, which maps security descriptor hashes to
+ * the security descriptor's storage location within the $SDS data attribute.
+ * The $SDH index is sorted by security descriptor hash and is stored in a B+
+ * tree. When searching $SDH (with the intent of determining whether or not a
+ * new security descriptor is already present in the $SDS data stream), if a
+ * matching hash is found, but the security descriptors do not match, the
+ * search in the $SDH index is continued, searching for a next matching hash.
+ *
+ * When a precise match is found, the security_id corresponding to the security
+ * descriptor in the $SDS attribute is read from the found $SDH index entry and
+ * is stored in the $STANDARD_INFORMATION attribute of the file/directory to
+ * which the security descriptor is being applied. The $STANDARD_INFORMATION
+ * attribute is present in all base mft records (i.e. in all files and
+ * directories).
+ *
+ * If a match is not found, the security descriptor is assigned a new unique
+ * security_id and is added to the $SDS data attribute. Then, entries
+ * referencing the this security descriptor in the $SDS data attribute are
+ * added to the $SDH and $SII indexes.
+ *
+ * Note: Entries are never deleted from FILE_Secure, even if nothing
+ * references an entry any more.
+ */
+
+/**
+ * struct SECURITY_DESCRIPTOR_HEADER -
+ *
+ * This header precedes each security descriptor in the $SDS data stream.
+ * This is also the index entry data part of both the $SII and $SDH indexes.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 hash; /* Hash of the security descriptor. */
+ le32 security_id; /* The security_id assigned to the descriptor. */
+ le64 offset; /* Byte offset of this entry in the $SDS stream. */
+ le32 length; /* Size in bytes of this entry in $SDS stream. */
+} __attribute__((__packed__)) SECURITY_DESCRIPTOR_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SDH_INDEX_DATA -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 hash; /* Hash of the security descriptor. */
+ le32 security_id; /* The security_id assigned to the descriptor. */
+ le64 offset; /* Byte offset of this entry in the $SDS stream. */
+ le32 length; /* Size in bytes of this entry in $SDS stream. */
+ le32 reserved_II; /* Padding - always unicode "II" or zero. This field
+ isn't counted in INDEX_ENTRY's data_length. */
+} __attribute__((__packed__)) SDH_INDEX_DATA;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SII_INDEX_DATA -
+ */
+typedef SECURITY_DESCRIPTOR_HEADER SII_INDEX_DATA;
+
+/**
+ * struct SDS_ENTRY -
+ *
+ * The $SDS data stream contains the security descriptors, aligned on 16-byte
+ * boundaries, sorted by security_id in a B+ tree. Security descriptors cannot
+ * cross 256kib boundaries (this restriction is imposed by the Windows cache
+ * manager). Each security descriptor is contained in a SDS_ENTRY structure.
+ * Also, each security descriptor is stored twice in the $SDS stream with a
+ * fixed offset of 0x40000 bytes (256kib, the Windows cache manager's max size)
+ * between them; i.e. if a SDS_ENTRY specifies an offset of 0x51d0, then the
+ * the first copy of the security descriptor will be at offset 0x51d0 in the
+ * $SDS data stream and the second copy will be at offset 0x451d0.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 SECURITY_DESCRIPTOR_HEADER; -- Unfolded here as gcc doesn't like
+ unnamed structs. */
+ le32 hash; /* Hash of the security descriptor. */
+ le32 security_id; /* The security_id assigned to the descriptor. */
+ le64 offset; /* Byte offset of this entry in the $SDS stream. */
+ le32 length; /* Size in bytes of this entry in $SDS stream. */
+/* 20*/ SECURITY_DESCRIPTOR_RELATIVE sid; /* The self-relative security
+ descriptor. */
+} __attribute__((__packed__)) SDS_ENTRY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SII_INDEX_KEY - The index entry key used in the $SII index.
+ *
+ * The collation type is COLLATION_NTOFS_ULONG.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 security_id; /* The security_id assigned to the descriptor. */
+} __attribute__((__packed__)) SII_INDEX_KEY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct SDH_INDEX_KEY - The index entry key used in the $SDH index.
+ *
+ * The keys are sorted first by hash and then by security_id.
+ * The collation rule is COLLATION_NTOFS_SECURITY_HASH.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 hash; /* Hash of the security descriptor. */
+ le32 security_id; /* The security_id assigned to the descriptor. */
+} __attribute__((__packed__)) SDH_INDEX_KEY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifndef __sun
+/**
+ * struct VOLUME_NAME - Attribute: Volume name (0x60).
+ *
+ * NOTE: Always resident.
+ * NOTE: Present only in FILE_Volume.
+ */
+typedef struct {
+ ntfschar name[]; /* The name of the volume in Unicode. */
+} __attribute__((__packed__)) VOLUME_NAME;
+#endif
+
+/**
+ * enum VOLUME_FLAGS - Possible flags for the volume (16-bit).
+ *
+ * WARNING: Setting VOLUME_MOUNTED_ON_NT4 on a Volume causes Windows Vista to
+ * fail to boot (it hangs on a black screen).
+ */
+#ifdef __sun
+typedef uint16_t VOLUME_FLAGS;
+#define VOLUME_IS_DIRTY (const_cpu_to_le16(0x0001))
+#define VOLUME_RESIZE_LOG_FILE (const_cpu_to_le16(0x0002))
+#define VOLUME_UPGRADE_ON_MOUNT (const_cpu_to_le16(0x0004))
+#define VOLUME_MOUNTED_ON_NT4 (const_cpu_to_le16(0x0008))
+#define VOLUME_DELETE_USN_UNDERWAY (const_cpu_to_le16(0x0010))
+#define VOLUME_REPAIR_OBJECT_ID (const_cpu_to_le16(0x0020))
+#define VOLUME_CHKDSK_UNDERWAY (const_cpu_to_le16(0x4000))
+#define VOLUME_MODIFIED_BY_CHKDSK (const_cpu_to_le16(0x8000))
+#define VOLUME_FLAGS_MASK (const_cpu_to_le16(0xc03f))
+#else /* not __sun */
+typedef enum {
+ VOLUME_IS_DIRTY = const_cpu_to_le16(0x0001),
+ VOLUME_RESIZE_LOG_FILE = const_cpu_to_le16(0x0002),
+ VOLUME_UPGRADE_ON_MOUNT = const_cpu_to_le16(0x0004),
+ VOLUME_MOUNTED_ON_NT4 = const_cpu_to_le16(0x0008),
+ VOLUME_DELETE_USN_UNDERWAY = const_cpu_to_le16(0x0010),
+ VOLUME_REPAIR_OBJECT_ID = const_cpu_to_le16(0x0020),
+ VOLUME_CHKDSK_UNDERWAY = const_cpu_to_le16(0x4000),
+ VOLUME_MODIFIED_BY_CHKDSK = const_cpu_to_le16(0x8000),
+ VOLUME_FLAGS_MASK = const_cpu_to_le16(0xc03f),
+} __attribute__((__packed__)) VOLUME_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct VOLUME_INFORMATION - Attribute: Volume information (0x70).
+ *
+ * NOTE: Always resident.
+ * NOTE: Present only in FILE_Volume.
+ * NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses
+ * NTFS 1.2. I haven't personally seen other values yet.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le64 reserved; /* Not used (yet?). */
+ u8 major_ver; /* Major version of the ntfs format. */
+ u8 minor_ver; /* Minor version of the ntfs format. */
+ VOLUME_FLAGS flags; /* Bit array of VOLUME_* flags. */
+} __attribute__((__packed__)) VOLUME_INFORMATION;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifndef __sun
+/**
+ * struct DATA_ATTR - Attribute: Data attribute (0x80).
+ *
+ * NOTE: Can be resident or non-resident.
+ *
+ * Data contents of a file (i.e. the unnamed stream) or of a named stream.
+ */
+typedef struct {
+ u8 data[]; /* The file's data contents. */
+} __attribute__((__packed__)) DATA_ATTR;
+#endif
+
+/**
+ * enum INDEX_HEADER_FLAGS - Index header flags (8-bit).
+ */
+#ifdef __sun
+typedef uint8_t INDEX_HEADER_FLAGS;
+#define SMALL_INDEX (0)
+#define LARGE_INDEX (1)
+#define LEAF_NODE (0)
+#define INDEX_NODE (1)
+#define NODE_MASK (1)
+#else /* not __sun */
+typedef enum {
+ /* When index header is in an index root attribute: */
+ SMALL_INDEX = 0, /* The index is small enough to fit inside the
+ index root attribute and there is no index
+ allocation attribute present. */
+ LARGE_INDEX = 1, /* The index is too large to fit in the index
+ root attribute and/or an index allocation
+ attribute is present. */
+ /*
+ * When index header is in an index block, i.e. is part of index
+ * allocation attribute:
+ */
+ LEAF_NODE = 0, /* This is a leaf node, i.e. there are no more
+ nodes branching off it. */
+ INDEX_NODE = 1, /* This node indexes other nodes, i.e. is not a
+ leaf node. */
+ NODE_MASK = 1, /* Mask for accessing the *_NODE bits. */
+} __attribute__((__packed__)) INDEX_HEADER_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct INDEX_HEADER -
+ *
+ * This is the header for indexes, describing the INDEX_ENTRY records, which
+ * follow the INDEX_HEADER. Together the index header and the index entries
+ * make up a complete index.
+ *
+ * IMPORTANT NOTE: The offset, length and size structure members are counted
+ * relative to the start of the index header structure and not relative to the
+ * start of the index root or index allocation structures themselves.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 entries_offset; /* Byte offset to first INDEX_ENTRY
+ aligned to 8-byte boundary. */
+ le32 index_length; /* Data size of the index in bytes,
+ i.e. bytes used from allocated
+ size, aligned to 8-byte boundary. */
+ le32 allocated_size; /* Byte size of this index (block),
+ multiple of 8 bytes. */
+ /* NOTE: For the index root attribute, the above two numbers are always
+ equal, as the attribute is resident and it is resized as needed. In
+ the case of the index allocation attribute the attribute is not
+ resident and hence the allocated_size is a fixed value and must
+ equal the index_block_size specified by the INDEX_ROOT attribute
+ corresponding to the INDEX_ALLOCATION attribute this INDEX_BLOCK
+ belongs to. */
+ INDEX_HEADER_FLAGS flags; /* Bit field of INDEX_HEADER_FLAGS. */
+ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */
+} __attribute__((__packed__)) INDEX_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct INDEX_ROOT - Attribute: Index root (0x90).
+ *
+ * NOTE: Always resident.
+ *
+ * This is followed by a sequence of index entries (INDEX_ENTRY structures)
+ * as described by the index header.
+ *
+ * When a directory is small enough to fit inside the index root then this
+ * is the only attribute describing the directory. When the directory is too
+ * large to fit in the index root, on the other hand, two additional attributes
+ * are present: an index allocation attribute, containing sub-nodes of the B+
+ * directory tree (see below), and a bitmap attribute, describing which virtual
+ * cluster numbers (VCNs) in the index allocation attribute are in use by an
+ * index block.
+ *
+ * NOTE: The root directory (FILE_root) contains an entry for itself. Other
+ * directories do not contain entries for themselves, though.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ ATTR_TYPES type; /* Type of the indexed attribute. Is
+ $FILE_NAME for directories, zero
+ for view indexes. No other values
+ allowed. */
+ COLLATION_RULES collation_rule; /* Collation rule used to sort the
+ index entries. If type is $FILE_NAME,
+ this must be COLLATION_FILE_NAME. */
+ le32 index_block_size; /* Size of each index block in bytes (in
+ the index allocation attribute). */
+ u8 clusters_per_index_block; /* Cluster size of each index block (in
+ the index allocation attribute), when
+ an index block is >= than a cluster,
+ otherwise sectors per index block. */
+ u8 reserved[3]; /* Reserved/align to 8-byte boundary. */
+ INDEX_HEADER index; /* Index header describing the
+ following index entries. */
+} __attribute__((__packed__)) INDEX_ROOT;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct INDEX_BLOCK - Attribute: Index allocation (0xa0).
+ *
+ * NOTE: Always non-resident (doesn't make sense to be resident anyway!).
+ *
+ * This is an array of index blocks. Each index block starts with an
+ * INDEX_BLOCK structure containing an index header, followed by a sequence of
+ * index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
+ NTFS_RECORD_TYPES magic;/* Magic is "INDX". */
+ le16 usa_ofs; /* See NTFS_RECORD definition. */
+ le16 usa_count; /* See NTFS_RECORD definition. */
+
+/* 8*/ leLSN lsn; /* $LogFile sequence number of the last
+ modification of this index block. */
+/* 16*/ leVCN index_block_vcn; /* Virtual cluster number of the index block. */
+/* 24*/ INDEX_HEADER index; /* Describes the following index entries. */
+/* sizeof()= 40 (0x28) bytes */
+/*
+ * When creating the index block, we place the update sequence array at this
+ * offset, i.e. before we start with the index entries. This also makes sense,
+ * otherwise we could run into problems with the update sequence array
+ * containing in itself the last two bytes of a sector which would mean that
+ * multi sector transfer protection wouldn't work. As you can't protect data
+ * by overwriting it since you then can't get it back...
+ * When reading use the data from the ntfs record header.
+ */
+} __attribute__((__packed__)) INDEX_BLOCK;
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef INDEX_BLOCK INDEX_ALLOCATION;
+
+/**
+ * struct REPARSE_INDEX_KEY -
+ *
+ * The system file FILE_Extend/$Reparse contains an index named $R listing
+ * all reparse points on the volume. The index entry keys are as defined
+ * below. Note, that there is no index data associated with the index entries.
+ *
+ * The index entries are sorted by the index key file_id. The collation rule is
+ * COLLATION_NTOFS_ULONGS. FIXME: Verify whether the reparse_tag is not the
+ * primary key / is not a key at all. (AIA)
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 reparse_tag; /* Reparse point type (inc. flags). */
+ leMFT_REF file_id; /* Mft record of the file containing the
+ reparse point attribute. */
+} __attribute__((__packed__)) REPARSE_INDEX_KEY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum QUOTA_FLAGS - Quota flags (32-bit).
+ */
+typedef enum {
+ /* The user quota flags. Names explain meaning. */
+ QUOTA_FLAG_DEFAULT_LIMITS = const_cpu_to_le32(0x00000001),
+ QUOTA_FLAG_LIMIT_REACHED = const_cpu_to_le32(0x00000002),
+ QUOTA_FLAG_ID_DELETED = const_cpu_to_le32(0x00000004),
+
+ QUOTA_FLAG_USER_MASK = const_cpu_to_le32(0x00000007),
+ /* Bit mask for user quota flags. */
+
+ /* These flags are only present in the quota defaults index entry,
+ i.e. in the entry where owner_id = QUOTA_DEFAULTS_ID. */
+ QUOTA_FLAG_TRACKING_ENABLED = const_cpu_to_le32(0x00000010),
+ QUOTA_FLAG_ENFORCEMENT_ENABLED = const_cpu_to_le32(0x00000020),
+ QUOTA_FLAG_TRACKING_REQUESTED = const_cpu_to_le32(0x00000040),
+ QUOTA_FLAG_LOG_THRESHOLD = const_cpu_to_le32(0x00000080),
+ QUOTA_FLAG_LOG_LIMIT = const_cpu_to_le32(0x00000100),
+ QUOTA_FLAG_OUT_OF_DATE = const_cpu_to_le32(0x00000200),
+ QUOTA_FLAG_CORRUPT = const_cpu_to_le32(0x00000400),
+ QUOTA_FLAG_PENDING_DELETES = const_cpu_to_le32(0x00000800),
+} QUOTA_FLAGS;
+
+/**
+ * struct QUOTA_CONTROL_ENTRY -
+ *
+ * The system file FILE_Extend/$Quota contains two indexes $O and $Q. Quotas
+ * are on a per volume and per user basis.
+ *
+ * The $Q index contains one entry for each existing user_id on the volume. The
+ * index key is the user_id of the user/group owning this quota control entry,
+ * i.e. the key is the owner_id. The user_id of the owner of a file, i.e. the
+ * owner_id, is found in the standard information attribute. The collation rule
+ * for $Q is COLLATION_NTOFS_ULONG.
+ *
+ * The $O index contains one entry for each user/group who has been assigned
+ * a quota on that volume. The index key holds the SID of the user_id the
+ * entry belongs to, i.e. the owner_id. The collation rule for $O is
+ * COLLATION_NTOFS_SID.
+ *
+ * The $O index entry data is the user_id of the user corresponding to the SID.
+ * This user_id is used as an index into $Q to find the quota control entry
+ * associated with the SID.
+ *
+ * The $Q index entry data is the quota control entry and is defined below.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 version; /* Currently equals 2. */
+ QUOTA_FLAGS flags; /* Flags describing this quota entry. */
+ le64 bytes_used; /* How many bytes of the quota are in use. */
+ sle64 change_time; /* Last time this quota entry was changed. */
+ sle64 threshold; /* Soft quota (-1 if not limited). */
+ sle64 limit; /* Hard quota (-1 if not limited). */
+ sle64 exceeded_time; /* How long the soft quota has been exceeded. */
+/* The below field is NOT present for the quota defaults entry. */
+ SID sid; /* The SID of the user/object associated with
+ this quota entry. If this field is missing
+ then the INDEX_ENTRY is padded with zeros
+ to multiply of 8 which are not counted in
+ the data_length field. If the SID is present
+ then this structure is padded with zeros to
+ multiply of 8 and the padding is counted in
+ the INDEX_ENTRY's data_length. */
+} __attribute__((__packed__)) QUOTA_CONTROL_ENTRY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct QUOTA_O_INDEX_DATA -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 owner_id;
+ le32 unknown; /* Always 32. Seems to be padding and it's not
+ counted in the INDEX_ENTRY's data_length.
+ This field shouldn't be really here. */
+} __attribute__((__packed__)) QUOTA_O_INDEX_DATA;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum PREDEFINED_OWNER_IDS - Predefined owner_id values (32-bit).
+ */
+typedef enum {
+ QUOTA_INVALID_ID = const_cpu_to_le32(0x00000000),
+ QUOTA_DEFAULTS_ID = const_cpu_to_le32(0x00000001),
+ QUOTA_FIRST_USER_ID = const_cpu_to_le32(0x00000100),
+} PREDEFINED_OWNER_IDS;
+
+/**
+ * enum INDEX_ENTRY_FLAGS - Index entry flags (16-bit).
+ */
+#ifdef __sun
+typedef uint16_t INDEX_ENTRY_FLAGS;
+#define INDEX_ENTRY_NODE (const_cpu_to_le16(1))
+#define INDEX_ENTRY_END (const_cpu_to_le16(2))
+#else /* not __sun */
+typedef enum {
+ INDEX_ENTRY_NODE = const_cpu_to_le16(1), /* This entry contains a
+ sub-node, i.e. a reference to an index
+ block in form of a virtual cluster
+ number (see below). */
+ INDEX_ENTRY_END = const_cpu_to_le16(2), /* This signifies the last
+ entry in an index block. The index
+ entry does not represent a file but it
+ can point to a sub-node. */
+ INDEX_ENTRY_SPACE_FILLER = const_cpu_to_le16(0xffff),
+ /* Just to force 16-bit width. */
+} __attribute__((__packed__)) INDEX_ENTRY_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct INDEX_ENTRY_HEADER - This the index entry header (see below).
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0*/ union { /* Only valid when INDEX_ENTRY_END is not set. */
+ leMFT_REF indexed_file; /* The mft reference of the file
+ described by this index
+ entry. Used for directory
+ indexes. */
+ struct { /* Used for views/indexes to find the entry's data. */
+ le16 data_offset; /* Data byte offset from this
+ INDEX_ENTRY. Follows the
+ index key. */
+ le16 data_length; /* Data length in bytes. */
+ le32 reservedV; /* Reserved (zero). */
+ } __attribute__((__packed__)) s;
+ } __attribute__((__packed__)) u;
+/* 8*/ le16 length; /* Byte size of this index entry, multiple of
+ 8-bytes. */
+/* 10*/ le16 key_length; /* Byte size of the key value, which is in the
+ index entry. It follows field reserved. Not
+ multiple of 8-bytes. */
+/* 12*/ INDEX_ENTRY_FLAGS flags; /* Bit field of INDEX_ENTRY_* flags. */
+/* 14*/ le16 reserved; /* Reserved/align to 8-byte boundary. */
+/* sizeof() = 16 bytes */
+} __attribute__((__packed__)) INDEX_ENTRY_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct INDEX_ENTRY - This is an index entry.
+ *
+ * A sequence of such entries follows each INDEX_HEADER structure. Together
+ * they make up a complete index. The index follows either an index root
+ * attribute or an index allocation attribute.
+ *
+ * NOTE: Before NTFS 3.0 only filename attributes were indexed.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 INDEX_ENTRY_HEADER; -- Unfolded here as gcc dislikes unnamed structs. */
+ union { /* Only valid when INDEX_ENTRY_END is not set. */
+ leMFT_REF indexed_file; /* The mft reference of the file
+ described by this index
+ entry. Used for directory
+ indexes. */
+ struct { /* Used for views/indexes to find the entry's data. */
+ le16 data_offset; /* Data byte offset from this
+ INDEX_ENTRY. Follows the
+ index key. */
+ le16 data_length; /* Data length in bytes. */
+ le32 reservedV; /* Reserved (zero). */
+ } __attribute__((__packed__)) s;
+ } __attribute__((__packed__)) u;
+ le16 length; /* Byte size of this index entry, multiple of
+ 8-bytes. */
+ le16 key_length; /* Byte size of the key value, which is in the
+ index entry. It follows field reserved. Not
+ multiple of 8-bytes. */
+ INDEX_ENTRY_FLAGS flags; /* Bit field of INDEX_ENTRY_* flags. */
+ le16 reserved; /* Reserved/align to 8-byte boundary. */
+
+/* 16*/ union { /* The key of the indexed attribute. NOTE: Only present
+ if INDEX_ENTRY_END bit in flags is not set. NOTE: On
+ NTFS versions before 3.0 the only valid key is the
+ FILE_NAME_ATTR. On NTFS 3.0+ the following
+ additional index keys are defined: */
+ FILE_NAME_ATTR file_name;/* $I30 index in directories. */
+ SII_INDEX_KEY sii; /* $SII index in $Secure. */
+ SDH_INDEX_KEY sdh; /* $SDH index in $Secure. */
+ GUID object_id; /* $O index in FILE_Extend/$ObjId: The
+ object_id of the mft record found in
+ the data part of the index. */
+ REPARSE_INDEX_KEY reparse; /* $R index in
+ FILE_Extend/$Reparse. */
+ SID sid; /* $O index in FILE_Extend/$Quota:
+ SID of the owner of the user_id. */
+ le32 owner_id; /* $Q index in FILE_Extend/$Quota:
+ user_id of the owner of the quota
+ control entry in the data part of
+ the index. */
+ } __attribute__((__packed__)) key;
+ /* The (optional) index data is inserted here when creating. */
+ /* VCN vcn; */ /* If INDEX_ENTRY_NODE bit in flags is set, the last
+ eight bytes of this index entry contain the virtual
+ cluster number of the index block that holds the
+ entries immediately preceding the current entry (the
+ vcn references the corresponding cluster in the data
+ of the non-resident index allocation attribute). If
+ the key_length is zero, then the vcn immediately
+ follows the INDEX_ENTRY_HEADER. Regardless of
+ key_length, the address of the 8-byte boundary
+ aligned vcn of INDEX_ENTRY{_HEADER} *ie is given by
+ (char*)ie + le16_to_cpu(ie->length) - sizeof(VCN),
+ where sizeof(VCN) can be hardcoded as 8 if wanted. */
+} __attribute__((__packed__)) INDEX_ENTRY;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifndef __sun
+/**
+ * struct BITMAP_ATTR - Attribute: Bitmap (0xb0).
+ *
+ * Contains an array of bits (aka a bitfield).
+ *
+ * When used in conjunction with the index allocation attribute, each bit
+ * corresponds to one index block within the index allocation attribute. Thus
+ * the number of bits in the bitmap * index block size / cluster size is the
+ * number of clusters in the index allocation attribute.
+ */
+typedef struct {
+ u8 bitmap[]; /* Array of bits. */
+} __attribute__((__packed__)) BITMAP_ATTR;
+#endif
+
+/**
+ * enum PREDEFINED_REPARSE_TAGS -
+ *
+ * The reparse point tag defines the type of the reparse point. It also
+ * includes several flags, which further describe the reparse point.
+ *
+ * The reparse point tag is an unsigned 32-bit value divided in three parts:
+ *
+ * 1. The least significant 16 bits (i.e. bits 0 to 15) specify the type of
+ * the reparse point.
+ * 2. The 13 bits after this (i.e. bits 16 to 28) are reserved for future use.
+ * 3. The most significant three bits are flags describing the reparse point.
+ * They are defined as follows:
+ * bit 29: Name surrogate bit. If set, the filename is an alias for
+ * another object in the system.
+ * bit 30: High-latency bit. If set, accessing the first byte of data will
+ * be slow. (E.g. the data is stored on a tape drive.)
+ * bit 31: Microsoft bit. If set, the tag is owned by Microsoft. User
+ * defined tags have to use zero here.
+ */
+typedef enum {
+ IO_REPARSE_TAG_IS_ALIAS = const_cpu_to_le32(0x20000000),
+ IO_REPARSE_TAG_IS_HIGH_LATENCY = const_cpu_to_le32(0x40000000),
+ IO_REPARSE_TAG_IS_MICROSOFT = const_cpu_to_le32(0x80000000),
+
+ IO_REPARSE_TAG_RESERVED_ZERO = const_cpu_to_le32(0x00000000),
+ IO_REPARSE_TAG_RESERVED_ONE = const_cpu_to_le32(0x00000001),
+ IO_REPARSE_TAG_RESERVED_RANGE = const_cpu_to_le32(0x00000001),
+
+ IO_REPARSE_TAG_NSS = const_cpu_to_le32(0x68000005),
+ IO_REPARSE_TAG_NSS_RECOVER = const_cpu_to_le32(0x68000006),
+ IO_REPARSE_TAG_SIS = const_cpu_to_le32(0x68000007),
+ IO_REPARSE_TAG_DFS = const_cpu_to_le32(0x68000008),
+
+ IO_REPARSE_TAG_MOUNT_POINT = const_cpu_to_le32(0x88000003),
+
+ IO_REPARSE_TAG_HSM = const_cpu_to_le32(0xa8000004),
+
+ IO_REPARSE_TAG_SYMBOLIC_LINK = const_cpu_to_le32(0xe8000000),
+
+ IO_REPARSE_TAG_VALID_VALUES = const_cpu_to_le32(0xe000ffff),
+} PREDEFINED_REPARSE_TAGS;
+
+/**
+ * struct REPARSE_POINT - Attribute: Reparse point (0xc0).
+ *
+ * NOTE: Can be resident or non-resident.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 reparse_tag; /* Reparse point type (inc. flags). */
+ le16 reparse_data_length; /* Byte size of reparse data. */
+ le16 reserved; /* Align to 8-byte boundary. */
+ u8 reparse_data[]; /* Meaning depends on reparse_tag. */
+} __attribute__((__packed__)) REPARSE_POINT;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct EA_INFORMATION - Attribute: Extended attribute information (0xd0).
+ *
+ * NOTE: Always resident.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le16 ea_length; /* Byte size of the packed extended
+ attributes. */
+ le16 need_ea_count; /* The number of extended attributes which have
+ the NEED_EA bit set. */
+ le32 ea_query_length; /* Byte size of the buffer required to query
+ the extended attributes when calling
+ ZwQueryEaFile() in Windows NT/2k. I.e. the
+ byte size of the unpacked extended
+ attributes. */
+} __attribute__((__packed__)) EA_INFORMATION;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifdef __sun
+typedef uint8_t EA_FLAGS;
+#define NEED_EA (0x80)
+#else /* not __sun */
+/**
+ * enum EA_FLAGS - Extended attribute flags (8-bit).
+ */
+typedef enum {
+ NEED_EA = 0x80, /* Indicate that the file to which the EA
+ belongs cannot be interpreted without
+ understanding the associated extended
+ attributes. */
+} __attribute__((__packed__)) EA_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct EA_ATTR - Attribute: Extended attribute (EA) (0xe0).
+ *
+ * Like the attribute list and the index buffer list, the EA attribute value is
+ * a sequence of EA_ATTR variable length records.
+ *
+ * FIXME: It appears weird that the EA name is not Unicode. Is it true?
+ * FIXME: It seems that name is always uppercased. Is it true?
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 next_entry_offset; /* Offset to the next EA_ATTR. */
+ EA_FLAGS flags; /* Flags describing the EA. */
+ u8 name_length; /* Length of the name of the extended
+ attribute in bytes. */
+ le16 value_length; /* Byte size of the EA's value. */
+ u8 name[]; /* Name of the EA. */
+#ifndef __sun
+ u8 value[]; /* The value of the EA. Immediately
+ follows the name. */
+#endif
+} __attribute__((__packed__)) EA_ATTR;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifndef __sun
+/**
+ * struct PROPERTY_SET - Attribute: Property set (0xf0).
+ *
+ * Intended to support Native Structure Storage (NSS) - a feature removed from
+ * NTFS 3.0 during beta testing.
+ */
+typedef struct {
+ /* Irrelevant as feature unused. */
+} __attribute__((__packed__)) PROPERTY_SET;
+#endif
+
+#ifndef __sun
+/**
+ * struct LOGGED_UTILITY_STREAM - Attribute: Logged utility stream (0x100).
+ *
+ * NOTE: Can be resident or non-resident.
+ *
+ * Operations on this attribute are logged to the journal ($LogFile) like
+ * normal metadata changes.
+ *
+ * Used by the Encrypting File System (EFS). All encrypted files have this
+ * attribute with the name $EFS. See below for the relevant structures.
+ */
+typedef struct {
+ /* Can be anything the creator chooses. */
+} __attribute__((__packed__)) LOGGED_UTILITY_STREAM;
+#endif
+
+/*
+ * $EFS Data Structure:
+ *
+ * The following information is about the data structures that are contained
+ * inside a logged utility stream (0x100) with a name of "$EFS".
+ *
+ * The stream starts with an instance of EFS_ATTR_HEADER.
+ *
+ * Next, at offsets offset_to_ddf_array and offset_to_drf_array (unless any of
+ * them is 0) there is a EFS_DF_ARRAY_HEADER immediately followed by a sequence
+ * of multiple data decryption/recovery fields.
+ *
+ * Each data decryption/recovery field starts with a EFS_DF_HEADER and the next
+ * one (if it exists) can be found by adding EFS_DF_HEADER->df_length bytes to
+ * the offset of the beginning of the current EFS_DF_HEADER.
+ *
+ * The data decryption/recovery field contains an EFS_DF_CERTIFICATE_HEADER, a
+ * SID, an optional GUID, an optional container name, a non-optional user name,
+ * and the encrypted FEK.
+ *
+ * Note all the below are best guesses so may have mistakes/inaccuracies.
+ * Corrections/clarifications/additions are always welcome!
+ *
+ * Ntfs.sys takes an EFS value length of <= 0x54 or > 0x40000 to BSOD, i.e. it
+ * is invalid.
+ */
+
+/**
+ * struct EFS_ATTR_HEADER - "$EFS" header.
+ *
+ * The header of the Logged utility stream (0x100) attribute named "$EFS".
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0*/ le32 length; /* Length of EFS attribute in bytes. */
+ le32 state; /* Always 0? */
+ le32 version; /* Efs version. Always 2? */
+ le32 crypto_api_version; /* Always 0? */
+/* 16*/ u8 unknown4[16]; /* MD5 hash of decrypted FEK? This field is
+ created with a call to UuidCreate() so is
+ unlikely to be an MD5 hash and is more
+ likely to be GUID of this encrytped file
+ or something like that. */
+/* 32*/ u8 unknown5[16]; /* MD5 hash of DDFs? */
+/* 48*/ u8 unknown6[16]; /* MD5 hash of DRFs? */
+/* 64*/ le32 offset_to_ddf_array;/* Offset in bytes to the array of data
+ decryption fields (DDF), see below. Zero if
+ no DDFs are present. */
+ le32 offset_to_drf_array;/* Offset in bytes to the array of data
+ recovery fields (DRF), see below. Zero if
+ no DRFs are present. */
+ le32 reserved; /* Reserved. */
+} __attribute__((__packed__)) EFS_ATTR_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct EFS_DF_ARRAY_HEADER -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ le32 df_count; /* Number of data decryption/recovery fields in
+ the array. */
+} __attribute__((__packed__)) EFS_DF_ARRAY_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct EFS_DF_HEADER -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0*/ le32 df_length; /* Length of this data decryption/recovery
+ field in bytes. */
+ le32 cred_header_offset;/* Offset in bytes to the credential header. */
+ le32 fek_size; /* Size in bytes of the encrypted file
+ encryption key (FEK). */
+ le32 fek_offset; /* Offset in bytes to the FEK from the start of
+ the data decryption/recovery field. */
+/* 16*/ le32 unknown1; /* always 0? Might be just padding. */
+} __attribute__((__packed__)) EFS_DF_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct EFS_DF_CREDENTIAL_HEADER -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0*/ le32 cred_length; /* Length of this credential in bytes. */
+ le32 sid_offset; /* Offset in bytes to the user's sid from start
+ of this structure. Zero if no sid is
+ present. */
+/* 8*/ le32 type; /* Type of this credential:
+ 1 = CryptoAPI container.
+ 2 = Unexpected type.
+ 3 = Certificate thumbprint.
+ other = Unknown type. */
+ union {
+ /* CryptoAPI container. */
+ struct {
+/* 12*/ le32 container_name_offset; /* Offset in bytes to
+ the name of the container from start of this
+ structure (may not be zero). */
+/* 16*/ le32 provider_name_offset; /* Offset in bytes to
+ the name of the provider from start of this
+ structure (may not be zero). */
+ le32 public_key_blob_offset; /* Offset in bytes to
+ the public key blob from start of this
+ structure. */
+/* 24*/ le32 public_key_blob_size; /* Size in bytes of
+ public key blob. */
+ } __attribute__((__packed__)) crypt;
+ /* Certificate thumbprint. */
+ struct {
+/* 12*/ le32 cert_thumbprint_header_size; /* Size in
+ bytes of the header of the certificate
+ thumbprint. */
+/* 16*/ le32 cert_thumbprint_header_offset; /* Offset in
+ bytes to the header of the certificate
+ thumbprint from start of this structure. */
+ le32 unknown1; /* Always 0? Might be padding... */
+ le32 unknown2; /* Always 0? Might be padding... */
+ } __attribute__((__packed__)) cert;
+ } __attribute__((__packed__)) u;
+} __attribute__((__packed__)) EFS_DF_CREDENTIAL_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef EFS_DF_CREDENTIAL_HEADER EFS_DF_CRED_HEADER;
+
+/**
+ * struct EFS_DF_CERTIFICATE_THUMBPRINT_HEADER -
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0*/ le32 thumbprint_offset; /* Offset in bytes to the thumbprint. */
+ le32 thumbprint_size; /* Size of thumbprint in bytes. */
+/* 8*/ le32 container_name_offset; /* Offset in bytes to the name of the
+ container from start of this
+ structure or 0 if no name present. */
+ le32 provider_name_offset; /* Offset in bytes to the name of the
+ cryptographic provider from start of
+ this structure or 0 if no name
+ present. */
+/* 16*/ le32 user_name_offset; /* Offset in bytes to the user name
+ from start of this structure or 0 if
+ no user name present. (This is also
+ known as lpDisplayInformation.) */
+} __attribute__((__packed__)) EFS_DF_CERTIFICATE_THUMBPRINT_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef EFS_DF_CERTIFICATE_THUMBPRINT_HEADER EFS_DF_CERT_THUMBPRINT_HEADER;
+
+#ifdef __sun
+typedef uint64_t INTX_FILE_TYPES;
+#define INTX_SYMBOLIC_LINK (const_cpu_to_le64(0x014B4E4C78746E49ULL))
+#define INTX_CHARACTER_DEVICE (const_cpu_to_le64(0x0052484378746E49ULL))
+#define INTX_BLOCK_DEVICE (const_cpu_to_le64(0x004B4C4278746E49ULL))
+#else /* not __sun */
+typedef enum {
+ INTX_SYMBOLIC_LINK =
+ const_cpu_to_le64(0x014B4E4C78746E49ULL), /* "IntxLNK\1" */
+ INTX_CHARACTER_DEVICE =
+ const_cpu_to_le64(0x0052484378746E49ULL), /* "IntxCHR\0" */
+ INTX_BLOCK_DEVICE =
+ const_cpu_to_le64(0x004B4C4278746E49ULL), /* "IntxBLK\0" */
+} INTX_FILE_TYPES;
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ INTX_FILE_TYPES magic; /* Intx file magic. */
+ union {
+ /* For character and block devices. */
+ struct {
+ le64 major; /* Major device number. */
+ le64 minor; /* Minor device number. */
+ char device_end; /* Marker for offsetof(). */
+ } __attribute__((__packed__)) s;
+ /* For symbolic links. */
+ ntfschar target[1];
+ } __attribute__((__packed__)) u;
+} __attribute__((__packed__)) INTX_FILE;
+#ifdef __sun
+#pragma pack()
+#endif
+
+#endif /* defined _NTFS_LAYOUT_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h b/usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h
new file mode 100644
index 0000000000..07ab020d2d
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/lcnalloc.h
@@ -0,0 +1,50 @@
+/*
+ * lcnalloc.h - Exports for cluster (de)allocation. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2002 Anton Altaparmakov
+ * Copyright (c) 2004 Yura Pakhuchiy
+ *
+ * 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 _NTFS_LCNALLOC_H
+#define _NTFS_LCNALLOC_H
+
+#include "types.h"
+#include "runlist.h"
+#include "volume.h"
+
+/**
+ * enum NTFS_CLUSTER_ALLOCATION_ZONES -
+ */
+typedef enum {
+ FIRST_ZONE = 0, /* For sanity checking. */
+ MFT_ZONE = 0, /* Allocate from $MFT zone. */
+ DATA_ZONE = 1, /* Allocate from $DATA zone. */
+ LAST_ZONE = 1, /* For sanity checking. */
+} NTFS_CLUSTER_ALLOCATION_ZONES;
+
+extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
+ LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone);
+
+extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl);
+
+extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn,
+ s64 count);
+
+#endif /* defined _NTFS_LCNALLOC_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/list.h b/usr/src/lib/libntfs/common/include/ntfs/list.h
new file mode 100644
index 0000000000..e3c774423d
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/list.h
@@ -0,0 +1,192 @@
+/*
+ * list.h - Linked list implementation. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2002 Anton Altaparmakov and others
+ *
+ * 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 _NTFS_LIST_H
+#define _NTFS_LIST_H
+
+/**
+ * struct list_head - Simple doubly linked list implementation.
+ *
+ * Copied from Linux kernel 2.4.2-ac18 into Linux-NTFS (with minor
+ * modifications). - AIA
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/**
+ * __list_add - Insert a new entry between two known consecutive entries.
+ * @new:
+ * @prev:
+ * @next:
+ *
+ * This is only for internal list manipulation where we know the prev/next
+ * entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev, struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/**
+ * __list_del -
+ * @prev:
+ * @next:
+ *
+ * Delete a list entry by making the prev/next entries point to each other.
+ *
+ * This is only for internal list manipulation where we know the prev/next
+ * entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty on entry does not return true after this, the entry is in
+ * an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#endif /* defined _NTFS_LIST_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/logfile.h b/usr/src/lib/libntfs/common/include/ntfs/logfile.h
new file mode 100644
index 0000000000..9c597ecb4d
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/logfile.h
@@ -0,0 +1,441 @@
+/*
+ * logfile.h - Exports for $LogFile handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2005 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_LOGFILE_H
+#define _NTFS_LOGFILE_H
+
+#include "types.h"
+#include "endians.h"
+#include "layout.h"
+
+/*
+ * Journal ($LogFile) organization:
+ *
+ * Two restart areas present in the first two pages (restart pages, one restart
+ * area in each page). When the volume is dismounted they should be identical,
+ * except for the update sequence array which usually has a different update
+ * sequence number.
+ *
+ * These are followed by log records organized in pages headed by a log record
+ * header going up to log file size. Not all pages contain log records when a
+ * volume is first formatted, but as the volume ages, all records will be used.
+ * When the log file fills up, the records at the beginning are purged (by
+ * modifying the oldest_lsn to a higher value presumably) and writing begins
+ * at the beginning of the file. Effectively, the log file is viewed as a
+ * circular entity.
+ *
+ * NOTE: Windows NT, 2000, and XP all use log file version 1.1 but they accept
+ * versions <= 1.x, including 0.-1. (Yes, that is a minus one in there!) We
+ * probably only want to support 1.1 as this seems to be the current version
+ * and we don't know how that differs from the older versions. The only
+ * exception is if the journal is clean as marked by the two restart pages
+ * then it doesn't matter whether we are on an earlier version. We can just
+ * reinitialize the logfile and start again with version 1.1.
+ */
+
+/* Some $LogFile related constants. */
+#define MaxLogFileSize 0x100000000ULL
+#define DefaultLogPageSize 4096
+#define MinLogRecordPages 48
+
+/**
+ * struct RESTART_PAGE_HEADER - Log file restart page header.
+ *
+ * Begins the restart area.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
+/* 0*/ NTFS_RECORD_TYPES magic;/* The magic is "RSTR". */
+/* 4*/ le16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
+ When creating, set this to be immediately
+ after this header structure (without any
+ alignment). */
+/* 6*/ le16 usa_count; /* See NTFS_RECORD definition in layout.h. */
+
+/* 8*/ leLSN chkdsk_lsn; /* The last log file sequence number found by
+ chkdsk. Only used when the magic is changed
+ to "CHKD". Otherwise this is zero. */
+/* 16*/ le32 system_page_size; /* Byte size of system pages when the log file
+ was created, has to be >= 512 and a power of
+ 2. Use this to calculate the required size
+ of the usa (usa_count) and add it to usa_ofs.
+ Then verify that the result is less than the
+ value of the restart_area_offset. */
+/* 20*/ le32 log_page_size; /* Byte size of log file pages, has to be >=
+ 512 and a power of 2. The default is 4096
+ and is used when the system page size is
+ between 4096 and 8192. Otherwise this is
+ set to the system page size instead. */
+/* 24*/ le16 restart_area_offset;/* Byte offset from the start of this header to
+ the RESTART_AREA. Value has to be aligned
+ to 8-byte boundary. When creating, set this
+ to be after the usa. */
+/* 26*/ sle16 minor_ver; /* Log file minor version. Only check if major
+ version is 1. */
+/* 28*/ sle16 major_ver; /* Log file major version. We only support
+ version 1.1. */
+/* sizeof() = 30 (0x1e) bytes */
+} __attribute__((__packed__)) RESTART_PAGE_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/*
+ * Constant for the log client indices meaning that there are no client records
+ * in this particular client array. Also inside the client records themselves,
+ * this means that there are no client records preceding or following this one.
+ */
+#define LOGFILE_NO_CLIENT const_cpu_to_le16(0xffff)
+#define LOGFILE_NO_CLIENT_CPU 0xffff
+
+#ifdef __sun
+#define RESTART_VOLUME_IS_CLEAN (const_cpu_to_le16(0x0002))
+#else /* not __sun */
+/*
+ * These are the so far known RESTART_AREA_* flags (16-bit) which contain
+ * information about the log file in which they are present.
+ */
+enum {
+ RESTART_VOLUME_IS_CLEAN = const_cpu_to_le16(0x0002),
+ RESTART_SPACE_FILLER = const_cpu_to_le16(0xffff),
+ /* gcc: Force enum bit width to 16. */
+} __attribute__((__packed__));
+#endif /* __sun */
+
+typedef le16 RESTART_AREA_FLAGS;
+
+/**
+ * struct RESTART_AREA - Log file restart area record.
+ *
+ * The offset of this record is found by adding the offset of the
+ * RESTART_PAGE_HEADER to the restart_area_offset value found in it.
+ * See notes at restart_area_offset above.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0*/ leLSN current_lsn; /* The current, i.e. last LSN inside the log
+ when the restart area was last written.
+ This happens often but what is the interval?
+ Is it just fixed time or is it every time a
+ check point is written or something else?
+ On create set to 0. */
+/* 8*/ le16 log_clients; /* Number of log client records in the array of
+ log client records which follows this
+ restart area. Must be 1. */
+/* 10*/ le16 client_free_list; /* The index of the first free log client record
+ in the array of log client records.
+ LOGFILE_NO_CLIENT means that there are no
+ free log client records in the array.
+ If != LOGFILE_NO_CLIENT, check that
+ log_clients > client_free_list. On Win2k
+ and presumably earlier, on a clean volume
+ this is != LOGFILE_NO_CLIENT, and it should
+ be 0, i.e. the first (and only) client
+ record is free and thus the logfile is
+ closed and hence clean. A dirty volume
+ would have left the logfile open and hence
+ this would be LOGFILE_NO_CLIENT. On WinXP
+ and presumably later, the logfile is always
+ open, even on clean shutdown so this should
+ always be LOGFILE_NO_CLIENT. */
+/* 12*/ le16 client_in_use_list;/* The index of the first in-use log client
+ record in the array of log client records.
+ LOGFILE_NO_CLIENT means that there are no
+ in-use log client records in the array. If
+ != LOGFILE_NO_CLIENT check that log_clients
+ > client_in_use_list. On Win2k and
+ presumably earlier, on a clean volume this
+ is LOGFILE_NO_CLIENT, i.e. there are no
+ client records in use and thus the logfile
+ is closed and hence clean. A dirty volume
+ would have left the logfile open and hence
+ this would be != LOGFILE_NO_CLIENT, and it
+ should be 0, i.e. the first (and only)
+ client record is in use. On WinXP and
+ presumably later, the logfile is always
+ open, even on clean shutdown so this should
+ always be 0. */
+/* 14*/ RESTART_AREA_FLAGS flags;/* Flags modifying LFS behaviour. On Win2k
+ and presumably earlier this is always 0. On
+ WinXP and presumably later, if the logfile
+ was shutdown cleanly, the second bit,
+ RESTART_VOLUME_IS_CLEAN, is set. This bit
+ is cleared when the volume is mounted by
+ WinXP and set when the volume is dismounted,
+ thus if the logfile is dirty, this bit is
+ clear. Thus we don't need to check the
+ Windows version to determine if the logfile
+ is clean. Instead if the logfile is closed,
+ we know it must be clean. If it is open and
+ this bit is set, we also know it must be
+ clean. If on the other hand the logfile is
+ open and this bit is clear, we can be almost
+ certain that the logfile is dirty. */
+/* 16*/ le32 seq_number_bits; /* How many bits to use for the sequence
+ number. This is calculated as 67 - the
+ number of bits required to store the logfile
+ size in bytes and this can be used in with
+ the specified file_size as a consistency
+ check. */
+/* 20*/ le16 restart_area_length;/* Length of the restart area including the
+ client array. Following checks required if
+ version matches. Otherwise, skip them.
+ restart_area_offset + restart_area_length
+ has to be <= system_page_size. Also,
+ restart_area_length has to be >=
+ client_array_offset + (log_clients *
+ sizeof(log client record)). */
+/* 22*/ le16 client_array_offset;/* Offset from the start of this record to
+ the first log client record if versions are
+ matched. When creating, set this to be
+ after this restart area structure, aligned
+ to 8-bytes boundary. If the versions do not
+ match, this is ignored and the offset is
+ assumed to be (sizeof(RESTART_AREA) + 7) &
+ ~7, i.e. rounded up to first 8-byte
+ boundary. Either way, client_array_offset
+ has to be aligned to an 8-byte boundary.
+ Also, restart_area_offset +
+ client_array_offset has to be <= 510.
+ Finally, client_array_offset + (log_clients
+ * sizeof(log client record)) has to be <=
+ system_page_size. On Win2k and presumably
+ earlier, this is 0x30, i.e. immediately
+ following this record. On WinXP and
+ presumably later, this is 0x40, i.e. there
+ are 16 extra bytes between this record and
+ the client array. This probably means that
+ the RESTART_AREA record is actually bigger
+ in WinXP and later. */
+/* 24*/ sle64 file_size; /* Usable byte size of the log file. If the
+ restart_area_offset + the offset of the
+ file_size are > 510 then corruption has
+ occurred. This is the very first check when
+ starting with the restart_area as if it
+ fails it means that some of the above values
+ will be corrupted by the multi sector
+ transfer protection. The file_size has to
+ be rounded down to be a multiple of the
+ log_page_size in the RESTART_PAGE_HEADER and
+ then it has to be at least big enough to
+ store the two restart pages and 48 (0x30)
+ log record pages. */
+/* 32*/ le32 last_lsn_data_length;/* Length of data of last LSN, not including
+ the log record header. On create set to
+ 0. */
+/* 36*/ le16 log_record_header_length;/* Byte size of the log record header.
+ If the version matches then check that the
+ value of log_record_header_length is a
+ multiple of 8, i.e.
+ (log_record_header_length + 7) & ~7 ==
+ log_record_header_length. When creating set
+ it to sizeof(LOG_RECORD_HEADER), aligned to
+ 8 bytes. */
+/* 38*/ le16 log_page_data_offset;/* Offset to the start of data in a log record
+ page. Must be a multiple of 8. On create
+ set it to immediately after the update
+ sequence array of the log record page. */
+/* 40*/ le32 restart_log_open_count;/* A counter that gets incremented every
+ time the logfile is restarted which happens
+ at mount time when the logfile is opened.
+ When creating set to a random value. Win2k
+ sets it to the low 32 bits of the current
+ system time in NTFS format (see time.h). */
+/* 44*/ le32 reserved; /* Reserved/alignment to 8-byte boundary. */
+/* sizeof() = 48 (0x30) bytes */
+} __attribute__((__packed__)) RESTART_AREA;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct LOG_CLIENT_RECORD - Log client record.
+ *
+ * The offset of this record is found by adding the offset of the
+ * RESTART_AREA to the client_array_offset value found in it.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/*Ofs*/
+/* 0*/ leLSN oldest_lsn; /* Oldest LSN needed by this client. On create
+ set to 0. */
+/* 8*/ leLSN client_restart_lsn;/* LSN at which this client needs to restart
+ the volume, i.e. the current position within
+ the log file. At present, if clean this
+ should = current_lsn in restart area but it
+ probably also = current_lsn when dirty most
+ of the time. At create set to 0. */
+/* 16*/ le16 prev_client; /* The offset to the previous log client record
+ in the array of log client records.
+ LOGFILE_NO_CLIENT means there is no previous
+ client record, i.e. this is the first one.
+ This is always LOGFILE_NO_CLIENT. */
+/* 18*/ le16 next_client; /* The offset to the next log client record in
+ the array of log client records.
+ LOGFILE_NO_CLIENT means there are no next
+ client records, i.e. this is the last one.
+ This is always LOGFILE_NO_CLIENT. */
+/* 20*/ le16 seq_number; /* On Win2k and presumably earlier, this is set
+ to zero every time the logfile is restarted
+ and it is incremented when the logfile is
+ closed at dismount time. Thus it is 0 when
+ dirty and 1 when clean. On WinXP and
+ presumably later, this is always 0. */
+/* 22*/ u8 reserved[6]; /* Reserved/alignment. */
+/* 28*/ le32 client_name_length;/* Length of client name in bytes. Should
+ always be 8. */
+/* 32*/ ntfschar client_name[64];/* Name of the client in Unicode. Should
+ always be "NTFS" with the remaining bytes
+ set to 0. */
+/* sizeof() = 160 (0xa0) bytes */
+} __attribute__((__packed__)) LOG_CLIENT_RECORD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct RECORD_PAGE_HEADER - Log page record page header.
+ *
+ * Each log page begins with this header and is followed by several LOG_RECORD
+ * structures, starting at offset 0x40 (the size of this structure and the
+ * following update sequence array and then aligned to 8 byte boundary, but is
+ * this specified anywhere?).
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+/* 0 NTFS_RECORD; -- Unfolded here as gcc doesn't like unnamed structs. */
+ NTFS_RECORD_TYPES magic;/* Usually the magic is "RCRD". */
+ u16 usa_ofs; /* See NTFS_RECORD definition in layout.h.
+ When creating, set this to be immediately
+ after this header structure (without any
+ alignment). */
+ u16 usa_count; /* See NTFS_RECORD definition in layout.h. */
+
+ union {
+ LSN last_lsn;
+ s64 file_offset;
+ } __attribute__((__packed__)) copy;
+ u32 flags;
+ u16 page_count;
+ u16 page_position;
+ union {
+ struct {
+ u16 next_record_offset;
+ u8 reserved[6];
+ LSN last_end_lsn;
+ } __attribute__((__packed__)) packed;
+ } __attribute__((__packed__)) header;
+} __attribute__((__packed__)) RECORD_PAGE_HEADER;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * enum LOG_RECORD_FLAGS - Possible 16-bit flags for log records.
+ *
+ * (Or is it log record pages?)
+ */
+#ifdef __sun
+typedef const uint16_t LOG_RECORD_FLAGS;
+#define LOG_RECORD_MULTI_PAGE (const_cpu_to_le16(0x0001))
+#else /* not __sun */
+typedef enum {
+ LOG_RECORD_MULTI_PAGE = const_cpu_to_le16(0x0001), /* ??? */
+ LOG_RECORD_SIZE_PLACE_HOLDER = 0xffff,
+ /* This has nothing to do with the log record. It is only so
+ gcc knows to make the flags 16-bit. */
+} __attribute__((__packed__)) LOG_RECORD_FLAGS;
+#endif /* __sun */
+
+/**
+ * struct LOG_CLIENT_ID - The log client id structure identifying a log client.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ u16 seq_number;
+ u16 client_index;
+} __attribute__((__packed__)) LOG_CLIENT_ID;
+#ifdef __sun
+#pragma pack()
+#endif
+
+/**
+ * struct LOG_RECORD - Log record header.
+ *
+ * Each log record seems to have a constant size of 0x70 bytes.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ LSN this_lsn;
+ LSN client_previous_lsn;
+ LSN client_undo_next_lsn;
+ u32 client_data_length;
+ LOG_CLIENT_ID client_id;
+ u32 record_type;
+ u32 transaction_id;
+ u16 flags;
+ u16 reserved_or_alignment[3];
+/* Now are at ofs 0x30 into struct. */
+ u16 redo_operation;
+ u16 undo_operation;
+ u16 redo_offset;
+ u16 redo_length;
+ u16 undo_offset;
+ u16 undo_length;
+ u16 target_attribute;
+ u16 lcns_to_follow; /* Number of lcn_list entries
+ following this entry. */
+/* Now at ofs 0x40. */
+ u16 record_offset;
+ u16 attribute_offset;
+ u32 alignment_or_reserved;
+ VCN target_vcn;
+/* Now at ofs 0x50. */
+ struct { /* Only present if lcns_to_follow
+ is not 0. */
+ LCN lcn;
+ } __attribute__((__packed__)) lcn_list[];
+} __attribute__((__packed__)) LOG_RECORD;
+#ifdef __sun
+#pragma pack()
+#endif
+
+extern BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp);
+extern BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp);
+extern int ntfs_empty_logfile(ntfs_attr *na);
+
+#endif /* defined _NTFS_LOGFILE_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/logging.h b/usr/src/lib/libntfs/common/include/ntfs/logging.h
new file mode 100644
index 0000000000..524643a149
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/logging.h
@@ -0,0 +1,143 @@
+/*
+ * logging.h - Centralised logging. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Richard Russon
+ *
+ * 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 _LOGGING_H_
+#define _LOGGING_H_
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+
+#include "types.h"
+
+/* Function prototype for the logging handlers */
+typedef int (ntfs_log_handler)(const char *function, const char *file, int line,
+ u32 level, void *data, const char *format, va_list args);
+
+/* Set the logging handler from one of the functions, below. */
+void ntfs_log_set_handler(ntfs_log_handler *handler);
+
+/* Logging handlers */
+ntfs_log_handler ntfs_log_handler_syslog __attribute__((format(printf, 6, 0)));
+ntfs_log_handler ntfs_log_handler_fprintf __attribute__((format(printf, 6, 0)));
+ntfs_log_handler ntfs_log_handler_null __attribute__((format(printf, 6, 0)));
+ntfs_log_handler ntfs_log_handler_stdout __attribute__((format(printf, 6, 0)));
+ntfs_log_handler ntfs_log_handler_outerr __attribute__((format(printf, 6, 0)));
+ntfs_log_handler ntfs_log_handler_stderr __attribute__((format(printf, 6, 0)));
+
+/* Enable/disable certain log levels */
+u32 ntfs_log_set_levels(u32 levels);
+u32 ntfs_log_clear_levels(u32 levels);
+u32 ntfs_log_get_levels(void);
+
+/* Enable/disable certain log flags */
+u32 ntfs_log_set_flags(u32 flags);
+u32 ntfs_log_clear_flags(u32 flags);
+u32 ntfs_log_get_flags(void);
+
+/* Turn command-line options into logging flags */
+BOOL ntfs_log_parse_option(const char *option);
+
+int ntfs_log_redirect(const char *function, const char *file, int line,
+ u32 level, void *data, const char *format, ...)
+ __attribute__((format(printf, 6, 7)));
+
+/* Logging levels - Determine what gets logged */
+#define NTFS_LOG_LEVEL_DEBUG ((u32)1 << 0) /* x = 42 */
+#define NTFS_LOG_LEVEL_TRACE ((u32)1 << 1) /* Entering function x() */
+#define NTFS_LOG_LEVEL_QUIET ((u32)1 << 2) /* Quietable output */
+#define NTFS_LOG_LEVEL_INFO ((u32)1 << 3) /* Volume needs defragmenting */
+#define NTFS_LOG_LEVEL_VERBOSE ((u32)1 << 4) /* Forced to continue */
+#define NTFS_LOG_LEVEL_PROGRESS ((u32)1 << 5) /* 54% complete */
+#define NTFS_LOG_LEVEL_WARNING ((u32)1 << 6) /* You should backup before starting */
+#define NTFS_LOG_LEVEL_ERROR ((u32)1 << 7) /* Operation failed, no damage done */
+#define NTFS_LOG_LEVEL_PERROR ((u32)1 << 8) /* Message : standard error description */
+#define NTFS_LOG_LEVEL_CRITICAL ((u32)1 << 9) /* Operation failed,damage may have occurred */
+
+/* Logging style flags - Manage the style of the output */
+#define NTFS_LOG_FLAG_PREFIX ((u32)1 << 0) /* Prefix messages with "ERROR: ", etc */
+#define NTFS_LOG_FLAG_FILENAME ((u32)1 << 1) /* Show the file origin of the message */
+#define NTFS_LOG_FLAG_LINE ((u32)1 << 2) /* Show the line number of the message */
+#define NTFS_LOG_FLAG_FUNCTION ((u32)1 << 3) /* Show the function name containing the message */
+#define NTFS_LOG_FLAG_ONLYNAME ((u32)1 << 4) /* Only display the filename, not the pathname */
+#define NTFS_LOG_FLAG_COLOUR ((u32)1 << 5) /* Colour highlight some messages */
+
+/* Macros to simplify logging. One for each level defined above.
+ * Note, if DEBUG is not defined, then ntfs_log_debug/trace have no effect.
+ */
+#if defined(__GNUC__)
+
+#define ntfs_log_critical(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,FORMAT,##ARGS)
+#define ntfs_log_error(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,FORMAT,##ARGS)
+#define ntfs_log_info(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,FORMAT,##ARGS)
+#define ntfs_log_perror(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,FORMAT,##ARGS)
+#define ntfs_log_progress(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,FORMAT,##ARGS)
+#define ntfs_log_quiet(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,FORMAT,##ARGS)
+#define ntfs_log_verbose(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,FORMAT,##ARGS)
+#define ntfs_log_warning(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,FORMAT,##ARGS)
+
+#else /* not __GNUC__ */
+
+#define PRINT(...) printf(__VA_ARGS__)
+
+#define ntfs_log_critical(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_CRITICAL,NULL,__VA_ARGS__)
+#define ntfs_log_error(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_ERROR,NULL,__VA_ARGS__)
+#define ntfs_log_info(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_INFO,NULL,__VA_ARGS__)
+#define ntfs_log_perror(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_PERROR,NULL,__VA_ARGS__)
+#define ntfs_log_progress(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_PROGRESS,NULL,__VA_ARGS__)
+#define ntfs_log_quiet(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_QUIET,NULL,__VA_ARGS__)
+#define ntfs_log_verbose(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_VERBOSE,NULL,__VA_ARGS__)
+#define ntfs_log_warning(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_WARNING,NULL,__VA_ARGS__)
+
+#endif /* __GNUC__ */
+
+/*
+ * By default debug and trace messages are compiled into the program,
+ * but not displayed.
+ */
+#if defined(__GNUC__)
+
+#ifndef DEBUG
+#define ntfs_log_debug(FORMAT, ARGS...)do {} while (0)
+#define ntfs_log_trace(FORMAT, ARGS...)do {} while (0)
+#else /* !DEBUG */
+#define ntfs_log_debug(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,FORMAT,##ARGS)
+#define ntfs_log_trace(FORMAT, ARGS...) ntfs_log_redirect(__FUNCTION__,__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,FORMAT,##ARGS)
+#endif /* DEBUG */
+
+#else /* not __GNUC__ */
+
+#ifndef DEBUG
+#define ntfs_log_debug(...) do {} while (0)
+#define ntfs_log_trace(...) do {} while (0)
+#else /* !DEBUG */
+#define ntfs_log_debug(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_DEBUG,NULL,__VA_ARGS__)
+#define ntfs_log_trace(...) ntfs_log_redirect("unknown",__FILE__,__LINE__,NTFS_LOG_LEVEL_TRACE,NULL,__VA_ARGS__)
+#endif /* DEBUG */
+
+#endif /* __GNUC__ */
+
+#endif /* _LOGGING_H_ */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/mft.h b/usr/src/lib/libntfs/common/include/ntfs/mft.h
new file mode 100644
index 0000000000..180f61531d
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/mft.h
@@ -0,0 +1,116 @@
+/*
+ * mft.h - Exports for MFT record handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2002 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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 _NTFS_MFT_H
+#define _NTFS_MFT_H
+
+#include "volume.h"
+#include "inode.h"
+#include "layout.h"
+
+extern int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
+ const s64 count, MFT_RECORD *b);
+
+/**
+ * ntfs_mft_record_read - read a record from the mft
+ * @vol: volume to read from
+ * @mref: mft record number to read
+ * @b: output data buffer
+ *
+ * Read the mft record specified by @mref from volume @vol into buffer @b.
+ * Return 0 on success or -1 on error, with errno set to the error code.
+ *
+ * The read mft record is mst deprotected and is hence ready to use. The caller
+ * should check the record with is_baad_record() in case mst deprotection
+ * failed.
+ *
+ * NOTE: @b has to be at least of size vol->mft_record_size.
+ */
+static __inline__ int ntfs_mft_record_read(const ntfs_volume *vol,
+ const MFT_REF mref, MFT_RECORD *b)
+{
+ return ntfs_mft_records_read(vol, mref, 1, b);
+}
+
+extern int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
+ MFT_RECORD **mrec, ATTR_RECORD **attr);
+
+extern int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
+ const s64 count, MFT_RECORD *b);
+
+/**
+ * ntfs_mft_record_write - write an mft record to disk
+ * @vol: volume to write to
+ * @mref: mft record number to write
+ * @b: data buffer containing the mft record to write
+ *
+ * Write the mft record specified by @mref from buffer @b to volume @vol.
+ * Return 0 on success or -1 on error, with errno set to the error code.
+ *
+ * Before the mft record is written, it is mst protected. After the write, it
+ * is deprotected again, thus resulting in an increase in the update sequence
+ * number inside the buffer @b.
+ *
+ * NOTE: @b has to be at least of size vol->mft_record_size.
+ */
+static __inline__ int ntfs_mft_record_write(const ntfs_volume *vol,
+ const MFT_REF mref, MFT_RECORD *b)
+{
+ return ntfs_mft_records_write(vol, mref, 1, b);
+}
+
+/**
+ * ntfs_mft_record_get_data_size - return number of bytes used in mft record @b
+ * @m: mft record to get the data size of
+ *
+ * Takes the mft record @m and returns the number of bytes used in the record
+ * or 0 on error (i.e. @m is not a valid mft record). Zero is not a valid size
+ * for an mft record as it at least has to have the MFT_RECORD itself and a
+ * zero length attribute of type AT_END, thus making the minimum size 56 bytes.
+ *
+ * Aside: The size is independent of NTFS versions 1.x/3.x because the 8-byte
+ * alignment of the first attribute mask the difference in MFT_RECORD size
+ * between NTFS 1.x and 3.x. Also, you would expect every mft record to
+ * contain an update sequence array as well but that could in theory be
+ * non-existent (don't know if Windows' NTFS driver/chkdsk wouldn't view this
+ * as corruption in itself though).
+ */
+static __inline__ u32 ntfs_mft_record_get_data_size(const MFT_RECORD *m)
+{
+ if (!m || !ntfs_is_mft_record(m->magic))
+ return 0;
+ /* Get the number of used bytes and return it. */
+ return le32_to_cpu(m->bytes_in_use);
+}
+
+extern int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
+ MFT_RECORD *mrec);
+
+extern int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref);
+
+extern ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni);
+
+extern int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni);
+
+extern int ntfs_mft_usn_dec(MFT_RECORD *mrec);
+
+#endif /* defined _NTFS_MFT_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/mst.h b/usr/src/lib/libntfs/common/include/ntfs/mst.h
new file mode 100644
index 0000000000..0808b3c115
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/mst.h
@@ -0,0 +1,34 @@
+/*
+ * mst.h - Exports for multi sector transfer fixup functions. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2002 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 _NTFS_MST_H
+#define _NTFS_MST_H
+
+#include "types.h"
+#include "layout.h"
+
+extern int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size);
+extern int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size);
+extern void ntfs_mst_post_write_fixup(NTFS_RECORD *b);
+
+#endif /* defined _NTFS_MST_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/ntfstime.h b/usr/src/lib/libntfs/common/include/ntfs/ntfstime.h
new file mode 100644
index 0000000000..2fb85a5413
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/ntfstime.h
@@ -0,0 +1,69 @@
+/*
+ * ntfstime.h - NTFS time related functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_NTFSTIME_H
+#define _NTFS_NTFSTIME_H
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "types.h"
+
+#define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000)
+
+/**
+ * ntfs2utc - Convert an NTFS time to Unix time
+ * @ntfs_time: An NTFS time in 100ns units since 1601
+ *
+ * NTFS stores times as the number of 100ns intervals since January 1st 1601 at
+ * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD.
+ *
+ * Return: n A Unix time (number of seconds since 1970)
+ */
+static __inline__ time_t ntfs2utc(sle64 ntfs_time)
+{
+ return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000;
+}
+
+/**
+ * utc2ntfs - Convert Linux time to NTFS time
+ * @utc_time: Linux time to convert to NTFS
+ *
+ * Convert the Linux time @utc_time to its corresponding NTFS time.
+ *
+ * Linux stores time in a long at present and measures it as the number of
+ * 1-second intervals since 1st January 1970, 00:00:00 UTC.
+ *
+ * NTFS uses Microsoft's standard time format which is stored in a s64 and is
+ * measured as the number of 100 nano-second intervals since 1st January 1601,
+ * 00:00:00 UTC.
+ *
+ * Return: n An NTFS time (100ns units since Jan 1601)
+ */
+static __inline__ sle64 utc2ntfs(time_t utc_time)
+{
+ /* Convert to 100ns intervals and then add the NTFS time offset. */
+ return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET);
+}
+
+#endif /* _NTFS_NTFSTIME_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/runlist.h b/usr/src/lib/libntfs/common/include/ntfs/runlist.h
new file mode 100644
index 0000000000..f35202971f
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/runlist.h
@@ -0,0 +1,90 @@
+/*
+ * runlist.h - Exports for runlist handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002 Anton Altaparmakov
+ * Copyright (c) 2002 Richard Russon
+ *
+ * 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 _NTFS_RUNLIST_H
+#define _NTFS_RUNLIST_H
+
+#include "types.h"
+
+/* Forward declarations */
+typedef struct _runlist_element runlist_element;
+typedef runlist_element runlist;
+
+#include "attrib.h"
+#include "volume.h"
+
+/**
+ * struct _runlist_element - in memory vcn to lcn mapping array element.
+ * @vcn: starting vcn of the current array element
+ * @lcn: starting lcn of the current array element
+ * @length: length in clusters of the current array element
+ *
+ * The last vcn (in fact the last vcn + 1) is reached when length == 0.
+ *
+ * When lcn == -1 this means that the count vcns starting at vcn are not
+ * physically allocated (i.e. this is a hole / data is sparse).
+ */
+struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
+ VCN vcn; /* vcn = Starting virtual cluster number. */
+ LCN lcn; /* lcn = Starting logical cluster number. */
+ s64 length; /* Run length in clusters. */
+};
+
+extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
+
+extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
+ const s64 pos, s64 count, void *b);
+extern s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
+ const s64 pos, s64 count, void *b);
+
+extern int ntfs_rl_fill_zero(const ntfs_volume *vol, const runlist *rl,
+ s64 pos, const s64 count);
+
+extern runlist_element *ntfs_runlists_merge(runlist_element *drl,
+ runlist_element *srl);
+
+extern runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
+ const ATTR_RECORD *attr, runlist_element *old_rl);
+
+extern int ntfs_get_nr_significant_bytes(const s64 n);
+
+extern int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+ const runlist_element *rl, const VCN start_vcn);
+
+extern int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max,
+ const s64 n);
+
+extern int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
+ const int dst_len, const runlist_element *rl,
+ const VCN start_vcn, VCN *const stop_vcn);
+
+extern int ntfs_rl_truncate(runlist **arl, const VCN start_vcn);
+
+extern int ntfs_rl_sparse(runlist *rl);
+extern s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl);
+
+#ifdef NTFS_TEST
+int test_rl_main(int argc, char *argv[]);
+#endif
+
+#endif /* defined _NTFS_RUNLIST_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/security.h b/usr/src/lib/libntfs/common/include/ntfs/security.h
new file mode 100644
index 0000000000..a61aabd754
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/security.h
@@ -0,0 +1,55 @@
+/*
+ * security.h - Exports for handling security/ACLs in NTFS. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2004 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 _NTFS_SECURITY_H
+#define _NTFS_SECURITY_H
+
+#include "types.h"
+#include "layout.h"
+
+extern const GUID *const zero_guid;
+
+extern BOOL ntfs_guid_is_zero(const GUID *guid);
+extern char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str);
+
+/**
+ * ntfs_sid_is_valid - determine if a SID is valid
+ * @sid: SID for which to determine if it is valid
+ *
+ * Determine if the SID pointed to by @sid is valid.
+ *
+ * Return TRUE if it is valid and FALSE otherwise.
+ */
+static __inline__ BOOL ntfs_sid_is_valid(const SID *sid)
+{
+ if (!sid || sid->revision != SID_REVISION ||
+ sid->sub_authority_count > SID_MAX_SUB_AUTHORITIES)
+ return FALSE;
+ return TRUE;
+}
+
+extern int ntfs_sid_to_mbs_size(const SID *sid);
+extern char *ntfs_sid_to_mbs(const SID *sid, char *sid_str,
+ size_t sid_str_size);
+extern void ntfs_generate_guid(GUID *guid);
+
+#endif /* defined _NTFS_SECURITY_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/support.h b/usr/src/lib/libntfs/common/include/ntfs/support.h
new file mode 100644
index 0000000000..7c1eed632a
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/support.h
@@ -0,0 +1,121 @@
+/*
+ * support.h - Various useful things. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2006 Szabolcs Szakacsits
+ * Copyright (c) 2006 Yura Pakhuchiy
+ *
+ * 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 _NTFS_SUPPORT_H
+#define _NTFS_SUPPORT_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "logging.h"
+
+/*
+ * Our mailing list. Use this define to prevent typos in email address.
+ */
+#define NTFS_DEV_LIST "linux-ntfs-dev@lists.sf.net"
+
+/*
+ * Generic macro to convert pointers to values for comparison purposes.
+ */
+#ifndef p2n
+#define p2n(p) ((ptrdiff_t)((ptrdiff_t*)(p)))
+#endif
+
+/*
+ * The classic min and max macros.
+ */
+#ifndef min
+#define min(a,b) ((a) <= (b) ? (a) : (b))
+#endif
+
+#ifndef max
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+#endif
+
+/*
+ * Useful macro for determining the offset of a struct member.
+ */
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+/*
+ * Round up and down @num to 2 in power of @order.
+ */
+#define ROUND_UP(num,order) (((num) + ((1 << (order)) - 1)) & \
+ ~((1 << (order)) - 1))
+#define ROUND_DOWN(num,order) ((num) & ~((1 << (order)) - 1))
+
+/*
+ * Simple bit operation macros. NOTE: These are NOT atomic.
+ */
+#define test_bit(bit, var) ((var) & (1 << (bit)))
+#define set_bit(bit, var) (var) |= 1 << (bit)
+#define clear_bit(bit, var) (var) &= ~(1 << (bit))
+
+#ifdef __sun
+#define test_and_set_bit(bit, var) _test_and_set_bit(bit, &var)
+static __inline__ BOOL _test_and_set_bit(unsigned long bit, unsigned long *var)
+{
+ const BOOL old_state = test_bit(bit, *var);
+ set_bit(bit, *var);
+ return old_state;
+}
+
+#define test_and_clear_bit(bit, var) _test_and_clear_bit(bit, &var)
+static __inline__ BOOL _test_and_clear_bit(unsigned long bit, unsigned long *var)
+{
+ const BOOL old_state = test_bit(bit, *var);
+ clear_bit(bit, *var);
+ return old_state;
+}
+#else /* !__sun */
+#define test_and_set_bit(bit, var) \
+({ \
+ const BOOL old_state = test_bit(bit, var); \
+ set_bit(bit, var); \
+ old_state; \
+})
+
+#define test_and_clear_bit(bit, var) \
+({ \
+ const BOOL old_state = test_bit(bit, var); \
+ clear_bit(bit, var); \
+ old_state; \
+})
+#endif /* __sun */
+
+/* Memory allocation with logging. */
+extern void *ntfs_calloc(size_t size);
+extern void *ntfs_malloc(size_t size);
+
+#endif /* defined _NTFS_SUPPORT_H */
diff --git a/usr/src/lib/libntfs/common/include/ntfs/types.h b/usr/src/lib/libntfs/common/include/ntfs/types.h
new file mode 100644
index 0000000000..cd9a9a998d
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/types.h
@@ -0,0 +1,142 @@
+/*
+ * types.h - Misc type definitions not related to on-disk structure. Part of
+ * the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2006 Szabolcs Szakacsits
+ * Copyright (c) 2007 Yura Pakhuchiy
+ *
+ * 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 _NTFS_TYPES_H
+#define _NTFS_TYPES_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if HAVE_STDINT_H || !HAVE_CONFIG_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+typedef uint8_t u8; /* Unsigned types of an exact size */
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef int8_t s8; /* Signed types of an exact size */
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+#if defined(__CHECKER__) && !defined(NTFS_DO_NOT_CHECK_ENDIANS)
+ #undef __bitwise
+ #undef __force
+ #define __bitwise __attribute__((bitwise))
+ #define __force __attribute__((force))
+#else
+ #undef __bitwise
+ #undef __force
+ #define __bitwise
+ #define __force
+#endif
+
+typedef u16 __bitwise le16;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise le64;
+
+/*
+ * Declare sle{16,32,64} to be unsigned because we do not want sign extension
+ * on BE architectures.
+ */
+typedef u16 __bitwise sle16;
+typedef u32 __bitwise sle32;
+typedef u64 __bitwise sle64;
+
+typedef u16 __bitwise be16;
+typedef u32 __bitwise be32;
+typedef u64 __bitwise be64;
+
+typedef le16 ntfschar; /* 2-byte Unicode character type. */
+#define UCHAR_T_SIZE_BITS 1
+
+/*
+ * Clusters are signed 64-bit values on NTFS volumes. We define two types, LCN
+ * and VCN, to allow for type checking and better code readability.
+ */
+typedef s64 VCN;
+typedef sle64 leVCN;
+typedef s64 LCN;
+typedef sle64 leLCN;
+
+/*
+ * The NTFS journal $LogFile uses log sequence numbers which are signed 64-bit
+ * values. We define our own type LSN, to allow for type checking and better
+ * code readability.
+ */
+typedef s64 LSN;
+typedef sle64 leLSN;
+
+/*
+ * Cygwin has a collision between our BOOL and <windef.h>'s
+ * As long as this file will be included after <windows.h> we're fine.
+ */
+#ifndef _WINDEF_H
+/**
+ * enum BOOL - These are just to make the code more readable...
+ */
+typedef enum {
+#ifndef FALSE
+ FALSE = 0,
+#endif
+#ifndef NO
+ NO = 0,
+#endif
+#ifndef ZERO
+ ZERO = 0,
+#endif
+#ifndef TRUE
+ TRUE = 1,
+#endif
+#ifndef YES
+ YES = 1,
+#endif
+#ifndef ONE
+ ONE = 1,
+#endif
+} BOOL;
+#endif /* defined _WINDEF_H */
+
+/**
+ * enum IGNORE_CASE_BOOL -
+ */
+typedef enum {
+ CASE_SENSITIVE = 0,
+ IGNORE_CASE = 1,
+} IGNORE_CASE_BOOL;
+
+#define STATUS_OK (0)
+#define STATUS_ERROR (-1)
+#define STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT (-2)
+#define STATUS_KEEP_SEARCHING (-3)
+#define STATUS_NOT_FOUND (-4)
+
+#endif /* defined _NTFS_TYPES_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/unistr.h b/usr/src/lib/libntfs/common/include/ntfs/unistr.h
new file mode 100644
index 0000000000..2c5fd5548c
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/unistr.h
@@ -0,0 +1,69 @@
+/*
+ * unistr.h - Exports for Unicode string handling. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2000-2006 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 _NTFS_UNISTR_H
+#define _NTFS_UNISTR_H
+
+#include "types.h"
+#include "layout.h"
+
+extern BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
+ const ntfschar *s2, size_t s2_len, const IGNORE_CASE_BOOL ic,
+ const ntfschar *upcase, const u32 upcase_size);
+
+extern int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
+ const ntfschar *name2, const u32 name2_len,
+ const int err_val, const IGNORE_CASE_BOOL ic,
+ const ntfschar *upcase, const u32 upcase_len);
+
+extern int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n);
+
+extern int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
+ const ntfschar *upcase, const u32 upcase_size);
+
+extern u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen);
+
+extern ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen);
+
+extern void ntfs_name_upcase(ntfschar *name, u32 name_len,
+ const ntfschar *upcase, const u32 upcase_len);
+
+extern void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
+ const ntfschar *upcase, const u32 upcase_len);
+
+extern int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
+ const FILE_NAME_ATTR *file_name_attr2,
+ const int err_val, const IGNORE_CASE_BOOL ic,
+ const ntfschar *upcase, const u32 upcase_len);
+
+extern int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
+ int outs_len);
+extern int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len);
+
+extern void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len);
+
+extern ntfschar *ntfs_str2ucs(const char *s, int *len);
+
+extern void ntfs_ucsfree(ntfschar *ucs);
+
+#endif /* defined _NTFS_UNISTR_H */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/version.h b/usr/src/lib/libntfs/common/include/ntfs/version.h
new file mode 100644
index 0000000000..ec6dbdca32
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/version.h
@@ -0,0 +1,29 @@
+/*
+ * version.h - Info about the NTFS library. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Anton Altaparmakov
+ * Copyright (c) 2005 Richard Russon
+ *
+ * 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 _NTFS_VERSION_H_
+#define _NTFS_VERSION_H_
+
+extern const char *ntfs_libntfs_version(void);
+
+#endif /* _NTFS_VERSION_H_ */
+
diff --git a/usr/src/lib/libntfs/common/include/ntfs/volume.h b/usr/src/lib/libntfs/common/include/ntfs/volume.h
new file mode 100644
index 0000000000..3183d69b13
--- /dev/null
+++ b/usr/src/lib/libntfs/common/include/ntfs/volume.h
@@ -0,0 +1,247 @@
+/*
+ * volume.h - Exports for NTFS volume handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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 _NTFS_VOLUME_H
+#define _NTFS_VOLUME_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+
+/* Forward declaration */
+typedef struct _ntfs_volume ntfs_volume;
+
+#include "list.h"
+#include "types.h"
+#include "support.h"
+#include "device.h"
+#include "inode.h"
+#include "attrib.h"
+
+/**
+ * enum ntfs_mount_flags -
+ *
+ * Flags for the ntfs_mount() function.
+ */
+typedef enum {
+ NTFS_MNT_RDONLY = 1,
+ NTFS_MNT_FORENSIC = 2,
+ NTFS_MNT_CASE_SENSITIVE = 4,
+ NTFS_MNT_NOT_EXCLUSIVE = 8,
+ NTFS_MNT_FORCE = 16,
+ NTFS_MNT_INTERIX = 32,
+} ntfs_mount_flags;
+
+/**
+ * enum ntfs_mounted_flags -
+ *
+ * Flags returned by the ntfs_check_if_mounted() function.
+ */
+typedef enum {
+ NTFS_MF_MOUNTED = 1, /* Device is mounted. */
+ NTFS_MF_ISROOT = 2, /* Device is mounted as system root. */
+ NTFS_MF_READONLY = 4, /* Device is mounted read-only. */
+} ntfs_mounted_flags;
+
+extern int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags);
+
+/**
+ * enum ntfs_volume_state_bits -
+ *
+ * Defined bits for the state field in the ntfs_volume structure.
+ */
+typedef enum {
+ NV_ReadOnly, /* 1: Volume is read-only. */
+ NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */
+ NV_LogFileEmpty, /* 1: $logFile journal is empty. */
+ NV_NoATime, /* 1: Do not update access time. */
+ NV_WasDirty, /* 1: Volume was marked dirty before we mounted
+ it. */
+ NV_ForensicMount, /* 1: Mount is forensic, i.e. no modifications
+ are to be done by mount/umount. */
+ NV_Interix, /* 1: Make libntfs recognize Interix special
+ files. */
+} ntfs_volume_state_bits;
+
+#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
+#define set_nvol_flag(nv, flag) set_bit(NV_##flag, (nv)->state)
+#define clear_nvol_flag(nv, flag) clear_bit(NV_##flag, (nv)->state)
+
+#define NVolReadOnly(nv) test_nvol_flag(nv, ReadOnly)
+#define NVolSetReadOnly(nv) set_nvol_flag(nv, ReadOnly)
+#define NVolClearReadOnly(nv) clear_nvol_flag(nv, ReadOnly)
+
+#define NVolCaseSensitive(nv) test_nvol_flag(nv, CaseSensitive)
+#define NVolSetCaseSensitive(nv) set_nvol_flag(nv, CaseSensitive)
+#define NVolClearCaseSensitive(nv) clear_nvol_flag(nv, CaseSensitive)
+
+#define NVolLogFileEmpty(nv) test_nvol_flag(nv, LogFileEmpty)
+#define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty)
+#define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty)
+
+#define NVolWasDirty(nv) test_nvol_flag(nv, WasDirty)
+#define NVolSetWasDirty(nv) set_nvol_flag(nv, WasDirty)
+#define NVolClearWasDirty(nv) clear_nvol_flag(nv, WasDirty)
+
+#define NVolForensicMount(nv) test_nvol_flag(nv, ForensicMount)
+#define NVolSetForensicMount(nv) set_nvol_flag(nv, ForensicMount)
+#define NVolClearForensicMount(nv) clear_nvol_flag(nv, ForensicMount)
+
+#define NVolInterix(nv) test_nvol_flag(nv, Interix)
+#define NVolSetInterix(nv) set_nvol_flag(nv, Interix)
+#define NVolClearInterix(nv) clear_nvol_flag(nv, Interix)
+
+/*
+ * NTFS version 1.1 and 1.2 are used by Windows NT4.
+ * NTFS version 2.x is used by Windows 2000 Beta
+ * NTFS version 3.0 is used by Windows 2000.
+ * NTFS version 3.1 is used by Windows XP, 2003 and Vista.
+ */
+
+#define NTFS_V1_1(major, minor) ((major) == 1 && (minor) == 1)
+#define NTFS_V1_2(major, minor) ((major) == 1 && (minor) == 2)
+#define NTFS_V2_X(major, minor) ((major) == 2)
+#define NTFS_V3_0(major, minor) ((major) == 3 && (minor) == 0)
+#define NTFS_V3_1(major, minor) ((major) == 3 && (minor) == 1)
+
+#define NTFS_BUF_SIZE 8192
+
+#define NTFS_INODE_CACHE_SIZE 512 /* WARNING: This should be power of 2. */
+#define NTFS_INODE_CACHE_SIZE_BITS (NTFS_INODE_CACHE_SIZE - 1)
+
+/**
+ * struct _ntfs_volume - structure describing an open volume in memory.
+ */
+struct _ntfs_volume {
+ union {
+ struct ntfs_device *dev; /* NTFS device associated with
+ the volume. */
+ void *sb; /* For kernel porting compatibility. */
+ } u;
+ char *vol_name; /* Name of the volume. */
+ unsigned long state; /* NTFS specific flags describing this volume.
+ See ntfs_volume_state_bits above. */
+
+ ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */
+ u8 major_ver; /* Ntfs major version of volume. */
+ u8 minor_ver; /* Ntfs minor version of volume. */
+ le16 flags; /* Bit array of VOLUME_* flags. */
+ GUID guid; /* The volume guid if present (otherwise it is
+ a NULL guid). */
+
+ u16 sector_size; /* Byte size of a sector. */
+ u8 sector_size_bits; /* Log(2) of the byte size of a sector. */
+ u32 cluster_size; /* Byte size of a cluster. */
+ u32 mft_record_size; /* Byte size of a mft record. */
+ u32 indx_record_size; /* Byte size of a INDX record. */
+ u8 cluster_size_bits; /* Log(2) of the byte size of a cluster. */
+ u8 mft_record_size_bits;/* Log(2) of the byte size of a mft record. */
+ u8 indx_record_size_bits;/* Log(2) of the byte size of a INDX record. */
+
+ /* Variables used by the cluster and mft allocators. */
+ u8 mft_zone_multiplier; /* Initial mft zone multiplier. */
+ s64 mft_data_pos; /* Mft record number at which to allocate the
+ next mft record. */
+ LCN mft_zone_start; /* First cluster of the mft zone. */
+ LCN mft_zone_end; /* First cluster beyond the mft zone. */
+ LCN mft_zone_pos; /* Current position in the mft zone. */
+ LCN data1_zone_pos; /* Current position in the first data zone. */
+ LCN data2_zone_pos; /* Current position in the second data zone. */
+
+ s64 nr_clusters; /* Volume size in clusters, hence also the
+ number of bits in lcn_bitmap. */
+ ntfs_inode *lcnbmp_ni; /* ntfs_inode structure for FILE_Bitmap. */
+ ntfs_attr *lcnbmp_na; /* ntfs_attr structure for the data attribute
+ of FILE_Bitmap. Each bit represents a
+ cluster on the volume, bit 0 representing
+ lcn 0 and so on. A set bit means that the
+ cluster and vice versa. */
+
+ LCN mft_lcn; /* Logical cluster number of the data attribute
+ for FILE_MFT. */
+ ntfs_inode *mft_ni; /* ntfs_inode structure for FILE_MFT. */
+ ntfs_attr *mft_na; /* ntfs_attr structure for the data attribute
+ of FILE_MFT. */
+ ntfs_attr *mftbmp_na; /* ntfs_attr structure for the bitmap attribute
+ of FILE_MFT. Each bit represents an mft
+ record in the $DATA attribute, bit 0
+ representing mft record 0 and so on. A set
+ bit means that the mft record is in use and
+ vice versa. */
+
+ int mftmirr_size; /* Size of the FILE_MFTMirr in mft records. */
+ LCN mftmirr_lcn; /* Logical cluster number of the data attribute
+ for FILE_MFTMirr. */
+ ntfs_inode *mftmirr_ni; /* ntfs_inode structure for FILE_MFTMirr. */
+ ntfs_attr *mftmirr_na; /* ntfs_attr structure for the data attribute
+ of FILE_MFTMirr. */
+
+ ntfschar *upcase; /* Upper case equivalents of all 65536 2-byte
+ Unicode characters. Obtained from
+ FILE_UpCase. */
+ u32 upcase_len; /* Length in Unicode characters of the upcase
+ table. */
+
+ ATTR_DEF *attrdef; /* Attribute definitions. Obtained from
+ FILE_AttrDef. */
+ s32 attrdef_len; /* Size of the attribute definition table in
+ bytes. */
+
+ long nr_free_clusters; /* This two are self explaining. */
+ long nr_free_mft_records;
+
+ struct list_head inode_cache[NTFS_INODE_CACHE_SIZE]; /* List of opened
+ inodes. */
+};
+
+extern ntfs_volume *ntfs_volume_alloc(void);
+
+extern ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
+ ntfs_mount_flags flags);
+
+extern ntfs_volume *ntfs_device_mount(struct ntfs_device *dev,
+ ntfs_mount_flags flags);
+extern int ntfs_device_umount(ntfs_volume *vol, const BOOL force);
+
+extern ntfs_volume *ntfs_mount(const char *name, ntfs_mount_flags flags);
+extern int ntfs_umount(ntfs_volume *vol, const BOOL force);
+
+extern int ntfs_version_is_supported(ntfs_volume *vol);
+extern int ntfs_logfile_reset(ntfs_volume *vol);
+
+extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags);
+
+#endif /* defined _NTFS_VOLUME_H */
diff --git a/usr/src/lib/libntfs/common/libntfs/attrib.c b/usr/src/lib/libntfs/common/libntfs/attrib.c
new file mode 100644
index 0000000000..3c239bdaa3
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/attrib.c
@@ -0,0 +1,5234 @@
+/**
+ * attrib.c - Attribute handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 Anton Altaparmakov
+ * Copyright (c) 2002-2005 Richard Russon
+ * Copyright (c) 2002-2006 Szabolcs Szakacsits
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "attrib.h"
+#include "attrlist.h"
+#include "device.h"
+#include "mft.h"
+#include "debug.h"
+#include "mst.h"
+#include "volume.h"
+#include "types.h"
+#include "layout.h"
+#include "inode.h"
+#include "runlist.h"
+#include "lcnalloc.h"
+#include "dir.h"
+#include "compress.h"
+#include "bitmap.h"
+#include "logging.h"
+#include "support.h"
+#include "crypto.h"
+
+ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
+
+/**
+ * ntfs_get_attribute_value_length - Find the length of an attribute
+ * @a:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a)
+{
+ if (!a) {
+ errno = EINVAL;
+ return 0;
+ }
+ errno = 0;
+ if (a->non_resident)
+ return sle64_to_cpu(a->u.nonres.data_size);
+ return (s64)le32_to_cpu(a->u.res.value_length);
+}
+
+/**
+ * ntfs_get_attribute_value - Get a copy of an attribute
+ * @vol:
+ * @a:
+ * @b:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+s64 ntfs_get_attribute_value(const ntfs_volume *vol,
+ const ATTR_RECORD *a, u8 *b)
+{
+ runlist *rl;
+ s64 total, r;
+ int i;
+
+ /* Sanity checks. */
+ if (!vol || !a || !b) {
+ errno = EINVAL;
+ return 0;
+ }
+ /* Complex attribute? */
+ /*
+ * Ignore the flags in case they are not zero for an attribute list
+ * attribute. Windows does not complain about invalid flags and chkdsk
+ * does not detect or fix them so we need to cope with it, too.
+ */
+ if (a->type != AT_ATTRIBUTE_LIST && a->flags) {
+ ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle "
+ "this yet.\n", le16_to_cpu(a->flags));
+ errno = EOPNOTSUPP;
+ return 0;
+ }
+ if (!a->non_resident) {
+ /* Attribute is resident. */
+
+ /* Sanity check. */
+ if (le32_to_cpu(a->u.res.value_length) + le16_to_cpu(a->u.res.value_offset)
+ > le32_to_cpu(a->length)) {
+ return 0;
+ }
+
+ memcpy(b, (const char*)a + le16_to_cpu(a->u.res.value_offset),
+ le32_to_cpu(a->u.res.value_length));
+ errno = 0;
+ return (s64)le32_to_cpu(a->u.res.value_length);
+ }
+
+ /* Attribute is not resident. */
+
+ /* If no data, return 0. */
+ if (!(a->u.nonres.data_size)) {
+ errno = 0;
+ return 0;
+ }
+ /*
+ * FIXME: What about attribute lists?!? (AIA)
+ */
+ /* Decompress the mapping pairs array into a runlist. */
+ rl = ntfs_mapping_pairs_decompress(vol, a, NULL);
+ if (!rl) {
+ errno = EINVAL;
+ return 0;
+ }
+ /*
+ * FIXED: We were overflowing here in a nasty fashion when we
+ * reach the last cluster in the runlist as the buffer will
+ * only be big enough to hold data_size bytes while we are
+ * reading in allocated_size bytes which is usually larger
+ * than data_size, since the actual data is unlikely to have a
+ * size equal to a multiple of the cluster size!
+ * FIXED2: We were also overflowing here in the same fashion
+ * when the data_size was more than one run smaller than the
+ * allocated size which happens with Windows XP sometimes.
+ */
+ /* Now load all clusters in the runlist into b. */
+ for (i = 0, total = 0; rl[i].length; i++) {
+ if (total + (rl[i].length << vol->cluster_size_bits) >=
+ sle64_to_cpu(a->u.nonres.data_size)) {
+ unsigned char *intbuf = NULL;
+ /*
+ * We have reached the last run so we were going to
+ * overflow when executing the ntfs_pread() which is
+ * BAAAAAAAD!
+ * Temporary fix:
+ * Allocate a new buffer with size:
+ * rl[i].length << vol->cluster_size_bits, do the
+ * read into our buffer, then memcpy the correct
+ * amount of data into the caller supplied buffer,
+ * free our buffer, and continue.
+ * We have reached the end of data size so we were
+ * going to overflow in the same fashion.
+ * Temporary fix: same as above.
+ */
+ intbuf = ntfs_malloc(rl[i].length <<
+ vol->cluster_size_bits);
+ if (!intbuf) {
+ int eo = errno;
+ free(rl);
+ errno = eo;
+ return 0;
+ }
+ /*
+ * FIXME: If compressed file: Only read if lcn != -1.
+ * Otherwise, we are dealing with a sparse run and we
+ * just memset the user buffer to 0 for the length of
+ * the run, which should be 16 (= compression unit
+ * size).
+ * FIXME: Really only when file is compressed, or can
+ * we have sparse runs in uncompressed files as well?
+ * - Yes we can, in sparse files! But not necessarily
+ * size of 16, just run length.
+ */
+ r = ntfs_pread(vol->u.dev, rl[i].lcn <<
+ vol->cluster_size_bits, rl[i].length <<
+ vol->cluster_size_bits, intbuf);
+ if (r != rl[i].length << vol->cluster_size_bits) {
+#define ESTR "Error reading attribute value"
+ if (r == -1) {
+ int eo = errno;
+ ntfs_log_perror(ESTR);
+ errno = eo;
+ } else if (r < rl[i].length <<
+ vol->cluster_size_bits) {
+ ntfs_log_debug(ESTR": Ran out of "
+ "input data.\n");
+ errno = EIO;
+ } else {
+ ntfs_log_debug(ESTR": unknown error\n");
+ errno = EIO;
+ }
+#undef ESTR
+ free(rl);
+ free(intbuf);
+ return 0;
+ }
+ memcpy(b + total, intbuf, sle64_to_cpu(a->u.nonres.data_size) -
+ total);
+ free(intbuf);
+ total = sle64_to_cpu(a->u.nonres.data_size);
+ break;
+ }
+ /*
+ * FIXME: If compressed file: Only read if lcn != -1.
+ * Otherwise, we are dealing with a sparse run and we just
+ * memset the user buffer to 0 for the length of the run, which
+ * should be 16 (= compression unit size).
+ * FIXME: Really only when file is compressed, or can
+ * we have sparse runs in uncompressed files as well?
+ * - Yes we can, in sparse files! But not necessarily size of
+ * 16, just run length.
+ */
+ r = ntfs_pread(vol->u.dev, rl[i].lcn << vol->cluster_size_bits,
+ rl[i].length << vol->cluster_size_bits,
+ b + total);
+ if (r != rl[i].length << vol->cluster_size_bits) {
+#define ESTR "Error reading attribute value"
+ if (r == -1) {
+ int eo = errno;
+ ntfs_log_perror(ESTR);
+ errno = eo;
+ } else if (r < rl[i].length << vol->cluster_size_bits) {
+ ntfs_log_debug(ESTR ": Ran out of "
+ "input data.\n");
+ errno = EIO;
+ } else {
+ ntfs_log_debug(ESTR ": unknown error\n");
+ errno = EIO;
+ }
+#undef ESTR
+ free(rl);
+ return 0;
+ }
+ total += r;
+ }
+ free(rl);
+ return total;
+}
+
+/* Already cleaned up code below, but still look for FIXME:... */
+
+/**
+ * __ntfs_attr_init - primary initialization of an ntfs attribute structure
+ * @na: ntfs attribute to initialize
+ * @ni: ntfs inode with which to initialize the ntfs attribute
+ * @type: attribute type
+ * @name: attribute name in little endian Unicode or NULL
+ * @name_len: length of attribute @name in Unicode characters (if @name given)
+ *
+ * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len.
+ */
+static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
+ const ATTR_TYPES type, ntfschar *name, const u32 name_len)
+{
+ na->rl = NULL;
+ na->ni = ni;
+ na->type = type;
+ na->name = name;
+ if (name)
+ na->name_len = name_len;
+ else
+ na->name_len = 0;
+}
+
+/**
+ * ntfs_attr_init - initialize an ntfs_attr with data sizes and status
+ * @na:
+ * @non_resident:
+ * @compressed:
+ * @encrypted:
+ * @sparse:
+ * @allocated_size:
+ * @data_size:
+ * @initialized_size:
+ * @compressed_size:
+ * @compression_unit:
+ *
+ * Final initialization for an ntfs attribute.
+ */
+void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
+ const BOOL compressed, const BOOL encrypted, const BOOL sparse,
+ const s64 allocated_size, const s64 data_size,
+ const s64 initialized_size, const s64 compressed_size,
+ const u8 compression_unit)
+{
+ if (!NAttrInitialized(na)) {
+ if (non_resident)
+ NAttrSetNonResident(na);
+ if (compressed)
+ NAttrSetCompressed(na);
+ if (encrypted)
+ NAttrSetEncrypted(na);
+ if (sparse)
+ NAttrSetSparse(na);
+ na->allocated_size = allocated_size;
+ na->data_size = data_size;
+ na->initialized_size = initialized_size;
+ if (compressed || sparse) {
+ ntfs_volume *vol = na->ni->vol;
+
+ na->compressed_size = compressed_size;
+ na->compression_block_clusters = 1 << compression_unit;
+ na->compression_block_size = 1 << (compression_unit +
+ vol->cluster_size_bits);
+ na->compression_block_size_bits = ffs(
+ na->compression_block_size) - 1;
+ }
+ NAttrSetInitialized(na);
+ }
+}
+
+/**
+ * ntfs_attr_open - open an ntfs attribute for access
+ * @ni: open ntfs inode in which the ntfs attribute resides
+ * @type: attribute type
+ * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL
+ * @name_len: length of attribute @name in Unicode characters (if @name given)
+ *
+ * Allocate a new ntfs attribute structure, initialize it with @ni, @type,
+ * @name, and @name_len, then return it. Return NULL on error with
+ * errno set to the error code.
+ *
+ * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you
+ * do not care whether the attribute is named or not set @name to NULL. In
+ * both those cases @name_len is not used at all.
+ */
+ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
+ ntfschar *name, u32 name_len)
+{
+ ntfs_attr_search_ctx *ctx;
+ ntfs_attr *na;
+ ATTR_RECORD *a;
+ struct list_head *pos;
+ int err;
+ BOOL cs;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
+ (unsigned long long)ni->mft_no, type);
+ if (!ni || !ni->vol || !ni->mrec) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* Check cache, maybe this attribute already opened? */
+ list_for_each(pos, &ni->attr_cache) {
+ ntfs_attr *tmp_na;
+
+ tmp_na = list_entry(pos, ntfs_attr, list_entry);
+ if (tmp_na->type == type && tmp_na->name_len == name_len &&
+ !ntfs_ucsncmp(tmp_na->name, name, name_len)) {
+ ntfs_log_trace("Found this attribute in cache, "
+ "increment reference count and "
+ "return it.\n");
+ tmp_na->nr_references++;
+ return tmp_na;
+ }
+ }
+ /* Search failed. Properly open attrbute. */
+ na = calloc(sizeof(ntfs_attr), 1);
+ if (!na)
+ return NULL;
+ if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) {
+ name = ntfs_ucsndup(name, name_len);
+ if (!name) {
+ err = errno;
+ free(na);
+ errno = err;
+ return NULL;
+ }
+ }
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = errno;
+ goto err_out;
+ }
+ if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) {
+ err = errno;
+ goto put_err_out;
+ }
+
+ a = ctx->attr;
+ /*
+ * Wipe the flags in case they are not zero for an attribute list
+ * attribute. Windows does not complain about invalid flags and chkdsk
+ * does not detect or fix them so we need to cope with it, too.
+ */
+ if (type == AT_ATTRIBUTE_LIST)
+ a->flags = 0;
+ cs = (a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? 1 : 0;
+ if (!name) {
+ if (a->name_length) {
+ name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu(
+ a->name_offset)), a->name_length);
+ if (!name) {
+ err = errno;
+ goto put_err_out;
+ }
+ name_len = a->name_length;
+ } else {
+ name = AT_UNNAMED;
+ name_len = 0;
+ }
+ }
+ __ntfs_attr_init(na, ni, type, name, name_len);
+ if (a->non_resident) {
+ ntfs_attr_init(na, TRUE, (a->flags & ATTR_IS_COMPRESSED)? 1 : 0,
+ (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0,
+ (a->flags & ATTR_IS_SPARSE) ? 1 : 0,
+ sle64_to_cpu(a->u.nonres.allocated_size),
+ sle64_to_cpu(a->u.nonres.data_size),
+ sle64_to_cpu(a->u.nonres.initialized_size),
+ cs ? sle64_to_cpu(a->u.nonres.compressed_size) : 0,
+ cs ? a->u.nonres.compression_unit : 0);
+ } else {
+ s64 l = le32_to_cpu(a->u.res.value_length);
+ ntfs_attr_init(na, FALSE, (a->flags & ATTR_IS_COMPRESSED) ? 1:0,
+ (a->flags & ATTR_IS_ENCRYPTED) ? 1 : 0,
+ (a->flags & ATTR_IS_SPARSE) ? 1 : 0,
+ (l + 7) & ~7, l, l, cs ? (l + 7) & ~7 : 0, 0);
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ if (NAttrEncrypted(na))
+ ntfs_crypto_attr_open(na);
+ list_add_tail(&na->list_entry, &ni->attr_cache);
+ na->nr_references = 1;
+ return na;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+err_out:
+ free(na);
+ errno = err;
+ return NULL;
+}
+
+/**
+ * ntfs_attr_close - free an ntfs attribute structure
+ * @na: ntfs attribute structure to free
+ *
+ * Release all memory associated with the ntfs attribute @na and then release
+ * @na itself.
+ */
+void ntfs_attr_close(ntfs_attr *na)
+{
+ if (!na)
+ return;
+ na->nr_references--;
+ if (na->nr_references) {
+ ntfs_log_trace("There are %d more references left to "
+ "this attribute.\n", na->nr_references);
+ return;
+ }
+ ntfs_log_trace("There are no more references left to this attribute\n");
+ list_del(&na->list_entry);
+ if (NAttrEncrypted(na))
+ ntfs_crypto_attr_close(na);
+ if (NAttrNonResident(na) && na->rl)
+ free(na->rl);
+ /* Don't release if using an internal constant. */
+ if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30)
+ free(na->name);
+ free(na);
+}
+
+/**
+ * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute
+ * @na: ntfs attribute for which to map (part of) a runlist
+ * @vcn: map runlist part containing this vcn
+ *
+ * Map the part of a runlist containing the @vcn of the ntfs attribute @na.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn)
+{
+ LCN lcn;
+ ntfs_attr_search_ctx *ctx;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n",
+ (unsigned long long)na->ni->mft_no, na->type, (long long)vcn);
+
+ lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
+ if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT)
+ return 0;
+
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* Find the attribute in the mft record. */
+ if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
+ vcn, NULL, 0, ctx)) {
+ runlist_element *rl;
+
+ /* Decode the runlist. */
+ rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr,
+ na->rl);
+ if (rl) {
+ na->rl = rl;
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ return -1;
+}
+
+/**
+ * ntfs_attr_map_runlist_range - map (a part of) a runlist of an ntfs attribute
+ * @na: ntfs attribute for which to map (part of) a runlist
+ * @from_vcn: map runlist part starting this vcn
+ * @to_vcn: map runlist part ending this vcn
+ *
+ * Map the part of a runlist from containing the @from_vcn to containing the
+ * @to_vcn of an ntfs attribute @na. It is OK for @to_vcn to be beyond last run.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attr_map_runlist_range(ntfs_attr *na, VCN from_vcn, VCN to_vcn)
+{
+ ntfs_attr_search_ctx *ctx = NULL;
+ runlist *rl;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, "
+ "from_vcn 0x%llx, to_vcn 0x%llx.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)from_vcn, (long long)to_vcn);
+
+ /* Map extent with @from_vcn. */
+ if (ntfs_attr_map_runlist(na, from_vcn))
+ goto err_out;
+
+ for (rl = na->rl; rl->vcn <= to_vcn;) {
+ /* Skip not interesting to us runs. */
+ if (rl->lcn >= 0 || rl->lcn == LCN_HOLE || (rl->vcn +
+ rl->length < from_vcn &&
+ rl->lcn == LCN_RL_NOT_MAPPED)) {
+ rl++;
+ continue;
+ }
+
+ /* We reached the end of runlist, just exit. */
+ if (rl->lcn == LCN_ENOENT)
+ break;
+
+ /* Check for errors. */
+ if (rl->lcn < 0 && rl->lcn != LCN_RL_NOT_MAPPED) {
+ errno = EIO;
+ goto err_out;
+ }
+
+ /* Runlist is not mapped here. */
+ if (!ctx) {
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ goto err_out;
+ }
+ /* Find the attribute in the mft record. */
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, rl->vcn, NULL, 0,
+ ctx))
+ goto err_out;
+
+ /* Decode the runlist. */
+ rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr,
+ na->rl);
+ if (!rl)
+ goto err_out;
+ na->rl = rl;
+ }
+
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_log_trace("Done.\n");
+ return 0;
+err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_log_trace("Failed.\n");
+ return -1;
+}
+
+/**
+ * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute
+ * @na: ntfs attribute for which to map the runlist
+ *
+ * Map the whole runlist of the ntfs attribute @na. For an attribute made up
+ * of only one attribute extent this is the same as calling
+ * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this
+ * will map the runlist fragments from each of the extents thus giving access
+ * to the entirety of the disk allocation of an attribute.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attr_map_whole_runlist(ntfs_attr *na)
+{
+ VCN next_vcn, last_vcn, highest_vcn;
+ ntfs_attr_search_ctx *ctx;
+ ntfs_volume *vol = na->ni->vol;
+ ATTR_RECORD *a;
+ int err;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
+ (unsigned long long)na->ni->mft_no, na->type);
+
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* Map all attribute extents one by one. */
+ next_vcn = last_vcn = highest_vcn = 0;
+ a = NULL;
+ while (1) {
+ runlist_element *rl;
+
+ int not_mapped = 0;
+ if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED)
+ not_mapped = 1;
+
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, next_vcn, NULL, 0, ctx))
+ break;
+
+ a = ctx->attr;
+
+ if (not_mapped) {
+ /* Decode the runlist. */
+ rl = ntfs_mapping_pairs_decompress(na->ni->vol,
+ a, na->rl);
+ if (!rl)
+ goto err_out;
+ na->rl = rl;
+ }
+
+ /* Are we in the first extent? */
+ if (!next_vcn) {
+ if (a->u.nonres.lowest_vcn) {
+ ntfs_log_trace("First extent of attribute has "
+ "non zero lowest_vcn. "
+ "Inode is corrupt.\n");
+ errno = EIO;
+ goto err_out;
+ }
+ /* Get the last vcn in the attribute. */
+ last_vcn = sle64_to_cpu(a->u.nonres.allocated_size) >>
+ vol->cluster_size_bits;
+ }
+
+ /* Get the lowest vcn for the next extent. */
+ highest_vcn = sle64_to_cpu(a->u.nonres.highest_vcn);
+ next_vcn = highest_vcn + 1;
+
+ /* Only one extent or error, which we catch below. */
+ if (next_vcn <= 0) {
+ errno = ENOENT;
+ break;
+ }
+
+ /* Avoid endless loops due to corruption. */
+ if (next_vcn < sle64_to_cpu(a->u.nonres.lowest_vcn)) {
+ ntfs_log_trace("Inode has corrupt attribute list "
+ "attribute.\n");
+ errno = EIO;
+ goto err_out;
+ }
+ }
+ if (!a) {
+ if (errno == ENOENT)
+ ntfs_log_trace("Attribute not found. "
+ "Inode is corrupt.\n");
+ else
+ ntfs_log_trace("Inode is corrupt.\n");
+ goto err_out;
+ }
+ if (highest_vcn && highest_vcn != last_vcn - 1) {
+ ntfs_log_trace("Failed to load the complete run list for the "
+ "attribute. Bug or corrupt inode.\n");
+ ntfs_log_trace("highest_vcn = 0x%llx, last_vcn - 1 = 0x%llx\n",
+ (long long)highest_vcn,
+ (long long)last_vcn - 1);
+ errno = EIO;
+ goto err_out;
+ }
+ err = errno;
+ ntfs_attr_put_search_ctx(ctx);
+ if (err == ENOENT)
+ return 0;
+out_now:
+ errno = err;
+ return -1;
+err_out:
+ err = errno;
+ ntfs_attr_put_search_ctx(ctx);
+ goto out_now;
+}
+
+/**
+ * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute
+ * @na: ntfs attribute whose runlist to use for conversion
+ * @vcn: vcn to convert
+ *
+ * Convert the virtual cluster number @vcn of an attribute into a logical
+ * cluster number (lcn) of a device using the runlist @na->rl to map vcns to
+ * their corresponding lcns.
+ *
+ * If the @vcn is not mapped yet, attempt to map the attribute extent
+ * containing the @vcn and retry the vcn to lcn conversion.
+ *
+ * Since lcns must be >= 0, we use negative return values with special meaning:
+ *
+ * Return value Meaning / Description
+ * ==========================================
+ * -1 = LCN_HOLE Hole / not allocated on disk.
+ * -3 = LCN_ENOENT There is no such vcn in the attribute.
+ * -4 = LCN_EINVAL Input parameter error.
+ * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory.
+ */
+LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn)
+{
+ LCN lcn;
+ BOOL is_retry = FALSE;
+
+ if (!na || !NAttrNonResident(na) || vcn < 0)
+ return (LCN)LCN_EINVAL;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
+ long)na->ni->mft_no, na->type);
+retry:
+ /* Convert vcn to lcn. If that fails map the runlist and retry once. */
+ lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn);
+ if (lcn >= 0)
+ return lcn;
+ if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
+ is_retry = TRUE;
+ goto retry;
+ }
+ /*
+ * If the attempt to map the runlist failed, or we are getting
+ * LCN_RL_NOT_MAPPED despite having mapped the attribute extent
+ * successfully, something is really badly wrong...
+ */
+ if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED)
+ return (LCN)LCN_EIO;
+ /* lcn contains the appropriate error code. */
+ return lcn;
+}
+
+/**
+ * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute
+ * @na: ntfs attribute whose runlist to search
+ * @vcn: vcn to find
+ *
+ * Find the virtual cluster number @vcn in the runlist of the ntfs attribute
+ * @na and return the the address of the runlist element containing the @vcn.
+ *
+ * Note you need to distinguish between the lcn of the returned runlist
+ * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes
+ * on read and allocate clusters on write. You need to update the runlist, the
+ * attribute itself as well as write the modified mft record to disk.
+ *
+ * If there is an error return NULL with errno set to the error code. The
+ * following error codes are defined:
+ * EINVAL Input parameter error.
+ * ENOENT There is no such vcn in the runlist.
+ * ENOMEM Not enough memory.
+ * EIO I/O error or corrupt metadata.
+ */
+runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn)
+{
+ runlist_element *rl;
+ BOOL is_retry = FALSE;
+
+ if (!na || !NAttrNonResident(na) || vcn < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)vcn);
+retry:
+ rl = na->rl;
+ if (!rl)
+ goto map_rl;
+ if (vcn < rl[0].vcn)
+ goto map_rl;
+ while (rl->length) {
+ if (vcn < rl[1].vcn) {
+ if (rl->lcn >= (LCN)LCN_HOLE)
+ return rl;
+ break;
+ }
+ rl++;
+ }
+ switch (rl->lcn) {
+ case (LCN)LCN_RL_NOT_MAPPED:
+ goto map_rl;
+ case (LCN)LCN_ENOENT:
+ errno = ENOENT;
+ break;
+ case (LCN)LCN_EINVAL:
+ errno = EINVAL;
+ break;
+ default:
+ errno = EIO;
+ break;
+ }
+ return NULL;
+map_rl:
+ /* The @vcn is in an unmapped region, map the runlist and retry. */
+ if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) {
+ is_retry = TRUE;
+ goto retry;
+ }
+ /*
+ * If we already retried or the mapping attempt failed something has
+ * gone badly wrong. EINVAL and ENOENT coming from a failed mapping
+ * attempt are equivalent to errors for us as they should not happen
+ * in our code paths.
+ */
+ if (is_retry || errno == EINVAL || errno == ENOENT)
+ errno = EIO;
+ return NULL;
+}
+
+/**
+ * ntfs_attr_pread - read from an attribute specified by an ntfs_attr structure
+ * @na: ntfs attribute to read from
+ * @pos: byte position in the attribute to begin reading from
+ * @count: number of bytes to read
+ * @b: output data buffer
+ *
+ * This function will read @count bytes starting at offset @pos from the ntfs
+ * attribute @na into the data buffer @b.
+ *
+ * On success, return the number of successfully read bytes. If this number is
+ * lower than @count this means that the read reached end of file or that an
+ * error was encountered during the read so that the read is partial. 0 means
+ * end of file or nothing was read (also return 0 when @count is 0).
+ *
+ * On error and nothing has been read, return -1 with errno set appropriately
+ * to the return code of ntfs_pread(), or to EINVAL in case of invalid
+ * arguments.
+ */
+s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b)
+{
+ s64 br, to_read, ofs, total, total2;
+ ntfs_volume *vol;
+ runlist_element *rl;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, "
+ "count 0x%llx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)pos, (long long)count);
+ if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ /*
+ * If this is a compressed attribute it needs special treatment, but
+ * only if it is non-resident.
+ */
+ if (NAttrCompressed(na) && NAttrNonResident(na))
+ return ntfs_compressed_attr_pread(na, pos, count, b);
+ /*
+ * Encrypted non-resident attributes are not supported. We return
+ * access denied, which is what Windows NT4 does, too.
+ */
+ if (NAttrEncrypted(na) && NAttrNonResident(na))
+ return ntfs_crypto_attr_pread(na, pos, count, b);
+
+ vol = na->ni->vol;
+ if (!count)
+ return 0;
+ /* Truncate reads beyond end of attribute. */
+ if (pos + count > na->data_size) {
+ if (pos >= na->data_size)
+ return 0;
+ count = na->data_size - pos;
+ }
+ /* If it is a resident attribute, get the value from the mft record. */
+ if (!NAttrNonResident(na)) {
+ ntfs_attr_search_ctx *ctx;
+ char *val;
+
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ return -1;
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
+ 0, NULL, 0, ctx)) {
+ int eo;
+res_err_out:
+ eo = errno;
+ ntfs_attr_put_search_ctx(ctx);
+ errno = eo;
+ return -1;
+ }
+ val = (char*)ctx->attr + le16_to_cpu(ctx->attr->u.res.value_offset);
+ if (val < (char*)ctx->attr || val +
+ le32_to_cpu(ctx->attr->u.res.value_length) >
+ (char*)ctx->mrec + vol->mft_record_size) {
+ errno = EIO;
+ goto res_err_out;
+ }
+ memcpy(b, val + pos, count);
+ ntfs_attr_put_search_ctx(ctx);
+ return count;
+ }
+ total = total2 = 0;
+ /* Zero out reads beyond initialized size. */
+ if (pos + count > na->initialized_size) {
+ if (pos >= na->initialized_size) {
+ memset(b, 0, count);
+ return count;
+ }
+ total2 = pos + count - na->initialized_size;
+ count -= total2;
+ memset((u8*)b + count, 0, total2);
+ }
+ /* Find the runlist element containing the vcn. */
+ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
+ if (!rl) {
+ /*
+ * If the vcn is not present it is an out of bounds read.
+ * However, we already truncated the read to the data_size,
+ * so getting this here is an error.
+ */
+ if (errno == ENOENT)
+ errno = EIO;
+ return -1;
+ }
+ /*
+ * Gather the requested data into the linear destination buffer. Note,
+ * a partial final vcn is taken care of by the @count capping of read
+ * length.
+ */
+ ofs = pos - (rl->vcn << vol->cluster_size_bits);
+ for (; count; rl++, ofs = 0) {
+ if (rl->lcn == LCN_RL_NOT_MAPPED) {
+ rl = ntfs_attr_find_vcn(na, rl->vcn);
+ if (!rl) {
+ if (errno == ENOENT)
+ errno = EIO;
+ goto rl_err_out;
+ }
+ /* Needed for case when runs merged. */
+ ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
+ }
+ if (!rl->length)
+ goto rl_err_out;
+ if (rl->lcn < (LCN)0) {
+ if (rl->lcn != (LCN)LCN_HOLE)
+ goto rl_err_out;
+ /* It is a hole, just zero the matching @b range. */
+ to_read = min(count, (rl->length <<
+ vol->cluster_size_bits) - ofs);
+ memset(b, 0, to_read);
+ /* Update progress counters. */
+ total += to_read;
+ count -= to_read;
+ b = (u8*)b + to_read;
+ continue;
+ }
+ /* It is a real lcn, read it into @dst. */
+ to_read = min(count, (rl->length << vol->cluster_size_bits) -
+ ofs);
+retry:
+ ntfs_log_trace("Reading 0x%llx bytes from vcn 0x%llx, "
+ "lcn 0x%llx, ofs 0x%llx.\n", to_read, rl->vcn,
+ rl->lcn, ofs);
+ br = ntfs_pread(vol->u.dev, (rl->lcn << vol->cluster_size_bits) +
+ ofs, to_read, b);
+ /* If everything ok, update progress counters and continue. */
+ if (br > 0) {
+ total += br;
+ count -= br;
+ b = (u8*)b + br;
+ continue;
+ }
+ /* If the syscall was interrupted, try again. */
+ if (br == (s64)-1 && errno == EINTR)
+ goto retry;
+ if (total)
+ return total;
+ if (!br)
+ errno = EIO;
+ return -1;
+ }
+ /* Finally, return the number of bytes read. */
+ return total + total2;
+rl_err_out:
+ if (total)
+ return total;
+ errno = EIO;
+ return -1;
+}
+
+/**
+ * ntfs_attr_pwrite - positioned write to an ntfs attribute
+ * @na: ntfs attribute to write to
+ * @pos: position in the attribute to write to
+ * @count: number of bytes to write
+ * @b: data buffer to write to disk
+ *
+ * This function will write @count bytes from data buffer @b to ntfs attribute
+ * @na at position @pos.
+ *
+ * On success, return the number of successfully written bytes. If this number
+ * is lower than @count this means that an error was encountered during the
+ * write so that the write is partial. 0 means nothing was written (also return
+ * 0 when @count is 0).
+ *
+ * On error and nothing has been written, return -1 with errno set
+ * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of
+ * invalid arguments.
+ */
+s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
+{
+ s64 written, to_write, ofs, total, old_initialized_size, old_data_size;
+ VCN update_from = -1;
+ ntfs_volume *vol;
+ ntfs_attr_search_ctx *ctx = NULL;
+ runlist_element *rl;
+ int eo;
+ struct {
+ unsigned int undo_initialized_size : 1;
+ unsigned int undo_data_size : 1;
+ unsigned int update_mapping_pairs : 1;
+ } need_to = { 0, 0, 0 };
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, "
+ "count 0x%llx.\n", na->ni->mft_no, na->type,
+ (long long)pos, (long long)count);
+ if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ vol = na->ni->vol;
+ /*
+ * Encrypted non-resident attributes are not supported. We return
+ * access denied, which is what Windows NT4 does, too.
+ */
+ if (NAttrEncrypted(na) && NAttrNonResident(na)) {
+ errno = EACCES;
+ return -1;
+ }
+ /* If this is a compressed attribute it needs special treatment. */
+ if (NAttrCompressed(na)) {
+ // TODO: Implement writing compressed attributes! (AIA)
+ // return ntfs_attr_pwrite_compressed(ntfs_attr *na,
+ // const s64 pos, s64 count, void *b);
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+ if (!count)
+ return 0;
+ /* If the write reaches beyond the end, extend the attribute. */
+ old_data_size = na->data_size;
+ if (pos + count > na->data_size) {
+ if (__ntfs_attr_truncate(na, pos + count, FALSE)) {
+ eo = errno;
+ ntfs_log_trace("Attribute extend failed.\n");
+ errno = eo;
+ return -1;
+ }
+ need_to.undo_data_size = 1;
+ }
+ old_initialized_size = na->initialized_size;
+ /* If it is a resident attribute, write the data to the mft record. */
+ if (!NAttrNonResident(na)) {
+ char *val;
+
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ goto err_out;
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
+ 0, NULL, 0, ctx))
+ goto err_out;
+ val = (char*)ctx->attr + le16_to_cpu(ctx->attr->u.res.value_offset);
+ if (val < (char*)ctx->attr || val +
+ le32_to_cpu(ctx->attr->u.res.value_length) >
+ (char*)ctx->mrec + vol->mft_record_size) {
+ errno = EIO;
+ goto err_out;
+ }
+ memcpy(val + pos, b, count);
+ if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
+ ctx->mrec)) {
+ /*
+ * NOTE: We are in a bad state at this moment. We have
+ * dirtied the mft record but we failed to commit it to
+ * disk. Since we have read the mft record ok before,
+ * it is unlikely to fail writing it, so is ok to just
+ * return error here... (AIA)
+ */
+ goto err_out;
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ return count;
+ }
+ total = 0;
+ /* Handle writes beyond initialized_size. */
+ if (pos + count > na->initialized_size) {
+ /*
+ * Map runlist between initialized size and place we start
+ * writing at.
+ */
+ if (ntfs_attr_map_runlist_range(na, na->initialized_size >>
+ vol->cluster_size_bits,
+ pos >> vol->cluster_size_bits))
+ goto err_out;
+ /* Set initialized_size to @pos + @count. */
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ goto err_out;
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0,
+ 0, NULL, 0, ctx))
+ goto err_out;
+ /* If write starts beyond initialized_size, zero the gap. */
+ if (pos > na->initialized_size && ntfs_rl_fill_zero(vol,
+ na->rl, na->initialized_size,
+ pos - na->initialized_size))
+ goto err_out;
+
+ ctx->attr->u.nonres.initialized_size = cpu_to_sle64(pos + count);
+ if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
+ ctx->mrec)) {
+ /*
+ * Undo the change in the in-memory copy and send it
+ * back for writing.
+ */
+ ctx->attr->u.nonres.initialized_size =
+ cpu_to_sle64(old_initialized_size);
+ ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
+ ctx->mrec);
+ goto err_out;
+ }
+ na->initialized_size = pos + count;
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ /*
+ * NOTE: At this point the initialized_size in the mft record
+ * has been updated BUT there is random data on disk thus if
+ * we decide to abort, we MUST change the initialized_size
+ * again.
+ */
+ need_to.undo_initialized_size = 1;
+ }
+ /* Find the runlist element containing the vcn. */
+ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
+ if (!rl) {
+ /*
+ * If the vcn is not present it is an out of bounds write.
+ * However, we already extended the size of the attribute,
+ * so getting this here must be an error of some kind.
+ */
+ if (errno == ENOENT)
+ errno = EIO;
+ goto err_out;
+ }
+ /*
+ * Scatter the data from the linear data buffer to the volume. Note, a
+ * partial final vcn is taken care of by the @count capping of write
+ * length.
+ */
+ ofs = pos - (rl->vcn << vol->cluster_size_bits);
+ for (; count; rl++, ofs = 0) {
+ if (rl->lcn == LCN_RL_NOT_MAPPED) {
+ rl = ntfs_attr_find_vcn(na, rl->vcn);
+ if (!rl) {
+ if (errno == ENOENT)
+ errno = EIO;
+ goto rl_err_out;
+ }
+ /* Needed for case when runs merged. */
+ ofs = pos + total - (rl->vcn << vol->cluster_size_bits);
+ }
+ if (!rl->length) {
+ errno = EIO;
+ goto rl_err_out;
+ }
+ if (rl->lcn < (LCN)0) {
+ LCN lcn_seek_from = -1;
+ runlist *rlc;
+ VCN cur_vcn, from_vcn;
+
+ if (rl->lcn != (LCN)LCN_HOLE) {
+ errno = EIO;
+ goto rl_err_out;
+ }
+
+ to_write = min(count, (rl->length <<
+ vol->cluster_size_bits) - ofs);
+
+ /* Instantiate the hole. */
+ cur_vcn = rl->vcn;
+ from_vcn = rl->vcn + (ofs >> vol->cluster_size_bits);
+ ntfs_log_trace("Instantiate hole with vcn 0x%llx.\n",
+ cur_vcn);
+ /*
+ * Map whole runlist to be able update mapping pairs
+ * later.
+ */
+ if (ntfs_attr_map_whole_runlist(na))
+ goto err_out;
+ /*
+ * Restore @rl, it probably get lost during runlist
+ * mapping.
+ */
+ rl = ntfs_attr_find_vcn(na, cur_vcn);
+ if (!rl) {
+ ntfs_log_error("BUG! Failed to find run after "
+ "mapping whole runlist. Please "
+ "report to the %s.\n",
+ NTFS_DEV_LIST);
+ errno = EIO;
+ goto err_out;
+ }
+ /*
+ * Search backwards to find the best lcn to start
+ * seek from.
+ */
+ rlc = rl;
+ while (rlc->vcn) {
+ rlc--;
+ if (rlc->lcn >= 0) {
+ lcn_seek_from = rlc->lcn +
+ (from_vcn - rlc->vcn);
+ break;
+ }
+ }
+ if (lcn_seek_from == -1) {
+ /* Backwards search failed, search forwards. */
+ rlc = rl;
+ while (rlc->length) {
+ rlc++;
+ if (rlc->lcn >= 0) {
+ lcn_seek_from = rlc->lcn -
+ (rlc->vcn - from_vcn);
+ if (lcn_seek_from < -1)
+ lcn_seek_from = -1;
+ break;
+ }
+ }
+ }
+ /* Allocate clusters to instantiate the hole. */
+ rlc = ntfs_cluster_alloc(vol, from_vcn,
+ ((ofs + to_write - 1) >>
+ vol->cluster_size_bits) + 1 +
+ rl->vcn - from_vcn,
+ lcn_seek_from, DATA_ZONE);
+ if (!rlc) {
+ eo = errno;
+ ntfs_log_trace("Failed to allocate clusters "
+ "for hole instantiating.\n");
+ errno = eo;
+ goto err_out;
+ }
+ /* Merge runlists. */
+ rl = ntfs_runlists_merge(na->rl, rlc);
+ if (!rl) {
+ eo = errno;
+ ntfs_log_trace("Failed to merge runlists.\n");
+ if (ntfs_cluster_free_from_rl(vol, rlc)) {
+ ntfs_log_trace("Failed to free just "
+ "allocated clusters. Leaving "
+ "inconsistent metadata. "
+ "Run chkdsk\n");
+ }
+ errno = eo;
+ goto err_out;
+ }
+ na->rl = rl;
+ need_to.update_mapping_pairs = 1;
+ if (update_from == -1)
+ update_from = from_vcn;
+ rl = ntfs_attr_find_vcn(na, cur_vcn);
+ if (!rl) {
+ /*
+ * It's definitely a BUG, if we failed to find
+ * @cur_vcn, because we missed it during
+ * instantiating of the hole.
+ */
+ ntfs_log_error("BUG! Failed to find run after "
+ "instantiating. Please report "
+ "to the %s.\n", NTFS_DEV_LIST);
+ errno = EIO;
+ goto err_out;
+ }
+ /* If leaved part of the hole go to the next run. */
+ if (rl->lcn < 0)
+ rl++;
+ /* Now LCN shoudn't be less than 0. */
+ if (rl->lcn < 0) {
+ ntfs_log_error("BUG! LCN is lesser than 0. "
+ "Please report to the %s.\n",
+ NTFS_DEV_LIST);
+ errno = EIO;
+ goto err_out;
+ }
+ if (rl->vcn < cur_vcn) {
+ /*
+ * Clusters that replaced hole are merged with
+ * previous run, so we need to update offset.
+ */
+ ofs += (cur_vcn - rl->vcn) <<
+ vol->cluster_size_bits;
+ }
+ if (rl->vcn > cur_vcn) {
+ /*
+ * We left part of the hole, so update we need
+ * to update offset
+ */
+ ofs -= (rl->vcn - cur_vcn) <<
+ vol->cluster_size_bits;
+ }
+ /*
+ * Clear region between start of @rl->vcn cluster and
+ * @ofs if necessary.
+ */
+ if (ofs && ntfs_rl_fill_zero(vol, na->rl, rl->vcn <<
+ vol->cluster_size_bits, ofs))
+ goto err_out;
+ }
+ /* It is a real lcn, write it to the volume. */
+ to_write = min(count, (rl->length << vol->cluster_size_bits) -
+ ofs);
+retry:
+ ntfs_log_trace("Writing 0x%llx bytes to vcn 0x%llx, lcn 0x%llx,"
+ " ofs 0x%llx.\n", to_write, rl->vcn, rl->lcn,
+ ofs);
+ if (!NVolReadOnly(vol)) {
+ s64 pos = (rl->lcn << vol->cluster_size_bits) + ofs;
+ int bsize = 4096; /* FIXME: Test whether we need
+ PAGE_SIZE here. Eg., on IA64. */
+ /*
+ * Write 4096 size blocks if it's possible. This will
+ * cause the kernel not to seek and read disk blocks for
+ * filling the end of the buffer which increases write
+ * speed.
+ */
+ if (vol->cluster_size >= bsize && !(ofs % bsize) &&
+ (to_write % bsize) && ofs + to_write ==
+ na->initialized_size) {
+ char *cb;
+ s64 rounded = (to_write + bsize - 1) &
+ ~(bsize - 1);
+
+ cb = ntfs_malloc(rounded);
+ if (!cb)
+ goto err_out;
+ memcpy(cb, b, to_write);
+ memset(cb + to_write, 0, rounded - to_write);
+ written = ntfs_pwrite(vol->u.dev, pos, rounded,
+ cb);
+ if (written > to_write)
+ written = to_write;
+ free(cb);
+ } else
+ written = ntfs_pwrite(vol->u.dev, pos, to_write,
+ b);
+ } else
+ written = to_write;
+ /* If everything ok, update progress counters and continue. */
+ if (written > 0) {
+ total += written;
+ count -= written;
+ b = (const u8*)b + written;
+ continue;
+ }
+ /* If the syscall was interrupted, try again. */
+ if (written == (s64)-1 && errno == EINTR)
+ goto retry;
+ if (!written)
+ errno = EIO;
+ goto rl_err_out;
+ }
+done:
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ /* Update mapping pairs if needed. */
+ if (need_to.update_mapping_pairs) {
+ if (ntfs_attr_update_mapping_pairs(na, update_from)) {
+ /* FIXME: We want rollback here. */
+ ntfs_log_perror("%s(): Failed to update mapping pairs. "
+ "Leaving inconsistent metadata. "
+ "Run chkdsk!", "ntfs_attr_pwrite");
+ errno = EIO;
+ return -1;
+ }
+ }
+ /* Finally, return the number of bytes written. */
+ return total;
+rl_err_out:
+ eo = errno;
+ if (total) {
+ if (need_to.undo_initialized_size) {
+ if (pos + total > na->initialized_size)
+ goto done;
+ /*
+ * TODO: Need to try to change initialized_size. If it
+ * succeeds goto done, otherwise goto err_out. (AIA)
+ */
+ errno = EOPNOTSUPP;
+ goto err_out;
+ }
+ goto done;
+ }
+ errno = eo;
+err_out:
+ eo = errno;
+ if (need_to.undo_initialized_size) {
+ int err;
+
+ err = 0;
+ if (!ctx) {
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ err = 1;
+ } else
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ctx) {
+ err = ntfs_attr_lookup(na->type, na->name,
+ na->name_len, 0, 0, NULL, 0, ctx);
+ if (!err) {
+ na->initialized_size = old_initialized_size;
+ ctx->attr->u.nonres.initialized_size = cpu_to_sle64(
+ old_initialized_size);
+ err = ntfs_mft_record_write(vol,
+ ctx->ntfs_ino->mft_no,
+ ctx->mrec);
+ }
+ }
+ if (err) {
+ /*
+ * FIXME: At this stage could try to recover by filling
+ * old_initialized_size -> new_initialized_size with
+ * data or at least zeroes. (AIA)
+ */
+ ntfs_log_error("Eeek! Failed to recover from error. "
+ "Leaving metadata in inconsistent "
+ "state! Run chkdsk!\n");
+ }
+ }
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ /* Update mapping pairs if needed. */
+ if (need_to.update_mapping_pairs)
+ ntfs_attr_update_mapping_pairs(na, update_from);
+ /* Restore original data_size if needed. */
+ if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size))
+ ntfs_log_trace("Failed to restore data_size.\n");
+ errno = eo;
+ return -1;
+}
+
+/**
+ * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read
+ * @na: multi sector transfer protected ntfs attribute to read from
+ * @pos: byte position in the attribute to begin reading from
+ * @bk_cnt: number of mst protected blocks to read
+ * @bk_size: size of each mst protected block in bytes
+ * @dst: output data buffer
+ *
+ * This function will read @bk_cnt blocks of size @bk_size bytes each starting
+ * at offset @pos from the ntfs attribute @na into the data buffer @b.
+ *
+ * On success, the multi sector transfer fixups are applied and the number of
+ * read blocks is returned. If this number is lower than @bk_cnt this means
+ * that the read has either reached end of attribute or that an error was
+ * encountered during the read so that the read is partial. 0 means end of
+ * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0).
+ *
+ * On error and nothing has been read, return -1 with errno set appropriately
+ * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid
+ * arguments.
+ *
+ * NOTE: If an incomplete multi sector transfer is detected the magic is
+ * changed to BAAD but no error is returned, i.e. it is possible that any of
+ * the returned blocks have multi sector transfer errors. This should be
+ * detected by the caller by checking each block with is_baad_recordp(&block).
+ * The reasoning is that we want to fixup as many blocks as possible and we
+ * want to return even bad ones to the caller so, e.g. in case of ntfsck, the
+ * errors can be repaired.
+ */
+s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt,
+ const u32 bk_size, void *dst)
+{
+ s64 br;
+ u8 *end;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, "
+ "pos 0x%llx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)pos);
+ if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+ br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst);
+ if (br <= 0)
+ return br;
+ br /= bk_size;
+ for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst +
+ bk_size)
+ ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size);
+ /* Finally, return the number of blocks read. */
+ return br;
+}
+
+/**
+ * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write
+ * @na: multi sector transfer protected ntfs attribute to write to
+ * @pos: position in the attribute to write to
+ * @bk_cnt: number of mst protected blocks to write
+ * @bk_size: size of each mst protected block in bytes
+ * @src: data buffer to write to disk
+ *
+ * This function will write @bk_cnt blocks of size @bk_size bytes each from
+ * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na
+ * at position @pos.
+ *
+ * On success, return the number of successfully written blocks. If this number
+ * is lower than @bk_cnt this means that an error was encountered during the
+ * write so that the write is partial. 0 means nothing was written (also
+ * return 0 when @bk_cnt or @bk_size are 0).
+ *
+ * On error and nothing has been written, return -1 with errno set
+ * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case
+ * of invalid arguments.
+ *
+ * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
+ * deprotect algorithm (no checking). This saves us from making a copy before
+ * the write and at the same time causes the usn to be incremented in the
+ * buffer. This conceptually fits in better with the idea that cached data is
+ * always deprotected and protection is performed when the data is actually
+ * going to hit the disk and the cache is immediately deprotected again
+ * simulating an mst read on the written data. This way cache coherency is
+ * achieved.
+ */
+s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt,
+ const u32 bk_size, void *src)
+{
+ s64 written, i;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, "
+ "pos 0x%llx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)pos);
+ if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!bk_cnt)
+ return 0;
+ /* Prepare data for writing. */
+ for (i = 0; i < bk_cnt; ++i) {
+ int err;
+
+ err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
+ ((u8*)src + i * bk_size), bk_size);
+ if (err < 0) {
+ /* Abort write at this position. */
+ if (!i)
+ return err;
+ bk_cnt = i;
+ break;
+ }
+ }
+ /* Write the prepared data. */
+ written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src);
+ /* Quickly deprotect the data again. */
+ for (i = 0; i < bk_cnt; ++i)
+ ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i *
+ bk_size));
+ if (written <= 0)
+ return written;
+ /* Finally, return the number of complete blocks written. */
+ return written / bk_size;
+}
+
+/**
+ * ntfs_attr_find - find (next) attribute in mft record
+ * @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 or CASE_SENSITIVE (ignored if @name not present)
+ * @val: attribute value to find (optional, resident attributes only)
+ * @val_len: attribute value length
+ * @ctx: search context with mft record and attribute to search from
+ *
+ * You shouldn't need to call this function directly. Use lookup_attr() instead.
+ *
+ * ntfs_attr_find() takes a search context @ctx as parameter and searches the
+ * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an
+ * attribute of @type, optionally @name and @val. If found, ntfs_attr_find()
+ * returns 0 and @ctx->attr will point to the found attribute.
+ *
+ * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and
+ * @ctx->attr will point to the attribute before which the attribute being
+ * searched for would need to be inserted if such an action were to be desired.
+ *
+ * On actual error, ntfs_attr_find() returns -1 with errno set to the error
+ * code but not to ENOENT. In this case @ctx->attr is undefined and in
+ * particular do not rely on it not changing.
+ *
+ * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it
+ * is FALSE, the search begins after @ctx->attr.
+ *
+ * If @type is AT_UNUSED, return the first found attribute, i.e. one can
+ * enumerate all attributes by setting @type to AT_UNUSED and then calling
+ * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to
+ * indicate that there are no more entries. During the enumeration, each
+ * successful call of ntfs_attr_find() will return the next attribute in the
+ * mft record @ctx->mrec.
+ *
+ * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT.
+ * AT_END is not a valid attribute, its length is zero for example, thus it is
+ * safer to return error instead of success in this case. This also allows us
+ * to interoperate cleanly with ntfs_external_attr_find().
+ *
+ * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
+ * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
+ * match both named and unnamed attributes.
+ *
+ * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and
+ * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record
+ * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at
+ * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case
+ * sensitive. When @name is present, @name_len is the @name length in Unicode
+ * characters.
+ *
+ * If @name is not present (NULL), we assume that the unnamed attribute is
+ * being searched for.
+ *
+ * Finally, the resident attribute value @val is looked for, if present.
+ * If @val is not present (NULL), @val_len is ignored.
+ *
+ * ntfs_attr_find() only searches the specified mft record and it ignores the
+ * presence of an attribute list attribute (unless it is the one being searched
+ * for, obviously). If you need to take attribute lists into consideration, use
+ * ntfs_attr_lookup() instead (see below). This also means that you cannot use
+ * ntfs_attr_find() to search for extent records of non-resident attributes, as
+ * extents with lowest_vcn != 0 are usually described by the attribute list
+ * attribute only. - Note that it is possible that the first extent is only in
+ * the attribute list while the last extent is in the base mft record, so don't
+ * rely on being able to find the first extent in the base mft record.
+ *
+ * Warning: Never use @val when looking for attribute types which can be
+ * non-resident as this most likely will result in a crash!
+ */
+static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
+ const u32 name_len, const IGNORE_CASE_BOOL ic,
+ const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx)
+{
+ ATTR_RECORD *a;
+ ntfs_volume *vol;
+ ntfschar *upcase;
+ u32 upcase_len;
+
+ ntfs_log_trace("Entering for attribute type 0x%x.\n", type);
+
+ if (ctx->ntfs_ino) {
+ vol = ctx->ntfs_ino->vol;
+ upcase = vol->upcase;
+ upcase_len = vol->upcase_len;
+ } else {
+ if (name && name != AT_UNNAMED) {
+ errno = EINVAL;
+ return -1;
+ }
+ vol = NULL;
+ upcase = NULL;
+ upcase_len = 0;
+ }
+ /*
+ * Iterate over attributes in mft record starting at @ctx->attr, or the
+ * attribute following that, if @ctx->is_first is TRUE.
+ */
+ if (ctx->is_first) {
+ a = ctx->attr;
+ ctx->is_first = FALSE;
+ } else
+ a = (ATTR_RECORD*)((char*)ctx->attr +
+ le32_to_cpu(ctx->attr->length));
+ for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) {
+ if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec +
+ le32_to_cpu(ctx->mrec->bytes_allocated))
+ break;
+ ctx->attr = a;
+ if (((type != AT_UNUSED) && (le32_to_cpu(a->type) >
+ le32_to_cpu(type))) ||
+ (a->type == AT_END)) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (!a->length)
+ break;
+ /* If this is an enumeration return this attribute. */
+ if (type == AT_UNUSED)
+ return 0;
+ if (a->type != type)
+ continue;
+ /*
+ * If @name is AT_UNNAMED we want an unnamed attribute.
+ * If @name is present, compare the two names.
+ * Otherwise, match any attribute.
+ */
+ if (name == AT_UNNAMED) {
+ /* The search failed if the found attribute is named. */
+ if (a->name_length) {
+ errno = ENOENT;
+ return -1;
+ }
+ } else if (name && !ntfs_names_are_equal(name, name_len,
+ (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)),
+ a->name_length, ic, upcase, upcase_len)) {
+ register int rc;
+
+ rc = ntfs_names_collate(name, name_len,
+ (ntfschar*)((char*)a +
+ le16_to_cpu(a->name_offset)),
+ a->name_length, 1, IGNORE_CASE,
+ upcase, upcase_len);
+ /*
+ * If @name collates before a->name, there is no
+ * matching attribute.
+ */
+ if (rc == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+ /* If the strings are not equal, continue search. */
+ if (rc)
+ continue;
+ rc = ntfs_names_collate(name, name_len,
+ (ntfschar*)((char*)a +
+ le16_to_cpu(a->name_offset)),
+ a->name_length, 1, CASE_SENSITIVE,
+ upcase, upcase_len);
+ if (rc == -1) {
+ errno = ENOENT;
+ return -1;
+ }
+ if (rc)
+ continue;
+ }
+ /*
+ * The names match or @name not present and attribute is
+ * unnamed. If no @val specified, we have found the attribute
+ * and are done.
+ */
+ if (!val)
+ return 0;
+ /* @val is present; compare values. */
+ else {
+ register int rc;
+
+ rc = memcmp(val, (char*)a +le16_to_cpu(a->u.res.value_offset),
+ min(val_len,
+ le32_to_cpu(a->u.res.value_length)));
+ /*
+ * If @val collates before the current attribute's
+ * value, there is no matching attribute.
+ */
+ if (!rc) {
+ register u32 avl;
+ avl = le32_to_cpu(a->u.res.value_length);
+ if (val_len == avl)
+ return 0;
+ if (val_len < avl) {
+ errno = ENOENT;
+ return -1;
+ }
+ } else if (rc < 0) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ }
+ ntfs_log_debug("ntfs_attr_find(): File is corrupt. Run chkdsk.\n");
+ errno = EIO;
+ return -1;
+}
+
+/**
+ * ntfs_external_attr_find - find an attribute in the attribute list of an inode
+ * @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 or CASE_SENSITIVE (ignored if @name not present)
+ * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
+ * @val: attribute value to find (optional, resident attributes only)
+ * @val_len: attribute value length
+ * @ctx: search context with mft record and attribute to search from
+ *
+ * You shouldn't need to call this function directly. Use ntfs_attr_lookup()
+ * instead.
+ *
+ * Find an attribute by searching the attribute list for the corresponding
+ * attribute list entry. Having found the entry, map the mft record for read
+ * if the attribute is in a different mft record/inode, find the attribute in
+ * there and return it.
+ *
+ * If @type is AT_UNUSED, return the first found attribute, i.e. one can
+ * enumerate all attributes by setting @type to AT_UNUSED and then calling
+ * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to
+ * ENOENT to indicate that there are no more entries. During the enumeration,
+ * each successful call of ntfs_external_attr_find() will return the next
+ * attribute described by the attribute list of the base mft record described
+ * by the search context @ctx.
+ *
+ * If @type is AT_END, seek to the end of the base mft record ignoring the
+ * attribute list completely and return -1 with errno set to ENOENT. AT_END is
+ * not a valid attribute, its length is zero for example, thus it is safer to
+ * return error instead of success in this case.
+ *
+ * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
+ * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
+ * match both named and unnamed attributes.
+ *
+ * On first search @ctx->ntfs_ino must be the inode of the base mft record and
+ * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx().
+ * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too
+ * (@ctx->base_ntfs_ino is then the base inode).
+ *
+ * After finishing with the attribute/mft record you need to call
+ * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
+ * mapped extent inodes, etc).
+ *
+ * Return 0 if the search was successful and -1 if not, with errno set to the
+ * error code.
+ *
+ * On success, @ctx->attr is the found attribute, it is in mft record
+ * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
+ * attribute with @ctx->base_* being the base mft record to which @ctx->attr
+ * belongs.
+ *
+ * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
+ * attribute which collates just after the attribute being searched for in the
+ * base ntfs inode, i.e. if one wants to add the attribute to the mft record
+ * this is the correct place to insert it into, and if there is not enough
+ * space, the attribute should be placed in an extent mft record.
+ * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
+ * at which the new attribute's attribute list entry should be inserted. The
+ * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
+ * The only exception to this is when @type is AT_END, in which case
+ * @ctx->al_entry is set to NULL also (see above).
+ *
+ * The following error codes are defined:
+ * ENOENT Attribute not found, not an error as such.
+ * EINVAL Invalid arguments.
+ * EIO I/O error or corrupt data structures found.
+ * ENOMEM Not enough memory to allocate necessary buffers.
+ */
+static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
+ const u32 name_len, const IGNORE_CASE_BOOL ic,
+ const VCN lowest_vcn, const u8 *val, const u32 val_len,
+ ntfs_attr_search_ctx *ctx)
+{
+ ntfs_inode *base_ni, *ni;
+ ntfs_volume *vol;
+ ATTR_LIST_ENTRY *al_entry, *next_al_entry;
+ u8 *al_start, *al_end;
+ ATTR_RECORD *a;
+ ntfschar *al_name;
+ u32 al_name_len;
+ BOOL is_first_search = FALSE;
+
+ ni = ctx->ntfs_ino;
+ base_ni = ctx->base_ntfs_ino;
+ ntfs_log_trace("Entering for inode 0x%llx, attribute type 0x%x.\n",
+ (unsigned long long)ni->mft_no, type);
+ if (!base_ni) {
+ /* First call happens with the base mft record. */
+ base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino;
+ ctx->base_mrec = ctx->mrec;
+ }
+ if (ni == base_ni)
+ ctx->base_attr = ctx->attr;
+ if (type == AT_END)
+ goto not_found;
+ vol = base_ni->vol;
+ al_start = base_ni->attr_list;
+ al_end = al_start + base_ni->attr_list_size;
+ if (!ctx->al_entry) {
+ ctx->al_entry = (ATTR_LIST_ENTRY*)al_start;
+ is_first_search = TRUE;
+ }
+ /*
+ * Iterate over entries in attribute list starting at @ctx->al_entry,
+ * or the entry following that, if @ctx->is_first is TRUE.
+ */
+ if (ctx->is_first) {
+ al_entry = ctx->al_entry;
+ ctx->is_first = FALSE;
+ /*
+ * If an enumeration and the first attribute is higher than
+ * the attribute list itself, need to return the attribute list
+ * attribute.
+ */
+ if ((type == AT_UNUSED) && is_first_search &&
+ le32_to_cpu(al_entry->type) >
+ le32_to_cpu(AT_ATTRIBUTE_LIST))
+ goto find_attr_list_attr;
+ } else {
+ al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
+ le16_to_cpu(ctx->al_entry->length));
+ /*
+ * If this is an enumeration and the attribute list attribute
+ * is the next one in the enumeration sequence, just return the
+ * attribute list attribute from the base mft record as it is
+ * not listed in the attribute list itself.
+ */
+ if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) <
+ le32_to_cpu(AT_ATTRIBUTE_LIST) &&
+ le32_to_cpu(al_entry->type) >
+ le32_to_cpu(AT_ATTRIBUTE_LIST)) {
+ int rc;
+find_attr_list_attr:
+
+ /* Check for bogus calls. */
+ if (name || name_len || val || val_len || lowest_vcn) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* We want the base record. */
+ ctx->ntfs_ino = base_ni;
+ ctx->mrec = ctx->base_mrec;
+ ctx->is_first = TRUE;
+ /* Sanity checks are performed elsewhere. */
+ ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
+ le16_to_cpu(ctx->mrec->attrs_offset));
+
+ /* Find the attribute list attribute. */
+ rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0,
+ IGNORE_CASE, NULL, 0, ctx);
+
+ /*
+ * Setup the search context so the correct
+ * attribute is returned next time round.
+ */
+ ctx->al_entry = al_entry;
+ ctx->is_first = TRUE;
+
+ /* Got it. Done. */
+ if (!rc)
+ return 0;
+
+ /* Error! If other than not found return it. */
+ if (errno != ENOENT)
+ return rc;
+
+ /* Not found?!? Absurd! Must be a bug... )-: */
+ ntfs_log_trace("BUG! Attribute list attribute not "
+ "found but it exists! "
+ "Returning error (EINVAL).\n");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ for (;; al_entry = next_al_entry) {
+ /* Out of bounds check. */
+ if ((u8*)al_entry < base_ni->attr_list ||
+ (u8*)al_entry > al_end)
+ break; /* Inode is corrupt. */
+ ctx->al_entry = al_entry;
+ /* Catch the end of the attribute list. */
+ if ((u8*)al_entry == al_end)
+ goto not_found;
+ if (!al_entry->length)
+ break;
+ if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
+ le16_to_cpu(al_entry->length) > al_end)
+ break;
+ next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
+ le16_to_cpu(al_entry->length));
+ if (type != AT_UNUSED) {
+ if (le32_to_cpu(al_entry->type) > le32_to_cpu(type))
+ goto not_found;
+ if (type != al_entry->type)
+ continue;
+ }
+ al_name_len = al_entry->name_length;
+ al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset);
+ /*
+ * If !@type we want the attribute represented by this
+ * attribute list entry.
+ */
+ if (type == AT_UNUSED)
+ goto is_enumeration;
+ /*
+ * If @name is AT_UNNAMED we want an unnamed attribute.
+ * If @name is present, compare the two names.
+ * Otherwise, match any attribute.
+ */
+ if (name == AT_UNNAMED) {
+ if (al_name_len)
+ goto not_found;
+ } else if (name && !ntfs_names_are_equal(al_name, al_name_len,
+ name, name_len, ic, vol->upcase,
+ vol->upcase_len)) {
+ register int rc;
+
+ rc = ntfs_names_collate(name, name_len, al_name,
+ al_name_len, 1, IGNORE_CASE,
+ vol->upcase, vol->upcase_len);
+ /*
+ * If @name collates before al_name, there is no
+ * matching attribute.
+ */
+ if (rc == -1)
+ goto not_found;
+ /* If the strings are not equal, continue search. */
+ if (rc)
+ continue;
+ /*
+ * FIXME: Reverse engineering showed 0, IGNORE_CASE but
+ * that is inconsistent with ntfs_attr_find(). The
+ * subsequent rc checks were also different. Perhaps I
+ * made a mistake in one of the two. Need to recheck
+ * which is correct or at least see what is going
+ * on... (AIA)
+ */
+ rc = ntfs_names_collate(name, name_len, al_name,
+ al_name_len, 1, CASE_SENSITIVE,
+ vol->upcase, vol->upcase_len);
+ if (rc == -1)
+ goto not_found;
+ if (rc)
+ continue;
+ }
+ /*
+ * The names match or @name not present and attribute is
+ * unnamed. Now check @lowest_vcn. Continue search if the
+ * next attribute list entry still fits @lowest_vcn. Otherwise
+ * we have reached the right one or the search has failed.
+ */
+ if (lowest_vcn && (u8*)next_al_entry >= al_start &&
+ (u8*)next_al_entry + 6 < al_end &&
+ (u8*)next_al_entry + le16_to_cpu(
+ next_al_entry->length) <= al_end &&
+ sle64_to_cpu(next_al_entry->lowest_vcn) <=
+ lowest_vcn &&
+ next_al_entry->type == al_entry->type &&
+ next_al_entry->name_length == al_name_len &&
+ ntfs_names_are_equal((ntfschar*)((char*)
+ next_al_entry +
+ next_al_entry->name_offset),
+ next_al_entry->name_length,
+ al_name, al_name_len, CASE_SENSITIVE,
+ vol->upcase, vol->upcase_len))
+ continue;
+is_enumeration:
+ if (MREF_LE(al_entry->mft_reference) == ni->mft_no) {
+ if (MSEQNO_LE(al_entry->mft_reference) !=
+ le16_to_cpu(
+ ni->mrec->sequence_number)) {
+ ntfs_log_debug("Found stale mft reference in "
+ "attribute list!\n");
+ break;
+ }
+ } else { /* Mft references do not match. */
+ /* Do we want the base record back? */
+ if (MREF_LE(al_entry->mft_reference) ==
+ base_ni->mft_no) {
+ ni = ctx->ntfs_ino = base_ni;
+ ctx->mrec = ctx->base_mrec;
+ } else {
+ /* We want an extent record. */
+ ni = ntfs_extent_inode_open(base_ni,
+ al_entry->mft_reference);
+ if (!ni) {
+ ntfs_log_perror("Failed to map extent "
+ "inode");
+ break;
+ }
+ ctx->ntfs_ino = ni;
+ ctx->mrec = ni->mrec;
+ }
+ }
+ a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec +
+ le16_to_cpu(ctx->mrec->attrs_offset));
+ /*
+ * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the
+ * mft record containing the attribute represented by the
+ * current al_entry.
+ *
+ * We could call into ntfs_attr_find() to find the right
+ * attribute in this mft record but this would be less
+ * efficient and not quite accurate as ntfs_attr_find() ignores
+ * the attribute instance numbers for example which become
+ * important when one plays with attribute lists. Also, because
+ * a proper match has been found in the attribute list entry
+ * above, the comparison can now be optimized. So it is worth
+ * re-implementing a simplified ntfs_attr_find() here.
+ *
+ * Use a manual loop so we can still use break and continue
+ * with the same meanings as above.
+ */
+do_next_attr_loop:
+ if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +
+ le32_to_cpu(ctx->mrec->bytes_allocated))
+ break;
+ if (a->type == AT_END)
+ continue;
+ if (!a->length)
+ break;
+ if (al_entry->instance != a->instance)
+ goto do_next_attr;
+ /*
+ * If the type and/or the name are/is mismatched between the
+ * attribute list entry and the attribute record, there is
+ * corruption so we break and return error EIO.
+ */
+ if (al_entry->type != a->type)
+ break;
+ if (!ntfs_names_are_equal((ntfschar*)((char*)a +
+ le16_to_cpu(a->name_offset)),
+ a->name_length, al_name,
+ al_name_len, CASE_SENSITIVE,
+ vol->upcase, vol->upcase_len))
+ break;
+ ctx->attr = a;
+ /*
+ * If no @val specified or @val specified and it matches, we
+ * have found it! Also, if !@type, it is an enumeration, so we
+ * want the current attribute.
+ */
+ if ((type == AT_UNUSED) || !val || (!a->non_resident &&
+ le32_to_cpu(a->u.res.value_length) == val_len &&
+ !memcmp((char*)a + le16_to_cpu(a->u.res.value_offset),
+ val, val_len))) {
+ return 0;
+ }
+do_next_attr:
+ /* Proceed to the next attribute in the current mft record. */
+ a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
+ goto do_next_attr_loop;
+ }
+ if (ni != base_ni) {
+ ctx->ntfs_ino = base_ni;
+ ctx->mrec = ctx->base_mrec;
+ ctx->attr = ctx->base_attr;
+ }
+ ntfs_log_debug("Inode is corrupt.\n");
+ errno = EIO;
+ return -1;
+not_found:
+ /*
+ * If we were looking for AT_END or we were enumerating and reached the
+ * end, we reset the search context @ctx and use ntfs_attr_find() to
+ * seek to the end of the base mft record.
+ */
+ if (type == AT_UNUSED || type == AT_END) {
+ ntfs_attr_reinit_search_ctx(ctx);
+ return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len,
+ ctx);
+ }
+ /*
+ * The attribute wasn't found. Before we return, we want to ensure
+ * @ctx->mrec and @ctx->attr indicate the position at which the
+ * attribute should be inserted in the base mft record. Since we also
+ * want to preserve @ctx->al_entry we cannot reinitialize the search
+ * context using ntfs_attr_reinit_search_ctx() as this would set
+ * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see
+ * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve
+ * @ctx->al_entry as the remaining fields (base_*) are identical to
+ * their non base_ counterparts and we cannot set @ctx->base_attr
+ * correctly yet as we do not know what @ctx->attr will be set to by
+ * the call to ntfs_attr_find() below.
+ */
+ ctx->mrec = ctx->base_mrec;
+ ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
+ le16_to_cpu(ctx->mrec->attrs_offset));
+ ctx->is_first = TRUE;
+ ctx->ntfs_ino = ctx->base_ntfs_ino;
+ ctx->base_ntfs_ino = NULL;
+ ctx->base_mrec = NULL;
+ ctx->base_attr = NULL;
+ /*
+ * In case there are multiple matches in the base mft record, need to
+ * keep enumerating until we get an attribute not found response (or
+ * another error), otherwise we would keep returning the same attribute
+ * over and over again and all programs using us for enumeration would
+ * lock up in a tight loop.
+ */
+ {
+ int ret;
+
+ do {
+ ret = ntfs_attr_find(type, name, name_len, ic, val,
+ val_len, ctx);
+ } while (!ret);
+ return ret;
+ }
+}
+
+/**
+ * ntfs_attr_lookup - find an attribute in an ntfs inode
+ * @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 or CASE_SENSITIVE (ignored if @name not present)
+ * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only)
+ * @val: attribute value to find (optional, resident attributes only)
+ * @val_len: attribute value length
+ * @ctx: search context with mft record and attribute to search from
+ *
+ * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must
+ * be the base mft record and @ctx must have been obtained from a call to
+ * ntfs_attr_get_search_ctx().
+ *
+ * This function transparently handles attribute lists and @ctx is used to
+ * continue searches where they were left off at.
+ *
+ * If @type is AT_UNUSED, return the first found attribute, i.e. one can
+ * enumerate all attributes by setting @type to AT_UNUSED and then calling
+ * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT
+ * to indicate that there are no more entries. During the enumeration, each
+ * successful call of ntfs_attr_lookup() will return the next attribute, with
+ * the current attribute being described by the search context @ctx.
+ *
+ * If @type is AT_END, seek to the end of the base mft record ignoring the
+ * attribute list completely and return -1 with errno set to ENOENT. AT_END is
+ * not a valid attribute, its length is zero for example, thus it is safer to
+ * return error instead of success in this case. It should never be needed to
+ * do this, but we implement the functionality because it allows for simpler
+ * code inside ntfs_external_attr_find().
+ *
+ * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present
+ * but not AT_UNNAMED search for a named attribute matching @name. Otherwise,
+ * match both named and unnamed attributes.
+ *
+ * After finishing with the attribute/mft record you need to call
+ * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any
+ * mapped extent inodes, etc).
+ *
+ * Return 0 if the search was successful and -1 if not, with errno set to the
+ * error code.
+ *
+ * On success, @ctx->attr is the found attribute, it is in mft record
+ * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this
+ * attribute with @ctx->base_* being the base mft record to which @ctx->attr
+ * belongs. If no attribute list attribute is present @ctx->al_entry and
+ * @ctx->base_* are NULL.
+ *
+ * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the
+ * attribute which collates just after the attribute being searched for in the
+ * base ntfs inode, i.e. if one wants to add the attribute to the mft record
+ * this is the correct place to insert it into, and if there is not enough
+ * space, the attribute should be placed in an extent mft record.
+ * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list
+ * at which the new attribute's attribute list entry should be inserted. The
+ * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL.
+ * The only exception to this is when @type is AT_END, in which case
+ * @ctx->al_entry is set to NULL also (see above).
+ *
+ *
+ * The following error codes are defined:
+ * ENOENT Attribute not found, not an error as such.
+ * EINVAL Invalid arguments.
+ * EIO I/O error or corrupt data structures found.
+ * ENOMEM Not enough memory to allocate necessary buffers.
+ */
+int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name,
+ const u32 name_len, const IGNORE_CASE_BOOL ic,
+ const VCN lowest_vcn, const u8 *val, const u32 val_len,
+ ntfs_attr_search_ctx *ctx)
+{
+ ntfs_volume *vol;
+ ntfs_inode *base_ni;
+
+ if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED &&
+ (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) ||
+ !vol->upcase || !vol->upcase_len))) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (ctx->base_ntfs_ino)
+ base_ni = ctx->base_ntfs_ino;
+ else
+ base_ni = ctx->ntfs_ino;
+ if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST)
+ return ntfs_attr_find(type, name, name_len, ic, val, val_len,
+ ctx);
+ return ntfs_external_attr_find(type, name, name_len, ic, lowest_vcn,
+ val, val_len, ctx);
+}
+
+/**
+ * ntfs_attr_init_search_ctx - initialize an attribute search context
+ * @ctx: attribute search context to initialize
+ * @ni: ntfs inode with which to initialize the search context
+ * @mrec: mft record with which to initialize the search context
+ *
+ * Initialize the attribute search context @ctx with @ni and @mrec.
+ */
+static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx,
+ ntfs_inode *ni, MFT_RECORD *mrec)
+{
+ if (!mrec)
+ mrec = ni->mrec;
+ ctx->mrec = mrec;
+ /* Sanity checks are performed elsewhere. */
+ ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
+ ctx->is_first = TRUE;
+ ctx->ntfs_ino = ni;
+ ctx->al_entry = NULL;
+ ctx->base_ntfs_ino = NULL;
+ ctx->base_mrec = NULL;
+ ctx->base_attr = NULL;
+}
+
+/**
+ * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context
+ * @ctx: attribute search context to reinitialize
+ *
+ * Reinitialize the attribute search context @ctx.
+ *
+ * This is used when a search for a new attribute is being started to reset
+ * the search context to the beginning.
+ */
+void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx)
+{
+ if (!ctx->base_ntfs_ino) {
+ /* No attribute list. */
+ ctx->is_first = TRUE;
+ /* Sanity checks are performed elsewhere. */
+ ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec +
+ le16_to_cpu(ctx->mrec->attrs_offset));
+ /*
+ * This needs resetting due to ntfs_external_attr_find() which
+ * can leave it set despite having zeroed ctx->base_ntfs_ino.
+ */
+ ctx->al_entry = NULL;
+ return;
+ } /* Attribute list. */
+ ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec);
+ return;
+}
+
+/**
+ * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context
+ * @ni: ntfs inode with which to initialize the search context
+ * @mrec: mft record with which to initialize the search context
+ *
+ * Allocate a new attribute search context, initialize it with @ni and @mrec,
+ * and return it. Return NULL on error with errno set to ENOMEM.
+ *
+ * @mrec can be NULL, in which case the mft record is taken from @ni.
+ *
+ * Note: For low level utilities which know what they are doing we allow @ni to
+ * be NULL and @mrec to be set. Do NOT do this unless you understand the
+ * implications!!! For example it is no longer safe to call ntfs_attr_lookup()
+ * if you
+ */
+ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec)
+{
+ ntfs_attr_search_ctx *ctx;
+
+ if (!ni && !mrec) {
+ errno = EINVAL;
+ return NULL;
+ }
+ ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx));
+ if (ctx)
+ ntfs_attr_init_search_ctx(ctx, ni, mrec);
+ return ctx;
+}
+
+/**
+ * ntfs_attr_put_search_ctx - release an attribute search context
+ * @ctx: attribute search context to free
+ *
+ * Release the attribute search context @ctx. This function does not change
+ * errno and doing nothing if NULL passed to it.
+ */
+void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx)
+{
+ free(ctx);
+}
+
+/**
+ * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file
+ * @vol: ntfs volume to which the attribute belongs
+ * @type: attribute type which to find
+ *
+ * Search for the attribute definition record corresponding to the attribute
+ * @type in the $AttrDef system file.
+ *
+ * Return the attribute type definition record if found and NULL if not found
+ * or an error occurred. On error the error code is stored in errno. The
+ * following error codes are defined:
+ * ENOENT - The attribute @type is not specified in $AttrDef.
+ * EINVAL - Invalid parameters (e.g. @vol is not valid).
+ */
+ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
+ const ATTR_TYPES type)
+{
+ ATTR_DEF *ad;
+
+ if (!vol || !vol->attrdef || !type) {
+ errno = EINVAL;
+ return NULL;
+ }
+ for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
+ vol->attrdef_len && ad->type; ++ad) {
+ /* We haven't found it yet, carry on searching. */
+ if (le32_to_cpu(ad->type) < le32_to_cpu(type))
+ continue;
+ /* We found the attribute; return it. */
+ if (ad->type == type)
+ return ad;
+ /* We have gone too far already. No point in continuing. */
+ break;
+ }
+ /* Attribute not found?!? */
+ errno = ENOENT;
+ return NULL;
+}
+
+/**
+ * ntfs_attr_size_bounds_check - check a size of an attribute type for validity
+ * @vol: ntfs volume to which the attribute belongs
+ * @type: attribute type which to check
+ * @size: size which to check
+ *
+ * Check whether the @size in bytes is valid for an attribute of @type on the
+ * ntfs volume @vol. This information is obtained from $AttrDef system file.
+ *
+ * Return 0 if valid and -1 if not valid or an error occurred. On error the
+ * error code is stored in errno. The following error codes are defined:
+ * ERANGE - @size is not valid for the attribute @type.
+ * ENOENT - The attribute @type is not specified in $AttrDef.
+ * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid).
+ */
+int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type,
+ const s64 size)
+{
+ ATTR_DEF *ad;
+
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * $ATTRIBUTE_LIST should be not greater than 0x40000, but this is not
+ * listed in the AttrDef.
+ */
+ if (type == AT_ATTRIBUTE_LIST && size > 0x40000) {
+ errno = ERANGE;
+ return -1;
+ }
+
+ ad = ntfs_attr_find_in_attrdef(vol, type);
+ if (!ad)
+ return -1;
+ /* We found the attribute. - Do the bounds check. */
+ if ((sle64_to_cpu(ad->min_size) && size <
+ sle64_to_cpu(ad->min_size)) ||
+ ((sle64_to_cpu(ad->max_size) > 0) && size >
+ sle64_to_cpu(ad->max_size))) {
+ /* @size is out of range! */
+ errno = ERANGE;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident
+ * @vol: ntfs volume to which the attribute belongs
+ * @type: attribute type which to check
+ *
+ * Check whether the attribute of @type on the ntfs volume @vol is allowed to
+ * be non-resident. This information is obtained from $AttrDef system file.
+ *
+ * Return 0 if the attribute is allowed to be non-resident and -1 if not or an
+ * error occurred. On error the error code is stored in errno. The following
+ * error codes are defined:
+ * EPERM - The attribute is not allowed to be non-resident.
+ * ENOENT - The attribute @type is not specified in $AttrDef.
+ * EINVAL - Invalid parameters (e.g. @vol is not valid).
+ */
+int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type)
+{
+ ATTR_DEF *ad;
+
+ /* Find the attribute definition record in $AttrDef. */
+ ad = ntfs_attr_find_in_attrdef(vol, type);
+ if (!ad)
+ return -1;
+ /* Check the flags and return the result. */
+ if (ad->flags & ATTR_DEF_RESIDENT) {
+ ntfs_log_trace("Attribute can't be non-resident\n");
+ errno = EPERM;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_attr_can_be_resident - check if an attribute can be resident
+ * @vol: ntfs volume to which the attribute belongs
+ * @type: attribute type which to check
+ *
+ * Check whether the attribute of @type on the ntfs volume @vol is allowed to
+ * be resident. This information is derived from our ntfs knowledge and may
+ * not be completely accurate, especially when user defined attributes are
+ * present. Basically we allow everything to be resident except for index
+ * allocation and extended attribute attributes.
+ *
+ * Return 0 if the attribute is allowed to be resident and -1 if not or an
+ * error occurred. On error the error code is stored in errno. The following
+ * error codes are defined:
+ * EPERM - The attribute is not allowed to be resident.
+ * EINVAL - Invalid parameters (e.g. @vol is not valid).
+ *
+ * Warning: In the system file $MFT the attribute $Bitmap must be non-resident
+ * otherwise windows will not boot (blue screen of death)! We cannot
+ * check for this here as we don't know which inode's $Bitmap is being
+ * asked about so the caller needs to special case this.
+ */
+int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type)
+{
+ if (!vol || !vol->attrdef || !type) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (type != AT_INDEX_ALLOCATION)
+ return 0;
+
+ ntfs_log_trace("Attribute can't be resident\n");
+ errno = EPERM;
+ return -1;
+}
+
+/**
+ * ntfs_make_room_for_attr - make room for an attribute inside an mft record
+ * @m: mft record
+ * @pos: position at which to make space
+ * @size: byte size to make available at this position
+ *
+ * @pos points to the attribute in front of which we want to make space.
+ *
+ * Return 0 on success or -1 on error. On error the error code is stored in
+ * errno. Possible error codes are:
+ * ENOSPC - There is not enough space available to complete operation. The
+ * caller has to make space before calling this.
+ * EINVAL - Input parameters were faulty.
+ */
+int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
+{
+ u32 biu;
+
+ ntfs_log_trace("Entering for pos 0x%d, size %u.\n",
+ (int)(pos - (u8*)m), (unsigned) size);
+
+ /* Make size 8-byte alignment. */
+ size = (size + 7) & ~7;
+
+ /* Rigorous consistency checks. */
+ if (!m || !pos || pos < (u8*)m || pos + size >
+ (u8*)m + le32_to_cpu(m->bytes_allocated)) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* The -8 is for the attribute terminator. */
+ if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Nothing to do. */
+ if (!size)
+ return 0;
+
+ biu = le32_to_cpu(m->bytes_in_use);
+ /* Do we have enough space? */
+ if (biu + size > le32_to_cpu(m->bytes_allocated)) {
+ ntfs_log_trace("Not enough space in the MFT record\n");
+ errno = ENOSPC;
+ return -1;
+ }
+ /* Move everything after pos to pos + size. */
+ memmove(pos + size, pos, biu - (pos - (u8*)m));
+ /* Update mft record. */
+ m->bytes_in_use = cpu_to_le32(biu + size);
+ return 0;
+}
+
+/**
+ * ntfs_resident_attr_record_add - add resident attribute to inode
+ * @ni: opened ntfs inode to which MFT record add attribute
+ * @type: type of the new attribute
+ * @name: name of the new attribute
+ * @name_len: name length of the new attribute
+ * @val: value of the new attribute
+ * @size: size of new attribute (length of @val, if @val != NULL)
+ * @flags: flags of the new attribute
+ *
+ * Return offset to attribute from the beginning of the mft record on success
+ * and -1 on error. On error the error code is stored in errno.
+ * Possible error codes are:
+ * EINVAL - Invalid arguments passed to function.
+ * EEXIST - Attribute of such type and with same name already exists.
+ * EIO - I/O error occurred or damaged filesystem.
+ */
+int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, u8 *val, u32 size,
+ ATTR_FLAGS flags)
+{
+ ntfs_attr_search_ctx *ctx;
+ u32 length;
+ ATTR_RECORD *a;
+ MFT_RECORD *m;
+ int err, offset;
+ ntfs_inode *base_ni;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n",
+ (long long) ni->mft_no, le32_to_cpu(type), le16_to_cpu(flags));
+
+ if (!ni || (!name && name_len)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ntfs_attr_can_be_resident(ni->vol, type)) {
+ if (errno == EPERM)
+ ntfs_log_trace("Attribute can't be resident.\n");
+ else
+ ntfs_log_trace("ntfs_attr_can_be_resident failed.\n");
+ return -1;
+ }
+
+ /* Locate place where record should be. */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+ /*
+ * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
+ * attribute in @ni->mrec, not any extent inode in case if @ni is base
+ * file record.
+ */
+ if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size,
+ ctx)) {
+ err = EEXIST;
+ ntfs_log_trace("Attribute already present.\n");
+ goto put_err_out;
+ }
+ if (errno != ENOENT) {
+ err = EIO;
+ goto put_err_out;
+ }
+ a = ctx->attr;
+ m = ctx->mrec;
+
+ /* Make room for attribute. */
+ length = offsetof(ATTR_RECORD, u.res.resident_end) +
+ ((name_len * sizeof(ntfschar) + 7) & ~7) +
+ ((size + 7) & ~7);
+ if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
+ err = errno;
+ ntfs_log_trace("Failed to make room for attribute.\n");
+ goto put_err_out;
+ }
+
+ /* Setup record fields. */
+ offset = ((u8*)a - (u8*)m);
+ a->type = type;
+ a->length = cpu_to_le32(length);
+ a->non_resident = 0;
+ a->name_length = name_len;
+ a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, u.res.resident_end));
+ a->flags = flags;
+ a->instance = m->next_attr_instance;
+ a->u.res.value_length = cpu_to_le32(size);
+ a->u.res.value_offset = cpu_to_le16(length - ((size + 7) & ~7));
+ if (val)
+ memcpy((u8*)a + le16_to_cpu(a->u.res.value_offset), val, size);
+ else
+ memset((u8*)a + le16_to_cpu(a->u.res.value_offset), 0, size);
+ if (type == AT_FILE_NAME)
+ a->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED;
+ else
+ a->u.res.resident_flags = 0;
+ if (name_len)
+ memcpy((u8*)a + le16_to_cpu(a->name_offset),
+ name, sizeof(ntfschar) * name_len);
+ m->next_attr_instance =
+ cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
+ if (ni->nr_extents == -1)
+ base_ni = ni->u.base_ni;
+ else
+ base_ni = ni;
+ if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
+ if (ntfs_attrlist_entry_add(ni, a)) {
+ err = errno;
+ ntfs_attr_record_resize(m, a, 0);
+ ntfs_log_trace("Failed add attribute entry to "
+ "ATTRIBUTE_LIST.\n");
+ goto put_err_out;
+ }
+ }
+ ntfs_inode_mark_dirty(ni);
+ ntfs_attr_put_search_ctx(ctx);
+ return offset;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_non_resident_attr_record_add - add extent of non-resident attribute
+ * @ni: opened ntfs inode to which MFT record add attribute
+ * @type: type of the new attribute extent
+ * @name: name of the new attribute extent
+ * @name_len: name length of the new attribute extent
+ * @lowest_vcn: lowest vcn of the new attribute extent
+ * @dataruns_size: dataruns size of the new attribute extent
+ * @flags: flags of the new attribute extent
+ *
+ * Return offset to attribute from the beginning of the mft record on success
+ * and -1 on error. On error the error code is stored in errno.
+ * Possible error codes are:
+ * EINVAL - Invalid arguments passed to function.
+ * EEXIST - Attribute of such type, with same lowest vcn and with same
+ * name already exists.
+ * EIO - I/O error occurred or damaged filesystem.
+ */
+int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size,
+ ATTR_FLAGS flags)
+{
+ ntfs_attr_search_ctx *ctx;
+ u32 length;
+ ATTR_RECORD *a;
+ MFT_RECORD *m;
+ ntfs_inode *base_ni;
+ int err, offset;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, "
+ "dataruns_size %d, flags 0x%x.\n",
+ (long long) ni->mft_no, le32_to_cpu(type),
+ (long long) lowest_vcn, dataruns_size,
+ le16_to_cpu(flags));
+
+ if (!ni || dataruns_size <= 0 || (!name && name_len)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ntfs_attr_can_be_non_resident(ni->vol, type)) {
+ if (errno == EPERM)
+ ntfs_log_trace("Attribute can't be non resident.\n");
+ else
+ ntfs_log_trace("ntfs_attr_can_be_non_resident() "
+ "failed.\n");
+ return -1;
+ }
+
+ /* Locate place where record should be. */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+ /*
+ * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
+ * attribute in @ni->mrec, not any extent inode in case if @ni is base
+ * file record.
+ */
+ if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0,
+ ctx)) {
+ err = EEXIST;
+ ntfs_log_trace("Attribute already present.\n");
+ goto put_err_out;
+ }
+ if (errno != ENOENT) {
+ err = EIO;
+ goto put_err_out;
+ }
+ a = ctx->attr;
+ m = ctx->mrec;
+
+ /* Make room for attribute. */
+ dataruns_size = (dataruns_size + 7) & ~7;
+ length = offsetof(ATTR_RECORD, u.nonres.compressed_size) + ((sizeof(ntfschar) *
+ name_len + 7) & ~7) + dataruns_size +
+ ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
+ sizeof(a->u.nonres.compressed_size) : 0);
+ if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) {
+ err = errno;
+ ntfs_log_trace("Failed to make room for attribute.\n");
+ goto put_err_out;
+ }
+
+ /* Setup record fields. */
+ a->type = type;
+ a->length = cpu_to_le32(length);
+ a->non_resident = 1;
+ a->name_length = name_len;
+ a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, u.nonres.compressed_size) +
+ ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ?
+ sizeof(a->u.nonres.compressed_size) : 0));
+ a->flags = flags;
+ a->instance = m->next_attr_instance;
+ a->u.nonres.lowest_vcn = cpu_to_sle64(lowest_vcn);
+ a->u.nonres.mapping_pairs_offset = cpu_to_le16(length - dataruns_size);
+ a->u.nonres.compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0;
+ /* If @lowest_vcn == 0, than setup empty attribute. */
+ if (!lowest_vcn) {
+ a->u.nonres.highest_vcn = cpu_to_sle64(-1);
+ a->u.nonres.allocated_size = 0;
+ a->u.nonres.data_size = 0;
+ a->u.nonres.initialized_size = 0;
+ /* Set empty mapping pairs. */
+ *((u8*)a + le16_to_cpu(a->u.nonres.mapping_pairs_offset)) = 0;
+ }
+ if (name_len)
+ memcpy((u8*)a + le16_to_cpu(a->name_offset),
+ name, sizeof(ntfschar) * name_len);
+ m->next_attr_instance =
+ cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff);
+ if (ni->nr_extents == -1)
+ base_ni = ni->u.base_ni;
+ else
+ base_ni = ni;
+ if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) {
+ if (ntfs_attrlist_entry_add(ni, a)) {
+ err = errno;
+ ntfs_attr_record_resize(m, a, 0);
+ ntfs_log_trace("Failed add attribute entry to "
+ "ATTRIBUTE_LIST.\n");
+ goto put_err_out;
+ }
+ }
+ ntfs_inode_mark_dirty(ni);
+ /*
+ * Locate offset from start of the MFT record where new attribute is
+ * placed. We need relookup it, because record maybe moved during
+ * update of attribute list.
+ */
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE,
+ lowest_vcn, NULL, 0, ctx)) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed. Probably leaving "
+ "inconsistent metadata.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+ }
+ offset = (u8*)ctx->attr - (u8*)ctx->mrec;
+ ntfs_attr_put_search_ctx(ctx);
+ return offset;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_attr_record_rm - remove attribute extent
+ * @ctx: search context describing the attribute which should be removed
+ *
+ * If this function succeed, user should reinit search context if he/she wants
+ * use it anymore.
+ *
+ * Return 0 on success and -1 on error. On error the error code is stored in
+ * errno. Possible error codes are:
+ * EINVAL - Invalid arguments passed to function.
+ * EIO - I/O error occurred or damaged filesystem.
+ */
+int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx)
+{
+ ntfs_inode *base_ni, *ni;
+ ATTR_TYPES type;
+ int err;
+
+ if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
+ (long long) ctx->ntfs_ino->mft_no,
+ (unsigned) le32_to_cpu(ctx->attr->type));
+ type = ctx->attr->type;
+ ni = ctx->ntfs_ino;
+ if (ctx->base_ntfs_ino)
+ base_ni = ctx->base_ntfs_ino;
+ else
+ base_ni = ctx->ntfs_ino;
+
+ /* Remove attribute itself. */
+ if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) {
+ ntfs_log_trace("Couldn't remove attribute record. "
+ "Bug or damaged MFT record.\n");
+ if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST)
+ if (ntfs_attrlist_entry_add(ni, ctx->attr))
+ ntfs_log_trace("Rollback failed. Leaving "
+ "inconsistent metadata.\n");
+ err = EIO;
+ return -1;
+ }
+ ntfs_inode_mark_dirty(ni);
+
+ /*
+ * Remove record from $ATTRIBUTE_LIST if present and we don't want
+ * delete $ATTRIBUTE_LIST itself.
+ */
+ if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) {
+ if (ntfs_attrlist_entry_rm(ctx)) {
+ ntfs_log_trace("Couldn't delete record from "
+ "$ATTRIBUTE_LIST.\n");
+ return -1;
+ }
+ }
+
+ /* Post $ATTRIBUTE_LIST delete setup. */
+ if (type == AT_ATTRIBUTE_LIST) {
+ if (NInoAttrList(base_ni) && base_ni->attr_list)
+ free(base_ni->attr_list);
+ base_ni->attr_list = NULL;
+ NInoClearAttrList(base_ni);
+ NInoAttrListClearDirty(base_ni);
+ }
+
+ /* Free MFT record, if it isn't contain attributes. */
+ if (le32_to_cpu(ctx->mrec->bytes_in_use) -
+ le16_to_cpu(ctx->mrec->attrs_offset) == 8) {
+ if (ntfs_mft_record_free(ni->vol, ni)) {
+ // FIXME: We need rollback here.
+ ntfs_log_trace("Couldn't free MFT record.\n");
+ errno = EIO;
+ return -1;
+ }
+ /* Remove done if we freed base inode. */
+ if (ni == base_ni)
+ return 0;
+ }
+
+ if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni))
+ return 0;
+
+ /* Remove attribute list if we don't need it any more. */
+ if (!ntfs_attrlist_need(base_ni)) {
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ /*
+ * FIXME: Should we succeed here? Definitely something
+ * goes wrong because NInoAttrList(base_ni) returned
+ * that we have got attribute list.
+ */
+ ntfs_log_trace("Couldn't find attribute list. Succeed "
+ "anyway.\n");
+ return 0;
+ }
+ /* Deallocate clusters. */
+ if (ctx->attr->non_resident) {
+ runlist *al_rl;
+
+ al_rl = ntfs_mapping_pairs_decompress(base_ni->vol,
+ ctx->attr, NULL);
+ if (!al_rl) {
+ ntfs_log_trace("Couldn't decompress attribute "
+ "list runlist. Succeed "
+ "anyway.\n");
+ return 0;
+ }
+ if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) {
+ ntfs_log_trace("Leaking clusters! Run chkdsk. "
+ "Couldn't free clusters from "
+ "attribute list runlist.\n");
+ }
+ free(al_rl);
+ }
+ /* Remove attribute record itself. */
+ if (ntfs_attr_record_rm(ctx)) {
+ /*
+ * FIXME: Should we succeed here? BTW, chkdsk doesn't
+ * complain if it find MFT record with attribute list,
+ * but without extents.
+ */
+ ntfs_log_trace("Couldn't remove attribute list. "
+ "Succeed anyway.\n");
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ntfs_attr_add - add attribute to inode
+ * @ni: opened ntfs inode to which add attribute
+ * @type: type of the new attribute
+ * @name: name in unicode of the new attribute
+ * @name_len: name length in unicode characters of the new attribute
+ * @val: value of new attribute
+ * @size: size of the new attribute / length of @val (if specified)
+ *
+ * @val should always be specified for always resident attributes (eg. FILE_NAME
+ * attribute), for attributes that can become non-resident @val can be NULL
+ * (eg. DATA attribute). @size can be specified even if @val is NULL, in this
+ * case data size will be equal to @size and initialized size will be equal
+ * to 0.
+ *
+ * If inode haven't got enough space to add attribute, add attribute to one of
+ * it extents, if no extents present or no one of them have enough space, than
+ * allocate new extent and add attribute to it.
+ *
+ * If on one of this steps attribute list is needed but not present, than it is
+ * added transparently to caller. So, this function should not be called with
+ * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call
+ * ntfs_inode_add_attrlist instead.
+ *
+ * On success return 0. On error return -1 with errno set to the error code.
+ */
+int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
+ ntfschar *name, u8 name_len, u8 *val, s64 size)
+{
+ u32 attr_rec_size;
+ int err, i, offset;
+ BOOL is_resident = TRUE;
+ BOOL always_non_resident = FALSE, always_resident = FALSE;
+ ntfs_inode *attr_ni;
+ ntfs_attr *na;
+
+ if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) {
+ ntfs_log_trace("Invalid arguments passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr %x, size %lld.\n",
+ (long long) ni->mft_no, type, size);
+
+ if (ni->nr_extents == -1)
+ ni = ni->u.base_ni;
+
+ /* Check the attribute type and the size. */
+ if (ntfs_attr_size_bounds_check(ni->vol, type, size)) {
+ if (errno == ERANGE) {
+ ntfs_log_trace("Size bounds check failed.\n");
+ } else if (errno == ENOENT) {
+ ntfs_log_trace("Invalid attribute type. Aborting...\n");
+ errno = EIO;
+ }
+ return -1;
+ }
+
+ /* Sanity checks for always resident attributes. */
+ if (ntfs_attr_can_be_non_resident(ni->vol, type)) {
+ if (errno != EPERM) {
+ err = errno;
+ ntfs_log_trace("ntfs_attr_can_be_non_resident() "
+ "failed.\n");
+ goto err_out;
+ }
+ /* @val is mandatory. */
+ if (!val) {
+ ntfs_log_trace("@val is mandatory for always resident "
+ "attributes.\n");
+ errno = EINVAL;
+ return -1;
+ }
+ if (size > ni->vol->mft_record_size) {
+ ntfs_log_trace("Attribute is too big.\n");
+ errno = ERANGE;
+ return -1;
+ }
+ always_resident = TRUE;
+ }
+
+ /* Check whether attribute can be resident. */
+ if (ntfs_attr_can_be_resident(ni->vol, type)) {
+ if (errno != EPERM) {
+ err = errno;
+ ntfs_log_trace("ntfs_attr_can_be_resident() failed.\n");
+ goto err_out;
+ }
+ is_resident = FALSE;
+ always_non_resident = TRUE;
+ }
+
+retry:
+ /* Calculate attribute record size. */
+ if (is_resident)
+ attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) +
+ ROUND_UP(name_len * sizeof(ntfschar), 3) +
+ ROUND_UP(size, 3);
+ else /* We add 8 for space for mapping pairs. */
+ attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) +
+ ROUND_UP(name_len * sizeof(ntfschar), 3) + 8;
+
+ /*
+ * If we have enough free space for the new attribute in the base MFT
+ * record, then add attribute to it.
+ */
+ if (le32_to_cpu(ni->mrec->bytes_allocated) -
+ le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) {
+ attr_ni = ni;
+ goto add_attr_record;
+ }
+
+ /* Try to add to extent inodes. */
+ if (ntfs_inode_attach_all_extents(ni)) {
+ err = errno;
+ ntfs_log_trace("Failed to attach all extents to inode.\n");
+ goto err_out;
+ }
+ for (i = 0; i < ni->nr_extents; i++) {
+ attr_ni = ni->u.extent_nis[i];
+ if (le32_to_cpu(attr_ni->mrec->bytes_allocated) -
+ le32_to_cpu(attr_ni->mrec->bytes_in_use) >=
+ attr_rec_size)
+ goto add_attr_record;
+ }
+
+ /*
+ * If failed to find space for resident attribute, then try to find
+ * space for non resident one.
+ */
+ if (is_resident && !always_resident) {
+ is_resident = FALSE;
+ goto retry;
+ }
+
+ /*
+ * FIXME: Try to make other attributes non-resident here. Factor out
+ * code from ntfs_resident_attr_resize.
+ */
+
+ /* There is no extent that contain enough space for new attribute. */
+ if (!NInoAttrList(ni)) {
+ /* Add attribute list not present, add it and retry. */
+ if (ntfs_inode_add_attrlist(ni)) {
+ err = errno;
+ ntfs_log_trace("Failed to add attribute list.\n");
+ goto err_out;
+ }
+ return ntfs_attr_add(ni, type, name, name_len, val, size);
+ }
+ /* Allocate new extent for attribute. */
+ attr_ni = ntfs_mft_record_alloc(ni->vol, ni);
+ if (!attr_ni) {
+ err = errno;
+ ntfs_log_trace("Failed to allocate extent record.\n");
+ goto err_out;
+ }
+
+ /*
+ * Determine resident or not will be attribute using heuristics and
+ * calculate attribute record size. FIXME: small code duplication here.
+ */
+ if (always_resident || (!always_non_resident && size < 256)) {
+ is_resident = TRUE;
+ attr_rec_size = offsetof(ATTR_RECORD, u.res.resident_end) +
+ ROUND_UP(name_len * sizeof(ntfschar), 3) +
+ ROUND_UP(size, 3);
+ } else { /* We add 8 for space for mapping pairs. */
+ is_resident = FALSE;
+ attr_rec_size = offsetof(ATTR_RECORD, u.nonres.non_resident_end) +
+ ROUND_UP(name_len * sizeof(ntfschar), 3) + 8;
+ }
+
+add_attr_record:
+ if (is_resident) {
+ /* Add resident attribute. */
+ offset = ntfs_resident_attr_record_add(attr_ni, type, name,
+ name_len, val, size, 0);
+ if (offset < 0) {
+ err = errno;
+ ntfs_log_trace("Failed to add resident attribute.\n");
+ goto free_err_out;
+ }
+ return 0;
+ }
+
+ /* Add non resident attribute. */
+ offset = ntfs_non_resident_attr_record_add(attr_ni, type, name,
+ name_len, 0, 8, 0);
+ if (offset < 0) {
+ err = errno;
+ ntfs_log_trace("Failed to add non resident attribute.\n");
+ goto free_err_out;
+ }
+
+ /* If @size == 0, we are done. */
+ if (!size)
+ return 0;
+
+ /* Open new attribute and resize it. */
+ na = ntfs_attr_open(ni, type, name, name_len);
+ if (!na) {
+ err = errno;
+ ntfs_log_trace("Failed to open just added attribute.\n");
+ goto rm_attr_err_out;
+ }
+ /* Resize and set attribute value. */
+ if (ntfs_attr_truncate(na, size) ||
+ (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) {
+ err = errno;
+ ntfs_log_trace("Failed to initialize just added attribute.\n");
+ if (ntfs_attr_rm(na))
+ ntfs_log_trace("Failed to remove just added attribute. "
+ "Probably leaving inconsistent "
+ "metadata.\n");
+ goto err_out;
+ }
+ ntfs_attr_close(na);
+ /* Done !*/
+ return 0;
+
+rm_attr_err_out:
+ /* Remove just added attribute. */
+ if (ntfs_attr_record_resize(attr_ni->mrec,
+ (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) {
+ ntfs_log_trace("Failed to remove just added attribute.\n");
+ }
+free_err_out:
+ /* Free MFT record, if it isn't contain attributes. */
+ if (le32_to_cpu(attr_ni->mrec->bytes_in_use) -
+ le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) {
+ if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) {
+ ntfs_log_trace("Failed to free MFT record. Leaving "
+ "inconsistent metadata.\n");
+ }
+ }
+err_out:
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_attr_rm - remove attribute from ntfs inode
+ * @na: opened ntfs attribute to delete
+ *
+ * Remove attribute and all it's extents from ntfs inode. If attribute was non
+ * resident also free all clusters allocated by attribute. This function always
+ * closes @na upon exit (both on success and failure).
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_attr_rm(ntfs_attr *na)
+{
+ ntfs_attr_search_ctx *ctx;
+ int ret = 0;
+
+ if (!na) {
+ ntfs_log_trace("Invalid arguments passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
+ (long long) na->ni->mft_no, na->type);
+
+ /* Free cluster allocation. */
+ if (NAttrNonResident(na)) {
+ if (ntfs_attr_map_whole_runlist(na)) {
+ ntfs_attr_close(na);
+ return -1;
+ }
+ if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) {
+ ntfs_log_trace("Failed to free cluster allocation. "
+ "Leaving inconsistent metadata.\n");
+ ret = -1;
+ }
+ }
+
+ /* Search for attribute extents and remove them all. */
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx) {
+ ntfs_attr_close(na);
+ return -1;
+ }
+ while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ if (ntfs_attr_record_rm(ctx)) {
+ ntfs_log_trace("Failed to remove attribute extent. "
+ "Leaving inconsistent metadata.\n");
+ ret = -1;
+ }
+ ntfs_attr_reinit_search_ctx(ctx);
+ }
+ if (errno != ENOENT) {
+ ntfs_log_trace("Attribute lookup failed. "
+ "Probably leaving inconsistent metadata.\n");
+ ret = -1;
+ }
+
+ /* Throw away now non-exist attribute. */
+ ntfs_attr_close(na);
+ /* Done. */
+ return ret;
+}
+
+/**
+ * ntfs_attr_record_resize - resize an attribute record
+ * @m: mft record containing attribute record
+ * @a: attribute record to resize
+ * @new_size: new size in bytes to which to resize the attribute record @a
+ *
+ * Resize the attribute record @a, i.e. the resident part of the attribute, in
+ * the mft record @m to @new_size bytes.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ * The following error codes are defined:
+ * ENOSPC - Not enough space in the mft record @m to perform the resize.
+ * Note that on error no modifications have been performed whatsoever.
+ *
+ * Warning: If you make a record smaller without having copied all the data you
+ * are interested in the data may be overwritten!
+ */
+int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
+{
+ ntfs_log_trace("Entering for new_size %u.\n", (unsigned) new_size);
+ /* Align to 8 bytes, just in case the caller hasn't. */
+ new_size = (new_size + 7) & ~7;
+ /* If the actual attribute length has changed, move things around. */
+ if (new_size != le32_to_cpu(a->length)) {
+ u32 new_muse = le32_to_cpu(m->bytes_in_use) -
+ le32_to_cpu(a->length) + new_size;
+ /* Not enough space in this mft record. */
+ if (new_muse > le32_to_cpu(m->bytes_allocated)) {
+ errno = ENOSPC;
+ return -1;
+ }
+ /* Move attributes following @a to their new location. */
+ memmove((u8*)a + new_size, (u8*)a + le32_to_cpu(a->length),
+ le32_to_cpu(m->bytes_in_use) - ((u8*)a -
+ (u8*)m) - le32_to_cpu(a->length));
+ /* Adjust @m to reflect the change in used space. */
+ m->bytes_in_use = cpu_to_le32(new_muse);
+ /* Adjust @a to reflect the new size. */
+ if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length))
+ a->length = cpu_to_le32(new_size);
+ }
+ return 0;
+}
+
+/**
+ * ntfs_resident_attr_value_resize - resize the value of a resident attribute
+ * @m: mft record containing attribute record
+ * @a: attribute record whose value to resize
+ * @new_size: new size in bytes to which to resize the attribute value of @a
+ *
+ * Resize the value of the attribute @a in the mft record @m to @new_size bytes.
+ * If the value is made bigger, the newly "allocated" space is cleared.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ * The following error codes are defined:
+ * ENOSPC - Not enough space in the mft record @m to perform the resize.
+ * Note that on error no modifications have been performed whatsoever.
+ */
+int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
+ const u32 new_size)
+{
+ ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size);
+
+ /*
+ * Check that the attribute name hasn't been placed after the
+ * attribute value. Chkdsk treat this as corruption.
+ */
+ if (a->name_length && le16_to_cpu(a->name_offset) >=
+ le16_to_cpu(a->u.res.value_offset)) {
+ ntfs_log_trace("Name is placed after the attribute value. "
+ "Corrupted inode. Run chkdsk. Aborting...\n");
+ errno = EIO;
+ return -1;
+ }
+ /* Resize the resident part of the attribute record. */
+ if (ntfs_attr_record_resize(m, a, (le16_to_cpu(a->u.res.value_offset) +
+ new_size + 7) & ~7) < 0) {
+ if (errno != ENOSPC) {
+ int eo = errno;
+ ntfs_log_trace("Attribute record resize failed. "
+ "Aborting...\n");
+ errno = eo;
+ }
+ return -1;
+ }
+ /*
+ * If we made the attribute value bigger, clear the area between the
+ * old size and @new_size.
+ */
+ if (new_size > le32_to_cpu(a->u.res.value_length))
+ memset((u8*)a + le16_to_cpu(a->u.res.value_offset) +
+ le32_to_cpu(a->u.res.value_length), 0, new_size -
+ le32_to_cpu(a->u.res.value_length));
+ /* Finally update the length of the attribute value. */
+ a->u.res.value_length = cpu_to_le32(new_size);
+ return 0;
+}
+
+/**
+ * ntfs_attr_record_move_to - move attribute record to target inode
+ * @ctx: attribute search context describing the attribute record
+ * @ni: opened ntfs inode to which move attribute record
+ *
+ * If this function succeed, user should reinit search context if he/she wants
+ * use it anymore.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni)
+{
+ ntfs_attr_search_ctx *nctx;
+ ATTR_RECORD *a;
+ int err;
+
+ if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) {
+ ntfs_log_trace("Invalid arguments passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for ctx->attr->type 0x%x, "
+ "ctx->ntfs_ino->mft_no 0x%llx, ni->mft_no 0x%llx.\n",
+ (unsigned) le32_to_cpu(ctx->attr->type),
+ (long long) ctx->ntfs_ino->mft_no,
+ (long long) ni->mft_no);
+
+ if (ctx->ntfs_ino == ni)
+ return 0;
+
+ if (!ctx->al_entry) {
+ ntfs_log_trace("Inode should contain attribute list to use "
+ "this function.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Find place in MFT record where attribute will be moved. */
+ a = ctx->attr;
+ nctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!nctx) {
+ ntfs_log_trace("Couldn't obtain search context.\n");
+ return -1;
+ }
+ /*
+ * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for
+ * attribute in @ni->mrec, not any extent inode in case if @ni is base
+ * file record.
+ */
+ if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu(
+ a->name_offset)), a->name_length, CASE_SENSITIVE, NULL,
+ 0, nctx)) {
+ ntfs_log_trace("Attribute of such type, with same name already "
+ "present in this MFT record.\n");
+ err = EEXIST;
+ goto put_err_out;
+ }
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_debug("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+
+ /* Make space and move attribute. */
+ if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr,
+ le32_to_cpu(a->length))) {
+ err = errno;
+ ntfs_log_trace("Couldn't make space for attribute.\n");
+ goto put_err_out;
+ }
+ memcpy(nctx->attr, a, le32_to_cpu(a->length));
+ nctx->attr->instance = nctx->mrec->next_attr_instance;
+ nctx->mrec->next_attr_instance = cpu_to_le16(
+ (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff);
+ ntfs_attr_record_resize(ctx->mrec, a, 0);
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_inode_mark_dirty(ni);
+
+ /* Update attribute list. */
+ ctx->al_entry->mft_reference =
+ MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
+ ctx->al_entry->instance = nctx->attr->instance;
+ ntfs_attrlist_mark_dirty(ni);
+
+ ntfs_attr_put_search_ctx(nctx);
+ return 0;
+put_err_out:
+ ntfs_attr_put_search_ctx(nctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_attr_record_move_away - move away attribute record from it's mft record
+ * @ctx: attribute search context describing the attribute record
+ * @extra: minimum amount of free space in the new holder of record
+ *
+ * New attribute record holder must have free @extra bytes after moving
+ * attribute record to it.
+ *
+ * If this function succeed, user should reinit search context if he/she wants
+ * use it anymore.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra)
+{
+ ntfs_inode *base_ni, *ni;
+ MFT_RECORD *m;
+ int i;
+
+ if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) {
+ ntfs_log_trace("Invalid arguments passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for attr 0x%x, inode 0x%llx.\n",
+ (unsigned) le32_to_cpu(ctx->attr->type),
+ (long long) ctx->ntfs_ino->mft_no);
+
+ if (ctx->ntfs_ino->nr_extents == -1)
+ base_ni = ctx->base_ntfs_ino;
+ else
+ base_ni = ctx->ntfs_ino;
+
+ if (!NInoAttrList(base_ni)) {
+ ntfs_log_trace("Inode should contain attribute list to use "
+ "this function.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) {
+ ntfs_log_trace("Couldn't attach extent inode.\n");
+ return -1;
+ }
+
+ /* Walk through all extents and try to move attribute to them. */
+ for (i = 0; i < base_ni->nr_extents; i++) {
+ ni = base_ni->u.extent_nis[i];
+ m = ni->mrec;
+
+ if (ctx->ntfs_ino->mft_no == ni->mft_no)
+ continue;
+
+ if (le32_to_cpu(m->bytes_allocated) -
+ le32_to_cpu(m->bytes_in_use) <
+ le32_to_cpu(ctx->attr->length) + extra)
+ continue;
+
+ /*
+ * ntfs_attr_record_move_to can fail if extent with other lowest
+ * VCN already present in inode we trying move record to. So,
+ * do not return error.
+ */
+ if (!ntfs_attr_record_move_to(ctx, ni))
+ return 0;
+ }
+
+ /*
+ * Failed to move attribute to one of the current extents, so allocate
+ * new extent and move attribute to it.
+ */
+ ni = ntfs_mft_record_alloc(base_ni->vol, base_ni);
+ if (!ni) {
+ ntfs_log_trace("Couldn't allocate new MFT record.\n");
+ return -1;
+ }
+ if (ntfs_attr_record_move_to(ctx, ni)) {
+ ntfs_log_trace("Couldn't move attribute to new MFT record.\n");
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
+ * @na: open ntfs attribute to make non-resident
+ * @ctx: ntfs search context describing the attribute
+ *
+ * Convert a resident ntfs attribute to a non-resident one.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code. The
+ * following error codes are defined:
+ * EPERM - The attribute is not allowed to be non-resident.
+ * TODO: others...
+ *
+ * NOTE to self: No changes in the attribute list are required to move from
+ * a resident to a non-resident attribute.
+ *
+ * Warning: We do not set the inode dirty and we do not write out anything!
+ * We expect the caller to do this as this is a fairly low level
+ * function and it is likely there will be further changes made.
+ */
+static int ntfs_attr_make_non_resident(ntfs_attr *na,
+ ntfs_attr_search_ctx *ctx)
+{
+ s64 new_allocated_size, bw;
+ ntfs_volume *vol = na->ni->vol;
+ ATTR_REC *a = ctx->attr;
+ runlist *rl;
+ int mp_size, mp_ofs, name_ofs, arec_size;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
+ long)na->ni->mft_no, na->type);
+
+ /* Some preliminary sanity checking. */
+ if (NAttrNonResident(na)) {
+ ntfs_log_trace("Eeek! Trying to make non-resident attribute "
+ "non-resident. Aborting...\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Check that the attribute is allowed to be non-resident. */
+ if (ntfs_attr_can_be_non_resident(vol, na->type))
+ return -1;
+
+ /*
+ * Check that the attribute name hasn't been placed after the
+ * attribute value. Chkdsk treat this as corruption.
+ */
+ if (a->name_length && le16_to_cpu(a->name_offset) >=
+ le16_to_cpu(a->u.res.value_offset)) {
+ ntfs_log_trace("Name is placed after the attribute value. "
+ "Corrupted inode. Run chkdsk. Aborting...\n");
+ errno = EIO;
+ return -1;
+ }
+
+ new_allocated_size = (le32_to_cpu(a->u.res.value_length) + vol->cluster_size
+ - 1) & ~(vol->cluster_size - 1);
+
+ if (new_allocated_size > 0) {
+ /* Start by allocating clusters to hold the attribute value. */
+ rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >>
+ vol->cluster_size_bits, -1, DATA_ZONE);
+ if (!rl) {
+ if (errno != ENOSPC) {
+ ntfs_log_trace("Eeek! Failed to allocate "
+ "cluster(s). Aborting...\n");
+ }
+ return -1;
+ }
+ } else
+ rl = NULL;
+ /*
+ * Setup the in-memory attribute structure to be non-resident so that
+ * we can use ntfs_attr_pwrite().
+ */
+ NAttrSetNonResident(na);
+ na->rl = rl;
+ na->allocated_size = new_allocated_size;
+ na->data_size = na->initialized_size = le32_to_cpu(a->u.res.value_length);
+ /*
+ * FIXME: For now just clear all of these as we don't support them when
+ * writing.
+ */
+ NAttrClearCompressed(na);
+ NAttrClearSparse(na);
+ NAttrClearEncrypted(na);
+
+ if (rl) {
+ /* Now copy the attribute value to the allocated cluster(s). */
+ bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->u.res.value_length),
+ (u8*)a + le16_to_cpu(a->u.res.value_offset));
+ if (bw != le32_to_cpu(a->u.res.value_length)) {
+ ntfs_log_debug("Failed to write out attribute value "
+ "(bw = %lli, errno = %i). "
+ "Aborting...\n", (long long)bw, errno);
+ if (bw >= 0)
+ errno = EIO;
+ goto cluster_free_err_out;
+ }
+ }
+ /* Determine the size of the mapping pairs array. */
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0);
+ if (mp_size < 0) {
+ ntfs_log_debug("Failed to get size for mapping pairs array. "
+ "Aborting...\n");
+ goto cluster_free_err_out;
+ }
+ /* Calculate new offsets for the name and the mapping pairs array. */
+ name_ofs = (sizeof(ATTR_REC) - sizeof(a->u.nonres.compressed_size) + 7) & ~7;
+ mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
+ /*
+ * Determine the size of the resident part of the non-resident
+ * attribute record. (Not compressed thus no compressed_size element
+ * present.)
+ */
+ arec_size = (mp_ofs + mp_size + 7) & ~7;
+
+ /* Resize the resident part of the attribute record. */
+ if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
+ if (errno != ENOSPC) {
+ ntfs_log_trace("Failed to resize attribute record. "
+ "Aborting...\n");
+ }
+ goto cluster_free_err_out;
+ }
+
+ /*
+ * Convert the resident part of the attribute record to describe a
+ * non-resident attribute.
+ */
+ a->non_resident = 1;
+
+ /* Move the attribute name if it exists and update the offset. */
+ if (a->name_length)
+ memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
+ a->name_length * sizeof(ntfschar));
+ a->name_offset = cpu_to_le16(name_ofs);
+
+ /* Update the flags to match the in-memory ones. */
+ a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED |
+ ATTR_COMPRESSION_MASK);
+
+ /* Setup the fields specific to non-resident attributes. */
+ a->u.nonres.lowest_vcn = cpu_to_sle64(0);
+ a->u.nonres.highest_vcn = cpu_to_sle64((new_allocated_size - 1) >>
+ vol->cluster_size_bits);
+
+ a->u.nonres.mapping_pairs_offset = cpu_to_le16(mp_ofs);
+
+ a->u.nonres.compression_unit = 0;
+
+ memset(&a->u.nonres.reserved1, 0, sizeof(a->u.nonres.reserved1));
+
+ a->u.nonres.allocated_size = cpu_to_sle64(new_allocated_size);
+ a->u.nonres.data_size = a->u.nonres.initialized_size = cpu_to_sle64(na->data_size);
+
+ /* Generate the mapping pairs array in the attribute record. */
+ if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs,
+ rl, 0, NULL) < 0) {
+ // FIXME: Eeek! We need rollback! (AIA)
+ ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving "
+ "corrupt attribute record on disk. In memory "
+ "runlist is still intact! Error code is %i. "
+ "FIXME: Need to rollback instead!\n", errno);
+ return -1;
+ }
+
+ /* Done! */
+ return 0;
+
+cluster_free_err_out:
+ if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0)
+ ntfs_log_trace("Failed to release allocated clusters in error "
+ "code path. Leaving inconsistent metadata...\n");
+ NAttrClearNonResident(na);
+ na->allocated_size = na->data_size;
+ na->rl = NULL;
+ free(rl);
+ return -1;
+}
+
+/**
+ * ntfs_resident_attr_resize - resize a resident, open ntfs attribute
+ * @na: resident ntfs attribute to resize
+ * @newsize: new size (in bytes) to which to resize the attribute
+ *
+ * Change the size of a resident, open ntfs attribute @na to @newsize bytes.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * ENOMEM - Not enough memory to complete operation.
+ * ERANGE - @newsize is not valid for the attribute type of @na.
+ * ENOSPC - There is no enough space on the volume to allocate
+ * new clusters or in base mft to resize $ATTRIBUTE_LIST.
+ * EOVERFLOW - Resident attribute can not become non resident and
+ * already filled whole MFT record, but had not reached
+ * @newsize bytes length.
+ */
+static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize)
+{
+ ntfs_attr_search_ctx *ctx;
+ ntfs_volume *vol;
+ ntfs_inode *ni;
+ int err;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)newsize);
+
+ /* Get the attribute record that needs modification. */
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx)
+ return -1;
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0,
+ ctx)) {
+ err = errno;
+ goto put_err_out;
+ }
+ vol = na->ni->vol;
+ /*
+ * Check the attribute type and the corresponding minimum and maximum
+ * sizes against @newsize and fail if @newsize is out of bounds.
+ */
+ if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
+ err = errno;
+ if (err == ERANGE) {
+ ntfs_log_trace("Size bounds check failed. "
+ "Aborting...\n");
+ } else if (err == ENOENT)
+ err = EIO;
+ goto put_err_out;
+ }
+ /*
+ * If @newsize is bigger than the MFT record we need to make the
+ * attribute non-resident if the attribute type supports it. If it is
+ * smaller we can go ahead and attempt the resize.
+ */
+ if (newsize < vol->mft_record_size) {
+ /* Perform the resize of the attribute record. */
+ if (!ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
+ newsize)) {
+ /* Update attribute size everywhere. */
+ na->data_size = na->initialized_size = newsize;
+ na->allocated_size = ROUND_UP(newsize, 3);
+ if (NAttrCompressed(na) || NAttrSparse(na))
+ na->compressed_size = na->allocated_size;
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) {
+ na->ni->data_size = na->data_size;
+ na->ni->allocated_size = na->allocated_size;
+ NInoFileNameSetDirty(na->ni);
+ }
+ goto resize_done;
+ }
+ /* Error! If not enough space, just continue. */
+ if (errno != ENOSPC) {
+ err = errno;
+ ntfs_log_trace("Failed to resize resident part "
+ "of attribute. Aborting...\n");
+ goto put_err_out;
+ }
+ }
+ /* There is not enough space in the MFT record to perform the resize. */
+
+ /* Make the attribute non-resident if possible. */
+ if (!ntfs_attr_make_non_resident(na, ctx)) {
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ /* Resize non-resident attribute */
+ return ntfs_attr_truncate(na, newsize);
+ } else if (errno != ENOSPC && errno != EPERM) {
+ err = errno;
+ ntfs_log_trace("Failed to make attribute non-resident. "
+ "Aborting...\n");
+ goto put_err_out;
+ }
+
+ /* Try to make other attributes non-resident and retry each time. */
+ ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec);
+ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ ntfs_attr *tna;
+ ATTR_RECORD *a;
+
+ a = ctx->attr;
+ if (a->non_resident)
+ continue;
+
+ /*
+ * Check out whether convert is reasonable. Assume that mapping
+ * pairs will take 8 bytes.
+ */
+ if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD,
+ u.nonres.compressed_size) + ROUND_UP(a->name_length *
+ sizeof(ntfschar), 3) + 8)
+ continue;
+
+ tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a +
+ le16_to_cpu(a->name_offset)), a->name_length);
+ if (!tna) {
+ err = errno;
+ ntfs_log_trace("Couldn't open attribute.\n");
+ goto put_err_out;
+ }
+ if (ntfs_attr_make_non_resident(tna, ctx)) {
+ ntfs_attr_close(tna);
+ continue;
+ }
+ ntfs_inode_mark_dirty(tna->ni);
+ ntfs_attr_close(tna);
+ ntfs_attr_put_search_ctx(ctx);
+ return ntfs_resident_attr_resize(na, newsize);
+ }
+ /* Check whether error occurred. */
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+
+ /* We can't move out attribute list, thus move out others. */
+ if (na->type == AT_ATTRIBUTE_LIST) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD,
+ u.nonres.non_resident_end) + 8)) {
+ ntfs_log_trace("Couldn't free space in the MFT record "
+ "to make attribute list non "
+ "resident.\n");
+ return -1;
+ }
+ return ntfs_resident_attr_resize(na, newsize);
+ }
+
+ /*
+ * Move the attribute to a new MFT record, creating an attribute list
+ * attribute or modifying it if it is already present.
+ */
+
+ /* Point search context back to attribute which we need resize. */
+ ntfs_attr_init_search_ctx(ctx, na->ni, NULL);
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ ntfs_log_trace("Attribute lookup failed.\n");
+ err = errno;
+ goto put_err_out;
+ }
+
+ /*
+ * Force index allocation creation instead of moving out index root
+ * from the base MFT record.
+ */
+ if (na->type == AT_INDEX_ROOT && na->data_size > sizeof(INDEX_ROOT) +
+ sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN)) {
+ INDEX_ROOT *ir;
+
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ if (!(ir->index.flags & LARGE_INDEX)) {
+ err = EOVERFLOW;
+ goto put_err_out;
+ }
+ }
+
+ /*
+ * Check whether attribute is already single in the this MFT record.
+ * 8 added for the attribute terminator.
+ */
+ if (le32_to_cpu(ctx->mrec->bytes_in_use) ==
+ le16_to_cpu(ctx->mrec->attrs_offset) +
+ le32_to_cpu(ctx->attr->length) + 8) {
+ err = EOVERFLOW;
+ goto put_err_out;
+ }
+
+ /* Add attribute list if not present. */
+ if (na->ni->nr_extents == -1)
+ ni = na->ni->u.base_ni;
+ else
+ ni = na->ni;
+ if (!NInoAttrList(ni)) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (ntfs_inode_add_attrlist(ni))
+ return -1;
+ return ntfs_resident_attr_resize(na, newsize);
+ }
+ /* Allocate new MFT record. */
+ ni = ntfs_mft_record_alloc(vol, ni);
+ if (!ni) {
+ err = errno;
+ ntfs_log_trace("Couldn't allocate new MFT record.\n");
+ goto put_err_out;
+ }
+ /* Move attribute to it. */
+ if (ntfs_attr_record_move_to(ctx, ni)) {
+ err = errno;
+ ntfs_log_trace("Couldn't move attribute to new MFT record.\n");
+ goto put_err_out;
+ }
+ /* Update ntfs attribute. */
+ if (na->ni->nr_extents == -1)
+ na->ni = ni;
+
+ ntfs_attr_put_search_ctx(ctx);
+ /* Try to perform resize once again. */
+ return ntfs_resident_attr_resize(na, newsize);
+
+resize_done:
+ /*
+ * Set the inode (and its base inode if it exists) dirty so it is
+ * written out later.
+ */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ /* Done! */
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_attr_make_resident - convert a non-resident to a resident attribute
+ * @na: open ntfs attribute to make resident
+ * @ctx: ntfs search context describing the attribute
+ *
+ * Convert a non-resident ntfs attribute to a resident one.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code. The
+ * following error codes are defined:
+ * EINVAL - Invalid arguments passed.
+ * EPERM - The attribute is not allowed to be resident.
+ * EIO - I/O error, damaged inode or bug.
+ * ENOSPC - There is no enough space to perform conversion.
+ * EOPNOTSUPP - Requested conversion is not supported yet.
+ *
+ * Warning: We do not set the inode dirty and we do not write out anything!
+ * We expect the caller to do this as this is a fairly low level
+ * function and it is likely there will be further changes made.
+ */
+static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
+{
+ ntfs_volume *vol = na->ni->vol;
+ ATTR_REC *a = ctx->attr;
+ int name_ofs, val_ofs;
+ s64 arec_size, bytes_read;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
+ long)na->ni->mft_no, na->type);
+
+ /* Should be called for the first extent of the attribute. */
+ if (sle64_to_cpu(a->u.nonres.lowest_vcn)) {
+ ntfs_log_trace("Should be called for the first extent of the "
+ "attribute. Aborting...\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Some preliminary sanity checking. */
+ if (!NAttrNonResident(na)) {
+ ntfs_log_trace("Trying to make resident attribute resident. "
+ "Aborting...\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */
+ if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) {
+ errno = EPERM;
+ return -1;
+ }
+
+ /* Check that the attribute is allowed to be resident. */
+ if (ntfs_attr_can_be_resident(vol, na->type))
+ return -1;
+
+ /*
+ * Check that the attribute name hasn't been placed after the
+ * mapping pairs array. Chkdsk treat this as corruption.
+ */
+ if (a->name_length && le16_to_cpu(a->name_offset) >=
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset)) {
+ ntfs_log_trace("Damaged attribute. Name is placed after the "
+ "mapping pairs array. Run chkdsk. Aborting.\n");
+ errno = EIO;
+ return -1;
+ }
+
+ if (NAttrCompressed(na) || NAttrEncrypted(na)) {
+ ntfs_log_trace("Making compressed or encrypted files resident "
+ "is not implemented yet.\n");
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+
+ /* Work out offsets into and size of the resident attribute. */
+ name_ofs = 24; /* = sizeof(resident_ATTR_REC); */
+ val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
+ arec_size = (val_ofs + na->data_size + 7) & ~7;
+
+ /* Sanity check the size before we start modifying the attribute. */
+ if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) +
+ arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) {
+ ntfs_log_trace("Not enough space to make attribute resident\n");
+ errno = ENOSPC;
+ return -1;
+ }
+
+ /* Read and cache the whole runlist if not already done. */
+ if (ntfs_attr_map_whole_runlist(na))
+ return -1;
+
+ /* Move the attribute name if it exists and update the offset. */
+ if (a->name_length) {
+ memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset),
+ a->name_length * sizeof(ntfschar));
+ }
+ a->name_offset = cpu_to_le16(name_ofs);
+
+ /* Resize the resident part of the attribute record. */
+ if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) {
+ /*
+ * Bug, because ntfs_attr_record_resize should not fail (we
+ * already checked that attribute fits MFT record).
+ */
+ ntfs_log_error("BUG! Failed to resize attribute record. "
+ "Please report to the %s. Aborting...\n",
+ NTFS_DEV_LIST);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Convert the attribute record to describe a resident attribute. */
+ a->non_resident = 0;
+ a->flags = 0;
+ a->u.res.value_length = cpu_to_le32(na->data_size);
+ a->u.res.value_offset = cpu_to_le16(val_ofs);
+ /*
+ * File names cannot be non-resident so we would never see this here
+ * but at least it serves as a reminder that there may be attributes
+ * for which we do need to set this flag. (AIA)
+ */
+ if (a->type == AT_FILE_NAME)
+ a->u.res.resident_flags = RESIDENT_ATTR_IS_INDEXED;
+ else
+ a->u.res.resident_flags = 0;
+ a->u.res.reservedR = 0;
+
+ /* Sanity fixup... Shouldn't really happen. (AIA) */
+ if (na->initialized_size > na->data_size)
+ na->initialized_size = na->data_size;
+
+ /* Copy data from run list to resident attribute value. */
+ bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size,
+ (u8*)a + val_ofs);
+ if (bytes_read != na->initialized_size) {
+ if (bytes_read >= 0)
+ errno = EIO;
+ ntfs_log_trace("Eeek! Failed to read attribute data. Leaving "
+ "inconsistent metadata. Run chkdsk. "
+ "Aborting...\n");
+ return -1;
+ }
+
+ /* Clear memory in gap between initialized_size and data_size. */
+ if (na->initialized_size < na->data_size)
+ memset((u8*)a + val_ofs + na->initialized_size, 0,
+ na->data_size - na->initialized_size);
+
+ /*
+ * Deallocate clusters from the runlist.
+ *
+ * NOTE: We can use ntfs_cluster_free() because we have already mapped
+ * the whole run list and thus it doesn't matter that the attribute
+ * record is in a transiently corrupted state at this moment in time.
+ */
+ if (ntfs_cluster_free(vol, na, 0, -1) < 0) {
+ ntfs_log_perror("Eeek! Failed to release allocated clusters");
+ ntfs_log_trace("Ignoring error and leaving behind wasted "
+ "clusters.\n");
+ }
+
+ /* Throw away the now unused runlist. */
+ free(na->rl);
+ na->rl = NULL;
+
+ /* Update in-memory struct ntfs_attr. */
+ NAttrClearNonResident(na);
+ NAttrClearCompressed(na);
+ NAttrClearSparse(na);
+ NAttrClearEncrypted(na);
+ na->initialized_size = na->data_size;
+ na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7;
+ na->compression_block_size = 0;
+ na->compression_block_size_bits = na->compression_block_clusters = 0;
+ return 0;
+}
+
+#define NTFS_VCN_DELETE_MARK -2
+/**
+ * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute
+ * @na: non-resident ntfs open attribute for which we need update
+ * @from_vcn: update runlist starting this VCN
+ *
+ * Build mapping pairs from @na->rl and write them to the disk. Also, this
+ * function updates sparse bit, allocated and compressed size (allocates/frees
+ * space for this field if required).
+ *
+ * @na->allocated_size should be set to correct value for the new runlist before
+ * call to this function. Vice-versa @na->compressed_size will be calculated and
+ * set to correct value during this function.
+ *
+ * New runlist should be fully formed starting @from_vcn. Runs before @from_vcn
+ * can be mapped or not, but on-disk structures should not be modified before
+ * call to this function so they can be mapped if necessary.
+ *
+ * FIXME: Make it O(1) for sparse files too, not only for normal.
+ *
+ * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define.
+ *
+ * NOTE: Be careful in the future with updating bits on compressed files (at
+ * present assumed that on-disk flag is already set/cleared before call to
+ * this function).
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * EINVAL - Invalid arguments passed.
+ * ENOMEM - Not enough memory to complete operation.
+ * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST
+ * or there is no free MFT records left to allocate.
+ */
+int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn)
+{
+ ntfs_attr_search_ctx *ctx;
+ ntfs_inode *ni, *base_ni;
+ MFT_RECORD *m;
+ ATTR_RECORD *a;
+ VCN stop_vcn;
+ int err, mp_size, cur_max_mp_size, exp_max_mp_size;
+ BOOL finished_build;
+
+retry:
+ if (!na || !na->rl) {
+ ntfs_log_trace("Invalid parameters passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!NAttrNonResident(na)) {
+ ntfs_log_trace("Attribute should be non resident.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, from vcn 0x%lld."
+ "\n", (unsigned long long)na->ni->mft_no, na->type,
+ from_vcn);
+
+ if (na->ni->nr_extents == -1)
+ base_ni = na->ni->u.base_ni;
+ else
+ base_ni = na->ni;
+
+ ctx = ntfs_attr_get_search_ctx(base_ni, NULL);
+ if (!ctx) {
+ ntfs_log_trace("Couldn't get search context.\n");
+ return -1;
+ }
+
+ /* Fill attribute records with new mapping pairs. */
+ stop_vcn = 0;
+ finished_build = FALSE;
+ while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, ctx->is_first ? 0 : from_vcn,
+ NULL, 0, ctx)) {
+ a = ctx->attr;
+ m = ctx->mrec;
+ /*
+ * If runlist is updating not from the beginning, then set
+ * @stop_vcn properly, i.e. to the lowest vcn of record that
+ * contain @from_vcn. Also we do not need @from_vcn anymore,
+ * set it to 0 to make ntfs_attr_lookup enumerate attributes.
+ */
+ if (from_vcn && a->u.nonres.lowest_vcn) {
+ LCN first_lcn;
+
+ stop_vcn = sle64_to_cpu(a->u.nonres.lowest_vcn);
+ from_vcn = 0;
+ /*
+ * Check whether the first run we need to update is
+ * the last run in runlist, if so, then deallocate
+ * all attribute extents starting this one.
+ */
+ first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn);
+ if (first_lcn == LCN_EINVAL) {
+ ntfs_log_trace("BUG! Incorrect runlist.\n");
+ err = EIO;
+ goto put_err_out;
+ }
+ if (first_lcn == LCN_ENOENT ||
+ first_lcn == LCN_RL_NOT_MAPPED)
+ finished_build = TRUE;
+ }
+
+ /*
+ * Check whether we finished mapping pairs build, if so mark
+ * extent as need to delete (by setting highest vcn to
+ * NTFS_VCN_DELETE_MARK (-2), we shall check it later and
+ * delete extent) and continue search.
+ */
+ if (finished_build) {
+ ntfs_log_trace("Mark attr 0x%x for delete in inode "
+ "0x%llx.\n", (unsigned)le32_to_cpu(
+ a->type), ctx->ntfs_ino->mft_no);
+ a->u.nonres.highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK);
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ continue;
+ }
+
+ /*
+ * Check that the attribute name hasn't been placed after the
+ * mapping pairs array. Windows treat this as a corruption.
+ */
+ if (a->name_length) {
+ if (le16_to_cpu(a->name_offset) >=
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset)) {
+ ntfs_log_error("Damaged attribute. Name is "
+ "placed after the mapping "
+ "pairs array. Run chkdsk.\n");
+ err = EIO;
+ goto put_err_out;
+ }
+ }
+ /*
+ * If we in the first extent, then set/clean sparse bit,
+ * update allocated and compressed size.
+ */
+ if (!a->u.nonres.lowest_vcn) {
+ int sparse;
+
+ /* Update allocated size. */
+ a->u.nonres.allocated_size = cpu_to_sle64(na->allocated_size);
+ /*
+ * Check whether part of runlist we are updating is
+ * sparse.
+ */
+ sparse = ntfs_rl_sparse(na->rl);
+ if (sparse == -1) {
+ ntfs_log_trace("Bad runlist.\n");
+ err = errno;
+ goto put_err_out;
+ }
+ /*
+ * If new part or on-disk attribute is not sparse, then
+ * we should fully map runlist to make final decision.
+ */
+ if (sparse || (a->flags & ATTR_IS_SPARSE)) {
+ if (from_vcn && ntfs_attr_map_runlist_range(na,
+ 0, from_vcn - 1)) {
+ ntfs_log_trace("Failed to map runlist "
+ "before @from_vcn.\n");
+ err = errno;
+ goto put_err_out;
+ }
+ /*
+ * Reconsider whether whole runlist is sparse
+ * if new part is not.
+ */
+ if (!sparse) {
+ sparse = ntfs_rl_sparse(na->rl);
+ if (sparse == -1) {
+ ntfs_log_trace("Bad "
+ "runlist.\n");
+ err = errno;
+ goto put_err_out;
+ }
+ }
+ }
+ /* Attribute becomes sparse/compressed. */
+ if (sparse && !(a->flags & (ATTR_IS_SPARSE |
+ ATTR_IS_COMPRESSED))) {
+ /*
+ * We need to move attribute to another mft
+ * record, if attribute is to small to add
+ * compressed_size field to it and we have no
+ * free space in the current mft record.
+ */
+ if ((le32_to_cpu(a->length) - le16_to_cpu(
+ a->u.nonres.mapping_pairs_offset)
+ == 8) && !(le32_to_cpu(
+ m->bytes_allocated) -
+ le32_to_cpu(m->bytes_in_use))) {
+ if (!NInoAttrList(na->ni)) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (ntfs_inode_add_attrlist(
+ na->ni))
+ return -1;
+ goto retry;
+ }
+ if (ntfs_attr_record_move_away(ctx,
+ 8)) {
+ ntfs_log_trace("Failed to move "
+ "attribute to another "
+ "extent. Aborting..\n");
+ err = errno;
+ goto put_err_out;
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ goto retry;
+ }
+ if (!(le32_to_cpu(a->length) - le16_to_cpu(
+ a->u.nonres.mapping_pairs_offset))) {
+ ntfs_log_trace("Size of the space "
+ "allocated for mapping "
+ "pairs should not be 0."
+ " Aborting ...\n");
+ err = EIO;
+ goto put_err_out;
+ }
+ NAttrSetSparse(na);
+ a->flags |= ATTR_IS_SPARSE;
+ a->u.nonres.compression_unit = 4; /* Windows set it so,
+ even if attribute
+ is not actually
+ compressed. */
+ memmove((u8*)a + le16_to_cpu(a->name_offset) +
+ 8, (u8*)a + le16_to_cpu(a->name_offset),
+ a->name_length * sizeof(ntfschar));
+ a->name_offset = cpu_to_le16(le16_to_cpu(
+ a->name_offset) + 8);
+ a->u.nonres.mapping_pairs_offset =
+ cpu_to_le16(le16_to_cpu(
+ a->u.nonres.mapping_pairs_offset) + 8);
+ /*
+ * We should update all mapping pairs, because
+ * we shifted their starting position.
+ */
+ from_vcn = 0;
+ }
+ /* Attribute becomes normal. */
+ if (!sparse && (a->flags & ATTR_IS_SPARSE) &&
+ !(a->flags & ATTR_IS_COMPRESSED)) {
+ NAttrClearSparse(na);
+ a->flags &= ~ATTR_IS_SPARSE;
+ a->u.nonres.compression_unit = 0;
+ memmove((u8*)a + le16_to_cpu(a->name_offset) -
+ 8, (u8*)a + le16_to_cpu(a->name_offset),
+ a->name_length * sizeof(ntfschar));
+ /*
+ * Windows defragmentation tool do not update
+ * name offset correctly for unnamed
+ * attributes, but chkdsk do not like when it
+ * negative, so do not change it at all if it
+ * would become negative.
+ */
+ if (le16_to_cpu(a->name_offset) >= 8)
+ a->name_offset = cpu_to_le16(
+ le16_to_cpu(
+ a->name_offset) - 8);
+ a->u.nonres.mapping_pairs_offset =
+ cpu_to_le16(le16_to_cpu(
+ a->u.nonres.mapping_pairs_offset) - 8);
+ /*
+ * We should update all mapping pairs, because
+ * we shifted their starting position.
+ */
+ from_vcn = 0;
+ }
+ /* Update compressed size if required. */
+ if (sparse || (a->flags & ATTR_IS_COMPRESSED)) {
+ s64 new_compr_size;
+
+ new_compr_size = ntfs_rl_get_compressed_size(
+ na->ni->vol, na->rl);
+ if (new_compr_size == -1) {
+ err = errno;
+ ntfs_log_trace("BUG! Leaving "
+ "inconsistent "
+ "metadata.\n");
+ goto put_err_out;
+ }
+ na->compressed_size = new_compr_size;
+ a->u.nonres.compressed_size = cpu_to_sle64(
+ new_compr_size);
+ }
+ /*
+ * Set FILE_NAME dirty flag, to update sparse bit and
+ * allocated size in the index.
+ */
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) {
+ if (sparse)
+ na->ni->allocated_size =
+ na->compressed_size;
+ else
+ na->ni->allocated_size =
+ na->allocated_size;
+ NInoFileNameSetDirty(na->ni);
+ }
+
+ /*
+ * We do want to do anything for the first extent in
+ * case we are updating mapping pairs not from the
+ * begging.
+ */
+ if (!a->u.nonres.highest_vcn || from_vcn <=
+ sle64_to_cpu(a->u.nonres.highest_vcn) + 1)
+ from_vcn = 0;
+ else {
+ if (from_vcn)
+ continue;
+ }
+ }
+
+ /* Get the size for the rest of mapping pairs array. */
+ mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, na->rl,
+ stop_vcn);
+ if (mp_size <= 0) {
+ err = errno;
+ ntfs_log_trace("Get size for mapping pairs failed.\n");
+ goto put_err_out;
+ }
+ /*
+ * Determine maximum possible length of mapping pairs,
+ * if we shall *not* expand space for mapping pairs.
+ */
+ cur_max_mp_size = le32_to_cpu(a->length) -
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset);
+ /*
+ * Determine maximum possible length of mapping pairs in the
+ * current mft record, if we shall expand space for mapping
+ * pairs.
+ */
+ exp_max_mp_size = le32_to_cpu(m->bytes_allocated) -
+ le32_to_cpu(m->bytes_in_use) + cur_max_mp_size;
+ /* Test mapping pairs for fitting in the current mft record. */
+ if (mp_size > exp_max_mp_size) {
+ /*
+ * Mapping pairs of $ATTRIBUTE_LIST attribute must fit
+ * in the base mft record. Try to move out other
+ * attributes and try again.
+ */
+ if (na->type == AT_ATTRIBUTE_LIST) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (ntfs_inode_free_space(na->ni, mp_size -
+ cur_max_mp_size)) {
+ if (errno != ENOSPC)
+ return -1;
+ ntfs_log_error("Attribute list mapping "
+ "pairs size to big, "
+ "can't fit them in the "
+ "base MFT record. "
+ "Defragment volume and "
+ "try once again.\n");
+ errno = ENOSPC;
+ return -1;
+ }
+ goto retry;
+ }
+
+ /* Add attribute list if it isn't present, and retry. */
+ if (!NInoAttrList(base_ni)) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (ntfs_inode_add_attrlist(base_ni)) {
+ ntfs_log_trace("Couldn't add attribute "
+ "list.\n");
+ return -1;
+ }
+ goto retry;
+ }
+
+ /*
+ * Set mapping pairs size to maximum possible for this
+ * mft record. We shall write the rest of mapping pairs
+ * to another MFT records.
+ */
+ mp_size = exp_max_mp_size;
+ }
+
+ /* Change space for mapping pairs if we need it. */
+ if (((mp_size + 7) & ~7) != cur_max_mp_size) {
+ if (ntfs_attr_record_resize(m, a,
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset) +
+ mp_size)) {
+ ntfs_log_error("BUG! Ran out of space in mft "
+ "record. Please run chkdsk and "
+ "if that doesn't find any "
+ "errors please report you saw "
+ "this message to %s.\n",
+ NTFS_DEV_LIST);
+ err = EIO;
+ goto put_err_out;
+ }
+ }
+
+ /* Update lowest vcn. */
+ a->u.nonres.lowest_vcn = cpu_to_sle64(stop_vcn);
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ if ((ctx->ntfs_ino->nr_extents == -1 ||
+ NInoAttrList(ctx->ntfs_ino)) &&
+ ctx->attr->type != AT_ATTRIBUTE_LIST) {
+ ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn);
+ ntfs_attrlist_mark_dirty(ctx->ntfs_ino);
+ }
+
+ /*
+ * Generate the new mapping pairs array directly into the
+ * correct destination, i.e. the attribute record itself.
+ */
+ if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu(
+ a->u.nonres.mapping_pairs_offset), mp_size, na->rl,
+ stop_vcn, &stop_vcn))
+ finished_build = TRUE;
+ if (!finished_build && errno != ENOSPC) {
+ err = errno;
+ ntfs_log_error("BUG! Mapping pairs build failed. "
+ "Please run chkdsk and if that doesn't "
+ "find any errors please report you saw "
+ "this message to %s.\n", NTFS_DEV_LIST);
+ goto put_err_out;
+ }
+ a->u.nonres.highest_vcn = cpu_to_sle64(stop_vcn - 1);
+ }
+ /* Check whether error occurred. */
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+ /* Sanity check. */
+ if (from_vcn) {
+ err = ENOMSG;
+ ntfs_log_error("Library BUG! @from_vcn is nonzero, please "
+ "report to %s.\n", NTFS_DEV_LIST);
+ goto put_err_out;
+ }
+
+ /* Deallocate not used attribute extents and return with success. */
+ if (finished_build) {
+ ntfs_attr_reinit_search_ctx(ctx);
+ ntfs_log_trace("Deallocate marked extents.\n");
+ while (!ntfs_attr_lookup(na->type, na->name, na->name_len,
+ CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ if (sle64_to_cpu(ctx->attr->u.nonres.highest_vcn) !=
+ NTFS_VCN_DELETE_MARK)
+ continue;
+ /* Remove unused attribute record. */
+ if (ntfs_attr_record_rm(ctx)) {
+ err = errno;
+ ntfs_log_trace("Couldn't remove unused "
+ "attribute record.\n");
+ goto put_err_out;
+ }
+ ntfs_attr_reinit_search_ctx(ctx);
+ }
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+ ntfs_log_trace("Deallocate done.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_log_trace("Done!");
+ return 0;
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+
+ /* Allocate new MFT records for the rest of mapping pairs. */
+ while (1) {
+ /* Calculate size of rest mapping pairs. */
+ mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol,
+ na->rl, stop_vcn);
+ if (mp_size <= 0) {
+ err = errno;
+ ntfs_log_trace("Get size for mapping pairs failed.\n");
+ goto put_err_out;
+ }
+ /* Allocate new mft record. */
+ ni = ntfs_mft_record_alloc(na->ni->vol, base_ni);
+ if (!ni) {
+ err = errno;
+ ntfs_log_trace("Couldn't allocate new MFT record.\n");
+ goto put_err_out;
+ }
+ m = ni->mrec;
+ /*
+ * If mapping size exceed available space, set them to
+ * possible maximum.
+ */
+ cur_max_mp_size = le32_to_cpu(m->bytes_allocated) -
+ le32_to_cpu(m->bytes_in_use) -
+ (offsetof(ATTR_RECORD, u.nonres.compressed_size) +
+ ((NAttrCompressed(na) || NAttrSparse(na)) ?
+ sizeof(a->u.nonres.compressed_size) : 0)) -
+ ((sizeof(ntfschar) * na->name_len + 7) & ~7);
+ if (mp_size > cur_max_mp_size)
+ mp_size = cur_max_mp_size;
+ /* Add attribute extent to new record. */
+ err = ntfs_non_resident_attr_record_add(ni, na->type,
+ na->name, na->name_len, stop_vcn, mp_size, 0);
+ if (err == -1) {
+ err = errno;
+ ntfs_log_trace("Couldn't add attribute extent into the "
+ "MFT record.\n");
+ if (ntfs_mft_record_free(na->ni->vol, ni)) {
+ ntfs_log_trace("Couldn't free MFT record.\n");
+ }
+ goto put_err_out;
+ }
+ a = (ATTR_RECORD*)((u8*)m + err);
+
+ err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a +
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp_size, na->rl,
+ stop_vcn, &stop_vcn);
+ if (err < 0 && errno != ENOSPC) {
+ err = errno;
+ ntfs_log_error("BUG! Mapping pairs build failed. "
+ "Please run chkdsk and if that doesn't "
+ "find any errors please report you saw "
+ "this message to %s.\n", NTFS_DEV_LIST);
+ if (ntfs_mft_record_free(na->ni->vol, ni))
+ ntfs_log_trace("Couldn't free MFT record.\n");
+ goto put_err_out;
+ }
+ a->u.nonres.highest_vcn = cpu_to_sle64(stop_vcn - 1);
+ ntfs_inode_mark_dirty(ni);
+ /* All mapping pairs has been written. */
+ if (!err)
+ break;
+ }
+ ntfs_log_trace("Done!\n");
+ return 0;
+put_err_out:
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+#undef NTFS_VCN_DELETE_MARK
+
+/**
+ * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute
+ * @na: non-resident ntfs attribute to shrink
+ * @newsize: new size (in bytes) to which to shrink the attribute
+ *
+ * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * ENOMEM - Not enough memory to complete operation.
+ * ERANGE - @newsize is not valid for the attribute type of @na.
+ */
+static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize)
+{
+ ntfs_volume *vol;
+ ntfs_attr_search_ctx *ctx;
+ VCN first_free_vcn;
+ s64 nr_freed_clusters;
+ int err;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, newsize %lld.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)newsize);
+
+ vol = na->ni->vol;
+
+ /*
+ * Check the attribute type and the corresponding minimum size
+ * against @newsize and fail if @newsize is too small.
+ */
+ if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
+ if (errno == ERANGE) {
+ ntfs_log_trace("Eeek! Size bounds check failed. "
+ "Aborting...\n");
+ } else if (errno == ENOENT)
+ errno = EIO;
+ return -1;
+ }
+
+ /* The first cluster outside the new allocation. */
+ first_free_vcn = (newsize + vol->cluster_size - 1) >>
+ vol->cluster_size_bits;
+ /*
+ * Compare the new allocation with the old one and only deallocate
+ * clusters if there is a change.
+ */
+ if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) {
+ if (ntfs_attr_map_whole_runlist(na)) {
+ ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist "
+ "failed.\n");
+ return -1;
+ }
+ /* Deallocate all clusters starting with the first free one. */
+ nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn,
+ -1);
+ if (nr_freed_clusters < 0) {
+ ntfs_log_trace("Eeek! Freeing of clusters failed. "
+ "Aborting...\n");
+ return -1;
+ }
+
+ /* Truncate the runlist itself. */
+ if (ntfs_rl_truncate(&na->rl, first_free_vcn)) {
+ err = errno;
+ /*
+ * Failed to truncate the runlist, so just throw it
+ * away, it will be mapped afresh on next use.
+ */
+ free(na->rl);
+ na->rl = NULL;
+ ntfs_log_trace("Eeek! Run list truncation failed.\n");
+ errno = err;
+ return -1;
+ }
+
+ /* Prepare to mapping pairs update. */
+ na->allocated_size = first_free_vcn << vol->cluster_size_bits;
+ /* Write mapping pairs for new runlist. */
+ if (ntfs_attr_update_mapping_pairs(na, first_free_vcn)) {
+ ntfs_log_trace("Eeek! Mapping pairs update failed. "
+ "Leaving inconsistent metadata. "
+ "Run chkdsk.\n");
+ return -1;
+ }
+ }
+
+ /* Get the first attribute record. */
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_trace("Couldn't get attribute search context.\n");
+ return -1;
+ }
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ err = errno;
+ if (err == ENOENT)
+ err = EIO;
+ ntfs_log_trace("Eeek! Lookup of first attribute extent failed. "
+ "Leaving inconsistent metadata.\n");
+ goto put_err_out;
+ }
+
+ /* Update data and initialized size. */
+ na->data_size = newsize;
+ ctx->attr->u.nonres.data_size = cpu_to_sle64(newsize);
+ if (newsize < na->initialized_size) {
+ na->initialized_size = newsize;
+ ctx->attr->u.nonres.initialized_size = cpu_to_sle64(newsize);
+ }
+ /* Update data size in the index. */
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) {
+ na->ni->data_size = na->data_size;
+ NInoFileNameSetDirty(na->ni);
+ }
+
+ /* If the attribute now has zero size, make it resident. */
+ if (!newsize) {
+ if (ntfs_attr_make_resident(na, ctx)) {
+ /* If couldn't make resident, just continue. */
+ if (errno != EPERM)
+ ntfs_log_error("Failed to make attribute "
+ "resident. Leaving as is...\n");
+ }
+ }
+
+ /* Set the inode dirty so it is written out later. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ /* Done! */
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute
+ * @na: non-resident ntfs attribute to expand
+ * @newsize: new size (in bytes) to which to expand the attribute
+ * @sparse: if TRUE then will create hole if possible
+ *
+ * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes,
+ * by allocating new clusters.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * ENOMEM - Not enough memory to complete operation.
+ * ERANGE - @newsize is not valid for the attribute type of @na.
+ * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST.
+ */
+static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize,
+ BOOL sparse)
+{
+ VCN first_free_vcn;
+ ntfs_volume *vol;
+ ntfs_attr_search_ctx *ctx;
+ runlist *rl, *rln;
+ s64 org_alloc_size;
+ int err;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, new size %lld, "
+ "current size %lld.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)newsize, (long long)na->data_size);
+
+ vol = na->ni->vol;
+
+ /*
+ * Check the attribute type and the corresponding maximum size
+ * against @newsize and fail if @newsize is too big.
+ */
+ if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) {
+ if (errno == ERANGE) {
+ ntfs_log_trace("Eeek! Size bounds check failed. "
+ "Aborting...\n");
+ } else if (errno == ENOENT)
+ errno = EIO;
+ return -1;
+ }
+
+ /* Save for future use. */
+ org_alloc_size = na->allocated_size;
+ /* The first cluster outside the new allocation. */
+ first_free_vcn = (newsize + vol->cluster_size - 1) >>
+ vol->cluster_size_bits;
+ /*
+ * Compare the new allocation with the old one and only allocate
+ * clusters if there is a change.
+ */
+ if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) {
+ /* Map required part of runlist. */
+ if (ntfs_attr_map_runlist(na, na->allocated_size >>
+ vol->cluster_size_bits)) {
+ ntfs_log_error("Failed to map runlist.\n");
+ return -1;
+ }
+
+ /*
+ * If we extend $DATA attribute on NTFS 3+ volume, we can add
+ * sparse runs instead of real allocation of clusters.
+ */
+ if (na->type == AT_DATA && vol->major_ver >= 3 && sparse) {
+ rl = ntfs_malloc(0x1000);
+ if (!rl)
+ return -1;
+
+ rl[0].vcn = (na->allocated_size >>
+ vol->cluster_size_bits);
+ rl[0].lcn = LCN_HOLE;
+ rl[0].length = first_free_vcn -
+ (na->allocated_size >> vol->cluster_size_bits);
+ rl[1].vcn = first_free_vcn;
+ rl[1].lcn = LCN_ENOENT;
+ rl[1].length = 0;
+ } else {
+ /*
+ * Determine first after last LCN of attribute.
+ * We will start seek clusters from this LCN to avoid
+ * fragmentation. If there are no valid LCNs in the
+ * attribute let the cluster allocator choose the
+ * starting LCN.
+ */
+ LCN lcn_seek_from;
+
+ lcn_seek_from = -1;
+ if (na->rl->length) {
+ /* Seek to the last run list element. */
+ for (rl = na->rl; (rl + 1)->length; rl++)
+ ;
+ /*
+ * If the last LCN is a hole or similar seek
+ * back to last valid LCN.
+ */
+ while (rl->lcn < 0 && rl != na->rl)
+ rl--;
+ /*
+ * Only set lcn_seek_from it the LCN is valid.
+ */
+ if (rl->lcn >= 0)
+ lcn_seek_from = rl->lcn + rl->length;
+ }
+
+ rl = ntfs_cluster_alloc(vol, na->allocated_size >>
+ vol->cluster_size_bits, first_free_vcn -
+ (na->allocated_size >>
+ vol->cluster_size_bits), lcn_seek_from,
+ DATA_ZONE);
+ if (!rl) {
+ ntfs_log_trace("Cluster allocation failed.\n");
+ return -1;
+ }
+ }
+
+ /* Append new clusters to attribute runlist. */
+ rln = ntfs_runlists_merge(na->rl, rl);
+ if (!rln) {
+ /* Failed, free just allocated clusters. */
+ err = errno;
+ ntfs_log_trace("Run list merge failed.\n");
+ ntfs_cluster_free_from_rl(vol, rl);
+ free(rl);
+ errno = err;
+ return -1;
+ }
+ na->rl = rln;
+
+ /* Prepare to mapping pairs update. */
+ na->allocated_size = first_free_vcn << vol->cluster_size_bits;
+ /* Write mapping pairs for new runlist. */
+ if (ntfs_attr_update_mapping_pairs(na, org_alloc_size >>
+ vol->cluster_size_bits)) {
+ err = errno;
+ ntfs_log_trace("Mapping pairs update failed.\n");
+ goto rollback;
+ }
+ }
+
+ ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_trace("Failed to get search context.\n");
+ if (na->allocated_size == org_alloc_size) {
+ return -1;
+ }
+ err = errno;
+ goto rollback;
+ }
+
+ if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ err = errno;
+ ntfs_log_trace("Lookup of first attribute extent failed.\n");
+ if (err == ENOENT)
+ err = EIO;
+ if (na->allocated_size != org_alloc_size) {
+ ntfs_attr_put_search_ctx(ctx);
+ goto rollback;
+ } else
+ goto put_err_out;
+ }
+
+ /* Update data size. */
+ na->data_size = newsize;
+ ctx->attr->u.nonres.data_size = cpu_to_sle64(newsize);
+ /* Update data size in the index. */
+ if (na->type == AT_DATA && na->name == AT_UNNAMED) {
+ na->ni->data_size = na->data_size;
+ NInoFileNameSetDirty(na->ni);
+ }
+ /* Set the inode dirty so it is written out later. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ /* Done! */
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+rollback:
+ /* Free allocated clusters. */
+ if (ntfs_cluster_free(vol, na, org_alloc_size >>
+ vol->cluster_size_bits, -1) < 0) {
+ ntfs_log_trace("Eeek! Leaking clusters. Run chkdsk!\n");
+ err = EIO;
+ }
+ /* Now, truncate the runlist itself. */
+ if (ntfs_rl_truncate(&na->rl, org_alloc_size >>
+ vol->cluster_size_bits)) {
+ /*
+ * Failed to truncate the runlist, so just throw it away, it
+ * will be mapped afresh on next use.
+ */
+ free(na->rl);
+ na->rl = NULL;
+ ntfs_log_trace("Couldn't truncate runlist. Rollback failed.\n");
+ } else {
+ /* Prepare to mapping pairs update. */
+ na->allocated_size = org_alloc_size;
+ /* Restore mapping pairs. */
+ if (ntfs_attr_update_mapping_pairs(na, na->allocated_size >>
+ vol->cluster_size_bits)) {
+ ntfs_log_trace("Failed to restore old mapping pairs. "
+ "Rollback failed.\n");
+ }
+ }
+ errno = err;
+ return -1;
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+
+/**
+ * __ntfs_attr_truncate - resize an ntfs attribute
+ * @na: open ntfs attribute to resize
+ * @newsize: new size (in bytes) to which to resize the attribute
+ * @sparse: if TRUE then will create hole if possible
+ *
+ * Change the size of an open ntfs attribute @na to @newsize bytes. If the
+ * attribute is made bigger and the attribute is resident the newly
+ * "allocated" space is cleared and if the attribute is non-resident the
+ * newly allocated space is marked as not initialised and no real allocation
+ * on disk is performed.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * EINVAL - Invalid arguments were passed to the function.
+ * EACCES - Attribute is encrypted.
+ * ERANGE - @newsize is not valid for the attribute type of @na.
+ * ENOSPC - There is no enough space on the volume to allocate
+ * new clusters or in base mft to resize $ATTRIBUTE_LIST.
+ * EOVERFLOW - Resident attribute can not become non resident and
+ * already filled whole MFT record, but had not reached
+ * @newsize bytes length.
+ * EOPNOTSUPP - The desired resize is not implemented yet.
+ */
+int __ntfs_attr_truncate(ntfs_attr *na, const s64 newsize, BOOL sparse)
+{
+ int ret;
+
+ if (!na || newsize < 0 ||
+ (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) {
+ ntfs_log_trace("Invalid arguments passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long
+ long)na->ni->mft_no, na->type);
+
+ if (na->data_size == newsize)
+ return 0;
+ /*
+ * Encrypted attributes are not supported. We return access denied,
+ * which is what Windows NT4 does, too.
+ */
+ if (NAttrEncrypted(na)) {
+ errno = EACCES;
+ ntfs_log_trace("Failed (encrypted).\n");
+ return -1;
+ }
+ /*
+ * TODO: Implement making handling of compressed attributes.
+ */
+ if (NAttrCompressed(na)) {
+ errno = EOPNOTSUPP;
+ ntfs_log_trace("Failed (compressed).\n");
+ return -1;
+ }
+ if (NAttrNonResident(na)) {
+ if (newsize > na->data_size)
+ ret = ntfs_non_resident_attr_expand(na, newsize,
+ sparse);
+ else
+ ret = ntfs_non_resident_attr_shrink(na, newsize);
+ } else
+ ret = ntfs_resident_attr_resize(na, newsize);
+ if (!ret)
+ ntfs_log_trace("Done!\n");
+ else
+ ntfs_log_trace("Failed.\n");
+ return ret;
+}
+
+
+/**
+ * Wrapper around __ntfs_attr_truncate that always tries to creates hole
+ */
+int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
+{
+ return __ntfs_attr_truncate(na, newsize, TRUE);
+}
+
+
+/**
+ * ntfs_attr_readall - read the entire data from an ntfs attribute
+ * @ni: open ntfs inode in which the ntfs attribute resides
+ * @type: attribute type
+ * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL
+ * @name_len: length of attribute @name in Unicode characters (if @name given)
+ * @data_size: if non-NULL then store here the data size
+ *
+ * This function will read the entire content of an ntfs attribute.
+ * If @name is AT_UNNAMED then look specifically for an unnamed attribute.
+ * If @name is NULL then the attribute could be either named or not.
+ * In both those cases @name_len is not used at all.
+ *
+ * On success a buffer is allocated with the content of the attribute
+ * and which needs to be freed when it's not needed anymore. If the
+ * @data_size parameter is non-NULL then the data size is set there.
+ *
+ * On error NULL is returned with errno set to the error code.
+ */
+void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
+ ntfschar *name, u32 name_len, s64 *data_size)
+{
+ ntfs_attr *na;
+ void *data, *ret = NULL;
+ s64 size;
+
+ na = ntfs_attr_open(ni, type, name, name_len);
+ if (!na) {
+ ntfs_log_perror("ntfs_attr_open failed");
+ return NULL;
+ }
+ data = ntfs_malloc(na->data_size);
+ if (!data)
+ goto out;
+
+ size = ntfs_attr_pread(na, 0, na->data_size, data);
+ if (size != na->data_size) {
+ ntfs_log_perror("ntfs_attr_pread failed");
+ free(data);
+ goto out;
+ }
+ ret = data;
+ if (data_size)
+ *data_size = size;
+out:
+ ntfs_attr_close(na);
+ return ret;
+}
+
+/**
+ * ntfs_attr_exist - FIXME: description
+ */
+int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name,
+ u32 name_len)
+{
+ ntfs_attr_search_ctx *ctx;
+ int ret;
+
+ ntfs_log_trace("Entering.\n");
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return 0;
+
+ ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0,
+ ctx);
+
+ ntfs_attr_put_search_ctx(ctx);
+ return !ret;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/attrlist.c b/usr/src/lib/libntfs/common/libntfs/attrlist.c
new file mode 100644
index 0000000000..3bbc6a3ca8
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/attrlist.c
@@ -0,0 +1,320 @@
+/**
+ * attrlist.c - Attribute list attribute handling code. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2004-2005 Anton Altaparmakov
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "types.h"
+#include "layout.h"
+#include "attrib.h"
+#include "attrlist.h"
+#include "debug.h"
+#include "unistr.h"
+#include "logging.h"
+
+/**
+ * ntfs_attrlist_need - check whether inode need attribute list
+ * @ni: opened ntfs inode for which perform check
+ *
+ * Check whether all are attributes belong to one MFT record, in that case
+ * attribute list is not needed.
+ *
+ * Return 1 if inode need attribute list, 0 if not, -1 on error with errno set
+ * to the error code. If function succeed errno set to 0. The following error
+ * codes are defined:
+ * EINVAL - Invalid arguments passed to function or attribute haven't got
+ * attribute list.
+ */
+int ntfs_attrlist_need(ntfs_inode *ni)
+{
+ ATTR_LIST_ENTRY *ale;
+
+ if (!ni) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ if (!NInoAttrList(ni)) {
+ ntfs_log_trace("Inode haven't got attribute list.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!ni->attr_list) {
+ ntfs_log_trace("Corrupt in-memory struct.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ errno = 0;
+ ale = (ATTR_LIST_ENTRY *)ni->attr_list;
+ while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
+ if (MREF_LE(ale->mft_reference) != ni->mft_no)
+ return 1;
+ ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
+ }
+ return 0;
+}
+
+/**
+ * ntfs_attrlist_entry_add - add an attribute list attribute entry
+ * @ni: opened ntfs inode, which contains that attribute
+ * @attr: attribute record to add to attribute list
+ *
+ * Return 0 on success and -1 on error with errno set to the error code. The
+ * following error codes are defined:
+ * EINVAL - Invalid arguments passed to function.
+ * ENOMEM - Not enough memory to allocate necessary buffers.
+ * EIO - I/O error occurred or damaged filesystem.
+ * EEXIST - Such attribute already present in attribute list.
+ */
+int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr)
+{
+ ATTR_LIST_ENTRY *ale;
+ leMFT_REF mref;
+ ntfs_attr *na = NULL;
+ ntfs_attr_search_ctx *ctx;
+ u8 *new_al;
+ int entry_len, entry_offset, err;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n",
+ (long long) ni->mft_no,
+ (unsigned) le32_to_cpu(attr->type));
+
+ if (!ni || !attr) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number));
+
+ if (ni->nr_extents == -1)
+ ni = ni->u.base_ni;
+
+ if (!NInoAttrList(ni)) {
+ ntfs_log_trace("Attribute list isn't present.\n");
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Determine size and allocate memory for new attribute list. */
+ entry_len = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
+ attr->name_length + 7) & ~7;
+ new_al = malloc(ni->attr_list_size + entry_len);
+ if (!new_al) {
+ ntfs_log_trace("Not enough memory.\n");
+ err = ENOMEM;
+ return -1;
+ }
+
+ /* Find place for the new entry. */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = errno;
+ ntfs_log_trace("Failed to obtain attribute search context.\n");
+ goto err_out;
+ }
+ if (!ntfs_attr_lookup(attr->type, (attr->name_length) ? (ntfschar*)
+ ((u8*)attr + le16_to_cpu(attr->name_offset)) :
+ AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
+ (attr->non_resident) ? sle64_to_cpu(attr->u.nonres.lowest_vcn) :
+ 0, (attr->non_resident) ? NULL : ((u8*)attr +
+ le16_to_cpu(attr->u.res.value_offset)), (attr->non_resident) ?
+ 0 : le32_to_cpu(attr->u.res.value_length), ctx)) {
+ /* Found some extent, check it to be before new extent. */
+ if (ctx->al_entry->lowest_vcn == attr->u.nonres.lowest_vcn) {
+ err = EEXIST;
+ ntfs_log_trace("Such attribute already present in the "
+ "attribute list.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ goto err_out;
+ }
+ /* Add new entry after this extent. */
+ ale = (ATTR_LIST_ENTRY*)((u8*)ctx->al_entry +
+ le16_to_cpu(ctx->al_entry->length));
+ } else {
+ /* Check for real errors. */
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ goto err_out;
+ }
+ /* No previous extents found. */
+ ale = ctx->al_entry;
+ }
+ /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
+ ntfs_attr_put_search_ctx(ctx);
+
+ /* Determine new entry offset. */
+ entry_offset = ((u8 *)ale - ni->attr_list);
+ /* Set pointer to new entry. */
+ ale = (ATTR_LIST_ENTRY *)(new_al + entry_offset);
+ /* Form new entry. */
+ ale->type = attr->type;
+ ale->length = cpu_to_le16(entry_len);
+ ale->name_length = attr->name_length;
+ ale->name_offset = offsetof(ATTR_LIST_ENTRY, name);
+ if (attr->non_resident)
+ ale->lowest_vcn = attr->u.nonres.lowest_vcn;
+ else
+ ale->lowest_vcn = 0;
+ ale->mft_reference = mref;
+ ale->instance = attr->instance;
+ NTFS_ON_DEBUG(memset(ale->name, 0, ((u8*)((u8*)ale + entry_len)) -
+ ((u8*)ale->name))); /* Shut up, valgrind. */
+ memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
+ attr->name_length * sizeof(ntfschar));
+
+ /* Resize $ATTRIBUTE_LIST to new length. */
+ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
+ if (!na) {
+ err = errno;
+ ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
+ goto err_out;
+ }
+ if (ntfs_attr_truncate(na, ni->attr_list_size + entry_len)) {
+ err = errno;
+ ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
+ goto err_out;
+ }
+
+ /* Copy entries from old attribute list to new. */
+ memcpy(new_al, ni->attr_list, entry_offset);
+ memcpy(new_al + entry_offset + entry_len, ni->attr_list +
+ entry_offset, ni->attr_list_size - entry_offset);
+
+ /* Set new runlist. */
+ free(ni->attr_list);
+ ni->attr_list = new_al;
+ ni->attr_list_size = ni->attr_list_size + entry_len;
+ NInoAttrListSetDirty(ni);
+ /* Done! */
+ ntfs_attr_close(na);
+ return 0;
+err_out:
+ if (na)
+ ntfs_attr_close(na);
+ free(new_al);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_attrlist_entry_rm - remove an attribute list attribute entry
+ * @ctx: attribute search context describing the attribute list entry
+ *
+ * Remove the attribute list entry @ctx->al_entry from the attribute list.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_attrlist_entry_rm(ntfs_attr_search_ctx *ctx)
+{
+ u8 *new_al;
+ int new_al_len;
+ ntfs_inode *base_ni;
+ ntfs_attr *na;
+ ATTR_LIST_ENTRY *ale;
+ int err;
+
+ if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ctx->base_ntfs_ino)
+ base_ni = ctx->base_ntfs_ino;
+ else
+ base_ni = ctx->ntfs_ino;
+ ale = ctx->al_entry;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld."
+ "\n", (long long) ctx->ntfs_ino->mft_no,
+ (unsigned) le32_to_cpu(ctx->al_entry->type),
+ (long long) sle64_to_cpu(ctx->al_entry->lowest_vcn));
+
+ if (!NInoAttrList(base_ni)) {
+ ntfs_log_trace("Attribute list isn't present.\n");
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Allocate memory for new attribute list. */
+ new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
+ new_al = malloc(new_al_len);
+ if (!new_al) {
+ ntfs_log_trace("Not enough memory.\n");
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Reisze $ATTRIBUTE_LIST to new length. */
+ na = ntfs_attr_open(base_ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
+ if (!na) {
+ err = errno;
+ ntfs_log_trace("Failed to open $ATTRIBUTE_LIST attribute.\n");
+ goto err_out;
+ }
+ if (ntfs_attr_truncate(na, new_al_len)) {
+ err = errno;
+ ntfs_log_trace("$ATTRIBUTE_LIST resize failed.\n");
+ goto err_out;
+ }
+
+ /* Copy entries from old attribute list to new. */
+ memcpy(new_al, base_ni->attr_list, (u8*)ale - base_ni->attr_list);
+ memcpy(new_al + ((u8*)ale - base_ni->attr_list), (u8*)ale + le16_to_cpu(
+ ale->length), new_al_len - ((u8*)ale - base_ni->attr_list));
+
+ /* Set new runlist. */
+ free(base_ni->attr_list);
+ base_ni->attr_list = new_al;
+ base_ni->attr_list_size = new_al_len;
+ NInoAttrListSetDirty(base_ni);
+ /* Done! */
+ ntfs_attr_close(na);
+ return 0;
+err_out:
+ if (na)
+ ntfs_attr_close(na);
+ free(new_al);
+ errno = err;
+ return -1;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/bitmap.c b/usr/src/lib/libntfs/common/libntfs/bitmap.c
new file mode 100644
index 0000000000..69d498fd66
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/bitmap.c
@@ -0,0 +1,248 @@
+/**
+ * bitmap.c - Bitmap handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2006 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "types.h"
+#include "attrib.h"
+#include "bitmap.h"
+#include "debug.h"
+#include "logging.h"
+
+/**
+ * ntfs_bitmap_set_bits_in_run - set a run of bits in a bitmap to a value
+ * @na: attribute containing the bitmap
+ * @start_bit: first bit to set
+ * @count: number of bits to set
+ * @value: value to set the bits to (i.e. 0 or 1)
+ *
+ * Set @count bits starting at bit @start_bit in the bitmap described by the
+ * attribute @na to @value, where @value is either 0 or 1.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+static int ntfs_bitmap_set_bits_in_run(ntfs_attr *na, s64 start_bit,
+ s64 count, int value)
+{
+ ntfs_volume *vol = na->ni->vol;
+ s64 bufsize, br, left = count;
+ u8 *buf, *lastbyte_buf;
+ int bit, firstbyte, lastbyte, lastbyte_pos, tmp, err;
+
+ if (!na || start_bit < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ bit = start_bit & 7;
+ if (bit)
+ firstbyte = 1;
+ else
+ firstbyte = 0;
+
+ /* Calculate the required buffer size in bytes, capping it at 8kiB. */
+ bufsize = ((count - (bit ? 8 - bit : 0) + 7) >> 3) + firstbyte;
+ if (bufsize > 8192)
+ bufsize = 8192;
+
+ buf = (u8*)ntfs_malloc(bufsize);
+ if (!buf)
+ return -1;
+
+ /* Depending on @value, zero or set all bits in the allocated buffer. */
+ memset(buf, value ? 0xff : 0, bufsize);
+
+ /* If there is a first partial byte... */
+ if (bit) {
+ /* read it in... */
+ br = ntfs_attr_pread(na, start_bit >> 3, 1, buf);
+ if (br != 1) {
+ free(buf);
+ errno = EIO;
+ return -1;
+ }
+ /* and set or clear the appropriate bits in it. */
+ while ((bit & 7) && left--) {
+ if (value)
+ *buf |= 1 << bit++;
+ else
+ *buf &= ~(1 << bit++);
+ }
+ /* Update @start_bit to the new position. */
+ start_bit = (start_bit + 7) & ~7;
+ }
+
+ /* Loop until @left reaches zero. */
+ lastbyte = 0;
+ lastbyte_buf = NULL;
+ bit = left & 7;
+ do {
+ /* If there is a last partial byte... */
+ if (left > 0 && bit) {
+ lastbyte_pos = ((left + 7) >> 3) + firstbyte;
+ if (!lastbyte_pos) {
+ // FIXME: Eeek! BUG!
+ ntfs_log_trace("lastbyte is zero. Leaving "
+ "inconsistent metadata.\n");
+ err = EIO;
+ goto free_err_out;
+ }
+ /* and it is in the currently loaded bitmap window... */
+ if (lastbyte_pos <= bufsize) {
+ lastbyte_buf = buf + lastbyte_pos - 1;
+
+ /* read the byte in... */
+ br = ntfs_attr_pread(na, (start_bit + left) >>
+ 3, 1, lastbyte_buf);
+ if (br != 1) {
+ // FIXME: Eeek! We need rollback! (AIA)
+ ntfs_log_trace("Read of last byte "
+ "failed. Leaving "
+ "inconsistent "
+ "metadata.\n");
+ err = EIO;
+ goto free_err_out;
+ }
+ /* and set/clear the appropriate bits in it. */
+ while (bit && left--) {
+ if (value)
+ *lastbyte_buf |= 1 << --bit;
+ else
+ *lastbyte_buf &= ~(1 << --bit);
+ }
+ /* We don't want to come back here... */
+ bit = 0;
+ /* We have a last byte that we have handled. */
+ lastbyte = 1;
+ }
+ }
+
+ /* Write the prepared buffer to disk. */
+ tmp = (start_bit >> 3) - firstbyte;
+ br = ntfs_attr_pwrite(na, tmp, bufsize, buf);
+ if (br != bufsize) {
+ // FIXME: Eeek! We need rollback! (AIA)
+ ntfs_log_trace("Failed to write buffer to bitmap. "
+ "Leaving inconsistent metadata.\n");
+ err = EIO;
+ goto free_err_out;
+ }
+
+ /* Update counters. */
+ tmp = (bufsize - firstbyte - lastbyte) << 3;
+ if (firstbyte) {
+ firstbyte = 0;
+ /*
+ * Re-set the partial first byte so a subsequent write
+ * of the buffer does not have stale, incorrect bits.
+ */
+ *buf = value ? 0xff : 0;
+ }
+ start_bit += tmp;
+ left -= tmp;
+ if (bufsize > (tmp = (left + 7) >> 3))
+ bufsize = tmp;
+
+ if (lastbyte && left != 0) {
+ // FIXME: Eeek! BUG!
+ ntfs_log_trace("Last buffer but count is not zero (= "
+ "%lli). Leaving inconsistent metadata."
+ "\n", (long long)left);
+ err = EIO;
+ goto free_err_out;
+ }
+ } while (left > 0);
+
+ /* Update free clusters and MFT records. */
+ if (na == vol->mftbmp_na) {
+ if (value)
+ vol->nr_free_mft_records -= count;
+ else
+ vol->nr_free_mft_records += count;
+ }
+ if (na == vol->lcnbmp_na) {
+ if (value)
+ vol->nr_free_clusters -= count;
+ else
+ vol->nr_free_clusters += count;
+ }
+
+ /* Done! */
+ free(buf);
+ return 0;
+
+free_err_out:
+ free(buf);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_bitmap_set_run - set a run of bits in a bitmap
+ * @na: attribute containing the bitmap
+ * @start_bit: first bit to set
+ * @count: number of bits to set
+ *
+ * Set @count bits starting at bit @start_bit in the bitmap described by the
+ * attribute @na.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_bitmap_set_run(ntfs_attr *na, s64 start_bit, s64 count)
+{
+ return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 1);
+}
+
+/**
+ * ntfs_bitmap_clear_run - clear a run of bits in a bitmap
+ * @na: attribute containing the bitmap
+ * @start_bit: first bit to clear
+ * @count: number of bits to clear
+ *
+ * Clear @count bits starting at bit @start_bit in the bitmap described by the
+ * attribute @na.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_bitmap_clear_run(ntfs_attr *na, s64 start_bit, s64 count)
+{
+ ntfs_log_trace("Dealloc from bit 0x%llx, count 0x%llx.\n",
+ (long long)start_bit, (long long)count);
+
+ return ntfs_bitmap_set_bits_in_run(na, start_bit, count, 0);
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/bootsect.c b/usr/src/lib/libntfs/common/libntfs/bootsect.c
new file mode 100644
index 0000000000..3c4e9ca9b2
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/bootsect.c
@@ -0,0 +1,273 @@
+/**
+ * bootsect.c - Boot sector handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 Anton Altaparmakov
+ * Copyright (c) 2005 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "bootsect.h"
+#include "debug.h"
+#include "logging.h"
+
+/**
+ * ntfs_boot_sector_is_ntfs - check if buffer contains a valid ntfs boot sector
+ * @b: buffer containing putative boot sector to analyze
+ * @silent: if zero, output progress messages to stderr
+ *
+ * Check if the buffer @b contains a valid ntfs boot sector. The buffer @b
+ * must be at least 512 bytes in size.
+ *
+ * If @silent is zero, output progress messages to stderr. Otherwise, do not
+ * output any messages (except when configured with --enable-debug in which
+ * case warning/debug messages may be displayed).
+ *
+ * Return TRUE if @b contains a valid ntfs boot sector and FALSE if not.
+ */
+BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b,
+ const BOOL silent __attribute__((unused)))
+{
+ u32 i;
+
+ ntfs_log_debug("\nBeginning bootsector check...\n");
+
+ /*
+ * Check that checksum == sum of u32 values from b to the checksum
+ * field. If checksum is zero, no checking is done. We will work when
+ * the checksum test fails, since some utilities update the boot sector
+ * ignoring the checksum which leaves the checksum out-of-date. We
+ * report a warning if this is the case.
+ */
+ if ((void*)b < (void*)&b->checksum && b->checksum) {
+ u32 *u = (u32 *)b;
+ u32 *bi = (u32 *)(&b->checksum);
+
+ ntfs_log_debug("Calculating bootsector checksum... ");
+ for (i = 0; u < bi; ++u)
+ i += le32_to_cpup(u);
+ if (le32_to_cpu(b->checksum) && le32_to_cpu(b->checksum) != i) {
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("The NTFS bootsector contains an "
+ "incorrect checksum.");
+ } else
+ ntfs_log_debug("OK\n");
+ }
+
+ /* Check OEMidentifier is "NTFS " */
+ ntfs_log_debug("Checking OEMid... ");
+ if (b->oem_id != NTFS_SB_MAGIC) /* "NTFS " */
+ goto not_ntfs;
+ ntfs_log_debug("OK\n");
+
+ /* Check bytes per sector value is between 256 and 4096. */
+ ntfs_log_debug("Checking bytes per sector... ");
+ if (le16_to_cpu(b->bpb.bytes_per_sector) < 0x100 ||
+ le16_to_cpu(b->bpb.bytes_per_sector) > 0x1000)
+ goto not_ntfs;
+ ntfs_log_debug("OK\n");
+
+ /* Check sectors per cluster value is valid. */
+ ntfs_log_debug("Checking sectors per cluster... ");
+ switch (b->bpb.sectors_per_cluster) {
+ case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ ntfs_log_debug("OK\n");
+
+ /* Check the cluster size is not above 65536 bytes. */
+ ntfs_log_debug("Checking cluster size... ");
+ if ((u32)le16_to_cpu(b->bpb.bytes_per_sector) *
+ b->bpb.sectors_per_cluster > 0x10000)
+ goto not_ntfs;
+ ntfs_log_debug("OK\n");
+
+ /* Check reserved/unused fields are really zero. */
+ ntfs_log_debug("Checking reserved fields are zero... ");
+ if (le16_to_cpu(b->bpb.reserved_sectors) ||
+ le16_to_cpu(b->bpb.root_entries) ||
+ le16_to_cpu(b->bpb.sectors) ||
+ le16_to_cpu(b->bpb.sectors_per_fat) ||
+ le32_to_cpu(b->bpb.large_sectors) ||
+ b->bpb.fats)
+ goto not_ntfs;
+ ntfs_log_debug("OK\n");
+
+ /* Check clusters per file mft record value is valid. */
+ ntfs_log_debug("Checking clusters per mft record... ");
+ if ((u8)b->clusters_per_mft_record < 0xe1 ||
+ (u8)b->clusters_per_mft_record > 0xf7) {
+ switch (b->clusters_per_mft_record) {
+ case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ }
+ ntfs_log_debug("OK\n");
+
+ /* Check clusters per index block value is valid. */
+ ntfs_log_debug("Checking clusters per index block... ");
+ if ((u8)b->clusters_per_index_record < 0xe1 ||
+ (u8)b->clusters_per_index_record > 0xf7) {
+ switch (b->clusters_per_index_record) {
+ case 1: case 2: case 4: case 8: case 0x10: case 0x20: case 0x40:
+ break;
+ default:
+ goto not_ntfs;
+ }
+ }
+ ntfs_log_debug("OK\n");
+
+ if (b->end_of_sector_marker != cpu_to_le16(0xaa55))
+ ntfs_log_debug("Warning: Bootsector has invalid end of sector "
+ "marker.\n");
+
+ ntfs_log_debug("Bootsector check completed successfully.\n");
+ return TRUE;
+not_ntfs:
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("Bootsector check failed. Aborting...\n");
+ return FALSE;
+}
+
+/**
+ * ntfs_boot_sector_parse - setup an ntfs volume from an ntfs boot sector
+ * @vol: ntfs_volume to setup
+ * @bs: buffer containing ntfs boot sector to parse
+ *
+ * Parse the ntfs bootsector @bs and setup the ntfs volume @vol with the
+ * obtained values.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code EINVAL.
+ */
+int ntfs_boot_sector_parse(ntfs_volume *vol, const NTFS_BOOT_SECTOR *bs)
+{
+ u8 sectors_per_cluster;
+ s8 c;
+
+ /* We return -1 with errno = EINVAL on error. */
+ errno = EINVAL;
+
+ vol->sector_size = le16_to_cpu(bs->bpb.bytes_per_sector);
+ vol->sector_size_bits = ffs(vol->sector_size) - 1;
+ ntfs_log_debug("SectorSize = 0x%x\n", vol->sector_size);
+ ntfs_log_debug("SectorSizeBits = %u\n", vol->sector_size_bits);
+ /*
+ * The bounds checks on mft_lcn and mft_mirr_lcn (i.e. them being
+ * below or equal the number_of_clusters) really belong in the
+ * ntfs_boot_sector_is_ntfs but in this way we can just do this once.
+ */
+ sectors_per_cluster = bs->bpb.sectors_per_cluster;
+ ntfs_log_debug("NumberOfSectors = %lli\n",
+ sle64_to_cpu(bs->number_of_sectors));
+ ntfs_log_debug("SectorsPerCluster = 0x%x\n", sectors_per_cluster);
+ if (sectors_per_cluster & (sectors_per_cluster - 1)) {
+ ntfs_log_debug("Error: %s is not a valid NTFS partition! "
+ "sectors_per_cluster is not a power of 2.\n",
+ vol->u.dev->d_name);
+ return -1;
+ }
+ vol->nr_clusters = sle64_to_cpu(bs->number_of_sectors) >>
+ (ffs(sectors_per_cluster) - 1);
+
+ vol->mft_lcn = sle64_to_cpu(bs->mft_lcn);
+ vol->mftmirr_lcn = sle64_to_cpu(bs->mftmirr_lcn);
+ ntfs_log_debug("MFT LCN = 0x%llx\n", vol->mft_lcn);
+ ntfs_log_debug("MFTMirr LCN = 0x%llx\n", vol->mftmirr_lcn);
+ if (vol->mft_lcn > vol->nr_clusters ||
+ vol->mftmirr_lcn > vol->nr_clusters) {
+ ntfs_log_debug("Error: %s is not a valid NTFS partition!\n",
+ vol->u.dev->d_name);
+ ntfs_log_debug("($Mft LCN or $MftMirr LCN is greater than the "
+ "number of clusters!)\n");
+ return -1;
+ }
+ vol->cluster_size = sectors_per_cluster * vol->sector_size;
+ if (vol->cluster_size & (vol->cluster_size - 1)) {
+ ntfs_log_debug("Error: %s is not a valid NTFS partition! "
+ "cluster_size is not a power of 2.\n",
+ vol->u.dev->d_name);
+ return -1;
+ }
+ vol->cluster_size_bits = ffs(vol->cluster_size) - 1;
+ /*
+ * Need to get the clusters per mft record and handle it if it is
+ * negative. Then calculate the mft_record_size. A value of 0x80 is
+ * illegal, thus signed char is actually ok!
+ */
+ c = bs->clusters_per_mft_record;
+ ntfs_log_debug("ClusterSize = 0x%x\n", (unsigned)vol->cluster_size);
+ ntfs_log_debug("ClusterSizeBits = %u\n", vol->cluster_size_bits);
+ ntfs_log_debug("ClustersPerMftRecord = 0x%x\n", c);
+ /*
+ * When clusters_per_mft_record is negative, it means that it is to
+ * be taken to be the negative base 2 logarithm of the mft_record_size
+ * min bytes. Then:
+ * mft_record_size = 2^(-clusters_per_mft_record) bytes.
+ */
+ if (c < 0)
+ vol->mft_record_size = 1 << -c;
+ else
+ vol->mft_record_size = c << vol->cluster_size_bits;
+ if (vol->mft_record_size & (vol->mft_record_size - 1)) {
+ ntfs_log_debug("Error: %s is not a valid NTFS partition! "
+ "mft_record_size is not a power of 2.\n",
+ vol->u.dev->d_name);
+ return -1;
+ }
+ vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
+ ntfs_log_debug("MftRecordSize = 0x%x\n",
+ (unsigned)vol->mft_record_size);
+ ntfs_log_debug("MftRecordSizeBits = %u\n", vol->mft_record_size_bits);
+ /* Same as above for INDX record. */
+ c = bs->clusters_per_index_record;
+ ntfs_log_debug("ClustersPerINDXRecord = 0x%x\n", c);
+ if (c < 0)
+ vol->indx_record_size = 1 << -c;
+ else
+ vol->indx_record_size = c << vol->cluster_size_bits;
+ vol->indx_record_size_bits = ffs(vol->indx_record_size) - 1;
+ ntfs_log_debug("INDXRecordSize = 0x%x\n",
+ (unsigned)vol->indx_record_size);
+ ntfs_log_debug("INDXRecordSizeBits = %u\n", vol->indx_record_size_bits);
+ /*
+ * Windows cares only about first 4 records in $MFTMirr and inores
+ * everything beyend them.
+ */
+ vol->mftmirr_size = 4;
+ return 0;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/collate.c b/usr/src/lib/libntfs/common/libntfs/collate.c
new file mode 100644
index 0000000000..a63c405d98
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/collate.c
@@ -0,0 +1,219 @@
+/**
+ * collate.c - NTFS collation handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004 Anton Altaparmakov
+ * Copyright (c) 2005 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "collate.h"
+#include "debug.h"
+#include "unistr.h"
+#include "logging.h"
+
+/**
+ * ntfs_collate_binary - Which of two binary objects should be listed first
+ * @vol: unused
+ * @data1:
+ * @data1_len:
+ * @data2:
+ * @data2_len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_collate_binary(ntfs_volume *vol __attribute__((unused)),
+ const void *data1, size_t data1_len,
+ const void *data2, size_t data2_len)
+{
+ int rc;
+
+ ntfs_log_trace("Entering.\n");
+ rc = memcmp(data1, data2, min(data1_len, data2_len));
+ if (!rc && (data1_len != data2_len)) {
+ if (data1_len < data2_len)
+ rc = -1;
+ else
+ rc = 1;
+ }
+ ntfs_log_trace("Done, returning %i.\n", rc);
+ return rc;
+}
+
+/**
+ * ntfs_collate_ntofs_ulong - Which of two long ints should be listed first
+ * @vol: unused
+ * @data1:
+ * @data1_len:
+ * @data2:
+ * @data2_len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_collate_ntofs_ulong(ntfs_volume *vol __attribute__((unused)),
+ const void *data1, size_t data1_len,
+ const void *data2, size_t data2_len)
+{
+ int rc;
+ u32 d1, d2;
+
+ ntfs_log_trace("Entering.\n");
+ if (data1_len != data2_len || data1_len != 4) {
+ ntfs_log_error("data1_len or/and data2_len not equal to 4.\n");
+ return NTFS_COLLATION_ERROR;
+ }
+ d1 = le32_to_cpup(data1);
+ d2 = le32_to_cpup(data2);
+ if (d1 < d2)
+ rc = -1;
+ else {
+ if (d1 == d2)
+ rc = 0;
+ else
+ rc = 1;
+ }
+ ntfs_log_trace("Done, returning %i.\n", rc);
+ return rc;
+}
+
+/**
+ * ntfs_collate_file_name - Which of two filenames should be listed first
+ * @vol:
+ * @data1:
+ * @data1_len: unused
+ * @data2:
+ * @data2_len: unused
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_collate_file_name(ntfs_volume *vol,
+ const void *data1, size_t data1_len __attribute__((unused)),
+ const void *data2, size_t data2_len __attribute__((unused)))
+{
+ int rc;
+
+ ntfs_log_trace("Entering.\n");
+ rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR,
+ IGNORE_CASE, vol->upcase, vol->upcase_len);
+ if (!rc)
+ rc = ntfs_file_values_compare(data1, data2,
+ NTFS_COLLATION_ERROR, CASE_SENSITIVE,
+ vol->upcase, vol->upcase_len);
+ ntfs_log_trace("Done, returning %i.\n", rc);
+ return rc;
+}
+
+typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, size_t,
+ const void *, size_t);
+
+static ntfs_collate_func_t ntfs_do_collate0x0[3] = {
+ ntfs_collate_binary,
+ ntfs_collate_file_name,
+ NULL/*ntfs_collate_unicode_string*/,
+};
+
+static ntfs_collate_func_t ntfs_do_collate0x1[4] = {
+ ntfs_collate_ntofs_ulong,
+ NULL/*ntfs_collate_ntofs_sid*/,
+ NULL/*ntfs_collate_ntofs_security_hash*/,
+ NULL/*ntfs_collate_ntofs_ulongs*/,
+};
+
+/**
+ * ntfs_is_collation_rule_supported - Check if a collation rule is implemented.
+ * @cr: The to-be-checked collation rule
+ *
+ * Use this function to know if @cr is supported by libntfs.
+ *
+ * 7 collation rules are known to be supported by NTFS as defined
+ * in layout.h. However, libntfs only support 3 of them ATM.
+ *
+ * Return TRUE if @cr is supported. FALSE otherwise.
+ */
+BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr)
+{
+ return (cr == COLLATION_BINARY || cr == COLLATION_NTOFS_ULONG ||
+ cr == COLLATION_FILE_NAME);
+ /*
+ * FIXME: At the moment we only support COLLATION_BINARY,
+ * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME.
+ * The correct future implementation of this function should be:
+ *
+ * u32 i = le32_to_cpu(cr);
+ * return ((i <= 0x02) || ((i >= 0x10) && (i <= 0x13)));
+ */
+}
+
+/**
+ * ntfs_collate - collate two data items using a specified collation rule
+ * @vol: ntfs volume to which the data items belong
+ * @cr: collation rule to use when comparing the items
+ * @data1: first data item to collate
+ * @data1_len: length in bytes of @data1
+ * @data2: second data item to collate
+ * @data2_len: length in bytes of @data2
+ *
+ * Collate the two data items @data1 and @data2 using the collation rule @cr
+ * and return -1, 0, or 1 if @data1 is found, respectively, to collate before,
+ * to match, or to collate after @data2.
+ *
+ * For speed we use the collation rule @cr as an index into two tables of
+ * function pointers to call the appropriate collation function.
+ *
+ * Return NTFS_COLLATION_ERROR if error occurred.
+ */
+int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr,
+ const void *data1, size_t data1_len,
+ const void *data2, size_t data2_len)
+{
+ u32 i;
+
+ ntfs_log_trace("Entering.\n");
+ if (!vol || !data1 || !data2) {
+ ntfs_log_error("Invalid arguments passed.\n");
+ return NTFS_COLLATION_ERROR;
+ }
+
+ if (!ntfs_is_collation_rule_supported(cr))
+ goto err;
+ i = le32_to_cpu(cr);
+ if (i <= 0x02)
+ return ntfs_do_collate0x0[i](vol, data1, data1_len,
+ data2, data2_len);
+ if (i < 0x10)
+ goto err;
+ i -= 0x10;
+ if (i <= 3)
+ return ntfs_do_collate0x1[i](vol, data1, data1_len,
+ data2, data2_len);
+err:
+ ntfs_log_debug("Unknown collation rule.\n");
+ return NTFS_COLLATION_ERROR;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/compat.c b/usr/src/lib/libntfs/common/libntfs/compat.c
new file mode 100644
index 0000000000..acdf4db7ce
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/compat.c
@@ -0,0 +1,73 @@
+/**
+ * compat.c - Tweaks for Windows compatibility
+ *
+ * Copyright (c) 2002 Richard Russon
+ * Copyright (c) 2002-2004 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
+ */
+
+#ifdef WINDOWS
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "compat.h"
+
+/* TODO: Add check for FFS in the configure script... (AIA) */
+
+#ifndef HAVE_FFS
+/**
+ * ffs - Find the first set bit in an int
+ * @x:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+int ffs(int x)
+{
+ int r = 1;
+
+ if (!x)
+ return 0;
+ if (!(x & 0xffff)) {
+ x >>= 16;
+ r += 16;
+ }
+ if (!(x & 0xff)) {
+ x >>= 8;
+ r += 8;
+ }
+ if (!(x & 0xf)) {
+ x >>= 4;
+ r += 4;
+ }
+ if (!(x & 3)) {
+ x >>= 2;
+ r += 2;
+ }
+ if (!(x & 1)) {
+ x >>= 1;
+ r += 1;
+ }
+ return r;
+}
+#endif /* HAVE_FFS */
+
+#endif /* WINDOWS */
+
diff --git a/usr/src/lib/libntfs/common/libntfs/compress.c b/usr/src/lib/libntfs/common/libntfs/compress.c
new file mode 100644
index 0000000000..ed04c3e829
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/compress.c
@@ -0,0 +1,552 @@
+/**
+ * compress.c - Compressed attribute handling code. Part of the Linux-NTFS
+ * project.
+ *
+ * Copyright (c) 2004-2005 Anton Altaparmakov
+ * Copyright (c) 2005 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "attrib.h"
+#include "debug.h"
+#include "volume.h"
+#include "types.h"
+#include "layout.h"
+#include "runlist.h"
+#include "compress.h"
+#include "logging.h"
+
+/**
+ * enum ntfs_compression_constants - constants used in the compression code
+ */
+typedef enum {
+ /* Token types and access mask. */
+ NTFS_SYMBOL_TOKEN = 0,
+ NTFS_PHRASE_TOKEN = 1,
+ NTFS_TOKEN_MASK = 1,
+
+ /* Compression sub-block constants. */
+ NTFS_SB_SIZE_MASK = 0x0fff,
+ NTFS_SB_SIZE = 0x1000,
+ NTFS_SB_IS_COMPRESSED = 0x8000,
+} ntfs_compression_constants;
+
+/**
+ * ntfs_decompress - decompress a compression block into an array of pages
+ * @dest: buffer to which to write the decompressed data
+ * @dest_size: size of buffer @dest in bytes
+ * @cb_start: compression block to decompress
+ * @cb_size: size of compression block @cb_start in bytes
+ *
+ * This decompresses the compression block @cb_start into the destination
+ * buffer @dest.
+ *
+ * @cb_start is a pointer to the compression block which needs decompressing
+ * and @cb_size is the size of @cb_start in bytes (8-64kiB).
+ *
+ * Return 0 if success or -EOVERFLOW on error in the compressed stream.
+ */
+static int ntfs_decompress(u8 *dest, const u32 dest_size,
+ u8 *const cb_start, const u32 cb_size)
+{
+ /*
+ * Pointers into the compressed data, i.e. the compression block (cb),
+ * and the therein contained sub-blocks (sb).
+ */
+ u8 *cb_end = cb_start + cb_size; /* End of cb. */
+ u8 *cb = cb_start; /* Current position in cb. */
+ u8 *cb_sb_start = cb; /* Beginning of the current sb in the cb. */
+ u8 *cb_sb_end; /* End of current sb / beginning of next sb. */
+ /* Variables for uncompressed data / destination. */
+ u8 *dest_end = dest + dest_size; /* End of dest buffer. */
+ u8 *dest_sb_start; /* Start of current sub-block in dest. */
+ u8 *dest_sb_end; /* End of current sb in dest. */
+ /* Variables for tag and token parsing. */
+ u8 tag; /* Current tag. */
+ int token; /* Loop counter for the eight tokens in tag. */
+
+ ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size);
+do_next_sb:
+ ntfs_log_debug("Beginning sub-block at offset = 0x%x in the cb.\n",
+ cb - cb_start);
+ /*
+ * Have we reached the end of the compression block or the end of the
+ * decompressed data? The latter can happen for example if the current
+ * position in the compression block is one byte before its end so the
+ * first two checks do not detect it.
+ */
+ if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) {
+ ntfs_log_debug("Completed. Returning success (0).\n");
+ return 0;
+ }
+ /* Setup offset for the current sub-block destination. */
+ dest_sb_start = dest;
+ dest_sb_end = dest + NTFS_SB_SIZE;
+ /* Check that we are still within allowed boundaries. */
+ if (dest_sb_end > dest_end)
+ goto return_overflow;
+ /* Does the minimum size of a compressed sb overflow valid range? */
+ if (cb + 6 > cb_end)
+ goto return_overflow;
+ /* Setup the current sub-block source pointers and validate range. */
+ cb_sb_start = cb;
+ cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
+ + 3;
+ if (cb_sb_end > cb_end)
+ goto return_overflow;
+ /* Now, we are ready to process the current sub-block (sb). */
+ if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
+ ntfs_log_debug("Found uncompressed sub-block.\n");
+ /* This sb is not compressed, just copy it into destination. */
+ /* Advance source position to first data byte. */
+ cb += 2;
+ /* An uncompressed sb must be full size. */
+ if (cb_sb_end - cb != NTFS_SB_SIZE)
+ goto return_overflow;
+ /* Copy the block and advance the source position. */
+ memcpy(dest, cb, NTFS_SB_SIZE);
+ cb += NTFS_SB_SIZE;
+ /* Advance destination position to next sub-block. */
+ dest += NTFS_SB_SIZE;
+ goto do_next_sb;
+ }
+ ntfs_log_debug("Found compressed sub-block.\n");
+ /* This sb is compressed, decompress it into destination. */
+ /* Forward to the first tag in the sub-block. */
+ cb += 2;
+do_next_tag:
+ if (cb == cb_sb_end) {
+ /* Check if the decompressed sub-block was not full-length. */
+ if (dest < dest_sb_end) {
+ int nr_bytes = dest_sb_end - dest;
+
+ ntfs_log_debug("Filling incomplete sub-block with zeroes.\n");
+ /* Zero remainder and update destination position. */
+ memset(dest, 0, nr_bytes);
+ dest += nr_bytes;
+ }
+ /* We have finished the current sub-block. */
+ goto do_next_sb;
+ }
+ /* Check we are still in range. */
+ if (cb > cb_sb_end || dest > dest_sb_end)
+ goto return_overflow;
+ /* Get the next tag and advance to first token. */
+ tag = *cb++;
+ /* Parse the eight tokens described by the tag. */
+ for (token = 0; token < 8; token++, tag >>= 1) {
+ u16 lg, pt, length, max_non_overlap;
+ register u16 i;
+ u8 *dest_back_addr;
+
+ /* Check if we are done / still in range. */
+ if (cb >= cb_sb_end || dest > dest_sb_end)
+ break;
+ /* Determine token type and parse appropriately.*/
+ if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
+ /*
+ * We have a symbol token, copy the symbol across, and
+ * advance the source and destination positions.
+ */
+ *dest++ = *cb++;
+ /* Continue with the next token. */
+ continue;
+ }
+ /*
+ * We have a phrase token. Make sure it is not the first tag in
+ * the sb as this is illegal and would confuse the code below.
+ */
+ if (dest == dest_sb_start)
+ goto return_overflow;
+ /*
+ * Determine the number of bytes to go back (p) and the number
+ * of bytes to copy (l). We use an optimized algorithm in which
+ * we first calculate log2(current destination position in sb),
+ * which allows determination of l and p in O(1) rather than
+ * O(n). We just need an arch-optimized log2() function now.
+ */
+ lg = 0;
+ for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
+ lg++;
+ /* Get the phrase token into i. */
+ pt = le16_to_cpup((u16*)cb);
+ /*
+ * Calculate starting position of the byte sequence in
+ * the destination using the fact that p = (pt >> (12 - lg)) + 1
+ * and make sure we don't go too far back.
+ */
+ dest_back_addr = dest - (pt >> (12 - lg)) - 1;
+ if (dest_back_addr < dest_sb_start)
+ goto return_overflow;
+ /* Now calculate the length of the byte sequence. */
+ length = (pt & (0xfff >> lg)) + 3;
+ /* Verify destination is in range. */
+ if (dest + length > dest_sb_end)
+ goto return_overflow;
+ /* The number of non-overlapping bytes. */
+ max_non_overlap = dest - dest_back_addr;
+ if (length <= max_non_overlap) {
+ /* The byte sequence doesn't overlap, just copy it. */
+ memcpy(dest, dest_back_addr, length);
+ /* Advance destination pointer. */
+ dest += length;
+ } else {
+ /*
+ * The byte sequence does overlap, copy non-overlapping
+ * part and then do a slow byte by byte copy for the
+ * overlapping part. Also, advance the destination
+ * pointer.
+ */
+ memcpy(dest, dest_back_addr, max_non_overlap);
+ dest += max_non_overlap;
+ dest_back_addr += max_non_overlap;
+ length -= max_non_overlap;
+ while (length--)
+ *dest++ = *dest_back_addr++;
+ }
+ /* Advance source position and continue with the next token. */
+ cb += 2;
+ }
+ /* No tokens left in the current tag. Continue with the next tag. */
+ goto do_next_tag;
+return_overflow:
+ ntfs_log_debug("Failed. Returning -EOVERFLOW.\n");
+ errno = EOVERFLOW;
+ return -1;
+}
+
+/**
+ * ntfs_is_cb_compressed - internal function, do not use
+ *
+ * This is a very specialised function determining if a cb is compressed or
+ * uncompressed. It is assumed that checking for a sparse cb has already been
+ * performed and that the cb is not sparse. It makes all sorts of other
+ * assumptions as well and hence it is not useful anywhere other than where it
+ * is used at the moment. Please, do not make this function available for use
+ * outside of compress.c as it is bound to confuse people and not do what they
+ * want.
+ *
+ * Return TRUE on errors so that the error will be detected later on in the
+ * code. Might be a bit confusing to debug but there really should never be
+ * errors coming from here.
+ */
+static BOOL ntfs_is_cb_compressed(ntfs_attr *na,
+ runlist_element *rl, VCN cb_start_vcn, int cb_clusters)
+{
+ /*
+ * The simplest case: the run starting at @cb_start_vcn contains
+ * @cb_clusters clusters which are all not sparse, thus the cb is not
+ * compressed.
+ */
+restart:
+ cb_clusters -= rl->length - (cb_start_vcn - rl->vcn);
+ while (cb_clusters > 0) {
+ /* Go to the next run. */
+ rl++;
+ /* Map the next runlist fragment if it is not mapped. */
+ if (rl->lcn < LCN_HOLE || !rl->length) {
+ cb_start_vcn = rl->vcn;
+ rl = ntfs_attr_find_vcn(na, rl->vcn);
+ if (!rl || rl->lcn < LCN_HOLE || !rl->length)
+ return TRUE;
+ /*
+ * If the runs were merged need to deal with the
+ * resulting partial run so simply restart.
+ */
+ if (rl->vcn < cb_start_vcn)
+ goto restart;
+ }
+ /* If the current run is sparse, the cb is compressed. */
+ if (rl->lcn == LCN_HOLE)
+ return TRUE;
+ /* If the whole cb is not sparse, it is not compressed. */
+ if (rl->length >= cb_clusters)
+ return FALSE;
+ cb_clusters -= rl->length;
+ };
+ /* All cb_clusters were not sparse thus the cb is not compressed. */
+ return FALSE;
+}
+
+/**
+ * ntfs_compressed_attr_pread - read from a compressed attribute
+ * @na: ntfs attribute to read from
+ * @pos: byte position in the attribute to begin reading from
+ * @count: number of bytes to read
+ * @b: output data buffer
+ *
+ * NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead.
+ *
+ * This function will read @count bytes starting at offset @pos from the
+ * compressed ntfs attribute @na into the data buffer @b.
+ *
+ * On success, return the number of successfully read bytes. If this number
+ * is lower than @count this means that the read reached end of file or that
+ * an error was encountered during the read so that the read is partial.
+ * 0 means end of file or nothing was read (also return 0 when @count is 0).
+ *
+ * On error and nothing has been read, return -1 with errno set appropriately
+ * to the return code of ntfs_pread(), or to EINVAL in case of invalid
+ * arguments.
+ */
+s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
+{
+ s64 br, to_read, ofs, total, total2;
+ u64 cb_size_mask;
+ VCN start_vcn, vcn, end_vcn;
+ ntfs_volume *vol;
+ runlist_element *rl;
+ u8 *dest, *cb, *cb_pos, *cb_end;
+ u32 cb_size;
+ int err;
+ unsigned int nr_cbs, cb_clusters;
+
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n",
+ (unsigned long long)na->ni->mft_no, na->type,
+ (long long)pos, (long long)count);
+ if (!na || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b ||
+ pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ /*
+ * Encrypted attributes are not supported. We return access denied,
+ * which is what Windows NT4 does, too.
+ */
+ if (NAttrEncrypted(na)) {
+ errno = EACCES;
+ return -1;
+ }
+ if (!count)
+ return 0;
+ /* Truncate reads beyond end of attribute. */
+ if (pos + count > na->data_size) {
+ if (pos >= na->data_size) {
+ return 0;
+ }
+ count = na->data_size - pos;
+ }
+ /* If it is a resident attribute, simply use ntfs_attr_pread(). */
+ if (!NAttrNonResident(na))
+ return ntfs_attr_pread(na, pos, count, b);
+ total = total2 = 0;
+ /* Zero out reads beyond initialized size. */
+ if (pos + count > na->initialized_size) {
+ if (pos >= na->initialized_size) {
+ memset(b, 0, count);
+ return count;
+ }
+ total2 = pos + count - na->initialized_size;
+ count -= total2;
+ memset((u8*)b + count, 0, total2);
+ }
+ vol = na->ni->vol;
+ cb_size = na->compression_block_size;
+ cb_size_mask = cb_size - 1UL;
+ cb_clusters = na->compression_block_clusters;
+
+ /* Need a temporary buffer for each loaded compression block. */
+ cb = ntfs_malloc(cb_size);
+ if (!cb)
+ return -1;
+
+ /* Need a temporary buffer for each uncompressed block. */
+ dest = ntfs_malloc(cb_size);
+ if (!dest) {
+ err = errno;
+ free(cb);
+ errno = err;
+ return -1;
+ }
+ /*
+ * The first vcn in the first compression block (cb) which we need to
+ * decompress.
+ */
+ start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits;
+ /* Offset in the uncompressed cb at which to start reading data. */
+ ofs = pos & cb_size_mask;
+ /*
+ * The first vcn in the cb after the last cb which we need to
+ * decompress.
+ */
+ end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >>
+ vol->cluster_size_bits;
+ /* Number of compression blocks (cbs) in the wanted vcn range. */
+ nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >>
+ na->compression_block_size_bits;
+ cb_end = cb + cb_size;
+do_next_cb:
+ nr_cbs--;
+ cb_pos = cb;
+ vcn = start_vcn;
+ start_vcn += cb_clusters;
+
+ /* Check whether the compression block is sparse. */
+ rl = ntfs_attr_find_vcn(na, vcn);
+ if (!rl || rl->lcn < LCN_HOLE) {
+ free(cb);
+ free(dest);
+ if (total)
+ return total;
+ /* FIXME: Do we want EIO or the error code? (AIA) */
+ errno = EIO;
+ return -1;
+ }
+ if (rl->lcn == LCN_HOLE) {
+ /* Sparse cb, zero out destination range overlapping the cb. */
+ ntfs_log_debug("Found sparse compression block.\n");
+ to_read = min(count, cb_size - ofs);
+ memset(b, 0, to_read);
+ ofs = 0;
+ total += to_read;
+ count -= to_read;
+ b = (u8*)b + to_read;
+ } else if (!ntfs_is_cb_compressed(na, rl, vcn, cb_clusters)) {
+ s64 tdata_size, tinitialized_size;
+ /*
+ * Uncompressed cb, read it straight into the destination range
+ * overlapping the cb.
+ */
+ ntfs_log_debug("Found uncompressed compression block.\n");
+ /*
+ * Read the uncompressed data into the destination buffer.
+ * NOTE: We cheat a little bit here by marking the attribute as
+ * not compressed in the ntfs_attr structure so that we can
+ * read the data by simply using ntfs_attr_pread(). (-8
+ * NOTE: we have to modify data_size and initialized_size
+ * temporarily as well...
+ */
+ to_read = min(count, cb_size - ofs);
+ ofs += vcn << vol->cluster_size_bits;
+ NAttrClearCompressed(na);
+ tdata_size = na->data_size;
+ tinitialized_size = na->initialized_size;
+ na->data_size = na->initialized_size = na->allocated_size;
+ do {
+ br = ntfs_attr_pread(na, ofs, to_read, b);
+ if (br < 0) {
+ err = errno;
+ na->data_size = tdata_size;
+ na->initialized_size = tinitialized_size;
+ NAttrSetCompressed(na);
+ free(cb);
+ free(dest);
+ if (total)
+ return total;
+ errno = err;
+ return br;
+ }
+ total += br;
+ count -= br;
+ b = (u8*)b + br;
+ to_read -= br;
+ ofs += br;
+ } while (to_read > 0);
+ na->data_size = tdata_size;
+ na->initialized_size = tinitialized_size;
+ NAttrSetCompressed(na);
+ ofs = 0;
+ } else {
+ s64 tdata_size, tinitialized_size;
+
+ /*
+ * Compressed cb, decompress it into the temporary buffer, then
+ * copy the data to the destination range overlapping the cb.
+ */
+ ntfs_log_debug("Found compressed compression block.\n");
+ /*
+ * Read the compressed data into the temporary buffer.
+ * NOTE: We cheat a little bit here by marking the attribute as
+ * not compressed in the ntfs_attr structure so that we can
+ * read the raw, compressed data by simply using
+ * ntfs_attr_pread(). (-8
+ * NOTE: We have to modify data_size and initialized_size
+ * temporarily as well...
+ */
+ to_read = cb_size;
+ NAttrClearCompressed(na);
+ tdata_size = na->data_size;
+ tinitialized_size = na->initialized_size;
+ na->data_size = na->initialized_size = na->allocated_size;
+ do {
+ br = ntfs_attr_pread(na,
+ (vcn << vol->cluster_size_bits) +
+ (cb_pos - cb), to_read, cb_pos);
+ if (br < 0) {
+ err = errno;
+ na->data_size = tdata_size;
+ na->initialized_size = tinitialized_size;
+ NAttrSetCompressed(na);
+ free(cb);
+ free(dest);
+ if (total)
+ return total;
+ errno = err;
+ return br;
+ }
+ cb_pos += br;
+ to_read -= br;
+ } while (to_read > 0);
+ na->data_size = tdata_size;
+ na->initialized_size = tinitialized_size;
+ NAttrSetCompressed(na);
+ /* Just a precaution. */
+ if (cb_pos + 2 <= cb_end)
+ *(u16*)cb_pos = 0;
+ ntfs_log_debug("Successfully read the compression block.\n");
+ if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) {
+ err = errno;
+ free(cb);
+ free(dest);
+ if (total)
+ return total;
+ errno = err;
+ return -1;
+ }
+ to_read = min(count, cb_size - ofs);
+ memcpy(b, dest + ofs, to_read);
+ total += to_read;
+ count -= to_read;
+ b = (u8*)b + to_read;
+ ofs = 0;
+ }
+ /* Do we have more work to do? */
+ if (nr_cbs)
+ goto do_next_cb;
+ /* We no longer need the buffers. */
+ free(cb);
+ free(dest);
+ /* Return number of bytes read. */
+ return total + total2;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/crypto.c b/usr/src/lib/libntfs/common/libntfs/crypto.c
new file mode 100644
index 0000000000..9478f05301
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/crypto.c
@@ -0,0 +1,1518 @@
+/**
+ * crypto.c - Routines for dealing with encrypted files. Part of the
+ * Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Yuval Fledel
+ * Copyright (c) 2005-2007 Anton Altaparmakov
+ * Copyright (c) 2007 Yura Pakhuchiy
+ *
+ * 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
+ * distribution in the file COPYING); if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * TODO: Cleanup this file. Write nice descriptions for non-exported functions
+ * and maybe clean up namespace (not necessary for all functions to belong to
+ * ntfs_crypto, we can have ntfs_fek, ntfs_rsa, etc.., but there should be
+ * maximum 2-3 namespaces, not every function begins with it own namespace
+ * like now).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "attrib.h"
+#include "types.h"
+#include "volume.h"
+#include "debug.h"
+#include "dir.h"
+#include "layout.h"
+#include "crypto.h"
+
+#ifdef ENABLE_CRYPTO
+
+#include <gcrypt.h>
+#include <gnutls/pkcs12.h>
+#include <gnutls/x509.h>
+
+#include <libconfig.h>
+
+#define NTFS_CONFIG_PATH_SYSTEM "/etc/libntfs/config"
+#define NTFS_CONFIG_PATH_USER ".libntfs/config"
+
+#define NTFS_SHA1_THUMBPRINT_SIZE 0x14
+
+#define NTFS_CRED_TYPE_CERT_THUMBPRINT const_cpu_to_le32(3)
+
+#define NTFS_EFS_CERT_PURPOSE_OID_DDF "1.3.6.1.4.1.311.10.3.4"
+#define NTFS_EFS_CERT_PURPOSE_OID_DRF "1.3.6.1.4.1.311.10.3.4.1"
+
+#define NTFS_EFS_SECTOR_SIZE 512
+
+typedef enum {
+ DF_TYPE_UNKNOWN,
+ DF_TYPE_DDF,
+ DF_TYPE_DRF,
+} NTFS_DF_TYPES;
+
+/**
+ * enum NTFS_CRYPTO_ALGORITHMS - List of crypto algorithms used by EFS (32 bit)
+ *
+ * To choose which one is used in Windows, create or set the REG_DWORD registry
+ * key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\EFS\
+ * AlgorithmID to the value of your chosen crypto algorithm, e.g. to use DesX,
+ * set AlgorithmID to 0x6604.
+ *
+ * Note that the Windows versions I have tried so far (all are high crypto
+ * enabled) ignore the AlgorithmID value if it is not one of CALG_3DES,
+ * CALG_DESX, or CALG_AES_256, i.e. you cannot select CALG_DES at all using
+ * this registry key. It would be interesting to check out encryption on one
+ * of the "crippled" crypto Windows versions...
+ */
+typedef enum {
+ CALG_DES = const_cpu_to_le32(0x6601),
+ /* If not one of the below three, fall back to standard Des. */
+ CALG_3DES = const_cpu_to_le32(0x6603),
+ CALG_DESX = const_cpu_to_le32(0x6604),
+ CALG_AES_256 = const_cpu_to_le32(0x6610),
+} NTFS_CRYPTO_ALGORITHMS;
+
+/**
+ * struct ntfs_fek - Decrypted, in-memory file encryption key.
+ */
+struct _ntfs_fek {
+ gcry_cipher_hd_t gcry_cipher_hd;
+ le32 alg_id;
+ u8 *key_data;
+ gcry_cipher_hd_t *des_gcry_cipher_hd_ptr;
+};
+
+typedef struct _ntfs_fek ntfs_fek;
+
+struct _ntfs_crypto_attr {
+ ntfs_fek *fek;
+};
+
+typedef struct {
+ u64 in_whitening, out_whitening;
+ gcry_cipher_hd_t gcry_cipher_hd;
+} ntfs_desx_ctx;
+
+ntfschar NTFS_EFS[5] = {
+ const_cpu_to_le16('$'), const_cpu_to_le16('E'), const_cpu_to_le16('F'),
+ const_cpu_to_le16('S'), const_cpu_to_le16(0)
+};
+
+typedef struct {
+ gcry_sexp_t key;
+ NTFS_DF_TYPES df_type;
+ char thumbprint[NTFS_SHA1_THUMBPRINT_SIZE];
+} ntfs_rsa_private_key_t;
+
+/*
+ * Yes, global variables sucks, but we need to keep whether we performed
+ * gcrypt/gnutls global initialization and keep user's RSA keys.
+ */
+typedef struct {
+ int initialized;
+ int desx_alg_id;
+ gcry_module_t desx_module;
+ ntfs_rsa_private_key_t **rsa_key;
+ int nr_rsa_keys;
+} ntfs_crypto_ctx_t;
+
+static ntfs_crypto_ctx_t ntfs_crypto_ctx = {
+ .desx_alg_id = -1,
+ .desx_module = NULL,
+};
+
+/**
+ * ntfs_pkcs12_load_pfxfile
+ */
+static int ntfs_pkcs12_load_pfxfile(const char *keyfile, u8 **pfx,
+ unsigned *pfx_size)
+{
+ int f, to_read, total, attempts, br;
+ struct stat key_stat;
+
+ if (!keyfile || !pfx || !pfx_size) {
+ ntfs_log_error("You have to specify the key file, a pointer "
+ "to hold the key file contents, and a pointer "
+ "to hold the size of the key file contents.\n");
+ return -1;
+ }
+ f = open(keyfile, O_RDONLY);
+ if (f == -1) {
+ ntfs_log_perror("Failed to open key file");
+ return -1;
+ }
+ if (fstat(f, &key_stat) == -1) {
+ ntfs_log_perror("Failed to stat key file");
+ goto file_out;
+ }
+ if (!S_ISREG(key_stat.st_mode)) {
+ ntfs_log_error("Key file is not a regular file, cannot read "
+ "it.\n");
+ goto file_out;
+ }
+ if (!key_stat.st_size) {
+ ntfs_log_error("Key file has zero size.\n");
+ goto file_out;
+ }
+ *pfx = malloc(key_stat.st_size + 1);
+ if (!*pfx) {
+ ntfs_log_perror("Failed to allocate buffer for key file "
+ "contents");
+ goto file_out;
+ }
+ to_read = key_stat.st_size;
+ total = attempts = 0;
+ do {
+ br = read(f, *pfx + total, to_read);
+ if (br == -1) {
+ ntfs_log_perror("Failed to read from key file");
+ goto free_out;
+ }
+ if (!br)
+ attempts++;
+ to_read -= br;
+ total += br;
+ } while (to_read > 0 && attempts < 3);
+ close(f);
+ /* Make sure it is zero terminated. */
+ (*pfx)[key_stat.st_size] = 0;
+ *pfx_size = key_stat.st_size;
+ return 0;
+free_out:
+ free(*pfx);
+file_out:
+ close(f);
+ return -1;
+}
+
+/**
+ * ntfs_rsa_private_key_import_from_gnutls
+ */
+static gcry_sexp_t ntfs_rsa_private_key_import_from_gnutls(
+ gnutls_x509_privkey_t priv_key)
+{
+ int i, j;
+ size_t tmp_size;
+ gnutls_datum_t rd[6];
+ gcry_mpi_t rm[6];
+ gcry_sexp_t rsa_key;
+
+ /* Extract the RSA parameters from the GNU TLS private key. */
+ if (gnutls_x509_privkey_export_rsa_raw(priv_key, &rd[0], &rd[1],
+ &rd[2], &rd[3], &rd[4], &rd[5])) {
+ ntfs_log_error("Failed to export rsa parameters. (Is the "
+ "key an RSA private key?)\n");
+ return NULL;
+ }
+ /* Convert each RSA parameter to MPI format. */
+ for (i = 0; i < 6; i++) {
+ if (gcry_mpi_scan(&rm[i], GCRYMPI_FMT_USG, rd[i].data,
+ rd[i].size, &tmp_size) != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to convert RSA parameter %i "
+ "to mpi format (size %d)\n", i,
+ rd[i].size);
+ rsa_key = NULL;
+ break;
+ }
+ }
+ /* Release the no longer needed datum values. */
+ for (j = 0; j < 6; j++) {
+ if (rd[j].data && rd[j].size)
+ gnutls_free(rd[j].data);
+ }
+ /*
+ * Build the gcrypt private key, note libgcrypt uses p and q inversed
+ * to what gnutls uses.
+ */
+ if (i == 6 && gcry_sexp_build(&rsa_key, NULL,
+ "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+ rm[0], rm[1], rm[2], rm[4], rm[3], rm[5]) !=
+ GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to build RSA private key s-exp.\n");
+ rsa_key = NULL;
+ }
+ /* Release the no longer needed MPI values. */
+ for (j = 0; j < i; j++)
+ gcry_mpi_release(rm[j]);
+ return rsa_key;
+}
+
+/**
+ * ntfs_rsa_private_key_release
+ */
+static void ntfs_rsa_private_key_release(ntfs_rsa_private_key_t *rsa_key)
+{
+ if (rsa_key) {
+ if (rsa_key->key)
+ gcry_sexp_release(rsa_key->key);
+ free(rsa_key);
+ }
+}
+
+/**
+ * ntfs_pkcs12_extract_rsa_key
+ */
+static ntfs_rsa_private_key_t *ntfs_pkcs12_extract_rsa_key(u8 *pfx,
+ int pfx_size, const char *password)
+{
+ int err, bag_index, flags;
+ gnutls_datum_t dpfx, dkey;
+ gnutls_pkcs12_t pkcs12 = NULL;
+ gnutls_pkcs12_bag_t bag = NULL;
+ gnutls_x509_privkey_t pkey = NULL;
+ gnutls_x509_crt_t crt = NULL;
+ ntfs_rsa_private_key_t *rsa_key = NULL;
+ char purpose_oid[100];
+ size_t purpose_oid_size = sizeof(purpose_oid);
+ size_t tp_size;
+ BOOL have_thumbprint = FALSE;
+
+ rsa_key = malloc(sizeof(ntfs_rsa_private_key_t));
+ if (!rsa_key) {
+ ntfs_log_perror("%s", "ntfs_pkcs12_extract_rsa_key");
+ return NULL;
+ }
+ rsa_key->df_type = DF_TYPE_UNKNOWN;
+ rsa_key->key = NULL;
+ tp_size = sizeof(rsa_key->thumbprint);
+ /* Create a pkcs12 structure. */
+ err = gnutls_pkcs12_init(&pkcs12);
+ if (err) {
+ ntfs_log_error("Failed to initialize PKCS#12 structure: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ /* Convert the PFX file (DER format) to native pkcs12 format. */
+ dpfx.data = pfx;
+ dpfx.size = pfx_size;
+ err = gnutls_pkcs12_import(pkcs12, &dpfx, GNUTLS_X509_FMT_DER, 0);
+ if (err) {
+ ntfs_log_error("Failed to convert the PFX file from DER to "
+ "native PKCS#12 format: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ /*
+ * Verify that the password is correct and that the key file has not
+ * been tampered with. Note if the password has zero length and the
+ * verification fails, retry with password set to NULL. This is needed
+ * to get password less .pfx files generated with Windows XP SP1 (and
+ * probably earlier versions of Windows) to work.
+ */
+retry_verify:
+ err = gnutls_pkcs12_verify_mac(pkcs12, password);
+ if (err) {
+ if (err == GNUTLS_E_MAC_VERIFY_FAILED &&
+ password && !strlen(password)) {
+ password = NULL;
+ goto retry_verify;
+ }
+ ntfs_log_error("You are probably misspelled password to PFX "
+ "file.\n");
+ goto err;
+ }
+ for (bag_index = 0; ; bag_index++) {
+ err = gnutls_pkcs12_bag_init(&bag);
+ if (err) {
+ ntfs_log_error("Failed to initialize PKCS#12 Bag "
+ "structure: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_pkcs12_get_bag(pkcs12, bag_index, bag);
+ if (err) {
+ if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ err = 0;
+ break;
+ }
+ ntfs_log_error("Failed to obtain Bag from PKCS#12 "
+ "structure: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+check_again:
+ err = gnutls_pkcs12_bag_get_count(bag);
+ if (err < 0) {
+ ntfs_log_error("Failed to obtain Bag count: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_pkcs12_bag_get_type(bag, 0);
+ if (err < 0) {
+ ntfs_log_error("Failed to determine Bag type: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ flags = 0;
+ switch (err) {
+ case GNUTLS_BAG_PKCS8_KEY:
+ flags = GNUTLS_PKCS_PLAIN;
+ case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
+ err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey);
+ if (err < 0) {
+ ntfs_log_error("Failed to obtain Bag data: "
+ "%s\n", gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_x509_privkey_init(&pkey);
+ if (err) {
+ ntfs_log_error("Failed to initialized "
+ "private key structure: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ /* Decrypt the private key into GNU TLS format. */
+ err = gnutls_x509_privkey_import_pkcs8(pkey, &dkey,
+ GNUTLS_X509_FMT_DER, password, flags);
+ if (err) {
+ ntfs_log_error("Failed to convert private "
+ "key from DER to GNU TLS "
+ "format: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+#if 0
+ /*
+ * Export the key again, but unencrypted, and output it
+ * to stderr. Note the output has an RSA header so to
+ * compare to openssl pkcs12 -nodes -in myfile.pfx
+ * output need to ignore the part of the key between
+ * the first "MII..." up to the second "MII...". The
+ * actual RSA private key begins at the second "MII..."
+ * and in my testing at least was identical to openssl
+ * output and was also identical both on big and little
+ * endian so gnutls should be endianness safe.
+ */
+ char *buf = malloc(8192);
+ size_t bufsize = 8192;
+ err = gnutls_x509_privkey_export_pkcs8(pkey,
+ GNUTLS_X509_FMT_PEM, "", GNUTLS_PKCS_PLAIN, buf,
+ &bufsize);
+ if (err) {
+ ntfs_log_error("eek1\n");
+ exit(1);
+ }
+ ntfs_log_error("%s\n", buf);
+ free(buf);
+#endif
+ /* Convert the private key to our internal format. */
+ rsa_key->key =
+ ntfs_rsa_private_key_import_from_gnutls(pkey);
+ if (!rsa_key->key)
+ goto err;
+ break;
+ case GNUTLS_BAG_ENCRYPTED:
+ err = gnutls_pkcs12_bag_decrypt(bag, password);
+ if (err) {
+ ntfs_log_error("Failed to decrypt Bag: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ goto check_again;
+ case GNUTLS_BAG_CERTIFICATE:
+ err = gnutls_pkcs12_bag_get_data(bag, 0, &dkey);
+ if (err < 0) {
+ ntfs_log_error("Failed to obtain Bag data: "
+ "%s\n", gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_x509_crt_init(&crt);
+ if (err) {
+ ntfs_log_error("Failed to initialize "
+ "certificate structure: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_x509_crt_import(crt, &dkey,
+ GNUTLS_X509_FMT_DER);
+ if (err) {
+ ntfs_log_error("Failed to convert certificate "
+ "from DER to GNU TLS format: "
+ "%s\n", gnutls_strerror(err));
+ goto err;
+ }
+ err = gnutls_x509_crt_get_key_purpose_oid(crt, 0,
+ purpose_oid, &purpose_oid_size, NULL);
+ if (err) {
+ ntfs_log_error("Failed to get key purpose "
+ "OID: %s\n",
+ gnutls_strerror(err));
+ goto err;
+ }
+ purpose_oid[purpose_oid_size - 1] = 0;
+ if (!strcmp(purpose_oid,
+ NTFS_EFS_CERT_PURPOSE_OID_DRF))
+ rsa_key->df_type = DF_TYPE_DRF;
+ else if (!strcmp(purpose_oid,
+ NTFS_EFS_CERT_PURPOSE_OID_DDF))
+ rsa_key->df_type = DF_TYPE_DDF;
+ else {
+ ntfs_log_error("Certificate has unknown "
+ "purpose OID %s.\n",
+ purpose_oid);
+ err = EINVAL;
+ goto err;
+ }
+ /* Return the thumbprint to the caller. */
+ err = gnutls_x509_crt_get_fingerprint(crt,
+ GNUTLS_DIG_SHA1, rsa_key->thumbprint,
+ &tp_size);
+ if (err) {
+ ntfs_log_error("Failed to get thumbprint: "
+ "%s\n", gnutls_strerror(err));
+ goto err;
+ }
+ if (tp_size != NTFS_SHA1_THUMBPRINT_SIZE) {
+ ntfs_log_error("Invalid thumbprint size %zd. "
+ "Should be %d.\n", tp_size,
+ sizeof(rsa_key->thumbprint));
+ err = EINVAL;
+ goto err;
+ }
+ have_thumbprint = TRUE;
+ gnutls_x509_crt_deinit(crt);
+ crt = NULL;
+ break;
+ default:
+ /* We do not care about other types. */
+ break;
+ }
+ gnutls_pkcs12_bag_deinit(bag);
+ }
+err:
+ if (err || !rsa_key->key || rsa_key->df_type == DF_TYPE_UNKNOWN ||
+ !have_thumbprint) {
+ if (!err)
+ ntfs_log_error("Key type or thumbprint not found, "
+ "aborting.\n");
+ ntfs_rsa_private_key_release(rsa_key);
+ rsa_key = NULL;
+ }
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+ if (pkey)
+ gnutls_x509_privkey_deinit(pkey);
+ if (bag)
+ gnutls_pkcs12_bag_deinit(bag);
+ if (pkcs12)
+ gnutls_pkcs12_deinit(pkcs12);
+ return rsa_key;
+}
+
+/**
+ * ntfs_buffer_reverse -
+ *
+ * This is a utility function for reversing the order of a buffer in place.
+ * Users of this function should be very careful not to sweep byte order
+ * problems under the rug.
+ */
+static inline void ntfs_buffer_reverse(u8 *buf, unsigned buf_size)
+{
+ unsigned i;
+ u8 t;
+
+ for (i = 0; i < buf_size / 2; i++) {
+ t = buf[i];
+ buf[i] = buf[buf_size - i - 1];
+ buf[buf_size - i - 1] = t;
+ }
+}
+
+#ifndef HAVE_STRNLEN
+/**
+ * strnlen - strnlen is a gnu extension so emulate it if not present
+ */
+static size_t strnlen(const char *s, size_t maxlen)
+{
+ const char *p, *end;
+
+ /* Look for a '\0' character. */
+ for (p = s, end = s + maxlen; p < end && *p; p++)
+ ;
+ return p - s;
+}
+#endif /* ! HAVE_STRNLEN */
+
+/**
+ * ntfs_raw_fek_decrypt -
+ *
+ * Note: decrypting into the input buffer.
+ */
+static unsigned ntfs_raw_fek_decrypt(u8 *fek, u32 fek_size,
+ ntfs_rsa_private_key_t *rsa_key)
+{
+ gcry_mpi_t fek_mpi;
+ gcry_sexp_t fek_sexp, fek_sexp2;
+ gcry_error_t err;
+ size_t size, padding;
+
+ /* Reverse the raw FEK. */
+ ntfs_buffer_reverse(fek, fek_size);
+ /* Convert the FEK to internal MPI format. */
+ err = gcry_mpi_scan(&fek_mpi, GCRYMPI_FMT_USG, fek, fek_size, NULL);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to convert file encryption key to "
+ "internal MPI format: %s\n",
+ gcry_strerror(err));
+ return 0;
+ }
+ /* Create an internal S-expression from the FEK. */
+ err = gcry_sexp_build(&fek_sexp, NULL,
+ "(enc-val (flags) (rsa (a %m)))", fek_mpi);
+ gcry_mpi_release(fek_mpi);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to create internal S-expression of "
+ "the file encryption key: %s\n",
+ gcry_strerror(err));
+ return 0;
+ }
+ /* Decrypt the FEK. */
+ err = gcry_pk_decrypt(&fek_sexp2, fek_sexp, rsa_key->key);
+ gcry_sexp_release(fek_sexp);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to decrypt the file encryption key: "
+ "%s\n", gcry_strerror(err));
+ return 0;
+ }
+ /* Extract the actual FEK from the decrypted raw S-expression. */
+ fek_sexp = gcry_sexp_find_token(fek_sexp2, "value", 0);
+ gcry_sexp_release(fek_sexp2);
+ if (!fek_sexp) {
+ ntfs_log_error("Failed to find the decrypted file encryption "
+ "key in the internal S-expression.\n");
+ return 0;
+ }
+ /* Convert the decrypted FEK S-expression into MPI format. */
+ fek_mpi = gcry_sexp_nth_mpi(fek_sexp, 1, GCRYMPI_FMT_USG);
+ gcry_sexp_release(fek_sexp);
+ if (!fek_mpi) {
+ ntfs_log_error("Failed to convert the decrypted file "
+ "encryption key S-expression to internal MPI "
+ "format.\n");
+ return 0;
+ }
+ /* Convert the decrypted FEK from MPI format to binary data. */
+ err = gcry_mpi_print(GCRYMPI_FMT_USG, fek, fek_size, &size, fek_mpi);
+ gcry_mpi_release(fek_mpi);
+ if (err != GPG_ERR_NO_ERROR || !size) {
+ ntfs_log_error("Failed to convert decrypted file encryption "
+ "key from internal MPI format to binary data: "
+ "%s\n", gcry_strerror(err));
+ return 0;
+ }
+ /*
+ * Finally, remove the PKCS#1 padding and return the size of the
+ * decrypted FEK.
+ */
+ padding = strnlen((char *)fek, size) + 1;
+ if (padding > size) {
+ ntfs_log_error("Failed to remove PKCS#1 padding from "
+ "decrypted file encryption key.\n");
+ return 0;
+ }
+ size -= padding;
+ memmove(fek, fek + padding, size);
+ return size;
+}
+
+/**
+ * ntfs_desx_key_expand - expand a 128-bit desx key to the needed 192-bit key
+ * @src: source buffer containing 128-bit key
+ *
+ * Expands the on-disk 128-bit desx key to the needed des key, the in-, and the
+ * out-whitening keys required to perform desx {de,en}cryption.
+ */
+static gcry_error_t ntfs_desx_key_expand(const u8 *src, u32 *des_key,
+ u64 *out_whitening, u64 *in_whitening)
+{
+ static const u8 *salt1 = (const u8*)"Dan Simon ";
+ static const u8 *salt2 = (const u8*)"Scott Field";
+ static const int salt_len = 12;
+ gcry_md_hd_t hd1, hd2;
+ u32 *md;
+ gcry_error_t err;
+
+ err = gcry_md_open(&hd1, GCRY_MD_MD5, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to open MD5 digest.\n");
+ return err;
+ }
+ /* Hash the on-disk key. */
+ gcry_md_write(hd1, src, 128 / 8);
+ /* Copy the current hash for efficiency. */
+ err = gcry_md_copy(&hd2, hd1);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to copy MD5 digest object.\n");
+ goto out;
+ }
+ /* Hash with the first salt and store the result. */
+ gcry_md_write(hd1, salt1, salt_len);
+ md = (u32*)gcry_md_read(hd1, 0);
+ des_key[0] = md[0] ^ md[1];
+ des_key[1] = md[2] ^ md[3];
+ /* Hash with the second salt and store the result. */
+ gcry_md_write(hd2, salt2, salt_len);
+ md = (u32*)gcry_md_read(hd2, 0);
+ *out_whitening = *(u64*)md;
+ *in_whitening = *(u64*)(md + 2);
+ gcry_md_close(hd2);
+out:
+ gcry_md_close(hd1);
+ return err;
+}
+
+/**
+ * ntfs_desx_setkey - libgcrypt set_key implementation for DES-X-MS128
+ * @context: pointer to a variable of type ntfs_desx_ctx
+ * @key: the 128 bit DES-X-MS128 key, concated with the DES handle
+ * @keylen: must always be 16
+ *
+ * This is the libgcrypt set_key implementation for DES-X-MS128.
+ */
+static gcry_err_code_t ntfs_desx_setkey(void *context, const u8 *key,
+ unsigned keylen)
+{
+ ntfs_desx_ctx *ctx = context;
+ gcry_error_t err;
+ u8 des_key[8];
+
+ if (keylen != 16) {
+ ntfs_log_error("Key length for desx must be 16.\n");
+ return GPG_ERR_INV_KEYLEN;
+ }
+ err = gcry_cipher_open(&ctx->gcry_cipher_hd, GCRY_CIPHER_DES,
+ GCRY_CIPHER_MODE_ECB, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to open des cipher (error 0x%x).\n",
+ err);
+ return err;
+ }
+ err = ntfs_desx_key_expand(key, (u32*)des_key, &ctx->out_whitening,
+ &ctx->in_whitening);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to expand desx key (error 0x%x).\n",
+ err);
+ gcry_cipher_close(ctx->gcry_cipher_hd);
+ return err;
+ }
+ err = gcry_cipher_setkey(ctx->gcry_cipher_hd, des_key, sizeof(des_key));
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to set des key (error 0x%x).\n", err);
+ gcry_cipher_close(ctx->gcry_cipher_hd);
+ return err;
+ }
+ /*
+ * Take a note of the ctx->gcry_cipher_hd since we need to close it at
+ * ntfs_decrypt_data_key_close() time.
+ */
+ **(gcry_cipher_hd_t***)(key + ((keylen + 7) & ~7)) =
+ &ctx->gcry_cipher_hd;
+ return GPG_ERR_NO_ERROR;
+}
+
+/**
+ * ntfs_desx_decrypt
+ */
+static void ntfs_desx_decrypt(void *context, u8 *outbuf, const u8 *inbuf)
+{
+ ntfs_desx_ctx *ctx = context;
+ gcry_error_t err;
+
+ err = gcry_cipher_reset(ctx->gcry_cipher_hd);
+ if (err != GPG_ERR_NO_ERROR)
+ ntfs_log_error("Failed to reset des cipher (error 0x%x).\n",
+ err);
+ *(u64*)outbuf = *(const u64*)inbuf ^ ctx->out_whitening;
+ err = gcry_cipher_encrypt(ctx->gcry_cipher_hd, outbuf, 8, NULL, 0);
+ if (err != GPG_ERR_NO_ERROR)
+ ntfs_log_error("Des decryption failed (error 0x%x).\n", err);
+ *(u64*)outbuf ^= ctx->in_whitening;
+}
+
+static gcry_cipher_spec_t ntfs_desx_cipher = {
+ .name = "DES-X-MS128",
+ .blocksize = 8,
+ .keylen = 128,
+ .contextsize = sizeof(ntfs_desx_ctx),
+ .setkey = ntfs_desx_setkey,
+ .decrypt = ntfs_desx_decrypt,
+};
+
+#ifdef NTFS_TEST
+/*
+ * Do not remove this test code from this file! (AIA)
+ * It would be nice to move all tests (these and runlist) out of the library
+ * (at least, into the separate file{,s}), so they would not annoy eyes. (Yura)
+ */
+
+/**
+ * ntfs_desx_key_expand_test
+ */
+static BOOL ntfs_desx_key_expand_test(void)
+{
+ const u8 known_desx_on_disk_key[16] = {
+ 0xa1, 0xf9, 0xe0, 0xb2, 0x53, 0x23, 0x9e, 0x8f,
+ 0x0f, 0x91, 0x45, 0xd9, 0x8e, 0x20, 0xec, 0x30
+ };
+ const u8 known_des_key[8] = {
+ 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f,
+ };
+ const u8 known_out_whitening[8] = {
+ 0xed, 0xda, 0x4c, 0x47, 0x60, 0x49, 0xdb, 0x8d,
+ };
+ const u8 known_in_whitening[8] = {
+ 0x75, 0xf6, 0xa0, 0x1a, 0xc0, 0xca, 0x28, 0x1e
+ };
+ u64 test_out_whitening, test_in_whitening;
+ union {
+ u64 u64;
+ u32 u32[2];
+ } test_des_key;
+ gcry_error_t err;
+ BOOL res;
+
+ err = ntfs_desx_key_expand(known_desx_on_disk_key, test_des_key.u32,
+ &test_out_whitening, &test_in_whitening);
+ if (err != GPG_ERR_NO_ERROR)
+ res = FALSE;
+ else
+ res = test_des_key.u64 == *(u64*)known_des_key &&
+ test_out_whitening ==
+ *(u64*)known_out_whitening &&
+ test_in_whitening ==
+ *(u64*)known_in_whitening;
+ ntfs_log_error("Testing whether ntfs_desx_key_expand() works: %s\n",
+ res ? "SUCCESS" : "FAILED");
+ return res;
+}
+
+/**
+ * ntfs_des_test
+ */
+static BOOL ntfs_des_test(void)
+{
+ const u8 known_des_key[8] = {
+ 0x27, 0xd1, 0x93, 0x09, 0xcb, 0x78, 0x93, 0x1f
+ };
+ const u8 known_des_encrypted_data[8] = {
+ 0xdc, 0xf7, 0x68, 0x2a, 0xaf, 0x48, 0x53, 0x0f
+ };
+ const u8 known_decrypted_data[8] = {
+ 0xd8, 0xd9, 0x15, 0x23, 0x5b, 0x88, 0x0e, 0x09
+ };
+ u8 test_decrypted_data[8];
+ int res;
+ gcry_error_t err;
+ gcry_cipher_hd_t gcry_cipher_hd;
+
+ err = gcry_cipher_open(&gcry_cipher_hd, GCRY_CIPHER_DES,
+ GCRY_CIPHER_MODE_ECB, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to open des cipher (error 0x%x).\n",
+ err);
+ return FALSE;
+ }
+ err = gcry_cipher_setkey(gcry_cipher_hd, known_des_key,
+ sizeof(known_des_key));
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to set des key (error 0x%x.\n", err);
+ gcry_cipher_close(gcry_cipher_hd);
+ return FALSE;
+ }
+ /*
+ * Apply DES decryption (ntfs actually uses encryption when decrypting).
+ */
+ err = gcry_cipher_encrypt(gcry_cipher_hd, test_decrypted_data,
+ sizeof(test_decrypted_data), known_des_encrypted_data,
+ sizeof(known_des_encrypted_data));
+ gcry_cipher_close(gcry_cipher_hd);
+ if (err) {
+ ntfs_log_error("Failed to des decrypt test data (error "
+ "0x%x).\n", err);
+ return FALSE;
+ }
+ res = !memcmp(test_decrypted_data, known_decrypted_data,
+ sizeof(known_decrypted_data));
+ ntfs_log_error("Testing whether des decryption works: %s\n",
+ res ? "SUCCESS" : "FAILED");
+ return res;
+}
+
+#else /* !defined(NTFS_TEST) */
+
+/**
+ * ntfs_desx_key_expand_test
+ */
+static inline BOOL ntfs_desx_key_expand_test(void)
+{
+ return TRUE;
+}
+
+/**
+ * ntfs_des_test
+ */
+static inline BOOL ntfs_des_test(void)
+{
+ return TRUE;
+}
+
+#endif /* !defined(NTFS_TEST) */
+
+/**
+ * ntfs_fek_import_from_raw
+ */
+static ntfs_fek *ntfs_fek_import_from_raw(u8 *fek_buf,
+ unsigned fek_size)
+{
+ ntfs_fek *fek;
+ u32 key_size, wanted_key_size, gcry_algo;
+ gcry_error_t err;
+
+ key_size = le32_to_cpup(fek_buf);
+ ntfs_log_debug("key_size 0x%x\n", key_size);
+ if (key_size + 16 > fek_size) {
+ ntfs_log_debug("Invalid FEK. It was probably decrypted with "
+ "the incorrect RSA key.");
+ errno = EINVAL;
+ return NULL;
+ }
+ fek = malloc(((((sizeof(*fek) + 7) & ~7) + key_size + 7) & ~7) +
+ sizeof(gcry_cipher_hd_t));
+ if (!fek) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ fek->alg_id = *(le32*)(fek_buf + 8);
+ ntfs_log_debug("algorithm_id 0x%x\n", le32_to_cpu(fek->alg_id));
+ fek->key_data = (u8*)fek + ((sizeof(*fek) + 7) & ~7);
+ memcpy(fek->key_data, fek_buf + 16, key_size);
+ fek->des_gcry_cipher_hd_ptr = NULL;
+ *(gcry_cipher_hd_t***)(fek->key_data + ((key_size + 7) & ~7)) =
+ &fek->des_gcry_cipher_hd_ptr;
+ switch (fek->alg_id) {
+ case CALG_DESX:
+ if (!ntfs_crypto_ctx.desx_module) {
+ if (!ntfs_desx_key_expand_test() || !ntfs_des_test()) {
+ err = EINVAL;
+ goto out;
+ }
+ err = gcry_cipher_register(&ntfs_desx_cipher,
+ &ntfs_crypto_ctx.desx_alg_id,
+ &ntfs_crypto_ctx.desx_module);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to register desx "
+ "cipher: %s\n",
+ gcry_strerror(err));
+ err = EINVAL;
+ goto out;
+ }
+ }
+ wanted_key_size = 16;
+ gcry_algo = ntfs_crypto_ctx.desx_alg_id;
+ break;
+ case CALG_3DES:
+ wanted_key_size = 24;
+ gcry_algo = GCRY_CIPHER_3DES;
+ break;
+ case CALG_AES_256:
+ wanted_key_size = 32;
+ gcry_algo = GCRY_CIPHER_AES256;
+ break;
+ default:
+ wanted_key_size = 8;
+ gcry_algo = GCRY_CIPHER_DES;
+ if (fek->alg_id == CALG_DES)
+ ntfs_log_error("DES is not supported at present\n");
+ else
+ ntfs_log_error("Unknown crypto algorithm 0x%x\n",
+ le32_to_cpu(fek->alg_id));
+ ntfs_log_error(". Please email %s and say that you saw this "
+ "message. We will then try to implement "
+ "support for this algorithm.\n", NTFS_DEV_LIST);
+ err = EOPNOTSUPP;
+ goto out;
+ }
+ if (key_size != wanted_key_size) {
+ ntfs_log_error("%s key of %u bytes but needed size is %u "
+ "bytes, assuming corrupt or incorrect key. "
+ "Aborting.\n",
+ gcry_cipher_algo_name(gcry_algo),
+ (unsigned)key_size, (unsigned)wanted_key_size);
+ err = EIO;
+ goto out;
+ }
+ err = gcry_cipher_open(&fek->gcry_cipher_hd, gcry_algo,
+ GCRY_CIPHER_MODE_CBC, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("gcry_cipher_open() failed: %s\n",
+ gcry_strerror(err));
+ err = EINVAL;
+ goto out;
+ }
+ err = gcry_cipher_setkey(fek->gcry_cipher_hd, fek->key_data, key_size);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("gcry_cipher_setkey() failed: %s\n",
+ gcry_strerror(err));
+ gcry_cipher_close(fek->gcry_cipher_hd);
+ err = EINVAL;
+ goto out;
+ }
+ return fek;
+out:
+ free(fek);
+ errno = err;
+ return NULL;
+}
+
+/**
+ * ntfs_fek_release
+ */
+static void ntfs_fek_release(ntfs_fek *fek)
+{
+ if (fek->des_gcry_cipher_hd_ptr)
+ gcry_cipher_close(*fek->des_gcry_cipher_hd_ptr);
+ gcry_cipher_close(fek->gcry_cipher_hd);
+ free(fek);
+}
+
+/**
+ * ntfs_df_array_fek_get
+ */
+static ntfs_fek *ntfs_df_array_fek_get(EFS_DF_ARRAY_HEADER *df_array,
+ ntfs_rsa_private_key_t *rsa_key)
+{
+ EFS_DF_HEADER *df_header;
+ EFS_DF_CREDENTIAL_HEADER *df_cred;
+ EFS_DF_CERT_THUMBPRINT_HEADER *df_cert;
+ u8 *fek_buf;
+ ntfs_fek *fek;
+ u32 df_count, fek_size;
+ unsigned i, thumbprint_size = sizeof(rsa_key->thumbprint);
+
+ df_count = le32_to_cpu(df_array->df_count);
+ if (!df_count)
+ ntfs_log_error("There are no elements in the DF array.\n");
+ df_header = (EFS_DF_HEADER*)(df_array + 1);
+ for (i = 0; i < df_count; i++, df_header = (EFS_DF_HEADER*)(
+ (u8*)df_header + le32_to_cpu(df_header->df_length))) {
+ df_cred = (EFS_DF_CREDENTIAL_HEADER*)((u8*)df_header +
+ le32_to_cpu(df_header->cred_header_offset));
+ if (df_cred->type != NTFS_CRED_TYPE_CERT_THUMBPRINT) {
+ ntfs_log_debug("Credential type is not certificate "
+ "thumbprint, skipping DF entry.\n");
+ continue;
+ }
+ df_cert = (EFS_DF_CERT_THUMBPRINT_HEADER*)((u8*)df_cred +
+ le32_to_cpu(
+ df_cred->cert_thumbprint_header_offset));
+ if (le32_to_cpu(df_cert->thumbprint_size) != thumbprint_size) {
+ ntfs_log_error("Thumbprint size %d is not valid "
+ "(should be %d), skipping this DF "
+ "entry.\n",
+ le32_to_cpu(df_cert->thumbprint_size),
+ thumbprint_size);
+ continue;
+ }
+ if (memcmp((u8*)df_cert +
+ le32_to_cpu(df_cert->thumbprint_offset),
+ rsa_key->thumbprint, thumbprint_size)) {
+ ntfs_log_debug("Thumbprints do not match, skipping "
+ "this DF entry.\n");
+ continue;
+ }
+ /*
+ * The thumbprints match so this is probably the DF entry
+ * matching the RSA key. Try to decrypt the FEK with it.
+ */
+ fek_size = le32_to_cpu(df_header->fek_size);
+ fek_buf = (u8*)df_header + le32_to_cpu(df_header->fek_offset);
+ /* Decrypt the FEK. Note: This is done in place. */
+ fek_size = ntfs_raw_fek_decrypt(fek_buf, fek_size, rsa_key);
+ if (fek_size) {
+ /* Convert the FEK to our internal format. */
+ fek = ntfs_fek_import_from_raw(fek_buf, fek_size);
+ if (fek)
+ return fek;
+ ntfs_log_error("Failed to convert the decrypted file "
+ "encryption key to internal format.\n");
+ } else
+ ntfs_log_error("Failed to decrypt the file "
+ "encryption key.\n");
+ }
+ return NULL;
+}
+
+/**
+ * ntfs_inode_fek_get -
+ */
+static ntfs_fek *ntfs_inode_fek_get(ntfs_inode *inode,
+ ntfs_rsa_private_key_t *rsa_key)
+{
+ EFS_ATTR_HEADER *efs;
+ EFS_DF_ARRAY_HEADER *df_array = NULL;
+ ntfs_fek *fek = NULL;
+
+ /* Obtain the $EFS contents. */
+ efs = ntfs_attr_readall(inode, AT_LOGGED_UTILITY_STREAM, NTFS_EFS, 4,
+ NULL);
+ if (!efs) {
+ ntfs_log_perror("Failed to read $EFS attribute");
+ return NULL;
+ }
+ /*
+ * Depending on whether the key is a normal key or a data recovery key,
+ * iterate through the DDF or DRF array, respectively.
+ */
+ if (rsa_key->df_type == DF_TYPE_DDF) {
+ if (efs->offset_to_ddf_array)
+ df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs +
+ le32_to_cpu(efs->offset_to_ddf_array));
+ else
+ ntfs_log_error("There are no entries in the DDF "
+ "array.\n");
+ } else if (rsa_key->df_type == DF_TYPE_DRF) {
+ if (efs->offset_to_drf_array)
+ df_array = (EFS_DF_ARRAY_HEADER*)((u8*)efs +
+ le32_to_cpu(efs->offset_to_drf_array));
+ else
+ ntfs_log_error("There are no entries in the DRF "
+ "array.\n");
+ } else
+ ntfs_log_error("Invalid DF type.\n");
+ if (df_array)
+ fek = ntfs_df_array_fek_get(df_array, rsa_key);
+ free(efs);
+ return fek;
+}
+
+/**
+ * ntfs_fek_decrypt_sector
+ */
+static int ntfs_fek_decrypt_sector(ntfs_fek *fek, u8 *data, const u64 offset)
+{
+ gcry_error_t err;
+
+ err = gcry_cipher_reset(fek->gcry_cipher_hd);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to reset cipher: %s\n",
+ gcry_strerror(err));
+ return -1;
+ }
+ /*
+ * Note: You may wonder why we are not calling gcry_cipher_setiv() here
+ * instead of doing it by hand after the decryption. The answer is
+ * that gcry_cipher_setiv() wants an iv of length 8 bytes but we give
+ * it a length of 16 for AES256 so it does not like it.
+ */
+ err = gcry_cipher_decrypt(fek->gcry_cipher_hd, data, 512, NULL, 0);
+ if (err != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Decryption failed: %s\n", gcry_strerror(err));
+ return -1;
+ }
+ /* Apply the IV. */
+ if (fek->alg_id == CALG_AES_256) {
+ ((le64*)data)[0] ^= cpu_to_le64(0x5816657be9161312ULL + offset);
+ ((le64*)data)[1] ^= cpu_to_le64(0x1989adbe44918961ULL + offset);
+ } else {
+ /* All other algorithms (Des, 3Des, DesX) use the same IV. */
+ ((le64*)data)[0] ^= cpu_to_le64(0x169119629891ad13ULL + offset);
+ }
+ return 512;
+}
+
+/**
+ * ntfs_crypto_deinit - perform library-wide crypto deinitialization
+ */
+static void ntfs_crypto_deinit(void)
+{
+ int i;
+
+ if (!ntfs_crypto_ctx.initialized)
+ return;
+
+ for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++)
+ ntfs_rsa_private_key_release(ntfs_crypto_ctx.rsa_key[i]);
+ free(ntfs_crypto_ctx.rsa_key);
+ ntfs_crypto_ctx.rsa_key = NULL;
+ ntfs_crypto_ctx.nr_rsa_keys = 0;
+ gnutls_global_deinit();
+ if (ntfs_crypto_ctx.desx_module) {
+ gcry_cipher_unregister(ntfs_crypto_ctx.desx_module);
+ ntfs_crypto_ctx.desx_module = NULL;
+ ntfs_crypto_ctx.desx_alg_id = -1;
+ }
+ ntfs_crypto_ctx.initialized = 0;
+}
+
+
+static void ntfs_crypto_parse_config(struct config_t *cfg)
+{
+ ntfs_crypto_ctx_t *ctx = &ntfs_crypto_ctx;
+ config_setting_t *cfg_keys, *cfg_key;
+ const char *pfx_file, *pfx_pwd;
+ ntfs_rsa_private_key_t *key;
+ u8 *pfx_buf;
+ unsigned pfx_size;
+ int i;
+
+ /* Search for crypto.keys list. */
+ cfg_keys = config_lookup(cfg, "crypto.keys");
+ if (!cfg_keys) {
+ ntfs_log_error("Unable to find crypto.keys in config file.\n");
+ return;
+ }
+ /* Iterate trough list of records about keys. */
+ for (i = 0; (cfg_key = config_setting_get_elem(cfg_keys, i)); i++) {
+ /* Get path and password to key. */
+ pfx_file = config_setting_get_string_elem(cfg_key, 0);
+ pfx_pwd = config_setting_get_string_elem(cfg_key, 1);
+ if (!pfx_file) {
+ ntfs_log_error("Entry number %d in section crypto.keys "
+ "of configuration file formed "
+ "incorrectly.\n", i + 1);
+ continue;
+ }
+ if (!pfx_pwd)
+ pfx_pwd = "";
+ /* Load the PKCS#12 file containing the user's private key. */
+ if (ntfs_pkcs12_load_pfxfile(pfx_file, &pfx_buf, &pfx_size)) {
+ ntfs_log_error("Failed to load key file %s.\n",
+ pfx_file);
+ continue;
+ }
+ /*
+ * Check whether we need to allocate memory for new key pointer.
+ * If yes, allocate memory for it and for 3 more pointers.
+ */
+ if (!(ctx->nr_rsa_keys % 4)) {
+ ntfs_rsa_private_key_t **new;
+
+ new = realloc(ctx->rsa_key,
+ sizeof(ntfs_rsa_private_key_t *) *
+ (ctx->nr_rsa_keys + 4));
+ if (!new) {
+ ntfs_log_perror("Unable to store all keys");
+ break;
+ }
+ ctx->rsa_key = new;
+ }
+ /* Obtain the user's private RSA key from the key file. */
+ key = ntfs_pkcs12_extract_rsa_key(pfx_buf, pfx_size, pfx_pwd);
+ if (key)
+ ctx->rsa_key[ctx->nr_rsa_keys++] = key;
+ else
+ ntfs_log_error("Failed to obtain RSA key from %s\n",
+ pfx_file);
+ /* No longer need the pfx file contents. */
+ free(pfx_buf);
+ }
+}
+
+
+static void ntfs_crypto_read_configs(void)
+{
+ struct config_t cfg;
+ char *home;
+ int fd = -1;
+
+ config_init(&cfg);
+ /* Load system configuration file. */
+ if (config_read_file(&cfg, NTFS_CONFIG_PATH_SYSTEM))
+ ntfs_crypto_parse_config(&cfg);
+ else
+ if (config_error_line(&cfg)) /* Do not cry if file absent. */
+ ntfs_log_error("Failed to read system configuration "
+ "file: %s (line %d).\n",
+ config_error_text(&cfg),
+ config_error_line(&cfg));
+ /* Load user configuration file. */
+ fd = open(".", O_RDONLY); /* Save current working directory. */
+ if (fd == -1) {
+ ntfs_log_error("Failed to open working directory.\n");
+ goto out;
+ }
+ home = getenv("HOME");
+ if (!home) {
+ ntfs_log_error("Environment variable HOME is not set.\n");
+ goto out;
+ }
+ if (chdir(home) == -1) {
+ ntfs_log_perror("chdir() to home directory failed");
+ goto out;
+ }
+ if (config_read_file(&cfg, NTFS_CONFIG_PATH_USER))
+ ntfs_crypto_parse_config(&cfg);
+ else
+ if (config_error_line(&cfg)) /* Do not cry if file absent. */
+ ntfs_log_error("Failed to read user configuration "
+ "file: %s (line %d).\n",
+ config_error_text(&cfg),
+ config_error_line(&cfg));
+ if (fchdir(fd) == -1)
+ ntfs_log_error("Failed to restore original working "
+ "directory.\n");
+out:
+ if (fd != -1)
+ close(fd);
+ config_destroy(&cfg);
+}
+
+/**
+ * ntfs_crypto_init - perform library-wide crypto initializations
+ *
+ * This function is called during first call of ntfs_crypto_attr_open and
+ * performs gcrypt and GNU TLS initializations, then read list of PFX files
+ * from configuration files and load RSA keys from them.
+ */
+static int ntfs_crypto_init(void)
+{
+ int err;
+
+ if (ntfs_crypto_ctx.initialized)
+ return 0;
+
+ /* Initialize gcrypt library. Note: Must come before GNU TLS init. */
+ if (gcry_control(GCRYCTL_DISABLE_SECMEM, 0) != GPG_ERR_NO_ERROR) {
+ ntfs_log_error("Failed to initialize the gcrypt library.\n");
+ return -1;
+ }
+ /* Initialize GNU TLS library. Note: Must come after libgcrypt init. */
+ err = gnutls_global_init();
+ if (err < 0) {
+ ntfs_log_error("Failed to initialize GNU TLS library: %s\n",
+ gnutls_strerror(err));
+ return -1;
+ }
+ /* Read crypto related sections of libntfs configuration files. */
+ ntfs_crypto_read_configs();
+
+ ntfs_crypto_ctx.initialized = 1;
+ atexit(ntfs_crypto_deinit);
+ return 0;
+}
+
+
+/**
+ * ntfs_crypto_attr_open - perform crypto related initialization for attribute
+ * @na: ntfs attribute to perform initialization for
+ *
+ * This function is called from ntfs_attr_open for encrypted attributes and
+ * tries to decrypt FEK enumerating all user submitted RSA keys. If we
+ * successfully obtained FEK, then @na->crypto is allocated and FEK stored
+ * inside. In the other case @na->crypto is set to NULL.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_crypto_attr_open(ntfs_attr *na)
+{
+ ntfs_fek *fek;
+ int i;
+
+ na->crypto = NULL;
+ if (!na || !NAttrEncrypted(na)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (ntfs_crypto_init()) {
+ errno = EACCES;
+ return -1;
+ }
+
+ for (i = 0; i < ntfs_crypto_ctx.nr_rsa_keys; i++) {
+ fek = ntfs_inode_fek_get(na->ni, ntfs_crypto_ctx.rsa_key[i]);
+ if (fek) {
+ na->crypto = ntfs_malloc(sizeof(ntfs_crypto_attr));
+ if (!na->crypto)
+ return -1;
+ na->crypto->fek = fek;
+ return 0;
+ }
+ }
+
+ errno = EACCES;
+ return -1;
+}
+
+
+/**
+ * ntfs_crypto_attr_close - perform crypto related deinit for attribute
+ * @na: ntfs attribute to perform deinitialization for
+ *
+ * This function is called from ntfs_attr_close for encrypted attributes and
+ * frees memory that were allocated for it handling.
+ */
+void ntfs_crypto_attr_close(ntfs_attr *na)
+{
+ if (!na || !NAttrEncrypted(na))
+ return;
+
+ if (na->crypto) {
+ ntfs_fek_release(na->crypto->fek);
+ free(na->crypto);
+ }
+}
+
+
+/**
+ * ntfs_crypto_attr_pread - read from an encrypted attribute
+ * @na: ntfs attribute to read from
+ * @pos: byte position in the attribute to begin reading from
+ * @count: number of bytes to read
+ * @b: output data buffer
+ *
+ * This function is called from ntfs_attr_pread for encrypted attributes and
+ * should behave as described in ntfs_attr_pread description.
+ */
+s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count, void *b)
+{
+ unsigned char *buffer;
+ s64 bytes_read, offset, total, length;
+ int i;
+
+ if (!na || pos < 0 || count < 0 || !b || !NAttrEncrypted(na)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return 0;
+
+ if (!na->crypto) {
+ errno = EACCES;
+ return -1;
+ }
+
+ buffer = malloc(NTFS_EFS_SECTOR_SIZE);
+ if (!buffer)
+ return -1;
+
+ ntfs_attr_map_runlist_range(na, pos >> na->ni->vol->cluster_size_bits,
+ (pos + count - 1) >> na->ni->vol->cluster_size_bits);
+
+ total = 0;
+ offset = ROUND_DOWN(pos, 9);
+ while (total < count && offset < na->data_size) {
+ /* Calculate number of bytes we actually want. */
+ length = NTFS_EFS_SECTOR_SIZE;
+ if (offset + length > pos + count)
+ length = pos + count - offset;
+ if (offset + length > na->data_size)
+ length = na->data_size - offset;
+
+ if (length < 0) {
+ total = -1;
+ errno = EIO;
+ ntfs_log_error("LIBRARY BUG!!! Please report that you "
+ "saw this message to %s. Thanks!",
+ NTFS_DEV_LIST);
+ break;
+ }
+
+ /* Just write zeros if @offset fully beyond initialized size. */
+ if (offset >= na->initialized_size) {
+ memset(b + total, 0, length);
+ total += length;
+ continue;
+ }
+
+ bytes_read = ntfs_rl_pread(na->ni->vol, na->rl, offset,
+ NTFS_EFS_SECTOR_SIZE, buffer);
+ if (!bytes_read)
+ break;
+ if (bytes_read != NTFS_EFS_SECTOR_SIZE) {
+ ntfs_log_perror("%s(): ntfs_rl_pread returned %lld "
+ "bytes", "ntfs_crypto_attr_pread", bytes_read);
+ break;
+ }
+ if ((i = ntfs_fek_decrypt_sector(na->crypto->fek, buffer,
+ offset)) < bytes_read) {
+ ntfs_log_error("%s(): Couldn't decrypt all data "
+ "(%u/%lld/%lld/%lld)!", "ntfs_crypto_attr_pread",
+ i, (long long)bytes_read,
+ (long long)offset, (long long)total);
+ break;
+ }
+
+ /* Handle partially in initialized size situation. */
+ if (offset + length > na->initialized_size)
+ memset(buffer + (na->initialized_size - offset), 0,
+ offset + length - na->initialized_size);
+
+ if (offset >= pos)
+ memcpy(b + total, buffer, length);
+ else {
+ length -= (pos - offset);
+ memcpy(b + total, buffer + (pos - offset), length);
+ }
+ total += length;
+ offset += bytes_read;
+ }
+
+ free(buffer);
+ return total;
+}
+
+#else /* !ENABLE_CRYPTO */
+
+/* Stubs for crypto-disabled version of libntfs. */
+
+int ntfs_crypto_attr_open(ntfs_attr *na)
+{
+ na->crypto = NULL;
+ errno = EACCES;
+ return -1;
+}
+
+void ntfs_crypto_attr_close(ntfs_attr *na)
+{
+}
+
+s64 ntfs_crypto_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
+ void *b)
+{
+ errno = EACCES;
+ return -1;
+}
+
+#endif /* !ENABLE_CRYPTO */
+
diff --git a/usr/src/lib/libntfs/common/libntfs/debug.c b/usr/src/lib/libntfs/common/libntfs/debug.c
new file mode 100644
index 0000000000..8962051006
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/debug.c
@@ -0,0 +1,73 @@
+/**
+ * debug.c - Debugging output functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2004 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "types.h"
+#include "runlist.h"
+#include "debug.h"
+#include "logging.h"
+
+#ifdef DEBUG
+/**
+ * ntfs_debug_runlist_dump - Dump a runlist.
+ * @rl:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+void ntfs_debug_runlist_dump(const runlist_element *rl)
+{
+ int i = 0;
+ const char *lcn_str[5] = { "LCN_HOLE ", "LCN_RL_NOT_MAPPED",
+ "LCN_ENOENT ", "LCN_EINVAL ",
+ "LCN_unknown " };
+
+ ntfs_log_debug("NTFS-fs DEBUG: Dumping runlist (values in hex):\n");
+ if (!rl) {
+ ntfs_log_debug("Run list not present.\n");
+ return;
+ }
+ ntfs_log_debug("VCN LCN Run length\n");
+ do {
+ LCN lcn = (rl + i)->lcn;
+
+ if (lcn < (LCN)0) {
+ int idx = -lcn - 1;
+
+ if (idx > -LCN_EINVAL - 1)
+ idx = 4;
+ ntfs_log_debug("%-16llx %s %-16llx%s\n", rl[i].vcn, lcn_str[idx], rl[i].length, rl[i].length ? "" : " (runlist end)");
+ } else
+ ntfs_log_debug("%-16llx %-16llx %-16llx%s\n", rl[i].vcn, rl[i].lcn, rl[i].length, rl[i].length ? "" : " (runlist end)");
+ } while (rl[i++].length);
+}
+
+#endif
+
diff --git a/usr/src/lib/libntfs/common/libntfs/device.c b/usr/src/lib/libntfs/common/libntfs/device.c
new file mode 100644
index 0000000000..1e82614592
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/device.c
@@ -0,0 +1,795 @@
+/**
+ * device.c - Low level device io functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004-2006 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_LINUX_HDREG_H
+#include <linux/hdreg.h>
+#endif
+
+#include "types.h"
+#include "mst.h"
+#include "debug.h"
+#include "device.h"
+#include "logging.h"
+
+#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96) /* Get device size in 512-byte blocks. */
+#endif
+#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* Get device size in bytes. */
+#endif
+#if defined(linux) && !defined(HDIO_GETGEO)
+#define HDIO_GETGEO 0x0301 /* Get device geometry. */
+#endif
+#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
+#endif
+#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
+# define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
+#endif
+
+/**
+ * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
+ * @name: name of the device (must be present)
+ * @state: initial device state (usually zero)
+ * @dops: ntfs device operations to use with the device (must be present)
+ * @priv_data: pointer to private data (optional)
+ *
+ * Allocate an ntfs device structure and pre-initialize it with the user-
+ * specified device operations @dops, device state @state, device name @name,
+ * and optional private data @priv_data.
+ *
+ * Note, @name is copied and can hence be freed after this functions returns.
+ *
+ * On success return a pointer to the allocated ntfs device structure and on
+ * error return NULL with errno set to the error code returned by malloc().
+ */
+struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
+ struct ntfs_device_operations *dops, void *priv_data)
+{
+ struct ntfs_device *dev;
+
+ if (!name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ dev = (struct ntfs_device *)ntfs_malloc(sizeof(struct ntfs_device));
+ if (dev) {
+ if (!(dev->d_name = strdup(name))) {
+ int eo = errno;
+ free(dev);
+ errno = eo;
+ return NULL;
+ }
+ dev->d_ops = dops;
+ dev->d_state = state;
+ dev->d_private = priv_data;
+ }
+ return dev;
+}
+
+/**
+ * ntfs_device_free - free an ntfs device structure
+ * @dev: ntfs device structure to free
+ *
+ * Free the ntfs device structure @dev.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code. The
+ * following error codes are defined:
+ * EINVAL Invalid pointer @dev.
+ * EBUSY Device is still open. Close it before freeing it!
+ */
+int ntfs_device_free(struct ntfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (NDevOpen(dev)) {
+ errno = EBUSY;
+ return -1;
+ }
+ free(dev->d_name);
+ free(dev);
+ return 0;
+}
+
+/**
+ * fake_pread - read operation disguised as pread
+ * @dev: device to read from
+ * @b: output data buffer
+ * @count: number of bytes to read
+ * @pos: position in device to read from
+ *
+ * Auxiliary function, used when we emulate pread by seek() + a sequence of
+ * read()s.
+ */
+static s64 fake_pread(struct ntfs_device *dev, void *b, s64 count,
+ s64 pos __attribute__((unused)))
+{
+ return dev->d_ops->read(dev, b, count);
+}
+
+/**
+ * ntfs_pread - positioned read from disk
+ * @dev: device to read from
+ * @pos: position in device to read from
+ * @count: number of bytes to read
+ * @b: output data buffer
+ *
+ * This function will read @count bytes from device @dev at position @pos into
+ * the data buffer @b.
+ *
+ * On success, return the number of successfully read bytes. If this number is
+ * lower than @count this means that we have either reached end of file or
+ * encountered an error during the read so that the read is partial. 0 means
+ * end of file or nothing to read (@count is 0).
+ *
+ * On error and nothing has been read, return -1 with errno set appropriately
+ * to the return code of either seek, read, or set to EINVAL in case of
+ * invalid arguments.
+ */
+s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
+{
+ s64 br, total;
+ struct ntfs_device_operations *dops;
+ s64 (*_pread)(struct ntfs_device *, void *, s64, s64);
+
+ ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count);
+ if (!b || count < 0 || pos < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return 0;
+ dops = dev->d_ops;
+ _pread = dops->pread;
+ if (!_pread)
+ _pread = fake_pread;
+seek:
+ /* Locate to position if pread is to be emulated by seek() + read(). */
+ if (_pread == fake_pread &&
+ dops->seek(dev, pos, SEEK_SET) == (off_t)-1) {
+ ntfs_log_perror("ntfs_pread: device seek to 0x%llx returned "
+ "error", pos);
+ return -1;
+ }
+ /* Read the data. */
+ for (total = 0; count; count -= br, total += br) {
+ br = _pread(dev, (char*)b + total, count, pos + total);
+ /* If everything ok, continue. */
+ if (br > 0)
+ continue;
+ /* If EOF or error return number of bytes read. */
+ if (!br || total)
+ return total;
+ /*
+ * If pread is not supported by the OS, fall back to emulating
+ * it by seek() + read() and set the device pread() pointer to
+ * NULL so we automatically use seek() + read() from now on.
+ */
+ if (errno == ENOSYS && _pread != fake_pread) {
+ _pread = fake_pread;
+ dops->pread = NULL;
+ goto seek;
+ }
+ /* Nothing read and error, return error status. */
+ return br;
+ }
+ /* Finally, return the number of bytes read. */
+ return total;
+}
+
+/**
+ * fake_pwrite - write operation disguised as pwrite
+ * @dev: device to write to
+ * @b: input data buffer
+ * @count: number of bytes to write
+ * @pos: position in device to write to
+ *
+ * Auxiliary function, used when we emulate pwrite by seek() + a sequence of
+ * write()s.
+ */
+static s64 fake_pwrite(struct ntfs_device *dev, const void *b, s64 count,
+ s64 pos __attribute__((unused)))
+{
+ return dev->d_ops->write(dev, b, count);
+}
+
+/**
+ * ntfs_pwrite - positioned write to disk
+ * @dev: device to write to
+ * @pos: position in file descriptor to write to
+ * @count: number of bytes to write
+ * @b: data buffer to write to disk
+ *
+ * This function will write @count bytes from data buffer @b to the device @dev
+ * at position @pos.
+ *
+ * On success, return the number of successfully written bytes. If this number
+ * is lower than @count this means that the write has been interrupted in
+ * flight or that an error was encountered during the write so that the write
+ * is partial. 0 means nothing was written (also return 0 when @count is 0).
+ *
+ * On error and nothing has been written, return -1 with errno set
+ * appropriately to the return code of either seek, write, or set
+ * to EINVAL in case of invalid arguments.
+ */
+s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
+ const void *b)
+{
+ s64 written, total;
+ struct ntfs_device_operations *dops;
+ s64 (*_pwrite)(struct ntfs_device *, const void *, s64, s64);
+
+ ntfs_log_trace("Entering for pos 0x%llx, count 0x%llx.\n", pos, count);
+ if (!b || count < 0 || pos < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return 0;
+ if (NDevReadOnly(dev)) {
+ errno = EROFS;
+ return -1;
+ }
+ dops = dev->d_ops;
+ _pwrite = dops->pwrite;
+ if (!_pwrite)
+ _pwrite = fake_pwrite;
+seek:
+ /*
+ * Locate to position if pwrite is to be emulated by seek() + write().
+ */
+ if (_pwrite == fake_pwrite &&
+ dops->seek(dev, pos, SEEK_SET) == (off_t)-1) {
+ ntfs_log_perror("ntfs_pwrite: seek to 0x%llx returned error",
+ pos);
+ return -1;
+ }
+ NDevSetDirty(dev);
+ /* Write the data. */
+ for (total = 0; count; count -= written, total += written) {
+ written = _pwrite(dev, (const char*)b + total, count,
+ pos + total);
+ /* If everything ok, continue. */
+ if (written > 0)
+ continue;
+ /*
+ * If nothing written or error return number of bytes written.
+ */
+ if (!written || total)
+ break;
+ /*
+ * If pwrite is not supported by the OS, fall back to emulating
+ * it by seek() + write() and set the device pwrite() pointer
+ * to NULL so we automatically use seek() + write() from now
+ * on.
+ */
+ if (errno == ENOSYS && _pwrite != fake_pwrite) {
+ _pwrite = fake_pwrite;
+ dops->pwrite = NULL;
+ goto seek;
+ }
+ /* Nothing written and error, return error status. */
+ return written;
+ }
+ /* Finally, return the number of bytes written. */
+ return total;
+}
+
+/**
+ * ntfs_mst_pread - multi sector transfer (mst) positioned read
+ * @dev: device to read from
+ * @pos: position in file descriptor to read from
+ * @count: number of blocks to read
+ * @bksize: size of each block that needs mst deprotecting
+ * @b: output data buffer
+ *
+ * Multi sector transfer (mst) positioned read. This function will read @count
+ * blocks of size @bksize bytes each from device @dev at position @pos into the
+ * the data buffer @b.
+ *
+ * On success, return the number of successfully read blocks. If this number is
+ * lower than @count this means that we have reached end of file, that the read
+ * was interrupted, or that an error was encountered during the read so that
+ * the read is partial. 0 means end of file or nothing was read (also return 0
+ * when @count or @bksize are 0).
+ *
+ * On error and nothing was read, return -1 with errno set appropriately to the
+ * return code of either seek, read, or set to EINVAL in case of invalid
+ * arguments.
+ *
+ * NOTE: If an incomplete multi sector transfer has been detected the magic
+ * will have been changed to magic_BAAD but no error will be returned. Thus it
+ * is possible that we return count blocks as being read but that any number
+ * (between zero and count!) of these blocks is actually subject to a multi
+ * sector transfer error. This should be detected by the caller by checking for
+ * the magic being "BAAD".
+ */
+s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
+ const u32 bksize, void *b)
+{
+ s64 br, i;
+
+ if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Do the read. */
+ br = ntfs_pread(dev, pos, count * bksize, b);
+ if (br < 0)
+ return br;
+ /*
+ * Apply fixups to successfully read data, disregarding any errors
+ * returned from the MST fixup function. This is because we want to
+ * fixup everything possible and we rely on the fact that the "BAAD"
+ * magic will be detected later on.
+ */
+ count = br / bksize;
+ for (i = 0; i < count; ++i)
+ ntfs_mst_post_read_fixup((NTFS_RECORD*)
+ ((u8*)b + i * bksize), bksize);
+ /* Finally, return the number of complete blocks read. */
+ return count;
+}
+
+/**
+ * ntfs_mst_pwrite - multi sector transfer (mst) positioned write
+ * @dev: device to write to
+ * @pos: position in file descriptor to write to
+ * @count: number of blocks to write
+ * @bksize: size of each block that needs mst protecting
+ * @b: data buffer to write to disk
+ *
+ * Multi sector transfer (mst) positioned write. This function will write
+ * @count blocks of size @bksize bytes each from data buffer @b to the device
+ * @dev at position @pos.
+ *
+ * On success, return the number of successfully written blocks. If this number
+ * is lower than @count this means that the write has been interrupted or that
+ * an error was encountered during the write so that the write is partial. 0
+ * means nothing was written (also return 0 when @count or @bksize are 0).
+ *
+ * On error and nothing has been written, return -1 with errno set
+ * appropriately to the return code of either seek, write, or set
+ * to EINVAL in case of invalid arguments.
+ *
+ * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
+ * deprotect algorithm (no checking). This saves us from making a copy before
+ * the write and at the same time causes the usn to be incremented in the
+ * buffer. This conceptually fits in better with the idea that cached data is
+ * always deprotected and protection is performed when the data is actually
+ * going to hit the disk and the cache is immediately deprotected again
+ * simulating an mst read on the written data. This way cache coherency is
+ * achieved.
+ */
+s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
+ const u32 bksize, void *b)
+{
+ s64 written, i;
+
+ if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return 0;
+ /* Prepare data for writing. */
+ for (i = 0; i < count; ++i) {
+ int err;
+
+ err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
+ ((u8*)b + i * bksize), bksize);
+ if (err < 0) {
+ /* Abort write at this position. */
+ if (!i)
+ return err;
+ count = i;
+ break;
+ }
+ }
+ /* Write the prepared data. */
+ written = ntfs_pwrite(dev, pos, count * bksize, b);
+ /* Quickly deprotect the data again. */
+ for (i = 0; i < count; ++i)
+ ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
+ if (written <= 0)
+ return written;
+ /* Finally, return the number of complete blocks written. */
+ return written / bksize;
+}
+
+/**
+ * ntfs_cluster_read - read ntfs clusters
+ * @vol: volume to read from
+ * @lcn: starting logical cluster number
+ * @count: number of clusters to read
+ * @b: output data buffer
+ *
+ * Read @count ntfs clusters starting at logical cluster number @lcn from
+ * volume @vol into buffer @b. Return number of clusters read or -1 on error,
+ * with errno set to the error code.
+ */
+s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
+ void *b)
+{
+ s64 br;
+
+ if (!vol || lcn < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (vol->nr_clusters < lcn + count) {
+ errno = ESPIPE;
+ return -1;
+ }
+ br = ntfs_pread(vol->u.dev, lcn << vol->cluster_size_bits,
+ count << vol->cluster_size_bits, b);
+ if (br < 0) {
+ ntfs_log_perror("Error reading cluster(s)");
+ return br;
+ }
+ return br >> vol->cluster_size_bits;
+}
+
+/**
+ * ntfs_cluster_write - write ntfs clusters
+ * @vol: volume to write to
+ * @lcn: starting logical cluster number
+ * @count: number of clusters to write
+ * @b: data buffer to write to disk
+ *
+ * Write @count ntfs clusters starting at logical cluster number @lcn from
+ * buffer @b to volume @vol. Return the number of clusters written or -1 on
+ * error, with errno set to the error code.
+ */
+s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
+ const s64 count, const void *b)
+{
+ s64 bw;
+
+ if (!vol || lcn < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (vol->nr_clusters < lcn + count) {
+ errno = ESPIPE;
+ return -1;
+ }
+ if (!NVolReadOnly(vol))
+ bw = ntfs_pwrite(vol->u.dev, lcn << vol->cluster_size_bits,
+ count << vol->cluster_size_bits, b);
+ else
+ bw = count << vol->cluster_size_bits;
+ if (bw < 0) {
+ ntfs_log_perror("Error writing cluster(s)");
+ return bw;
+ }
+ return bw >> vol->cluster_size_bits;
+}
+
+/**
+ * ntfs_device_offset_valid - test if a device offset is valid
+ * @dev: open device
+ * @ofs: offset to test for validity
+ *
+ * Test if the offset @ofs is an existing location on the device described
+ * by the open device structure @dev.
+ *
+ * Return 0 if it is valid and -1 if it is not valid.
+ */
+static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
+{
+ char ch;
+
+ if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
+ dev->d_ops->read(dev, &ch, 1) == 1)
+ return 0;
+ return -1;
+}
+
+/**
+ * ntfs_device_size_get - return the size of a device in blocks
+ * @dev: open device
+ * @block_size: block size in bytes in which to return the result
+ *
+ * Return the number of @block_size sized blocks in the device described by the
+ * open device @dev.
+ *
+ * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
+ *
+ * On error return -1 with errno set to the error code.
+ */
+s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
+{
+ s64 high, low;
+
+ if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef BLKGETSIZE64
+ { u64 size;
+
+ if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
+ ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
+ (unsigned long long)size,
+ (unsigned long long)size);
+ return (s64)size / block_size;
+ }
+ }
+#endif
+#ifdef BLKGETSIZE
+ { unsigned long size;
+
+ if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
+ ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
+ size, size);
+ return (s64)size * 512 / block_size;
+ }
+ }
+#endif
+#ifdef FDGETPRM
+ { struct floppy_struct this_floppy;
+
+ if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
+ ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
+ (unsigned long)this_floppy.size,
+ (unsigned long)this_floppy.size);
+ return (s64)this_floppy.size * 512 / block_size;
+ }
+ }
+#endif
+ /*
+ * We couldn't figure it out by using a specialized ioctl,
+ * so do binary search to find the size of the device.
+ */
+ low = 0LL;
+ for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
+ low = high;
+ while (low < high - 1LL) {
+ const s64 mid = (low + high) / 2;
+
+ if (!ntfs_device_offset_valid(dev, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ dev->d_ops->seek(dev, 0LL, SEEK_SET);
+ return (low + 1LL) / block_size;
+}
+
+/**
+ * ntfs_device_partition_start_sector_get - get starting sector of a partition
+ * @dev: open device
+ *
+ * On success, return the starting sector of the partition @dev in the parent
+ * block device of @dev. On error return -1 with errno set to the error code.
+ *
+ * The following error codes are defined:
+ * EINVAL Input parameter error
+ * EOPNOTSUPP System does not support HDIO_GETGEO ioctl
+ * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
+ */
+s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef HDIO_GETGEO
+ { struct hd_geometry geo;
+
+ if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
+ ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
+ geo.start, geo.start);
+ return geo.start;
+ }
+ }
+#else
+ errno = EOPNOTSUPP;
+#endif
+ return -1;
+}
+
+/**
+ * ntfs_device_heads_get - get number of heads of device
+ * @dev: open device
+ *
+ * On success, return the number of heads on the device @dev. On error return
+ * -1 with errno set to the error code.
+ *
+ * The following error codes are defined:
+ * EINVAL Input parameter error
+ * EOPNOTSUPP System does not support HDIO_GETGEO ioctl
+ * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
+ */
+int ntfs_device_heads_get(struct ntfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef HDIO_GETGEO
+ { struct hd_geometry geo;
+
+ if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
+ ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n",
+ (unsigned)geo.heads,
+ (unsigned)geo.heads);
+ return geo.heads;
+ }
+ }
+#else
+ errno = EOPNOTSUPP;
+#endif
+ return -1;
+}
+
+/**
+ * ntfs_device_sectors_per_track_get - get number of sectors per track of device
+ * @dev: open device
+ *
+ * On success, return the number of sectors per track on the device @dev. On
+ * error return -1 with errno set to the error code.
+ *
+ * The following error codes are defined:
+ * EINVAL Input parameter error
+ * EOPNOTSUPP System does not support HDIO_GETGEO ioctl
+ * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO
+ */
+int ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef HDIO_GETGEO
+ { struct hd_geometry geo;
+
+ if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
+ ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n",
+ (unsigned)geo.sectors,
+ (unsigned)geo.sectors);
+ return geo.sectors;
+ }
+ }
+#else
+ errno = EOPNOTSUPP;
+#endif
+ return -1;
+}
+
+/**
+ * ntfs_device_sector_size_get - get sector size of a device
+ * @dev: open device
+ *
+ * On success, return the sector size in bytes of the device @dev.
+ * On error return -1 with errno set to the error code.
+ *
+ * The following error codes are defined:
+ * EINVAL Input parameter error
+ * EOPNOTSUPP System does not support BLKSSZGET ioctl
+ * ENOTTY @dev is a file or a device not supporting BLKSSZGET
+ */
+int ntfs_device_sector_size_get(struct ntfs_device *dev)
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef BLKSSZGET
+ {
+ int sect_size = 0;
+
+ if (!dev->d_ops->ioctl(dev, BLKSSZGET, &sect_size)) {
+ ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
+ sect_size);
+ return sect_size;
+ }
+ }
+#else
+ errno = EOPNOTSUPP;
+#endif
+ return -1;
+}
+
+/**
+ * ntfs_device_block_size_set - set block size of a device
+ * @dev: open device
+ * @block_size: block size to set @dev to
+ *
+ * On success, return 0.
+ * On error return -1 with errno set to the error code.
+ *
+ * The following error codes are defined:
+ * EINVAL Input parameter error
+ * EOPNOTSUPP System does not support BLKBSZSET ioctl
+ * ENOTTY @dev is a file or a device not supporting BLKBSZSET
+ */
+int ntfs_device_block_size_set(struct ntfs_device *dev,
+ int block_size __attribute__((unused)))
+{
+ if (!dev) {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef BLKBSZSET
+ {
+ size_t s_block_size = block_size;
+ if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
+ ntfs_log_debug("Used BLKBSZSET to set block size to "
+ "%d bytes.\n", block_size);
+ return 0;
+ }
+ /* If not a block device, pretend it was successful. */
+ if (!NDevBlock(dev))
+ return 0;
+ }
+#else
+ /* If not a block device, pretend it was successful. */
+ if (!NDevBlock(dev))
+ return 0;
+ errno = EOPNOTSUPP;
+#endif
+ return -1;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/device_io.c b/usr/src/lib/libntfs/common/libntfs/device_io.c
new file mode 100644
index 0000000000..706e935f34
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/device_io.c
@@ -0,0 +1,46 @@
+/*
+ * device_io.c - Default device io operations. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2003-2006 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 "config.h"
+
+#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
+
+#if defined(__CYGWIN32__)
+
+/* On Cygwin; use Win32 low level device operations. */
+#include "win32_io.c"
+
+#elif defined(__FreeBSD__)
+
+/* On FreeBSD; need to use sector aligned i/o. */
+#include "freebsd_io.c"
+
+#else
+
+/*
+ * Not on Cygwin or FreeBSD; use standard Unix style low level device
+ * operations.
+ */
+#include "unix_io.c"
+
+#endif
+
+#endif /* NO_NTFS_DEVICE_DEFAULT_IO_OPS */
diff --git a/usr/src/lib/libntfs/common/libntfs/dir.c b/usr/src/lib/libntfs/common/libntfs/dir.c
new file mode 100644
index 0000000000..b112c73205
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/dir.c
@@ -0,0 +1,1773 @@
+/**
+ * dir.c - Directory handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2005 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+
+#include "compat.h"
+#include "types.h"
+#include "debug.h"
+#include "attrib.h"
+#include "inode.h"
+#include "dir.h"
+#include "volume.h"
+#include "mft.h"
+#include "index.h"
+#include "ntfstime.h"
+#include "lcnalloc.h"
+#include "logging.h"
+
+/*
+ * The little endian Unicode strings "$I30", "$SII", "$SDH", "$O"
+ * and "$Q" as global constants.
+ */
+ntfschar NTFS_INDEX_I30[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('I'),
+ const_cpu_to_le16('3'), const_cpu_to_le16('0'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_SII[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
+ const_cpu_to_le16('I'), const_cpu_to_le16('I'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_SDH[5] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'),
+ const_cpu_to_le16('D'), const_cpu_to_le16('H'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_O[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('O'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_Q[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('Q'),
+ const_cpu_to_le16('\0') };
+ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'),
+ const_cpu_to_le16('\0') };
+
+/**
+ * ntfs_inode_lookup_by_name - find an inode in a directory given its name
+ * @dir_ni: ntfs inode of the directory in which to search for the name
+ * @uname: Unicode name for which to search in the directory
+ * @uname_len: length of the name @uname in Unicode characters
+ *
+ * Look for an inode with name @uname in the directory with inode @dir_ni.
+ * ntfs_inode_lookup_by_name() walks the contents of the directory looking for
+ * the Unicode name. If the name is found in the directory, the corresponding
+ * inode number (>= 0) is returned as a mft reference in cpu format, i.e. it
+ * is a 64-bit number containing the sequence number.
+ *
+ * On error, return -1 with errno set to the error code. If the inode is is not
+ * found errno is ENOENT.
+ *
+ * Note, @uname_len does not include the (optional) terminating NULL character.
+ *
+ * Note, we look for a case sensitive match first but we also look for a case
+ * insensitive match at the same time. If we find a case insensitive match, we
+ * save that for the case that we don't find an exact match, where we return
+ * the mft reference of the case insensitive match.
+ *
+ * If the volume is mounted with the case sensitive flag set, then we only
+ * allow exact matches.
+ */
+u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname,
+ const int uname_len)
+{
+ VCN vcn;
+ u64 mref = 0;
+ s64 br;
+ ntfs_volume *vol = dir_ni->vol;
+ ntfs_attr_search_ctx *ctx;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia;
+ u8 *index_end;
+ ntfs_attr *ia_na;
+ int eo, rc;
+ u32 index_block_size, index_vcn_size;
+ u8 index_vcn_size_bits;
+
+ if (!dir_ni || !dir_ni->mrec || !uname || uname_len <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ return -1;
+
+ /* Find the index root attribute in the mft record. */
+ if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ ntfs_log_perror("Index root attribute missing in directory "
+ "inode 0x%llx", (unsigned long long)dir_ni->
+ mft_no);
+ goto put_err_out;
+ }
+ /* Get to the index root value. */
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ index_block_size = le32_to_cpu(ir->index_block_size);
+ if (index_block_size < NTFS_BLOCK_SIZE ||
+ index_block_size & (index_block_size - 1)) {
+ ntfs_log_debug("Index block size %u is invalid.\n",
+ (unsigned)index_block_size);
+ goto put_err_out;
+ }
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end)
+ goto put_err_out;
+ /*
+ * The last entry cannot contain a name. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /*
+ * We perform a case sensitive comparison and if that matches
+ * we are done and return the mft reference of the inode (i.e.
+ * the inode number together with the sequence number for
+ * consistency checking). We convert it to cpu format before
+ * returning.
+ */
+ if (ntfs_names_are_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
+found_it:
+ /*
+ * We have a perfect match, so we don't need to care
+ * about having matched imperfectly before.
+ */
+ mref = le64_to_cpu(ie->u.indexed_file);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+ }
+ /*
+ * For a case insensitive mount, we also perform a case
+ * insensitive comparison. If the comparison matches, we cache
+ * the mft reference in mref. Use first case insensitive match
+ * in case if no name matches case sensitive, but several names
+ * matches case insensitive.
+ */
+ if (!mref && !NVolCaseSensitive(vol) &&
+ ntfs_names_are_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ IGNORE_CASE, vol->upcase, vol->upcase_len))
+ mref = le64_to_cpu(ie->u.indexed_file);
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_names_collate(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ IGNORE_CASE, vol->upcase, vol->upcase_len);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ /*
+ * Names match with case insensitive comparison, now try the
+ * case sensitive comparison, which is required for proper
+ * collation.
+ */
+ rc = ntfs_names_collate(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+ if (rc == -1)
+ break;
+ if (rc)
+ continue;
+ /*
+ * Perfect match, this will never happen as the
+ * ntfs_are_names_equal() call will have gotten a match but we
+ * still treat it correctly.
+ */
+ goto found_it;
+ }
+ /*
+ * We have finished with this index without success. Check for the
+ * presence of a child node and if not present return error code
+ * ENOENT, unless we have got the mft reference of a matching name
+ * cached in mref in which case return mref.
+ */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ ntfs_attr_put_search_ctx(ctx);
+ if (mref)
+ return mref;
+ ntfs_log_debug("Entry not found.\n");
+ errno = ENOENT;
+ return -1;
+ } /* Child node present, descend into it. */
+
+ /* Open the index allocation attribute. */
+ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
+ if (!ia_na) {
+ ntfs_log_perror("Failed to open index allocation attribute. "
+ "Directory inode 0x%llx is corrupt or driver "
+ "bug", (unsigned long long)dir_ni->mft_no);
+ goto put_err_out;
+ }
+
+ /* Allocate a buffer for the current index block. */
+ ia = (INDEX_ALLOCATION*)malloc(index_block_size);
+ if (!ia) {
+ ntfs_log_perror("Failed to allocate buffer for index block");
+ ntfs_attr_close(ia_na);
+ goto put_err_out;
+ }
+
+ /* Determine the size of a vcn in the directory index. */
+ if (vol->cluster_size <= index_block_size) {
+ index_vcn_size = vol->cluster_size;
+ index_vcn_size_bits = vol->cluster_size_bits;
+ } else {
+ index_vcn_size = vol->sector_size;
+ index_vcn_size_bits = vol->sector_size_bits;
+ }
+
+ /* Get the starting vcn of the index_block holding the child node. */
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+
+descend_into_child_node:
+
+ /* Read the index block starting at vcn. */
+ br = ntfs_attr_mst_pread(ia_na, vcn << index_vcn_size_bits, 1,
+ index_block_size, ia);
+ if (br != 1) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read vcn 0x%llx",
+ (unsigned long long)vcn);
+ goto close_err_out;
+ }
+
+ if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
+ ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
+ "different from expected VCN (0x%llx).\n",
+ (long long)sle64_to_cpu(ia->index_block_vcn),
+ (long long)vcn);
+ errno = EIO;
+ goto close_err_out;
+ }
+ if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
+ ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode "
+ "0x%llx has a size (%u) differing from the "
+ "directory specified size (%u).\n",
+ (long long)vcn, (unsigned long long)dir_ni->
+ mft_no, (unsigned)le32_to_cpu(ia->index.
+ allocated_size) + 0x18, (unsigned)
+ index_block_size);
+ errno = EIO;
+ goto close_err_out;
+ }
+ index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+ if (index_end > (u8*)ia + index_block_size) {
+ ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
+ "inode 0x%llx exceeds maximum size.\n",
+ (long long)vcn, (unsigned long long)dir_ni->
+ mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Iterate similar to above big loop but applied to index buffer, thus
+ * loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ /* Bounds check. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end) {
+ ntfs_log_debug("Index entry out of bounds in directory "
+ "inode 0x%llx.\n",
+ (unsigned long long)dir_ni->mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+ /*
+ * The last entry cannot contain a name. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /*
+ * We perform a case sensitive comparison and if that matches
+ * we are done and return the mft reference of the inode (i.e.
+ * the inode number together with the sequence number for
+ * consistency checking). We convert it to cpu format before
+ * returning.
+ */
+ if (ntfs_names_are_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len)) {
+found_it2:
+ /*
+ * We have a perfect match, so we don't need to care
+ * about having matched imperfectly before.
+ */
+ mref = le64_to_cpu(ie->u.indexed_file);
+ free(ia);
+ ntfs_attr_close(ia_na);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+ }
+ /*
+ * For a case insensitive mount, we also perform a case
+ * insensitive comparison. If the comparison matches, we cache
+ * the mft reference in mref. Use first case insensitive match
+ * in case if no name matches case sensitive, but several names
+ * matches case insensitive.
+ */
+ if (!mref && !NVolCaseSensitive(vol) &&
+ ntfs_names_are_equal(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length,
+ IGNORE_CASE, vol->upcase, vol->upcase_len))
+ mref = le64_to_cpu(ie->u.indexed_file);
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_names_collate(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ IGNORE_CASE, vol->upcase, vol->upcase_len);
+ /*
+ * If uname collates before the name of the current entry, there
+ * is definitely no such name in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+ /* The names are not equal, continue the search. */
+ if (rc)
+ continue;
+ /*
+ * Names match with case insensitive comparison, now try the
+ * case sensitive comparison, which is required for proper
+ * collation.
+ */
+ rc = ntfs_names_collate(uname, uname_len,
+ (ntfschar*)&ie->key.file_name.file_name,
+ ie->key.file_name.file_name_length, 1,
+ CASE_SENSITIVE, vol->upcase, vol->upcase_len);
+ if (rc == -1)
+ break;
+ if (rc)
+ continue;
+ /*
+ * Perfect match, this will never happen as the
+ * ntfs_are_names_equal() call will have gotten a match but we
+ * still treat it correctly.
+ */
+ goto found_it2;
+ }
+ /*
+ * We have finished with this index buffer without success. Check for
+ * the presence of a child node.
+ */
+ if (ie->flags & INDEX_ENTRY_NODE) {
+ if ((ia->index.flags & NODE_MASK) == LEAF_NODE) {
+ ntfs_log_debug("Index entry with child node found in a "
+ "leaf node in directory inode "
+ "0x%llx.\n",
+ (unsigned long long)dir_ni->mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+ /* Child node present, descend into it. */
+ vcn = sle64_to_cpup((u8*)ie + le16_to_cpu(ie->length) - 8);
+ if (vcn >= 0)
+ goto descend_into_child_node;
+ ntfs_log_debug("Negative child node vcn in directory inode "
+ "0x%llx.\n", (unsigned long long)dir_ni->
+ mft_no);
+ errno = EIO;
+ goto close_err_out;
+ }
+ free(ia);
+ ntfs_attr_close(ia_na);
+ ntfs_attr_put_search_ctx(ctx);
+ /*
+ * No child node present, return error code ENOENT, unless we have got
+ * the mft reference of a matching name cached in mref in which case
+ * return mref.
+ */
+ if (mref)
+ return mref;
+ ntfs_log_debug("Entry not found.\n");
+ errno = ENOENT;
+ return -1;
+put_err_out:
+ eo = EIO;
+ ntfs_log_debug("Corrupt directory. Aborting lookup.\n");
+eo_put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ errno = eo;
+ return -1;
+close_err_out:
+ eo = errno;
+ free(ia);
+ ntfs_attr_close(ia_na);
+ goto eo_put_err_out;
+}
+
+/**
+ * ntfs_pathname_to_inode_num - find the inode number which represents the
+ * given pathname
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @parent: A directory inode to begin the search (may be NULL)
+ * @pathname: Pathname to be located
+ *
+ * Take an ASCII pathname and find the inode that represents it. The function
+ * splits the path and then descends the directory tree. If @parent is NULL,
+ * then the root directory '.' will be used as the base for the search.
+ *
+ * Return: -1 Error, the pathname was invalid, or some other error occurred
+ * else Success, the pathname was valid
+ */
+u64 ntfs_pathname_to_inode_num(ntfs_volume *vol, ntfs_inode *parent,
+ const char *pathname)
+{
+ u64 inum, result;
+ int len, err = 0;
+ char *p, *q;
+ ntfs_inode *ni = NULL;
+ ntfschar *unicode = NULL;
+ char *ascii = NULL;
+
+ inum = result = (u64)-1;
+ if (!vol || !pathname) {
+ err = EINVAL;
+ goto close;
+ }
+ ntfs_log_trace("Path: '%s'\n", pathname);
+ if (parent) {
+ ni = parent;
+ } else
+ inum = FILE_root;
+ unicode = calloc(1, MAX_PATH);
+ ascii = strdup(pathname);
+ if (!unicode || !ascii) {
+ ntfs_log_error("Out of memory.\n");
+ err = ENOMEM;
+ goto close;
+ }
+ p = ascii;
+ /* Remove leading /'s. */
+ while (p && *p == PATH_SEP)
+ p++;
+ while (p && *p) {
+ if (!ni) {
+ ni = ntfs_inode_open(vol, inum);
+ if (!ni) {
+ ntfs_log_debug("Cannot open inode %llu.\n",
+ (unsigned long long)inum);
+ err = EIO;
+ goto close;
+ }
+ }
+ /* Find the end of the first token. */
+ q = strchr(p, PATH_SEP);
+ if (q != NULL) {
+ *q = 0;
+ q++;
+ }
+ len = ntfs_mbstoucs(p, &unicode, MAX_PATH);
+ if (len < 0) {
+ ntfs_log_debug("Couldn't convert name to Unicode: "
+ "%s.\n", p);
+ err = EILSEQ;
+ goto close;
+ }
+ inum = ntfs_inode_lookup_by_name(ni, unicode, len);
+ if (inum == (u64)-1) {
+ ntfs_log_debug("Couldn't find name '%s' in pathname "
+ "'%s'.\n", p, pathname);
+ err = ENOENT;
+ goto close;
+ }
+ inum = MREF(inum);
+ if (ni != parent)
+ ntfs_inode_close(ni);
+ ni = NULL;
+ p = q;
+ while (p && *p == PATH_SEP)
+ p++;
+ }
+ result = inum;
+close:
+ if (ni && (ni != parent))
+ ntfs_inode_close(ni);
+ free(ascii);
+ free(unicode);
+ if (err)
+ errno = err;
+ return result;
+}
+
+/**
+ * ntfs_pathname_to_inode - Find the inode which represents the given pathname
+ * @vol: An ntfs volume obtained from ntfs_mount
+ * @parent: A directory inode to begin the search (may be NULL)
+ * @pathname: Pathname to be located
+ *
+ * Take an ASCII pathname and find the inode that represents it. The function
+ * splits the path and then descends the directory tree. If @parent is NULL,
+ * then the root directory '.' will be used as the base for the search.
+ *
+ * Return: inode Success, the pathname was valid
+ * NULL Error, the pathname was invalid, or some other error occurred
+ */
+ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent,
+ const char *pathname)
+{
+ u64 inum;
+
+ inum = ntfs_pathname_to_inode_num(vol, parent, pathname);
+ if (inum == (u64)-1)
+ return NULL;
+ return ntfs_inode_open(vol, inum);
+}
+
+/*
+ * The little endian Unicode string ".." for ntfs_readdir().
+ */
+static const ntfschar dotdot[3] = { const_cpu_to_le16('.'),
+ const_cpu_to_le16('.'),
+ const_cpu_to_le16('\0') };
+
+/**
+ * ntfs_filldir - ntfs specific filldir method
+ * @vol: ntfs volume with wjich we are working
+ * @pos: current position in directory
+ * @ie: current index entry
+ * @dirent: context for filldir callback supplied by the caller
+ * @filldir: filldir callback supplied by the caller
+ *
+ * Pass information specifying the current directory entry @ie to the @filldir
+ * callback.
+ */
+static int ntfs_filldir(ntfs_volume *vol, s64 *pos, INDEX_ENTRY *ie,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ FILE_NAME_ATTR *fn = &ie->key.file_name;
+ unsigned dt_type;
+
+ ntfs_log_trace("Entering.\n");
+
+ /* Skip root directory self reference entry. */
+ if (MREF_LE(ie->u.indexed_file) == FILE_root)
+ return 0;
+ if (ie->key.file_name.file_attributes & FILE_ATTR_I30_INDEX_PRESENT)
+ dt_type = NTFS_DT_DIR;
+ else {
+ if (NVolInterix(vol) && fn->file_attributes & FILE_ATTR_SYSTEM)
+ dt_type = NTFS_DT_UNKNOWN;
+ else
+ dt_type = NTFS_DT_REG;
+ }
+ return filldir(dirent, fn->file_name, fn->file_name_length,
+ fn->file_name_type, *pos,
+ le64_to_cpu(ie->u.indexed_file), dt_type);
+}
+
+/**
+ * ntfs_mft_get_parent_ref - find mft reference of parent directory of an inode
+ * @ni: ntfs inode whose parent directory to find
+ *
+ * Find the parent directory of the ntfs inode @ni. To do this, find the first
+ * file name attribute in the mft record of @ni and return the parent mft
+ * reference from that.
+ *
+ * Note this only makes sense for directories, since files can be hard linked
+ * from multiple directories and there is no way for us to tell which one is
+ * being looked for.
+ *
+ * Technically directories can have hard links, too, but we consider that as
+ * illegal as Linux/UNIX do not support directory hard links.
+ *
+ * Return the mft reference of the parent directory on success or -1 on error
+ * with errno set to the error code.
+ */
+static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
+{
+ MFT_REF mref;
+ ntfs_attr_search_ctx *ctx;
+ FILE_NAME_ATTR *fn;
+ int eo;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni) {
+ errno = EINVAL;
+ return ERR_MREF(-1);
+ }
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return ERR_MREF(-1);
+ if (ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
+ ntfs_log_debug("No file name found in inode 0x%llx. Corrupt "
+ "inode.\n", (unsigned long long)ni->mft_no);
+ goto err_out;
+ }
+ if (ctx->attr->non_resident) {
+ ntfs_log_debug("File name attribute must be resident. "
+ "Corrupt inode 0x%llx.\n",
+ (unsigned long long)ni->mft_no);
+ goto io_err_out;
+ }
+ fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ if ((u8*)fn + le32_to_cpu(ctx->attr->u.res.value_length) >
+ (u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
+ ntfs_log_debug("Corrupt file name attribute in inode 0x%llx.\n",
+ (unsigned long long)ni->mft_no);
+ goto io_err_out;
+ }
+ mref = le64_to_cpu(fn->parent_directory);
+ ntfs_attr_put_search_ctx(ctx);
+ return mref;
+io_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ ntfs_attr_put_search_ctx(ctx);
+ errno = eo;
+ return ERR_MREF(-1);
+}
+
+/**
+ * ntfs_readdir - read the contents of an ntfs directory
+ * @dir_ni: ntfs inode of current directory
+ * @pos: current position in directory
+ * @dirent: context for filldir callback supplied by the caller
+ * @filldir: filldir callback supplied by the caller
+ *
+ * Parse the index root and the index blocks that are marked in use in the
+ * index bitmap and hand each found directory entry to the @filldir callback
+ * supplied by the caller.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ *
+ * Note: Index blocks are parsed in ascending vcn order, from which follows
+ * that the directory entries are not returned sorted.
+ */
+int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
+ void *dirent, ntfs_filldir_t filldir)
+{
+ s64 i_size, br, ia_pos, bmp_pos, ia_start;
+ ntfs_volume *vol;
+ ntfs_attr *ia_na, *bmp_na = NULL;
+ ntfs_attr_search_ctx *ctx = NULL;
+ u8 *index_end, *bmp = NULL;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_ALLOCATION *ia = NULL;
+ int rc, ir_pos, bmp_buf_size, bmp_buf_pos, eo;
+ u32 index_block_size, index_vcn_size;
+ u8 index_block_size_bits, index_vcn_size_bits;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!dir_ni || !pos || !filldir) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(dir_ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ vol = dir_ni->vol;
+
+ ntfs_log_trace("Entering for inode 0x%llx, *pos 0x%llx.\n",
+ (unsigned long long)dir_ni->mft_no, (long long)*pos);
+
+ /* Open the index allocation attribute. */
+ ia_na = ntfs_attr_open(dir_ni, AT_INDEX_ALLOCATION, NTFS_INDEX_I30, 4);
+ if (!ia_na) {
+ if (errno != ENOENT) {
+ ntfs_log_perror("Failed to open index allocation "
+ "attribute. Directory inode 0x%llx is "
+ "corrupt or bug", (unsigned long long)
+ dir_ni->mft_no);
+ return -1;
+ }
+ i_size = 0;
+ } else
+ i_size = ia_na->data_size;
+
+ rc = 0;
+
+ /* Are we at end of dir yet? */
+ if (*pos >= i_size + vol->mft_record_size)
+ goto done;
+
+ /* Emulate . and .. for all directories. */
+ if (!*pos) {
+ rc = filldir(dirent, dotdot, 1, FILE_NAME_POSIX, *pos,
+ MK_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number)),
+ NTFS_DT_DIR);
+ if (rc)
+ goto err_out;
+ ++*pos;
+ }
+ if (*pos == 1) {
+ MFT_REF parent_mref;
+
+ parent_mref = ntfs_mft_get_parent_ref(dir_ni);
+ if (parent_mref == ERR_MREF(-1)) {
+ ntfs_log_perror("Parent directory not found");
+ goto dir_err_out;
+ }
+
+ rc = filldir(dirent, dotdot, 2, FILE_NAME_POSIX, *pos,
+ parent_mref, NTFS_DT_DIR);
+ if (rc)
+ goto err_out;
+ ++*pos;
+ }
+
+ ctx = ntfs_attr_get_search_ctx(dir_ni, NULL);
+ if (!ctx)
+ goto err_out;
+
+ /* Get the offset into the index root attribute. */
+ ir_pos = (int)*pos;
+ /* Find the index root attribute in the mft record. */
+ if (ntfs_attr_lookup(AT_INDEX_ROOT, NTFS_INDEX_I30, 4, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ ntfs_log_debug("Index root attribute missing in directory "
+ "inode 0x%llx.\n", (unsigned long long)dir_ni->
+ mft_no);
+ goto dir_err_out;
+ }
+ /* Get to the index root value. */
+ ir = (INDEX_ROOT*)((u8*)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+
+ /* Determine the size of a vcn in the directory index. */
+ index_block_size = le32_to_cpu(ir->index_block_size);
+ if (index_block_size < NTFS_BLOCK_SIZE ||
+ index_block_size & (index_block_size - 1)) {
+ ntfs_log_debug("Index block size %u is invalid.\n",
+ (unsigned)index_block_size);
+ goto dir_err_out;
+ }
+ index_block_size_bits = ffs(index_block_size) - 1;
+ if (vol->cluster_size <= index_block_size) {
+ index_vcn_size = vol->cluster_size;
+ index_vcn_size_bits = vol->cluster_size_bits;
+ } else {
+ index_vcn_size = vol->sector_size;
+ index_vcn_size_bits = vol->sector_size_bits;
+ }
+
+ /* Are we jumping straight into the index allocation attribute? */
+ if (*pos >= vol->mft_record_size) {
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ goto skip_index_root;
+ }
+
+ index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ir->index +
+ le32_to_cpu(ir->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry or until filldir tells us it has had enough
+ * or signals an error (both covered by the rc test).
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ ntfs_log_debug("In index root, offset 0x%x.\n",
+ (u8*)ie - (u8*)ir);
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end)
+ goto dir_err_out;
+ /* The last entry cannot contain a name. */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Skip index root entry if continuing previous readdir. */
+ if (ir_pos > (u8*)ie - (u8*)ir)
+ continue;
+ /* Advance the position even if going to skip the entry. */
+ *pos = (u8*)ie - (u8*)ir;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(vol, pos, ie, dirent, filldir);
+ if (rc)
+ goto err_out;
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+
+ /* If there is no index allocation attribute we are finished. */
+ if (!ia_na)
+ goto EOD;
+
+ /* Advance *pos to the beginning of the index allocation. */
+ *pos = vol->mft_record_size;
+
+skip_index_root:
+
+ if (!ia_na)
+ goto done;
+
+ /* Allocate a buffer for the current index block. */
+ ia = (INDEX_ALLOCATION*)malloc(index_block_size);
+ if (!ia) {
+ ntfs_log_perror("Failed to allocate buffer for index block");
+ goto err_out;
+ }
+
+ bmp_na = ntfs_attr_open(dir_ni, AT_BITMAP, NTFS_INDEX_I30, 4);
+ if (!bmp_na) {
+ ntfs_log_perror("Failed to open index bitmap attribute");
+ goto dir_err_out;
+ }
+
+ /* Get the offset into the index allocation attribute. */
+ ia_pos = *pos - vol->mft_record_size;
+
+ bmp_pos = ia_pos >> index_block_size_bits;
+ if (bmp_pos >> 3 >= bmp_na->data_size) {
+ ntfs_log_debug("Current index position exceeds index bitmap "
+ "size.\n");
+ goto dir_err_out;
+ }
+
+ bmp_buf_size = min(bmp_na->data_size - (bmp_pos >> 3), 4096);
+ bmp = (u8*)malloc(bmp_buf_size);
+ if (!bmp) {
+ ntfs_log_perror("Failed to allocate bitmap buffer");
+ goto err_out;
+ }
+
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read from index bitmap attribute");
+ goto err_out;
+ }
+
+ bmp_buf_pos = 0;
+ /* If the index block is not in use find the next one that is. */
+ while (!(bmp[bmp_buf_pos >> 3] & (1 << (bmp_buf_pos & 7)))) {
+find_next_index_buffer:
+ bmp_pos++;
+ bmp_buf_pos++;
+ /* If we have reached the end of the bitmap, we are done. */
+ if (bmp_pos >> 3 >= bmp_na->data_size)
+ goto EOD;
+ ia_pos = bmp_pos << index_block_size_bits;
+ if (bmp_buf_pos >> 3 < bmp_buf_size)
+ continue;
+ /* Read next chunk from the index bitmap. */
+ if ((bmp_pos >> 3) + bmp_buf_size > bmp_na->data_size)
+ bmp_buf_size = bmp_na->data_size - (bmp_pos >> 3);
+ br = ntfs_attr_pread(bmp_na, bmp_pos >> 3, bmp_buf_size, bmp);
+ if (br != bmp_buf_size) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read from index bitmap "
+ "attribute");
+ goto err_out;
+ }
+ }
+
+ ntfs_log_debug("Handling index block 0x%llx.\n", (long long)bmp_pos);
+
+ /* Read the index block starting at bmp_pos. */
+ br = ntfs_attr_mst_pread(ia_na, bmp_pos << index_block_size_bits, 1,
+ index_block_size, ia);
+ if (br != 1) {
+ if (br != -1)
+ errno = EIO;
+ ntfs_log_perror("Failed to read index block");
+ goto err_out;
+ }
+
+ ia_start = ia_pos & ~(s64)(index_block_size - 1);
+ if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
+ index_vcn_size_bits) {
+ ntfs_log_debug("Actual VCN (0x%llx) of index buffer is "
+ "different from expected VCN (0x%llx) in "
+ "inode 0x%llx.\n",
+ (long long)sle64_to_cpu(ia->index_block_vcn),
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
+ ntfs_log_debug("Index buffer (VCN 0x%llx) of directory inode "
+ "0x%llx has a size (%u) differing from the "
+ "directory specified size (%u).\n",
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no,
+ (unsigned) le32_to_cpu(ia->index.allocated_size)
+ + 0x18, (unsigned)index_block_size);
+ goto dir_err_out;
+ }
+ index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
+ if (index_end > (u8*)ia + index_block_size) {
+ ntfs_log_debug("Size of index buffer (VCN 0x%llx) of directory "
+ "inode 0x%llx exceeds maximum size.\n",
+ (long long)ia_start >> index_vcn_size_bits,
+ (unsigned long long)dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* The first index entry. */
+ ie = (INDEX_ENTRY*)((u8*)&ia->index +
+ le32_to_cpu(ia->index.entries_offset));
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry or until ntfs_filldir tells us it has had
+ * enough or signals an error (both covered by the rc test).
+ */
+ for (;; ie = (INDEX_ENTRY*)((u8*)ie + le16_to_cpu(ie->length))) {
+ ntfs_log_debug("In index allocation, offset 0x%llx.\n",
+ (long long)ia_start + ((u8*)ie - (u8*)ia));
+ /* Bounds checks. */
+ if ((u8*)ie < (u8*)ia || (u8*)ie +
+ sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8*)ie + le16_to_cpu(ie->key_length) >
+ index_end) {
+ ntfs_log_debug("Index entry out of bounds in directory "
+ "inode 0x%llx.\n", (unsigned long long)
+ dir_ni->mft_no);
+ goto dir_err_out;
+ }
+ /* The last entry cannot contain a name. */
+ if (ie->flags & INDEX_ENTRY_END)
+ break;
+ /* Skip index entry if continuing previous readdir. */
+ if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
+ continue;
+ /* Advance the position even if going to skip the entry. */
+ *pos = (u8*)ie - (u8*)ia + (sle64_to_cpu(
+ ia->index_block_vcn) << index_vcn_size_bits) +
+ dir_ni->vol->mft_record_size;
+ /*
+ * Submit the directory entry to ntfs_filldir(), which will
+ * invoke the filldir() callback as appropriate.
+ */
+ rc = ntfs_filldir(vol, pos, ie, dirent, filldir);
+ if (rc)
+ goto err_out;
+ }
+ goto find_next_index_buffer;
+EOD:
+ /* We are finished, set *pos to EOD. */
+ *pos = i_size + vol->mft_record_size;
+done:
+ free(ia);
+ free(bmp);
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ if (ia_na)
+ ntfs_attr_close(ia_na);
+ ntfs_log_debug("EOD, *pos 0x%llx, returning 0.\n", (long long)*pos);
+ return 0;
+dir_err_out:
+ errno = EIO;
+err_out:
+ eo = errno;
+ if (rc)
+ ntfs_log_trace("filldir returned %i, *pos 0x%llx.", rc,
+ (long long)*pos);
+ ntfs_log_trace("Failed.\n");
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ free(ia);
+ free(bmp);
+ if (bmp_na)
+ ntfs_attr_close(bmp_na);
+ if (ia_na)
+ ntfs_attr_close(ia_na);
+ errno = eo;
+ return -1;
+}
+
+/**
+ * __ntfs_create - create object on ntfs volume
+ * @dir_ni: ntfs inode for directory in which create new object
+ * @name: unicode name of new object
+ * @name_len: length of the name in unicode characters
+ * @type: type of the object to create
+ * @dev: major and minor device numbers (obtained from makedev())
+ * @target: target in unicode (only for symlinks)
+ * @target_len: length of target in unicode characters
+ *
+ * Internal, use ntfs_create{,_device,_symlink} wrappers instead.
+ *
+ * @type can be:
+ * S_IFREG to create regular file
+ * S_IFDIR to create directory
+ * S_IFBLK to create block device
+ * S_IFCHR to create character device
+ * S_IFLNK to create symbolic link
+ * S_IFIFO to create FIFO
+ * S_IFSOCK to create socket
+ * other values are invalid.
+ *
+ * @dev is used only if @type is S_IFBLK or S_IFCHR, in other cases its value
+ * ignored.
+ *
+ * @target and @target_len are used only if @type is S_IFLNK, in other cases
+ * their value ignored.
+ *
+ * Return opened ntfs inode that describes created object on success or NULL
+ * on error with errno set to the error code.
+ */
+static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni,
+ ntfschar *name, u8 name_len, dev_t type, dev_t dev,
+ ntfschar *target, u8 target_len)
+{
+ ntfs_inode *ni;
+ int rollback_data = 0, rollback_sd = 0;
+ FILE_NAME_ATTR *fn = NULL;
+ STANDARD_INFORMATION *si = NULL;
+ SECURITY_DESCRIPTOR_ATTR *sd = NULL;
+ ACL *acl;
+ ACCESS_ALLOWED_ACE *ace;
+ SID *sid;
+ int err, fn_len, si_len, sd_len;
+
+ ntfs_log_trace("Entering.\n");
+
+ /* Sanity checks. */
+ if (!dir_ni || !name || !name_len) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ return NULL;
+ }
+ /* FIXME: Reparse points requires special handling. */
+ if (dir_ni->flags & FILE_ATTR_REPARSE_POINT) {
+ errno = EOPNOTSUPP;
+ return NULL;
+ }
+ /* Allocate MFT record for new file. */
+ ni = ntfs_mft_record_alloc(dir_ni->vol, NULL);
+ if (!ni) {
+ ntfs_log_error("Failed to allocate new MFT record: %s.\n",
+ strerror(errno));
+ return NULL;
+ }
+ /*
+ * Create STANDARD_INFORMATION attribute. Write STANDARD_INFORMATION
+ * version 1.2, windows will upgrade it to version 3 if needed.
+ */
+ si_len = offsetof(STANDARD_INFORMATION, u.v12.v1_end);
+ si = calloc(1, si_len);
+ if (!si) {
+ err = errno;
+ ntfs_log_error("Not enough memory.\n");
+ goto err_out;
+ }
+ si->creation_time = utc2ntfs(ni->creation_time);
+ si->last_data_change_time = utc2ntfs(ni->last_data_change_time);
+ si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
+ si->last_access_time = utc2ntfs(ni->last_access_time);
+ if (!S_ISREG(type) && !S_ISDIR(type)) {
+ si->file_attributes = FILE_ATTR_SYSTEM;
+ ni->flags = FILE_ATTR_SYSTEM;
+ }
+ /* Add STANDARD_INFORMATION to inode. */
+ if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0,
+ (u8*)si, si_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add STANDARD_INFORMATION "
+ "attribute.\n");
+ goto err_out;
+ }
+ /* Create SECURITY_DESCRIPTOR attribute (everyone has full access). */
+ /*
+ * Calculate security descriptor length. We have 2 sub-authorities in
+ * owner and group SIDs, but structure SID contain only one, so add
+ * 4 bytes to every SID.
+ */
+ sd_len = sizeof(SECURITY_DESCRIPTOR_ATTR) + 2 * (sizeof(SID) + 4) +
+ sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE);
+ sd = calloc(1, sd_len);
+ if (!sd) {
+ err = errno;
+ ntfs_log_error("Not enough memory.\n");
+ goto err_out;
+ }
+ sd->revision = 1;
+ sd->control = SE_DACL_PRESENT | SE_SELF_RELATIVE;
+ sid = (SID*)((u8*)sd + sizeof(SECURITY_DESCRIPTOR_ATTR));
+ sd->owner = cpu_to_le32((u8*)sid - (u8*)sd);
+ sid->revision = 1;
+ sid->sub_authority_count = 2;
+ sid->sub_authority[0] = cpu_to_le32(32);
+ sid->sub_authority[1] = cpu_to_le32(544);
+ sid->identifier_authority.value[5] = 5;
+ sid = (SID*)((u8*)sid + sizeof(SID) + 4);
+ sd->group = cpu_to_le32((u8*)sid - (u8*)sd);
+ sid->revision = 1;
+ sid->sub_authority_count = 2;
+ sid->sub_authority[0] = cpu_to_le32(32);
+ sid->sub_authority[1] = cpu_to_le32(544);
+ sid->identifier_authority.value[5] = 5;
+ acl = (ACL*)((u8*)sid + sizeof(SID) + 4);
+ sd->dacl = cpu_to_le32((u8*)acl - (u8*)sd);
+ acl->revision = 2;
+ acl->size = cpu_to_le16(sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE));
+ acl->ace_count = cpu_to_le16(1);
+ ace = (ACCESS_ALLOWED_ACE*)((u8*)acl + sizeof(ACL));
+ ace->type = ACCESS_ALLOWED_ACE_TYPE;
+ ace->flags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
+ ace->size = cpu_to_le16(sizeof(ACCESS_ALLOWED_ACE));
+ ace->mask = cpu_to_le32(0x1f01ff); /* FIXME */
+ ace->sid.revision = 1;
+ ace->sid.sub_authority_count = 1;
+ ace->sid.sub_authority[0] = 0;
+ ace->sid.identifier_authority.value[5] = 1;
+ /* Add SECURITY_DESCRIPTOR attribute to inode. */
+ if (ntfs_attr_add(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0,
+ (u8*)sd, sd_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add SECURITY_DESCRIPTOR "
+ "attribute.\n");
+ goto err_out;
+ }
+ rollback_sd = 1;
+ /* Add DATA/INDEX_ROOT attribute. */
+ if (S_ISDIR(type)) {
+ INDEX_ROOT *ir = NULL;
+ INDEX_ENTRY *ie;
+ int ir_len, index_len;
+
+ /* Create INDEX_ROOT attribute. */
+ index_len = sizeof(INDEX_HEADER) + sizeof(INDEX_ENTRY_HEADER);
+ ir_len = offsetof(INDEX_ROOT, index) + index_len;
+ ir = calloc(1, ir_len);
+ if (!ir) {
+ err = errno;
+ ntfs_log_error("Not enough memory.\n");
+ goto err_out;
+ }
+ ir->type = AT_FILE_NAME;
+ ir->collation_rule = COLLATION_FILE_NAME;
+ ir->index_block_size = cpu_to_le32(ni->vol->indx_record_size);
+ if (ni->vol->cluster_size <= ni->vol->indx_record_size)
+ ir->clusters_per_index_block =
+ ni->vol->indx_record_size >>
+ ni->vol->cluster_size_bits;
+ else
+ ir->clusters_per_index_block =
+ ni->vol->indx_record_size >>
+ ni->vol->sector_size_bits;
+ ir->index.entries_offset = cpu_to_le32(sizeof(INDEX_HEADER));
+ ir->index.index_length = cpu_to_le32(index_len);
+ ir->index.allocated_size = cpu_to_le32(index_len);
+ ie = (INDEX_ENTRY*)((u8*)ir + sizeof(INDEX_ROOT));
+ ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER));
+ ie->key_length = 0;
+ ie->flags = INDEX_ENTRY_END;
+ /* Add INDEX_ROOT attribute to inode. */
+ if (ntfs_attr_add(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4,
+ (u8*)ir, ir_len)) {
+ err = errno;
+ free(ir);
+ ntfs_log_error("Failed to add INDEX_ROOT attribute.\n");
+ goto err_out;
+ }
+ free(ir);
+ } else {
+ INTX_FILE *data;
+ int data_len;
+
+ switch (type) {
+ case S_IFBLK:
+ case S_IFCHR:
+ data_len = offsetof(INTX_FILE, u.s.device_end);
+ data = ntfs_malloc(data_len);
+ if (!data) {
+ err = errno;
+ goto err_out;
+ }
+ data->u.s.major = cpu_to_le64(major(dev));
+ data->u.s.minor = cpu_to_le64(minor(dev));
+ if (type == S_IFBLK)
+ data->magic = INTX_BLOCK_DEVICE;
+ if (type == S_IFCHR)
+ data->magic = INTX_CHARACTER_DEVICE;
+ break;
+ case S_IFLNK:
+ data_len = sizeof(INTX_FILE_TYPES) +
+ target_len * sizeof(ntfschar);
+ data = ntfs_malloc(data_len);
+ if (!data) {
+ err = errno;
+ goto err_out;
+ }
+ data->magic = INTX_SYMBOLIC_LINK;
+ memcpy(data->u.target, target,
+ target_len * sizeof(ntfschar));
+ break;
+ case S_IFSOCK:
+ data = NULL;
+ data_len = 1;
+ break;
+ default: /* FIFO or regular file. */
+ data = NULL;
+ data_len = 0;
+ break;
+ }
+ /* Add DATA attribute to inode. */
+ if (ntfs_attr_add(ni, AT_DATA, AT_UNNAMED, 0, (u8*)data,
+ data_len)) {
+ err = errno;
+ free(data);
+ ntfs_log_error("Failed to add DATA attribute.\n");
+ goto err_out;
+ }
+ rollback_data = 1;
+ free(data);
+ }
+ /* Create FILE_NAME attribute. */
+ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
+ fn = ntfs_calloc(fn_len);
+ if (!fn) {
+ err = errno;
+ goto err_out;
+ }
+ fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number));
+ fn->file_name_length = name_len;
+ fn->file_name_type = FILE_NAME_POSIX;
+ if (S_ISDIR(type))
+ fn->file_attributes = FILE_ATTR_I30_INDEX_PRESENT;
+ if (!S_ISREG(type) && !S_ISDIR(type))
+ fn->file_attributes = FILE_ATTR_SYSTEM;
+ fn->creation_time = utc2ntfs(ni->creation_time);
+ fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
+ fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
+ fn->last_access_time = utc2ntfs(ni->last_access_time);
+ memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
+ /* Add FILE_NAME attribute to inode. */
+ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
+ err = errno;
+ ntfs_log_error("Failed to add FILE_NAME attribute.\n");
+ goto err_out;
+ }
+ /* Add FILE_NAME attribute to index. */
+ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
+ le16_to_cpu(ni->mrec->sequence_number)))) {
+ err = errno;
+ ntfs_log_perror("Failed to add entry to the index");
+ goto err_out;
+ }
+ /* Set hard links count and directory flag. */
+ ni->mrec->link_count = cpu_to_le16(1);
+ if (S_ISDIR(type))
+ ni->mrec->flags |= MFT_RECORD_IS_DIRECTORY;
+ ntfs_inode_mark_dirty(ni);
+ /* Done! */
+ free(fn);
+ free(si);
+ free(sd);
+ ntfs_log_trace("Done.\n");
+ return ni;
+err_out:
+ ntfs_log_trace("Failed.\n");
+ if (rollback_sd) {
+ ntfs_attr *na;
+
+ na = ntfs_attr_open(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0);
+ if (!na)
+ ntfs_log_perror("Failed to open SD (0x50) attribute of "
+ " inode 0x%llx. Run chkdsk.\n",
+ (unsigned long long)ni->mft_no);
+ else if (ntfs_attr_rm(na))
+ ntfs_log_perror("Failed to remove SD (0x50) attribute "
+ "of inode 0x%llx. Run chkdsk.\n",
+ (unsigned long long)ni->mft_no);
+ }
+ if (rollback_data) {
+ ntfs_attr *na;
+
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na)
+ ntfs_log_perror("Failed to open data attribute of "
+ " inode 0x%llx. Run chkdsk.\n",
+ (unsigned long long)ni->mft_no);
+ else if (ntfs_attr_rm(na))
+ ntfs_log_perror("Failed to remove data attribute of "
+ "inode 0x%llx. Run chkdsk.\n",
+ (unsigned long long)ni->mft_no);
+ }
+ /*
+ * Free extent MFT records (should not exist any with current
+ * ntfs_create implementation, but for any case if something will be
+ * changed in the future).
+ */
+ while (ni->nr_extents)
+ if (ntfs_mft_record_free(ni->vol, *(ni->u.extent_nis))) {
+ err = errno;
+ ntfs_log_error("Failed to free extent MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ if (ntfs_mft_record_free(ni->vol, ni))
+ ntfs_log_error("Failed to free MFT record. "
+ "Leaving inconsistent metadata. Run chkdsk.\n");
+ free(fn);
+ free(si);
+ free(sd);
+ errno = err;
+ return NULL;
+}
+
+/**
+ * Some wrappers around __ntfs_create() ...
+ */
+
+ntfs_inode *ntfs_create(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
+ dev_t type)
+{
+ if (type != S_IFREG && type != S_IFDIR && type != S_IFIFO &&
+ type != S_IFSOCK) {
+ ntfs_log_error("Invalid arguments.\n");
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, name, name_len, type, 0, NULL, 0);
+}
+
+ntfs_inode *ntfs_create_device(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
+ dev_t type, dev_t dev)
+{
+ if (type != S_IFCHR && type != S_IFBLK) {
+ ntfs_log_error("Invalid arguments.\n");
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, name, name_len, type, dev, NULL, 0);
+}
+
+ntfs_inode *ntfs_create_symlink(ntfs_inode *dir_ni, ntfschar *name, u8 name_len,
+ ntfschar *target, u8 target_len)
+{
+ if (!target || !target_len) {
+ ntfs_log_error("Invalid arguments.\n");
+ return NULL;
+ }
+ return __ntfs_create(dir_ni, name, name_len, S_IFLNK, 0,
+ target, target_len);
+}
+
+/**
+ * ntfs_delete - delete file or directory from ntfs volume
+ * @pni: ntfs inode for object to delete
+ * @dir_ni: ntfs inode for directory in which delete object
+ * @name: unicode name of the object to delete
+ * @name_len: length of the name in unicode characters
+ *
+ * @pni is pointer to pointer to ntfs_inode structure. Upon successful
+ * completion and if inode is really deleted (there are no more links left to
+ * it) this function will close @*pni and set it to NULL, in the other cases
+ * @*pni will stay opened.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_delete(ntfs_inode **pni, ntfs_inode *dir_ni, ntfschar *name,
+ u8 name_len)
+{
+ ntfs_attr_search_ctx *actx = NULL;
+ ntfs_index_context *ictx = NULL;
+ ntfs_inode *ni;
+ FILE_NAME_ATTR *fn = NULL;
+ BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE;
+ BOOL case_sensitive_match = TRUE;
+ int err = 0;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!pni || !(ni = *pni) || !dir_ni || !name || !name_len ||
+ ni->nr_extents == -1 || dir_ni->nr_extents == -1) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ if (ni->nr_references > 1 && le16_to_cpu(ni->mrec->link_count) == 1) {
+ ntfs_log_error("Trying to deleting inode with left "
+ "references.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ /*
+ * Search for FILE_NAME attribute with such name. If it's in POSIX or
+ * WIN32_AND_DOS namespace, then simply remove it from index and inode.
+ * If filename in DOS or in WIN32 namespace, then remove DOS name first,
+ * only then remove WIN32 name. Mark WIN32 name as POSIX name to prevent
+ * chkdsk to complain about DOS name absence in case if DOS name had
+ * been successfully deleted, but WIN32 name remove failed.
+ */
+ actx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!actx)
+ goto err_out;
+search:
+ while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE,
+ 0, NULL, 0, actx)) {
+ errno = 0;
+ fn = (FILE_NAME_ATTR*)((u8*)actx->attr +
+ le16_to_cpu(actx->attr->u.res.value_offset));
+ ntfs_log_trace("Found filename with instance number %d.\n",
+ le16_to_cpu(actx->attr->instance));
+ if (looking_for_dos_name) {
+ if (fn->file_name_type == FILE_NAME_DOS)
+ break;
+ else
+ continue;
+ }
+ if (looking_for_win32_name) {
+ if (fn->file_name_type == FILE_NAME_WIN32)
+ break;
+ else
+ continue;
+ }
+ if (dir_ni->mft_no == MREF_LE(fn->parent_directory) &&
+ ntfs_names_are_equal(fn->file_name,
+ fn->file_name_length, name,
+ name_len, case_sensitive_match ?
+ CASE_SENSITIVE : IGNORE_CASE, ni->vol->upcase,
+ ni->vol->upcase_len)) {
+ if (fn->file_name_type == FILE_NAME_WIN32) {
+ looking_for_dos_name = TRUE;
+ ntfs_attr_reinit_search_ctx(actx);
+ ntfs_log_trace("Restart search. "
+ "Looking for DOS name.\n");
+ continue;
+ }
+ if (fn->file_name_type == FILE_NAME_DOS)
+ looking_for_dos_name = TRUE;
+ break;
+ }
+ }
+ if (errno) {
+ /*
+ * If case sensitive search failed and volume mounted case
+ * insensitive, then try once again ignoring case.
+ */
+ if (errno == ENOENT && !NVolCaseSensitive(ni->vol) &&
+ case_sensitive_match) {
+ case_sensitive_match = FALSE;
+ ntfs_attr_reinit_search_ctx(actx);
+ ntfs_log_trace("Restart search. Ignore case.");
+ goto search;
+ }
+ ntfs_log_error("Failed to find requested filename in FILE_NAME "
+ "attributes that belong to this inode.\n");
+ goto err_out;
+ }
+ /* If deleting directory check it to be empty. */
+ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
+ ntfs_attr *na;
+
+ na = ntfs_attr_open(ni, AT_INDEX_ROOT, NTFS_INDEX_I30, 4);
+ if (!na) {
+ ntfs_log_error("Corrupt directory or library bug.\n");
+ errno = EIO;
+ goto err_out;
+ }
+ /*
+ * Do not allow non-empty directory deletion if hard links count
+ * is 1 (always) or 2 (in case if filename in DOS namespace,
+ * because we delete it first in file which have both WIN32 and
+ * DOS names).
+ */
+ if ((na->data_size != sizeof(INDEX_ROOT) + sizeof(
+ INDEX_ENTRY_HEADER)) && (le16_to_cpu(
+ ni->mrec->link_count) == 1 ||
+ (le16_to_cpu(ni->mrec->link_count) == 2 &&
+ fn->file_name_type == FILE_NAME_DOS))) {
+ ntfs_attr_close(na);
+ ntfs_log_error("Directory is not empty.\n");
+ errno = ENOTEMPTY;
+ goto err_out;
+ }
+ ntfs_attr_close(na);
+ }
+ /* One more sanity check. */
+ if (ni->nr_references > 1 && looking_for_dos_name &&
+ le16_to_cpu(ni->mrec->link_count) == 2) {
+ ntfs_log_error("Trying to deleting inode with left "
+ "references.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ ntfs_log_trace("Found!\n");
+ /* Search for such FILE_NAME in index. */
+ ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
+ if (!ictx)
+ goto err_out;
+ if (ntfs_index_lookup(fn, le32_to_cpu(actx->attr->u.res.value_length), ictx))
+ goto err_out;
+ /* Set namespace to POSIX for WIN32 name. */
+ if (fn->file_name_type == FILE_NAME_WIN32) {
+ fn->file_name_type = FILE_NAME_POSIX;
+ ntfs_inode_mark_dirty(actx->ntfs_ino);
+ ((FILE_NAME_ATTR*)ictx->data)->file_name_type = FILE_NAME_POSIX;
+ ntfs_index_entry_mark_dirty(ictx);
+ }
+ /* Do not support reparse point deletion yet. */
+ if (((FILE_NAME_ATTR*)ictx->data)->file_attributes &
+ FILE_ATTR_REPARSE_POINT) {
+ errno = EOPNOTSUPP;
+ goto err_out;
+ }
+ /* Remove FILE_NAME from index. */
+ if (ntfs_index_rm(ictx))
+ goto err_out;
+
+ /* Remove FILE_NAME from inode. */
+ if (ntfs_attr_record_rm(actx))
+ goto err_out;
+ /* Decrement hard link count. */
+ ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
+ ni->mrec->link_count) - 1);
+ ntfs_inode_mark_dirty(ni);
+ if (looking_for_dos_name) {
+ looking_for_dos_name = FALSE;
+ looking_for_win32_name = TRUE;
+ ntfs_attr_reinit_search_ctx(actx);
+ ntfs_log_trace("DOS name deleted. "
+ "Now search for WIN32 name.\n");
+ goto search;
+ } else
+ ntfs_log_trace("Deleted.\n");
+ /* TODO: Update object id, quota and security indexes if required. */
+ /*
+ * If hard link count is not equal to zero then we are done. In other
+ * case there are no reference to this inode left, so we should free all
+ * non-resident attributes and mark all MFT record as not in use.
+ */
+ if (ni->mrec->link_count)
+ goto out;
+ ntfs_attr_reinit_search_ctx(actx);
+ while (!ntfs_attrs_walk(actx)) {
+ if (actx->attr->non_resident) {
+ runlist *rl;
+
+ rl = ntfs_mapping_pairs_decompress(ni->vol, actx->attr,
+ NULL);
+ if (!rl) {
+ err = errno;
+ ntfs_log_error("Failed to decompress runlist. "
+ "Leaving inconsistent "
+ "metadata.\n");
+ continue;
+ }
+ if (ntfs_cluster_free_from_rl(ni->vol, rl)) {
+ err = errno;
+ ntfs_log_error("Failed to free clusters. "
+ "Leaving inconsistent "
+ "metadata.\n");
+ continue;
+ }
+ free(rl);
+ }
+ }
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_error("Attribute enumeration failed. "
+ "Probably leaving inconsistent metadata.\n");
+ }
+ /* All extents should be attached after attribute walk. */
+ while (ni->nr_extents)
+ if (ntfs_mft_record_free(ni->vol, *(ni->u.extent_nis))) {
+ err = errno;
+ ntfs_log_error("Failed to free extent MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ if (ntfs_mft_record_free(ni->vol, ni)) {
+ err = errno;
+ ntfs_log_error("Failed to free base MFT record. "
+ "Leaving inconsistent metadata.\n");
+ }
+ *pni = NULL;
+out:
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ if (ictx)
+ ntfs_index_ctx_put(ictx);
+ if (err) {
+ ntfs_log_error("%s(): Failed.\n", "ntfs_delete");
+ errno = err;
+ return -1;
+ }
+ ntfs_log_trace("Done.\n");
+ return 0;
+err_out:
+ err = errno;
+ goto out;
+}
+
+/**
+ * ntfs_link - create hard link for file or directory
+ * @ni: ntfs inode for object to create hard link
+ * @dir_ni: ntfs inode for directory in which new link should be placed
+ * @name: unicode name of the new link
+ * @name_len: length of the name in unicode characters
+ *
+ * NOTE: At present we allow creating hard links to directories, we use them
+ * in a temporary state during rename. But it's definitely bad idea to have
+ * hard links to directories as a result of operation.
+ * FIXME: Create internal __ntfs_link that allows hard links to a directories
+ * and external ntfs_link that do not. Write ntfs_rename that uses __ntfs_link.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len)
+{
+ FILE_NAME_ATTR *fn = NULL;
+ int fn_len, err;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni || !dir_ni || !name || !name_len ||
+ ni->mft_no == dir_ni->mft_no) {
+ err = EINVAL;
+ ntfs_log_error("Invalid arguments.");
+ goto err_out;
+ }
+ /* FIXME: Reparse points requires special handling. */
+ if (ni->flags & FILE_ATTR_REPARSE_POINT) {
+ err = EOPNOTSUPP;
+ goto err_out;
+ }
+ /* Create FILE_NAME attribute. */
+ fn_len = sizeof(FILE_NAME_ATTR) + name_len * sizeof(ntfschar);
+ fn = ntfs_calloc(fn_len);
+ if (!fn) {
+ err = errno;
+ goto err_out;
+ }
+ fn->parent_directory = MK_LE_MREF(dir_ni->mft_no,
+ le16_to_cpu(dir_ni->mrec->sequence_number));
+ fn->file_name_length = name_len;
+ fn->file_name_type = FILE_NAME_POSIX;
+ fn->file_attributes = ni->flags;
+ if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
+ fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT;
+ fn->allocated_size = cpu_to_sle64(ni->allocated_size);
+ fn->data_size = cpu_to_sle64(ni->data_size);
+ fn->creation_time = utc2ntfs(ni->creation_time);
+ fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
+ fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
+ fn->last_access_time = utc2ntfs(ni->last_access_time);
+ memcpy(fn->file_name, name, name_len * sizeof(ntfschar));
+ /* Add FILE_NAME attribute to index. */
+ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no,
+ le16_to_cpu(ni->mrec->sequence_number)))) {
+ err = errno;
+ ntfs_log_error("Failed to add entry to the index.\n");
+ goto err_out;
+ }
+ /* Add FILE_NAME attribute to inode. */
+ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) {
+ ntfs_index_context *ictx;
+
+ err = errno;
+ ntfs_log_error("Failed to add FILE_NAME attribute.\n");
+ /* Try to remove just added attribute from index. */
+ ictx = ntfs_index_ctx_get(dir_ni, NTFS_INDEX_I30, 4);
+ if (!ictx)
+ goto rollback_failed;
+ if (ntfs_index_lookup(fn, fn_len, ictx)) {
+ ntfs_index_ctx_put(ictx);
+ goto rollback_failed;
+ }
+ if (ntfs_index_rm(ictx)) {
+ ntfs_index_ctx_put(ictx);
+ goto rollback_failed;
+ }
+ goto err_out;
+ }
+ /* Increment hard links count. */
+ ni->mrec->link_count = cpu_to_le16(le16_to_cpu(
+ ni->mrec->link_count) + 1);
+ /* Done! */
+ ntfs_inode_mark_dirty(ni);
+ free(fn);
+ ntfs_log_trace("Done.\n");
+ return 0;
+rollback_failed:
+ ntfs_log_error("Rollback failed. Leaving inconsistent metadata.\n");
+err_out:
+ ntfs_log_error("%s(): Failed.\n", "ntfs_link");
+ free(fn);
+ errno = err;
+ return -1;
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/gnome-vfs-method.c b/usr/src/lib/libntfs/common/libntfs/gnome-vfs-method.c
new file mode 100644
index 0000000000..f25f46d0cc
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/gnome-vfs-method.c
@@ -0,0 +1,915 @@
+/*
+ * gnome-vfs-method.c - Gnome-VFS init/shutdown implementation of interface to
+ * libntfs. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * Copyright (c) 2003-2006 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 "config.h"
+
+#undef FALSE
+#undef TRUE
+#include "types.h" /* for 'FALSE'/'TRUE' libntfs definition */
+#define FALSE FALSE
+#define TRUE TRUE
+
+#include "gnome-vfs-method.h" /* self */
+#include <libgnomevfs/gnome-vfs-method.h>
+#include <glib/gmessages.h>
+#include "gnome-vfs-module.h"
+#include <glib/ghash.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <libgnomevfs/gnome-vfs-utils.h>
+
+#include "volume.h"
+#include "dir.h"
+
+static GnomeVFSMethod GnomeVFSMethod_static;
+G_LOCK_DEFINE_STATIC(GnomeVFSMethod_static);
+
+/* map: (gchar *)method_name -> (struct method_name_info *) */
+static GHashTable *method_name_hash;
+G_LOCK_DEFINE_STATIC(method_name_hash);
+
+#ifdef __sun
+G_LOCK_DEFINE(libntfs);
+#endif
+
+struct method_name_info {
+ gchar *args;
+};
+
+static void method_name_hash_key_destroy_func(gchar *key)
+{
+ g_return_if_fail(key != NULL);
+
+ g_free(key);
+}
+
+static void method_name_hash_value_destroy_func(struct method_name_info *value)
+{
+ g_return_if_fail(value != NULL);
+
+ g_free(value->args);
+ g_free(value);
+}
+
+static void method_name_hash_init(void)
+{
+ G_LOCK(method_name_hash);
+ if (!method_name_hash) {
+ method_name_hash = g_hash_table_new_full(
+ g_str_hash, /* hash_func */
+ g_str_equal, /* key_equal_func */
+ (GDestroyNotify) method_name_hash_key_destroy_func, /* key_destroy_func */
+ (GDestroyNotify) method_name_hash_value_destroy_func); /* value_destroy_func */
+ }
+ G_UNLOCK(method_name_hash);
+}
+
+/*
+ * map: (gchar *)uri_parent_string "method_name:uri_parent" -> (ntfs_volume *)
+ */
+static GHashTable *uri_parent_string_hash;
+G_LOCK_DEFINE_STATIC(uri_parent_string_hash);
+
+static void uri_parent_string_hash_key_destroy_func(gchar *key)
+{
+ g_return_if_fail(key != NULL);
+
+ g_free(key);
+}
+
+static void uri_parent_string_hash_value_destroy_func(ntfs_volume *value)
+{
+ g_return_if_fail(value != NULL);
+
+ ntfs_umount( /* errors ignored */
+ value, /* vol */
+ TRUE); /* force; possibly loose modifications */
+}
+
+static void uri_parent_string_hash_init(void)
+{
+ G_LOCK(uri_parent_string_hash);
+ if (!uri_parent_string_hash) {
+ uri_parent_string_hash = g_hash_table_new_full(
+ g_str_hash, /* hash_func */
+ g_str_equal, /* key_equal_func */
+ (GDestroyNotify) uri_parent_string_hash_key_destroy_func, /* key_destroy_func */
+ (GDestroyNotify) uri_parent_string_hash_value_destroy_func); /* value_destroy_func */
+ }
+ G_UNLOCK(uri_parent_string_hash);
+}
+
+static GnomeVFSResult libntfs_gnomevfs_uri_parent_init(
+ ntfs_volume **volume_return, GnomeVFSURI *uri)
+{
+ gchar *uri_parent_string;
+ gchar *uri_parent_string_parent;
+ ntfs_volume *volume;
+
+ g_return_val_if_fail(uri != NULL, GNOME_VFS_ERROR_INVALID_URI);
+ g_return_val_if_fail(volume_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ uri_parent_string_hash_init();
+
+ if (!uri->parent)
+ return GNOME_VFS_ERROR_INVALID_URI;
+ if (!uri->text) /* not needed here but we don't permit non-specific fs-image reference */
+ return GNOME_VFS_ERROR_INVALID_URI;
+ uri_parent_string_parent = gnome_vfs_uri_to_string(uri->parent,
+ GNOME_VFS_URI_HIDE_NONE);
+ g_assert(uri_parent_string_parent != NULL);
+
+ uri_parent_string = g_strdup_printf("%s:%s", uri->method_string,
+ uri_parent_string_parent);
+ g_assert(uri_parent_string != NULL);
+
+ G_LOCK(uri_parent_string_hash);
+ volume = g_hash_table_lookup(uri_parent_string_hash, uri_parent_string);
+ G_UNLOCK(uri_parent_string_hash);
+ if (!volume) {
+ struct method_name_info *method_name_info;
+
+ G_LOCK(method_name_hash);
+ method_name_info = g_hash_table_lookup(method_name_hash,
+ uri->method_string);
+ G_UNLOCK(method_name_hash);
+ if (!method_name_info) {
+ /* should not happend */
+ g_return_val_if_reached(GNOME_VFS_ERROR_INVALID_URI);
+ }
+
+ /* TODO: Generic GnomeVFS filter. */
+ if (strcmp(uri->parent->method_string, "file")) {
+ g_free(uri_parent_string);
+ return GNOME_VFS_ERROR_INVALID_URI;
+ }
+
+ if (!(volume = ntfs_mount(uri->parent->text,
+ NTFS_MNT_RDONLY))) {
+ g_free(uri_parent_string);
+ return GNOME_VFS_ERROR_WRONG_FORMAT;
+ }
+
+ G_LOCK(uri_parent_string_hash);
+ g_hash_table_insert(uri_parent_string_hash,
+ g_strdup(uri_parent_string), volume);
+ G_UNLOCK(uri_parent_string_hash);
+ }
+ g_free(uri_parent_string);
+
+ *volume_return = volume;
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult inode_open_by_pathname(ntfs_inode **inode_return,
+ ntfs_volume *volume, const gchar *pathname)
+{
+ MFT_REF mref;
+ ntfs_inode *inode;
+ gchar *pathname_parse, *pathname_next;
+ int errint;
+
+ g_return_val_if_fail(inode_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(volume != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(pathname != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ pathname = g_path_skip_root(pathname);
+ pathname_parse = g_alloca(strlen(pathname) + 1);
+ strcpy(pathname_parse, pathname);
+ mref = FILE_root;
+ for (;;) {
+ ntfschar *pathname_parse_ucs2;
+ gchar *pathname_parse_unescaped;
+ int i;
+
+ G_LOCK(libntfs);
+ inode = ntfs_inode_open(volume, mref);
+ G_UNLOCK(libntfs);
+ if (!inode)
+ return GNOME_VFS_ERROR_NOT_FOUND;
+ if (!*pathname_parse) {
+ *inode_return = inode;
+ return GNOME_VFS_OK;
+ }
+ for (pathname_next = pathname_parse; *pathname_next &&
+ *pathname_next != G_DIR_SEPARATOR; pathname_next++) ;
+ if (*pathname_next) {
+ /* terminate current path element */
+ *pathname_next++ = 0;
+ }
+ while (*pathname_next == G_DIR_SEPARATOR)
+ pathname_next++;
+ /* FIXME: Is 'pathname' utf8? */
+ pathname_parse_unescaped = gnome_vfs_unescape_string(
+ pathname_parse, NULL); /* illegal_characters */
+#ifdef __sun
+ pathname_parse_ucs2 = g_malloc(strlen(pathname_parse_unescaped) + 1);
+#else /* !__sun */
+ libntfs_newn(pathname_parse_ucs2,
+ strlen(pathname_parse_unescaped) + 1);
+#endif /* __sun */
+ for (i = 0; pathname_parse_unescaped[i]; i++)
+ pathname_parse_ucs2[i] = cpu_to_le16(
+ pathname_parse_unescaped[i]);
+ pathname_parse_ucs2[i] = 0;
+ g_free(pathname_parse_unescaped);
+ G_LOCK(libntfs);
+ mref = ntfs_inode_lookup_by_name(inode, pathname_parse_ucs2, i);
+ G_UNLOCK(libntfs);
+ g_free(pathname_parse_ucs2);
+ if ((MFT_REF)-1 == mref)
+ return GNOME_VFS_ERROR_NOT_FOUND;
+ G_LOCK(libntfs);
+ errint = ntfs_inode_close(inode);
+ G_UNLOCK(libntfs);
+ if (errint)
+ g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
+ pathname_parse = pathname_next;
+ }
+ /* NOTREACHED */
+}
+
+struct libntfs_directory {
+ ntfs_inode *inode;
+ GList *file_info_list; /* of (GnomeVFSFileInfo *); last item has ->data == NULL */
+};
+
+static GnomeVFSResult libntfs_gnomevfs_open_directory(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle **method_handle, GnomeVFSURI *uri,
+ GnomeVFSFileInfoOptions options __attribute__((unused)),
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ ntfs_volume *volume;
+ ntfs_inode *inode;
+ struct libntfs_directory *libntfs_directory;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(method_handle != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_uri_parent_init(&volume, uri)))
+ return errvfsresult;
+
+ if (GNOME_VFS_OK != (errvfsresult = inode_open_by_pathname(&inode,
+ volume, uri->text)))
+ return errvfsresult;
+
+#ifdef __sun
+ libntfs_directory = g_new(struct libntfs_directory, 1);
+#else /* !__sun */
+ libntfs_new(libntfs_directory);
+#endif /* __sun */
+
+ libntfs_directory->inode = inode;
+ libntfs_directory->file_info_list = NULL;
+
+ *method_handle = (GnomeVFSMethodHandle *)libntfs_directory;
+ return errvfsresult;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_close_directory(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ struct libntfs_directory *libntfs_directory;
+ int errint;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_directory = (struct libntfs_directory *)method_handle;
+ g_return_val_if_fail(libntfs_directory != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ G_LOCK(libntfs);
+ errint = ntfs_inode_close(libntfs_directory->inode);
+ G_UNLOCK(libntfs);
+ if (errint)
+ g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
+
+ if (libntfs_directory->file_info_list) {
+ GList *last_l;
+
+ /*
+ * Prevent gnome_vfs_file_info_list_free() and its
+ * gnome_vfs_file_info_unref() on the last 'file_info_list'
+ * items as it is EOF with NULL '->data'.
+ */
+ last_l = g_list_last(libntfs_directory->file_info_list);
+ g_assert(last_l->data == NULL);
+ libntfs_directory->file_info_list = g_list_delete_link(
+ libntfs_directory->file_info_list, last_l);
+ gnome_vfs_file_info_list_free(
+ libntfs_directory->file_info_list);
+ }
+
+ g_free(libntfs_directory);
+
+ return GNOME_VFS_OK;
+}
+
+static gchar *libntfs_ntfscharo_utf8(const ntfschar *name, const int name_len)
+{
+ GString *gstring;
+ int i;
+
+ gstring = g_string_sized_new(name_len);
+ for (i = 0; i < name_len; i++)
+ gstring = g_string_append_unichar(gstring,
+ le16_to_cpu(name[i]));
+ return g_string_free(gstring, /* returns utf8-formatted string */
+ FALSE); /* free_segment */
+}
+
+/*
+ * Do not lock 'libntfs' here as we are already locked inside ntfs_readdir().
+ */
+static int libntfs_gnomevfs_read_directory_filldir(
+ struct libntfs_directory *libntfs_directory /* dirent */,
+ const ntfschar *name, const int name_len,
+ const int name_type __attribute__((unused)),
+ const s64 pos, const MFT_REF mref, const unsigned dt_type)
+{
+ GnomeVFSFileInfo *file_info;
+
+ g_return_val_if_fail(libntfs_directory != NULL, -1);
+ g_return_val_if_fail(name != NULL, -1);
+ g_return_val_if_fail(name_len >= 0, -1);
+ g_return_val_if_fail(pos >= 0, -1);
+
+ /* system directory */
+ if (MREF(mref) != FILE_root && MREF(mref) < FILE_first_user)
+ return 0; /* continue traversal */
+
+ file_info = gnome_vfs_file_info_new();
+ file_info->name = libntfs_ntfscharo_utf8(name, name_len);
+ file_info->valid_fields = 0;
+
+ switch (dt_type) {
+ case NTFS_DT_FIFO:
+ file_info->type = GNOME_VFS_FILE_TYPE_FIFO;
+ break;
+ case NTFS_DT_CHR:
+ file_info->type = GNOME_VFS_FILE_TYPE_CHARACTER_DEVICE;
+ break;
+ case NTFS_DT_DIR:
+ file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
+ break;
+ case NTFS_DT_BLK:
+ file_info->type = GNOME_VFS_FILE_TYPE_BLOCK_DEVICE;
+ break;
+ case NTFS_DT_REG:
+ file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
+ break;
+ case NTFS_DT_LNK:
+ file_info->type = GNOME_VFS_FILE_TYPE_SYMBOLIC_LINK;
+ break;
+ case NTFS_DT_SOCK:
+ file_info->type = GNOME_VFS_FILE_TYPE_SOCKET;
+ break;
+ /* FIXME: What is 'NTFS_DT_WHT'? */
+ default:
+ file_info->type = GNOME_VFS_FILE_TYPE_UNKNOWN;
+ }
+ if (file_info->type != GNOME_VFS_FILE_TYPE_UNKNOWN)
+ file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_TYPE;
+
+ /* Detect 'file_info->size': */
+ if (file_info->type == GNOME_VFS_FILE_TYPE_REGULAR) {
+ ntfs_inode *inode;
+
+ inode = ntfs_inode_open(libntfs_directory->inode->vol, mref);
+ /* FIXME: Check failed 'inode' open. */
+ if (inode) {
+ ntfs_attr *attr;
+ int errint;
+
+ attr = ntfs_attr_open(inode, /* ni */
+ AT_DATA, /* type */
+ AT_UNNAMED, /* name */
+ 0); /* name_len */
+ /* FIXME: Check failed 'attr' open. */
+ if (attr) {
+ /* FIXME: Is 'data_size' the right field? */
+ file_info->size = attr->data_size;
+ file_info->valid_fields |=
+ GNOME_VFS_FILE_INFO_FIELDS_SIZE;
+ ntfs_attr_close(attr);
+ }
+ errint = ntfs_inode_close(inode);
+ /* FIXME: Check 'errint'. */
+ }
+ }
+
+ libntfs_directory->file_info_list = g_list_prepend(
+ libntfs_directory->file_info_list, file_info);
+
+ return 0; /* continue traversal */
+}
+
+static GnomeVFSResult libntfs_gnomevfs_read_directory(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle,
+ GnomeVFSFileInfo *file_info,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ struct libntfs_directory *libntfs_directory;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_directory = (struct libntfs_directory *)method_handle;
+ g_return_val_if_fail(libntfs_directory != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (!libntfs_directory->file_info_list) {
+ int errint;
+ s64 pos;
+
+ pos = 0; /* read from the start; incl. "." and ".." entries */
+ G_LOCK(libntfs);
+ errint = ntfs_readdir(libntfs_directory->inode, /* dir_ni */
+ &pos, /* pos */
+ libntfs_directory, /* dirent */
+ (ntfs_filldir_t)libntfs_gnomevfs_read_directory_filldir); /* filldir */
+ G_UNLOCK(libntfs);
+ if (errint)
+ return GNOME_VFS_ERROR_INTERNAL;
+
+ libntfs_directory->file_info_list = g_list_prepend(
+ libntfs_directory->file_info_list, NULL); /* EOF */
+ libntfs_directory->file_info_list = g_list_reverse(
+ libntfs_directory->file_info_list);
+ }
+
+ if (!libntfs_directory->file_info_list->data) {
+ g_assert(libntfs_directory->file_info_list->next == NULL);
+ /*
+ * Do not clear the list to leave us stuck at EOF - GnomeVFS
+ * behaves that way.
+ */
+ errvfsresult = GNOME_VFS_ERROR_EOF;
+ } else {
+ /* Cut first list item. */
+ gnome_vfs_file_info_copy(file_info, /* dest */
+ libntfs_directory->file_info_list->data); /* src */
+ gnome_vfs_file_info_unref(
+ libntfs_directory->file_info_list->data);
+ libntfs_directory->file_info_list = g_list_delete_link(
+ libntfs_directory->file_info_list,
+ libntfs_directory->file_info_list);
+ errvfsresult = GNOME_VFS_OK;
+ }
+ return errvfsresult;
+}
+
+struct libntfs_file {
+ ntfs_inode *inode;
+ ntfs_attr *attr;
+ s64 pos;
+};
+
+static GnomeVFSResult libntfs_open_attr(struct libntfs_file *libntfs_file)
+{
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(libntfs_file->inode != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (!libntfs_file->attr) {
+ G_LOCK(libntfs);
+ libntfs_file->attr = ntfs_attr_open(
+ libntfs_file->inode, /* ni */
+ AT_DATA, /* type */
+ AT_UNNAMED, /* name */
+ 0); /* name_len */
+ G_UNLOCK(libntfs);
+ if (!libntfs_file->attr)
+ return GNOME_VFS_ERROR_BAD_FILE;
+ libntfs_file->pos = 0;
+ }
+
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_open(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle **method_handle_return, GnomeVFSURI *uri,
+ GnomeVFSOpenMode mode,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ ntfs_volume *volume;
+ ntfs_inode *inode;
+ struct libntfs_file *libntfs_file;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(method_handle_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_uri_parent_init(&volume, uri)))
+ return errvfsresult;
+
+ if (mode & GNOME_VFS_OPEN_WRITE)
+ return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM;
+
+ if (GNOME_VFS_OK != (errvfsresult =
+ inode_open_by_pathname(&inode, volume, uri->text)))
+ return errvfsresult;
+
+#ifdef __sun
+ libntfs_file = g_new(struct libntfs_file, 1);
+#else /* !__sun */
+ libntfs_new(libntfs_file);
+#endif /* __sun */
+
+ libntfs_file->inode = inode;
+ libntfs_file->attr = NULL;
+
+ *method_handle_return = (GnomeVFSMethodHandle *)libntfs_file;
+ return errvfsresult;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_create(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle **method_handle_return, GnomeVFSURI *uri,
+ GnomeVFSOpenMode mode __attribute__((unused)),
+ gboolean exclusive __attribute__((unused)),
+ guint perm __attribute__((unused)),
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ ntfs_volume *volume;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(method_handle_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_uri_parent_init(&volume, uri)))
+ return errvfsresult;
+
+ return GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_close(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ struct libntfs_file *libntfs_file;
+ int errint;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_file = (struct libntfs_file *) method_handle;
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (libntfs_file->attr) {
+ G_LOCK(libntfs);
+ ntfs_attr_close(libntfs_file->attr);
+ G_UNLOCK(libntfs);
+ }
+ G_LOCK(libntfs);
+ errint = ntfs_inode_close(libntfs_file->inode);
+ G_UNLOCK(libntfs);
+ if (errint)
+ g_return_val_if_reached(GNOME_VFS_ERROR_INTERNAL);
+
+ g_free(libntfs_file);
+
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_read(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle, gpointer buffer,
+ GnomeVFSFileSize num_bytes, GnomeVFSFileSize *bytes_read_return,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ struct libntfs_file *libntfs_file;
+ s64 count_s64, got;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_file = (struct libntfs_file *)method_handle;
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(buffer != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(bytes_read_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file)))
+ return errvfsresult;
+
+ count_s64 = num_bytes;
+ g_assert((GnomeVFSFileSize)count_s64 == num_bytes);
+ G_LOCK(libntfs);
+ got = ntfs_attr_pread(libntfs_file->attr, libntfs_file->pos, count_s64,
+ buffer);
+ G_UNLOCK(libntfs);
+ if (got == -1)
+ return GNOME_VFS_ERROR_IO;
+
+ libntfs_file->pos += got;
+ *bytes_read_return = got;
+ g_assert((s64)*bytes_read_return == got);
+
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_seek(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle,
+ GnomeVFSSeekPosition whence, GnomeVFSFileOffset offset,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ struct libntfs_file *libntfs_file;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_file = (struct libntfs_file *)method_handle;
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file)))
+ return errvfsresult;
+
+ switch (whence) {
+ case GNOME_VFS_SEEK_START:
+ libntfs_file->pos = offset;
+ break;
+ case GNOME_VFS_SEEK_CURRENT:
+ libntfs_file->pos += offset;
+ break;
+ case GNOME_VFS_SEEK_END:
+ /* FIXME: NOT IMPLEMENTED YET */
+ g_return_val_if_reached(GNOME_VFS_ERROR_BAD_PARAMETERS);
+ default:
+ g_assert_not_reached();
+ }
+
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_tell(GnomeVFSMethod *method,
+ GnomeVFSMethodHandle *method_handle,
+ GnomeVFSFileSize *offset_return)
+{
+ GnomeVFSResult errvfsresult;
+ struct libntfs_file *libntfs_file;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_file = (struct libntfs_file *)method_handle;
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(offset_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file)))
+ return errvfsresult;
+
+ *offset_return = libntfs_file->pos;
+ g_assert((s64)*offset_return == libntfs_file->pos);
+
+ return errvfsresult;
+}
+
+static gboolean libntfs_gnomevfs_is_local(GnomeVFSMethod *method,
+ const GnomeVFSURI *uri)
+{
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(uri != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ return gnome_vfs_uri_is_local(uri->parent);
+}
+
+static GnomeVFSResult libntfs_gnomevfs_get_file_info_from_handle(
+ GnomeVFSMethod *method, GnomeVFSMethodHandle *method_handle,
+ GnomeVFSFileInfo *file_info,
+ GnomeVFSFileInfoOptions options __attribute__((unused)),
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ GnomeVFSResult errvfsresult;
+ struct libntfs_file *libntfs_file;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ libntfs_file = (struct libntfs_file *)method_handle;
+ g_return_val_if_fail(libntfs_file != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+ /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */
+
+ file_info->valid_fields = 0;
+ /* FIXME: It is complicated to read filename of open 'ntfs_inode'. */
+ file_info->name = NULL;
+
+ if (GNOME_VFS_OK != (errvfsresult = libntfs_open_attr(libntfs_file))) {
+ /* Assume we are directory: */
+ file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
+ /*
+ * Do not: file_info->valid_fields |=
+ * GNOME_VFS_FILE_INFO_FIELDS_TYPE;
+ * as gnome-vfs-xfer.c/copy_items() does not check
+ * 'GNOME_VFS_FILE_INFO_FIELDS_TYPE' and we are just bluffing
+ * we know it.
+ */
+ return GNOME_VFS_OK;
+ }
+
+ /* FIXME: Is 'data_size' the right field? */
+ file_info->size = libntfs_file->attr->data_size;
+ file_info->valid_fields |= GNOME_VFS_FILE_INFO_FIELDS_SIZE;
+
+ /*
+ * FIXME: We do not really know the type of 'libntfs_file' but
+ * gnome-vfs-xfer.c/copy_items() requires 'GNOME_VFS_FILE_TYPE_REGULAR'
+ * to copy it.
+ */
+ file_info->type = GNOME_VFS_FILE_TYPE_REGULAR;
+ /*
+ * Do not: file_info->valid_fields|=GNOME_VFS_FILE_INFO_FIELDS_TYPE;
+ * as gnome-vfs-xfer.c/copy_items() does not check
+ * 'GNOME_VFS_FILE_INFO_FIELDS_TYPE' and we are just bluffing we know
+ * it.
+ */
+
+ return errvfsresult;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_get_file_info(GnomeVFSMethod *method,
+ GnomeVFSURI *uri, GnomeVFSFileInfo *file_info,
+ GnomeVFSFileInfoOptions options, GnomeVFSContext *context)
+{
+ GnomeVFSResult errvfsresult;
+ GnomeVFSMethodHandle *method_handle;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(file_info != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
+ /* handle 'options & GNOME_VFS_FILE_INFO_GET_MIME_TYPE'? */
+
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_open(method, &method_handle, uri,
+ GNOME_VFS_OPEN_READ, context)))
+ return errvfsresult;
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_get_file_info_from_handle(method,
+ method_handle, file_info, options, context)))
+ return errvfsresult;
+ if (GNOME_VFS_OK != (errvfsresult =
+ libntfs_gnomevfs_close(method, method_handle, context)))
+ return errvfsresult;
+
+ return GNOME_VFS_OK;
+}
+
+static GnomeVFSResult libntfs_gnomevfs_check_same_fs(GnomeVFSMethod *method,
+ GnomeVFSURI *a, GnomeVFSURI *b, gboolean *same_fs_return,
+ GnomeVFSContext *context __attribute__((unused)))
+{
+ ntfs_volume *volume_a;
+ ntfs_volume *volume_b;
+ GnomeVFSResult errvfsresult;
+
+ g_return_val_if_fail(method == &GnomeVFSMethod_static,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+ g_return_val_if_fail(same_fs_return != NULL,
+ GNOME_VFS_ERROR_BAD_PARAMETERS);
+
+ errvfsresult = libntfs_gnomevfs_uri_parent_init(&volume_a, a);
+ g_return_val_if_fail(errvfsresult == GNOME_VFS_OK, errvfsresult);
+
+ errvfsresult = libntfs_gnomevfs_uri_parent_init(&volume_b, b);
+ g_return_val_if_fail(errvfsresult == GNOME_VFS_OK, errvfsresult);
+
+ *same_fs_return = (volume_a == volume_b);
+
+ return GNOME_VFS_OK;
+}
+
+/**
+ * libntfs_gnomevfs_init:
+ *
+ * Returns: Initialized structure of #GnomeVFSMethod with static methods of
+ * libntfs-gnomevfs.
+ */
+GnomeVFSMethod *libntfs_gnomevfs_method_init(const gchar *method_name,
+ const gchar *args)
+{
+ struct method_name_info *method_name_info;
+
+ g_return_val_if_fail(method_name != NULL, NULL);
+ /* 'args' may be NULL if not supplied. */
+
+ method_name_hash_init();
+
+ G_LOCK(method_name_hash);
+ method_name_info = g_hash_table_lookup(method_name_hash, method_name);
+ if (method_name_info && strcmp(method_name_info->args, args))
+ method_name_info = NULL;
+ G_UNLOCK(method_name_hash);
+ if (!method_name_info) {
+
+#ifdef __sun
+ method_name_info = g_new(struct method_name_info, 1);
+#else /* !__sun */
+ libntfs_new(method_name_info);
+#endif /* __sun */
+
+ method_name_info->args = g_strdup(args);
+ G_LOCK(method_name_hash);
+ g_hash_table_replace(method_name_hash, g_strdup(method_name),
+ method_name_info);
+ G_UNLOCK(method_name_hash);
+ }
+
+ G_LOCK(GnomeVFSMethod_static);
+ LIBNTFS_MEMZERO(&GnomeVFSMethod_static);
+ GnomeVFSMethod_static.method_table_size = sizeof(GnomeVFSMethod_static);
+ GnomeVFSMethod_static.open = libntfs_gnomevfs_open; /* mandatory */
+ GnomeVFSMethod_static.create = libntfs_gnomevfs_create; /* mandatory */
+ GnomeVFSMethod_static.close = libntfs_gnomevfs_close;
+ GnomeVFSMethod_static.read = libntfs_gnomevfs_read;
+ GnomeVFSMethod_static.seek = libntfs_gnomevfs_seek;
+ GnomeVFSMethod_static.tell = libntfs_gnomevfs_tell;
+ GnomeVFSMethod_static.open_directory = libntfs_gnomevfs_open_directory;
+ GnomeVFSMethod_static.close_directory =
+ libntfs_gnomevfs_close_directory;
+ GnomeVFSMethod_static.read_directory = libntfs_gnomevfs_read_directory;
+ GnomeVFSMethod_static.get_file_info =
+ libntfs_gnomevfs_get_file_info; /* mandatory */
+ GnomeVFSMethod_static.get_file_info_from_handle =
+ libntfs_gnomevfs_get_file_info_from_handle;
+ GnomeVFSMethod_static.is_local =
+ libntfs_gnomevfs_is_local; /* mandatory */
+ GnomeVFSMethod_static.check_same_fs = libntfs_gnomevfs_check_same_fs;
+ /* TODO: GnomeVFSMethodFindDirectoryFunc find_directory; */
+ /* TODO: GnomeVFSMethodFileControlFunc file_control; */
+ /* R/W: GnomeVFSMethodCreateSymbolicLinkFunc create_symbolic_link; */
+ /* R/W: GnomeVFSMethodMonitorAddFunc monitor_add; */
+ /* R/W: GnomeVFSMethodMonitorCancelFunc monitor_cancel; */
+ /* R/W: GnomeVFSMethod_static.write; */
+ /* R/W: GnomeVFSMethod_static.truncate_handle; */
+ /* R/W: GnomeVFSMethod_static.make_directory; */
+ /* R/W: GnomeVFSMethod_static.remove_directory; */
+ /* R/W: GnomeVFSMethod_static.move; */
+ /* R/W: GnomeVFSMethod_static.unlink; */
+ /* R/W: GnomeVFSMethod_static.set_file_info; */
+ /* R/W: GnomeVFSMethod_static.truncate; */
+ G_UNLOCK(GnomeVFSMethod_static);
+
+ return &GnomeVFSMethod_static;
+}
+
+/**
+ * libntfs_gnomevfs_method_shutdown:
+ *
+ * Shutdowns libntfs-gnomevfs successfuly flushing all caches.
+ *
+ * Sad note about gnome-vfs-2.1.5 is that it never calls this function. :-)
+ */
+void libntfs_gnomevfs_method_shutdown(void)
+{
+ uri_parent_string_hash_init();
+ G_LOCK(uri_parent_string_hash);
+ g_hash_table_destroy(uri_parent_string_hash);
+ uri_parent_string_hash = NULL;
+ G_UNLOCK(uri_parent_string_hash);
+
+ method_name_hash_init();
+ G_LOCK(method_name_hash);
+ g_hash_table_destroy(method_name_hash);
+ method_name_hash = NULL;
+ G_UNLOCK(method_name_hash);
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/gnome-vfs-module.c b/usr/src/lib/libntfs/common/libntfs/gnome-vfs-module.c
new file mode 100644
index 0000000000..e1a85ad9da
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/gnome-vfs-module.c
@@ -0,0 +1,74 @@
+/*
+ * gnome-vfs-module.c - Gnome-VFS init/shutdown implementation of interface to
+ * libntfs. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ * Copyright (c) 2003 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 "config.h"
+
+#include "gnome-vfs-method.h"
+#include <libgnomevfs/gnome-vfs-module.h>
+#include <glib/gmessages.h>
+#include <glib/gutils.h> /* for g_atexit() */
+
+static void vfs_module_shutdown_atexit(void);
+
+/**
+ * vfs_module_init:
+ * @method_name: FIXME
+ * @args: FIXME
+ *
+ * FIXME
+ *
+ * Returns: FIXME
+ */
+GnomeVFSMethod *vfs_module_init(const char *method_name, const char *args)
+{
+ GnomeVFSMethod *libntfs_gnomevfs_method_ptr;
+
+ g_return_val_if_fail(method_name != NULL, NULL);
+ /* 'args' may be NULL if not supplied. */
+
+ libntfs_gnomevfs_method_ptr = libntfs_gnomevfs_method_init(method_name,
+ args);
+
+ g_atexit(vfs_module_shutdown_atexit);
+
+ return libntfs_gnomevfs_method_ptr;
+}
+
+/**
+ * vfs_module_shutdown:
+ */
+void vfs_module_shutdown(GnomeVFSMethod *method __attribute__((unused)))
+{
+ /*
+ * 'method' may be NULL if we are called from
+ * vfs_module_shutdown_atexit().
+ */
+
+ libntfs_gnomevfs_method_shutdown();
+}
+
+static void vfs_module_shutdown_atexit(void)
+{
+ vfs_module_shutdown(NULL);
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/index.c b/usr/src/lib/libntfs/common/libntfs/index.c
new file mode 100644
index 0000000000..efa4ae5913
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/index.c
@@ -0,0 +1,1862 @@
+/**
+ * index.c - NTFS index handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004-2005 Anton Altaparmakov
+ * Copyright (c) 2004-2005 Richard Russon
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2005-2006 Szabolcs Szakacsits
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "attrib.h"
+#include "collate.h"
+#include "debug.h"
+#include "index.h"
+#include "mst.h"
+#include "dir.h"
+#include "logging.h"
+#include "bitmap.h"
+#include "support.h"
+
+/**
+ * ntfs_index_entry_mark_dirty - mark an index entry dirty
+ * @ictx: ntfs index context describing the index entry
+ *
+ * Mark the index entry described by the index entry context @ictx dirty.
+ *
+ * If the index entry is in the index root attribute, simply mark the inode
+ * containing the index root attribute dirty. This ensures the mftrecord, and
+ * hence the index root attribute, will be written out to disk later.
+ *
+ * If the index entry is in an index block belonging to the index allocation
+ * attribute, set ib_dirty to TRUE, thus index block will be updated during
+ * ntfs_index_ctx_put.
+ */
+void ntfs_index_entry_mark_dirty(ntfs_index_context *ictx)
+{
+ if (ictx->is_in_root)
+ ntfs_inode_mark_dirty(ictx->actx->ntfs_ino);
+ else
+ ictx->ib_dirty = TRUE;
+}
+
+static s64 ntfs_ib_vcn_to_pos(ntfs_index_context *icx, VCN vcn)
+{
+ return vcn << icx->vcn_size_bits;
+}
+
+static VCN ntfs_ib_pos_to_vcn(ntfs_index_context *icx, s64 pos)
+{
+ return pos >> icx->vcn_size_bits;
+}
+
+static int ntfs_ib_write(ntfs_index_context *icx, VCN vcn, void *buf)
+{
+ s64 ret;
+
+ ntfs_log_trace("vcn: %lld\n", vcn);
+
+ ret = ntfs_attr_mst_pwrite(icx->ia_na, ntfs_ib_vcn_to_pos(icx, vcn),
+ 1, icx->block_size, buf);
+ if (ret != 1) {
+ ntfs_log_perror("Failed to write index block %lld of inode "
+ "%llu", (long long)vcn,
+ (unsigned long long)icx->ni->mft_no);
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+}
+
+static int ntfs_icx_ib_write(ntfs_index_context *icx)
+{
+ if (ntfs_ib_write(icx, icx->ib_vcn, icx->ib))
+ return STATUS_ERROR;
+
+ icx->ib_dirty = FALSE;
+
+ return STATUS_OK;
+}
+
+/**
+ * ntfs_index_ctx_get - allocate and initialize a new index context
+ * @ni: ntfs inode with which to initialize the context
+ * @name: name of the which context describes
+ * @name_len: length of the index name
+ *
+ * Allocate a new index context, initialize it with @ni and return it.
+ * Return NULL if allocation failed.
+ */
+ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
+ ntfschar *name, u32 name_len)
+{
+ ntfs_index_context *icx;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (ni->nr_extents == -1)
+ ni = ni->u.base_ni;
+ icx = ntfs_calloc(sizeof(ntfs_index_context));
+ if (icx)
+ *icx = (ntfs_index_context) {
+ .ni = ni,
+ .name = name,
+ .name_len = name_len,
+ };
+ return icx;
+}
+
+static void ntfs_index_ctx_free(ntfs_index_context *icx)
+{
+ ntfs_log_trace("Entering.\n");
+
+ if (!icx->entry)
+ return;
+
+ if (icx->actx)
+ ntfs_attr_put_search_ctx(icx->actx);
+
+ if (icx->is_in_root) {
+ if (icx->ia_na)
+ ntfs_attr_close(icx->ia_na);
+ return;
+ }
+
+ if (icx->ib_dirty) {
+ /* FIXME: Error handling!!! */
+ ntfs_ib_write(icx, icx->ib_vcn, icx->ib);
+ }
+
+ free(icx->ib);
+ ntfs_attr_close(icx->ia_na);
+}
+
+/**
+ * ntfs_index_ctx_put - release an index context
+ * @icx: index context to free
+ *
+ * Release the index context @icx, releasing all associated resources.
+ */
+void ntfs_index_ctx_put(ntfs_index_context *icx)
+{
+ ntfs_index_ctx_free(icx);
+ free(icx);
+}
+
+/**
+ * ntfs_index_ctx_reinit - reinitialize an index context
+ * @icx: index context to reinitialize
+ *
+ * Reinitialize the index context @icx so it can be used for ntfs_index_lookup.
+ */
+void ntfs_index_ctx_reinit(ntfs_index_context *icx)
+{
+ ntfs_log_trace("Entering.\n");
+
+ ntfs_index_ctx_free(icx);
+
+ *icx = (ntfs_index_context) {
+ .ni = icx->ni,
+ .name = icx->name,
+ .name_len = icx->name_len,
+ };
+}
+
+static leVCN *ntfs_ie_get_vcn_addr(INDEX_ENTRY *ie)
+{
+ return (leVCN *)((u8 *)ie + le16_to_cpu(ie->length) - sizeof(VCN));
+}
+
+/**
+ * Get the subnode vcn to which the index entry refers.
+ */
+VCN ntfs_ie_get_vcn(INDEX_ENTRY *ie)
+{
+ return sle64_to_cpup(ntfs_ie_get_vcn_addr(ie));
+}
+
+static INDEX_ENTRY *ntfs_ie_get_first(INDEX_HEADER *ih)
+{
+ return (INDEX_ENTRY *)((u8 *)ih + le32_to_cpu(ih->entries_offset));
+}
+
+static INDEX_ENTRY *ntfs_ie_get_next(INDEX_ENTRY *ie)
+{
+ return (INDEX_ENTRY *)((char *)ie + le16_to_cpu(ie->length));
+}
+
+static u8 *ntfs_ie_get_end(INDEX_HEADER *ih)
+{
+ /* FIXME: check if it isn't overflowing the index block size */
+ return (u8 *)ih + le32_to_cpu(ih->index_length);
+}
+
+static int ntfs_ie_end(INDEX_ENTRY *ie)
+{
+ return (ie->flags & INDEX_ENTRY_END) ? 1 : 0;
+}
+
+/**
+ * Find the last entry in the index block
+ */
+static INDEX_ENTRY *ntfs_ie_get_last(INDEX_ENTRY *ie, char *ies_end)
+{
+ ntfs_log_trace("Entering.\n");
+
+ while ((char *)ie < ies_end && !ntfs_ie_end(ie))
+ ie = ntfs_ie_get_next(ie);
+ return ie;
+}
+
+static INDEX_ENTRY *ntfs_ie_get_by_pos(INDEX_HEADER *ih, int pos)
+{
+ INDEX_ENTRY *ie;
+
+ ntfs_log_trace("pos: %d\n", pos);
+
+ ie = ntfs_ie_get_first(ih);
+
+ while (pos-- > 0)
+ ie = ntfs_ie_get_next(ie);
+ return ie;
+}
+
+static INDEX_ENTRY *ntfs_ie_prev(INDEX_HEADER *ih, INDEX_ENTRY *ie)
+{
+ INDEX_ENTRY *ie_prev, *tmp;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie_prev = NULL;
+ tmp = ntfs_ie_get_first(ih);
+
+ while (tmp != ie) {
+ ie_prev = tmp;
+ tmp = ntfs_ie_get_next(tmp);
+ }
+ return ie_prev;
+}
+
+char *ntfs_ie_filename_get(INDEX_ENTRY *ie)
+{
+ FILE_NAME_ATTR *fn;
+ char *name = NULL;
+ int name_len;
+
+ fn = (FILE_NAME_ATTR *)&ie->key;
+ name_len = ntfs_ucstombs(fn->file_name, fn->file_name_length, &name, 0);
+ if (name_len < 0) {
+ ntfs_log_perror("ntfs_ucstombs");
+ return NULL;
+ } else if (name_len > 0)
+ return name;
+ free(name);
+ return NULL;
+}
+
+void ntfs_ie_filename_dump(INDEX_ENTRY *ie)
+{
+ char *s;
+
+ s = ntfs_ie_filename_get(ie);
+ ntfs_log_debug("'%s' ", s);
+ free(s);
+}
+
+void ntfs_ih_filename_dump(INDEX_HEADER *ih)
+{
+ INDEX_ENTRY *ie;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ntfs_ie_get_first(ih);
+ while (!ntfs_ie_end(ie)) {
+ ntfs_ie_filename_dump(ie);
+ ie = ntfs_ie_get_next(ie);
+ }
+}
+
+static int ntfs_ih_numof_entries(INDEX_HEADER *ih)
+{
+ int n;
+ INDEX_ENTRY *ie;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ntfs_ie_get_first(ih);
+ for (n = 0; !ntfs_ie_end(ie); n++)
+ ie = ntfs_ie_get_next(ie);
+ return n;
+}
+
+static int ntfs_ih_one_entry(INDEX_HEADER *ih)
+{
+ return (ntfs_ih_numof_entries(ih) == 1);
+}
+
+static int ntfs_ih_zero_entry(INDEX_HEADER *ih)
+{
+ return (ntfs_ih_numof_entries(ih) == 0);
+}
+
+static void ntfs_ie_delete(INDEX_HEADER *ih, INDEX_ENTRY *ie)
+{
+ u32 new_size;
+
+ ntfs_log_trace("Entering.\n");
+
+ new_size = le32_to_cpu(ih->index_length) - le16_to_cpu(ie->length);
+ ih->index_length = cpu_to_le32(new_size);
+ memmove(ie, (u8 *)ie + le16_to_cpu(ie->length),
+ new_size - ((u8 *)ie - (u8 *)ih));
+}
+
+static void ntfs_ie_set_vcn(INDEX_ENTRY *ie, VCN vcn)
+{
+ *ntfs_ie_get_vcn_addr(ie) = cpu_to_sle64(vcn);
+}
+
+/**
+ * Insert @ie index entry at @pos entry. Used @ih values should be ok already.
+ */
+static void ntfs_ie_insert(INDEX_HEADER *ih, INDEX_ENTRY *ie, INDEX_ENTRY *pos)
+{
+ int ie_size = le16_to_cpu(ie->length);
+
+ ntfs_log_trace("Entering.\n");
+
+ ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) + ie_size);
+ memmove((u8 *)pos + ie_size, pos,
+ le32_to_cpu(ih->index_length) - ((u8 *)pos - (u8 *)ih) -
+ ie_size);
+ memcpy(pos, ie, ie_size);
+}
+
+static INDEX_ENTRY *ntfs_ie_dup(INDEX_ENTRY *ie)
+{
+ INDEX_ENTRY *dup;
+
+ ntfs_log_trace("Entering.\n");
+
+ dup = ntfs_malloc(le16_to_cpu(ie->length));
+ if (dup)
+ memcpy(dup, ie, le16_to_cpu(ie->length));
+ return dup;
+}
+
+static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie)
+{
+ INDEX_ENTRY *dup;
+ int size = le16_to_cpu(ie->length);
+
+ ntfs_log_trace("Entering.\n");
+
+ if (ie->flags & INDEX_ENTRY_NODE)
+ size -= sizeof(VCN);
+
+ dup = ntfs_malloc(size);
+ if (dup) {
+ memcpy(dup, ie, size);
+ dup->flags &= ~INDEX_ENTRY_NODE;
+ dup->length = cpu_to_le16(size);
+ }
+ return dup;
+}
+
+static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn)
+{
+ u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ntfs_is_indx_record(ib->magic)) {
+
+ ntfs_log_error("Corrupt index block signature: vcn %lld inode "
+ "%llu\n", (long long)vcn,
+ (unsigned long long)icx->ni->mft_no);
+ return -1;
+ }
+
+ if (sle64_to_cpu(ib->index_block_vcn) != vcn) {
+
+ ntfs_log_error("Corrupt index block: VCN (%lld) is different "
+ "from expected VCN (%lld) in inode %llu\n",
+ (long long)sle64_to_cpu(ib->index_block_vcn),
+ (long long)vcn,
+ (unsigned long long)icx->ni->mft_no);
+ return -1;
+ }
+
+ if (ib_size != icx->block_size) {
+
+ ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu "
+ "has a size (%u) differing from the index "
+ "specified size (%u)\n", (long long)vcn,
+ icx->ni->mft_no, (unsigned)ib_size,
+ (unsigned)icx->block_size);
+ return -1;
+ }
+ return 0;
+}
+
+static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name,
+ u32 name_len, ntfs_attr_search_ctx **ctx)
+{
+ ATTR_RECORD *a;
+ INDEX_ROOT *ir = NULL;
+
+ ntfs_log_trace("Entering.\n");
+
+ *ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!*ctx) {
+ ntfs_log_perror("Failed to get $INDEX_ROOT search context");
+ return NULL;
+ }
+
+ if (ntfs_attr_lookup(AT_INDEX_ROOT, name, name_len, CASE_SENSITIVE,
+ 0, NULL, 0, *ctx)) {
+ ntfs_log_perror("Failed to lookup $INDEX_ROOT");
+ goto err_out;
+ }
+
+ a = (*ctx)->attr;
+ if (a->non_resident) {
+ errno = EINVAL;
+ ntfs_log_perror("Non-resident $INDEX_ROOT detected");
+ goto err_out;
+ }
+
+ ir = (INDEX_ROOT *)((char *)a + le16_to_cpu(a->u.res.value_offset));
+err_out:
+ if (!ir)
+ ntfs_attr_put_search_ctx(*ctx);
+ return ir;
+}
+
+/**
+ * Find a key in the index block.
+ *
+ * Return values:
+ * STATUS_OK with errno set to ESUCCESS if we know for sure that the
+ * entry exists and @ie_out points to this entry.
+ * STATUS_NOT_FOUND with errno set to ENOENT if we know for sure the
+ * entry doesn't exist and @ie_out is the insertion point.
+ * STATUS_KEEP_SEARCHING if we can't answer the above question and
+ * @vcn will contain the node index block.
+ * STATUS_ERROR with errno set if on unexpected error during lookup.
+ */
+static int ntfs_ie_lookup(const void *key, const int key_len,
+ ntfs_index_context *icx, INDEX_HEADER *ih,
+ VCN *vcn, INDEX_ENTRY **ie_out)
+{
+ INDEX_ENTRY *ie;
+ u8 *index_end;
+ int rc, item = 0;
+
+ ntfs_log_trace("Entering.\n");
+
+ index_end = ntfs_ie_get_end(ih);
+
+ /*
+ * Loop until we exceed valid memory (corruption case) or until we
+ * reach the last entry.
+ */
+ for (ie = ntfs_ie_get_first(ih); ; ie = ntfs_ie_get_next(ie)) {
+ /* Bounds checks. */
+ if ((u8 *)ie + sizeof(INDEX_ENTRY_HEADER) > index_end ||
+ (u8 *)ie + le16_to_cpu(ie->length) > index_end) {
+ errno = ERANGE;
+ ntfs_log_error("Index entry out of bounds in inode "
+ "%llu.\n",
+ (unsigned long long)icx->ni->mft_no);
+ return STATUS_ERROR;
+ }
+ /*
+ * The last entry cannot contain a key. It can however contain
+ * a pointer to a child node in the B+tree so we just break out.
+ */
+ if (ntfs_ie_end(ie))
+ break;
+ /*
+ * Not a perfect match, need to do full blown collation so we
+ * know which way in the B+tree we have to go.
+ */
+ rc = ntfs_collate(icx->ni->vol, icx->cr, key, key_len, &ie->key,
+ le16_to_cpu(ie->key_length));
+ if (rc == NTFS_COLLATION_ERROR) {
+ ntfs_log_error("Collation error. Perhaps a filename "
+ "contains invalid characters?\n");
+ errno = ERANGE;
+ return STATUS_ERROR;
+ }
+ /*
+ * If @key collates before the key of the current entry, there
+ * is definitely no such key in this index but we might need to
+ * descend into the B+tree so we just break out of the loop.
+ */
+ if (rc == -1)
+ break;
+
+ if (!rc) {
+ *ie_out = ie;
+ errno = 0;
+ icx->parent_pos[icx->pindex] = item;
+ return STATUS_OK;
+ }
+
+ item++;
+ }
+ /*
+ * We have finished with this index block without success. Check for the
+ * presence of a child node and if not present return with errno ENOENT,
+ * otherwise we will keep searching in another index block.
+ */
+ if (!(ie->flags & INDEX_ENTRY_NODE)) {
+ ntfs_log_debug("Index entry wasn't found.\n");
+ *ie_out = ie;
+ errno = ENOENT;
+ return STATUS_NOT_FOUND;
+ }
+
+ /* Get the starting vcn of the index_block holding the child node. */
+ *vcn = ntfs_ie_get_vcn(ie);
+ if (*vcn < 0) {
+ errno = EINVAL;
+ ntfs_log_perror("Negative vcn in inode %llu\n",
+ icx->ni->mft_no);
+ return STATUS_ERROR;
+ }
+
+ ntfs_log_trace("Parent entry number %d\n", item);
+ icx->parent_pos[icx->pindex] = item;
+ return STATUS_KEEP_SEARCHING;
+}
+
+static ntfs_attr *ntfs_ia_open(ntfs_index_context *icx, ntfs_inode *ni)
+{
+ ntfs_attr *na;
+
+ na = ntfs_attr_open(ni, AT_INDEX_ALLOCATION, icx->name, icx->name_len);
+ if (!na) {
+ ntfs_log_perror("Failed to open index allocation of inode "
+ "%llu", (unsigned long long)ni->mft_no);
+ return NULL;
+ }
+ return na;
+}
+
+static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst)
+{
+ s64 pos, ret;
+
+ ntfs_log_trace("vcn: %lld\n", vcn);
+
+ pos = ntfs_ib_vcn_to_pos(icx, vcn);
+
+ ret = ntfs_attr_mst_pread(icx->ia_na, pos, 1, icx->block_size,
+ (u8 *)dst);
+ if (ret != 1) {
+ if (ret == -1)
+ ntfs_log_perror("Failed to read index block");
+ else
+ ntfs_log_error("Failed to read full index block at "
+ "%lld\n", (long long)pos);
+ return -1;
+ }
+
+ if (ntfs_ia_check(icx, dst, vcn))
+ return -1;
+ return 0;
+}
+
+static int ntfs_icx_parent_inc(ntfs_index_context *icx)
+{
+ icx->pindex++;
+ if (icx->pindex >= MAX_PARENT_VCN) {
+ errno = EOPNOTSUPP;
+ ntfs_log_perror("Index is over %d level deep", MAX_PARENT_VCN);
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+}
+
+static int ntfs_icx_parent_dec(ntfs_index_context *icx)
+{
+ icx->pindex--;
+ if (icx->pindex < 0) {
+ errno = EINVAL;
+ ntfs_log_perror("Corrupt index pointer (%d)", icx->pindex);
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+}
+
+/**
+ * ntfs_index_lookup - find a key in an index and return its index entry
+ * @key: [IN] key for which to search in the index
+ * @key_len: [IN] length of @key in bytes
+ * @icx: [IN/OUT] context describing the index and the returned entry
+ *
+ * Before calling ntfs_index_lookup(), @icx must have been obtained from a
+ * call to ntfs_index_ctx_get().
+ *
+ * Look for the @key in the index specified by the index lookup context @icx.
+ * ntfs_index_lookup() walks the contents of the index looking for the @key.
+ *
+ * If the @key is found in the index, 0 is returned and @icx is setup to
+ * describe the index entry containing the matching @key. @icx->entry is the
+ * index entry and @icx->data and @icx->data_len are the index entry data and
+ * its length in bytes, respectively.
+ *
+ * If the @key is not found in the index, -1 is returned, errno = ENOENT and
+ * @icx is setup to describe the index entry whose key collates immediately
+ * after the search @key, i.e. this is the position in the index at which
+ * an index entry with a key of @key would need to be inserted.
+ *
+ * If an error occurs return -1, set errno to error code and @icx is left
+ * untouched.
+ *
+ * When finished with the entry and its data, call ntfs_index_ctx_put() to free
+ * the context and other associated resources.
+ *
+ * If the index entry was modified, call ntfs_index_entry_mark_dirty() before
+ * the call to ntfs_index_ctx_put() to ensure that the changes are written
+ * to disk.
+ */
+int ntfs_index_lookup(const void *key, const int key_len,
+ ntfs_index_context *icx)
+{
+ VCN old_vcn, vcn;
+ ntfs_inode *ni = icx->ni;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_BLOCK *ib = NULL;
+ ntfs_attr_search_ctx *actx;
+ int ret, err = 0;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!key || key_len <= 0) {
+ errno = EINVAL;
+ ntfs_log_perror("key: %p key_len: %d", key, key_len);
+ return -1;
+ }
+
+ ir = ntfs_ir_lookup(ni, icx->name, icx->name_len, &actx);
+ if (!ir) {
+ if (errno == ENOENT)
+ errno = EIO;
+ return -1;
+ }
+
+ icx->block_size = le32_to_cpu(ir->index_block_size);
+ if (icx->block_size < NTFS_BLOCK_SIZE) {
+ errno = EINVAL;
+ ntfs_log_perror("Index block size (%u) is smaller than the "
+ "sector size (%d)", (unsigned)icx->block_size,
+ NTFS_BLOCK_SIZE);
+ return -1;
+ }
+
+ if (ni->vol->cluster_size <= icx->block_size)
+ icx->vcn_size_bits = ni->vol->cluster_size_bits;
+ else
+ icx->vcn_size_bits = ni->vol->sector_size_bits;
+
+ icx->cr = ir->collation_rule;
+ if (!ntfs_is_collation_rule_supported(icx->cr)) {
+ err = errno = EOPNOTSUPP;
+ ntfs_log_perror("Unknown collation rule 0x%x",
+ (unsigned)le32_to_cpu(icx->cr));
+ goto err_out;
+ }
+
+ old_vcn = VCN_INDEX_ROOT_PARENT;
+ /*
+ * FIXME: check for both ir and ib that the first index entry is
+ * within the index block.
+ */
+ ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie);
+ if (ret == STATUS_ERROR) {
+ err = errno;
+ goto err_out;
+ }
+
+ icx->actx = actx;
+ icx->ir = ir;
+
+ if (ret != STATUS_KEEP_SEARCHING) {
+ /* STATUS_OK or STATUS_NOT_FOUND */
+ err = errno;
+ icx->is_in_root = TRUE;
+ icx->parent_vcn[icx->pindex] = old_vcn;
+ goto done;
+ }
+
+ /* Child node present, descend into it. */
+ icx->ia_na = ntfs_ia_open(icx, ni);
+ if (!icx->ia_na)
+ goto err_out;
+
+ ib = ntfs_malloc(icx->block_size);
+ if (!ib) {
+ err = errno;
+ goto err_out;
+ }
+
+descend_into_child_node:
+ icx->parent_vcn[icx->pindex] = old_vcn;
+ if (ntfs_icx_parent_inc(icx)) {
+ err = errno;
+ goto err_out;
+ }
+ old_vcn = vcn;
+
+ ntfs_log_debug("Descend into node with VCN %lld.\n", vcn);
+
+ if (ntfs_ib_read(icx, vcn, ib))
+ goto err_out;
+
+ ret = ntfs_ie_lookup(key, key_len, icx, &ib->index, &vcn, &ie);
+ if (ret != STATUS_KEEP_SEARCHING) {
+ err = errno;
+ if (ret == STATUS_ERROR)
+ goto err_out;
+
+ /* STATUS_OK or STATUS_NOT_FOUND */
+ icx->is_in_root = FALSE;
+ icx->ib = ib;
+ icx->parent_vcn[icx->pindex] = icx->ib_vcn = vcn;
+ goto done;
+ }
+
+ if ((ib->index.flags & NODE_MASK) == LEAF_NODE) {
+ ntfs_log_error("Index entry with child node found in a leaf "
+ "node in inode 0x%llx.\n",
+ (unsigned long long)ni->mft_no);
+ goto err_out;
+ }
+
+ goto descend_into_child_node;
+err_out:
+ if (icx->ia_na) {
+ ntfs_attr_close(icx->ia_na);
+ icx->ia_na = NULL;
+ }
+ free(ib);
+ if (!err)
+ err = EIO;
+ if (actx)
+ ntfs_attr_put_search_ctx(actx);
+ errno = err;
+ return -1;
+done:
+ icx->entry = ie;
+ icx->data = (u8 *)ie + offsetof(INDEX_ENTRY, key);
+ icx->data_len = le16_to_cpu(ie->key_length);
+ icx->max_depth = icx->pindex;
+ ntfs_log_trace("Done.\n");
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+static INDEX_BLOCK *ntfs_ib_alloc(VCN ib_vcn, u32 ib_size,
+ INDEX_HEADER_FLAGS node_type)
+{
+ INDEX_BLOCK *ib;
+ int ih_size = sizeof(INDEX_HEADER);
+
+ ntfs_log_trace("Entering ib_vcn = %lld ib_size = %u\n", ib_vcn,
+ ib_size);
+
+ ib = ntfs_calloc(ib_size);
+ if (!ib)
+ return NULL;
+
+ ib->magic = magic_INDX;
+ ib->usa_ofs = cpu_to_le16(sizeof(INDEX_BLOCK));
+ ib->usa_count = cpu_to_le16(ib_size / NTFS_BLOCK_SIZE + 1);
+ /* Set USN to 1 */
+ *(le16 *)((char *)ib + le16_to_cpu(ib->usa_ofs)) = cpu_to_le16(1);
+ ib->lsn = 0;
+
+ ib->index_block_vcn = cpu_to_sle64(ib_vcn);
+
+ ib->index.entries_offset = cpu_to_le32((ih_size +
+ le16_to_cpu(ib->usa_count) * 2 + 7) & ~7);
+ ib->index.index_length = 0;
+ ib->index.allocated_size = cpu_to_le32(ib_size -
+ (sizeof(INDEX_BLOCK) - ih_size));
+ ib->index.flags = node_type;
+ return ib;
+}
+
+/**
+ * Find the median by going through all the entries
+ */
+static INDEX_ENTRY *ntfs_ie_get_median(INDEX_HEADER *ih)
+{
+ INDEX_ENTRY *ie, *ie_start;
+ u8 *ie_end;
+ int i = 0, median;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ie_start = ntfs_ie_get_first(ih);
+ ie_end = (u8 *)ntfs_ie_get_end(ih);
+
+ while ((u8 *)ie < ie_end && !ntfs_ie_end(ie)) {
+ ie = ntfs_ie_get_next(ie);
+ i++;
+ }
+ /*
+ * NOTE: this could be also the entry at the half of the index block.
+ */
+ median = i / 2 - 1;
+
+ ntfs_log_trace("Entries: %d median: %d\n", i, median);
+
+ for (i = 0, ie = ie_start; i <= median; i++)
+ ie = ntfs_ie_get_next(ie);
+
+ return ie;
+}
+
+static s64 ntfs_ibm_vcn_to_pos(ntfs_index_context *icx, VCN vcn)
+{
+ return ntfs_ib_vcn_to_pos(icx, vcn) / icx->block_size;
+}
+
+static s64 ntfs_ibm_pos_to_vcn(ntfs_index_context *icx, s64 pos)
+{
+ return ntfs_ib_pos_to_vcn(icx, pos * icx->block_size);
+}
+
+static int ntfs_ibm_add(ntfs_index_context *icx)
+{
+ u8 bmp[8];
+
+ ntfs_log_trace("Entering.\n");
+
+ if (ntfs_attr_exist(icx->ni, AT_BITMAP, icx->name, icx->name_len))
+ return STATUS_OK;
+ /*
+ * AT_BITMAP must be at least 8 bytes.
+ */
+ memset(bmp, 0, sizeof(bmp));
+ if (ntfs_attr_add(icx->ni, AT_BITMAP, icx->name, icx->name_len,
+ bmp, sizeof(bmp))) {
+ ntfs_log_perror("Failed to add AT_BITMAP");
+ return STATUS_ERROR;
+ }
+ return STATUS_OK;
+}
+
+static int ntfs_ibm_modify(ntfs_index_context *icx, VCN vcn, int set)
+{
+ u8 byte;
+ s64 pos = ntfs_ibm_vcn_to_pos(icx, vcn);
+ u32 bpos = pos / 8;
+ u32 bit = 1 << (pos % 8);
+ ntfs_attr *na;
+ int ret = STATUS_ERROR;
+
+ ntfs_log_trace("%s vcn: %lld\n", set ? "set" : "clear", vcn);
+
+ na = ntfs_attr_open(icx->ni, AT_BITMAP, icx->name, icx->name_len);
+ if (!na) {
+ ntfs_log_perror("Failed to open $BITMAP attribute");
+ return -1;
+ }
+
+ if (set) {
+ if (na->data_size < bpos + 1) {
+ if (ntfs_attr_truncate(na, (na->data_size + 8) & ~7)) {
+ ntfs_log_perror("Failed to truncate AT_BITMAP");
+ goto err_na;
+ }
+ }
+ }
+
+ if (ntfs_attr_pread(na, bpos, 1, &byte) != 1) {
+ ntfs_log_perror("Failed to read $BITMAP");
+ goto err_na;
+ }
+
+ if (set)
+ byte |= bit;
+ else
+ byte &= ~bit;
+
+ if (ntfs_attr_pwrite(na, bpos, 1, &byte) != 1) {
+ ntfs_log_perror("Failed to write $Bitmap");
+ goto err_na;
+ }
+
+ ret = STATUS_OK;
+err_na:
+ ntfs_attr_close(na);
+ return ret;
+}
+
+
+static int ntfs_ibm_set(ntfs_index_context *icx, VCN vcn)
+{
+ return ntfs_ibm_modify(icx, vcn, 1);
+}
+
+static int ntfs_ibm_clear(ntfs_index_context *icx, VCN vcn)
+{
+ return ntfs_ibm_modify(icx, vcn, 0);
+}
+
+static VCN ntfs_ibm_get_free(ntfs_index_context *icx)
+{
+ u8 *bm;
+ int bit;
+ s64 vcn, byte, size;
+
+ ntfs_log_trace("Entering.\n");
+
+ bm = ntfs_attr_readall(icx->ni, AT_BITMAP, icx->name, icx->name_len,
+ &size);
+ if (!bm)
+ return (VCN)-1;
+
+ for (byte = 0; byte < size; byte++) {
+
+ if (bm[byte] == 255)
+ continue;
+
+ for (bit = 0; bit < 8; bit++) {
+ if (!(bm[byte] & (1 << bit))) {
+ vcn = ntfs_ibm_pos_to_vcn(icx, byte * 8 + bit);
+ goto out;
+ }
+ }
+ }
+
+ vcn = ntfs_ibm_pos_to_vcn(icx, size * 8);
+out:
+ ntfs_log_trace("allocated vcn: %lld\n", vcn);
+
+ if (ntfs_ibm_set(icx, vcn))
+ vcn = (VCN)-1;
+
+ free(bm);
+ return vcn;
+}
+
+static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn)
+{
+ INDEX_BLOCK *ib;
+ INDEX_ENTRY *ie_last;
+ char *ies_start, *ies_end;
+ int i;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!(ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size),
+ LEAF_NODE)))
+ return NULL;
+
+ ies_start = (char *)ntfs_ie_get_first(&ir->index);
+ ies_end = (char *)ntfs_ie_get_end(&ir->index);
+
+ ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end);
+ /*
+ * Copy all entries, including the termination entry
+ * as well, which can never have any data.
+ */
+ i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length);
+ memcpy(ntfs_ie_get_first(&ib->index), ies_start, i);
+
+ ib->index.flags = ir->index.flags;
+ ib->index.index_length = cpu_to_le32(i +
+ le32_to_cpu(ib->index.entries_offset));
+ return ib;
+}
+
+static void ntfs_ir_nill(INDEX_ROOT *ir)
+{
+ INDEX_ENTRY *ie_last;
+ char *ies_start, *ies_end;
+
+ ntfs_log_trace("Entering\n");
+ /* TODO: This function could be much simpler. */
+ ies_start = (char *)ntfs_ie_get_first(&ir->index);
+ ies_end = (char *)ntfs_ie_get_end(&ir->index);
+ ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end);
+ /* Move the index root termination entry forward. */
+ if ((char *)ie_last > ies_start) {
+ memmove(ies_start, (char *)ie_last, le16_to_cpu(
+ ie_last->length));
+ ie_last = (INDEX_ENTRY *)ies_start;
+ }
+}
+
+static int ntfs_ib_copy_tail(ntfs_index_context *icx, INDEX_BLOCK *src,
+ INDEX_ENTRY *median, VCN new_vcn)
+{
+ u8 *ies_end;
+ INDEX_ENTRY *ie_head; /* first entry after the median */
+ int tail_size, ret;
+ INDEX_BLOCK *dst;
+
+ ntfs_log_trace("Entering.\n");
+
+ dst = ntfs_ib_alloc(new_vcn, icx->block_size,
+ src->index.flags & NODE_MASK);
+ if (!dst)
+ return STATUS_ERROR;
+
+ ie_head = ntfs_ie_get_next(median);
+
+ ies_end = (u8 *)ntfs_ie_get_end(&src->index);
+ tail_size = ies_end - (u8 *)ie_head;
+ memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size);
+
+ dst->index.index_length = cpu_to_le32(tail_size +
+ le32_to_cpu(dst->index.entries_offset));
+
+ ret = ntfs_ib_write(icx, new_vcn, dst);
+
+ free(dst);
+ return ret;
+}
+
+static int ntfs_ib_cut_tail(ntfs_index_context *icx, INDEX_BLOCK *src,
+ INDEX_ENTRY *ie)
+{
+ char *ies_start, *ies_end;
+ INDEX_ENTRY *ie_last;
+
+ ntfs_log_trace("Entering.\n");
+
+ ies_start = (char *)ntfs_ie_get_first(&src->index);
+ ies_end = (char *)ntfs_ie_get_end(&src->index);
+
+ ie_last = ntfs_ie_get_last((INDEX_ENTRY *)ies_start, ies_end);
+ if (ie_last->flags & INDEX_ENTRY_NODE)
+ ntfs_ie_set_vcn(ie_last, ntfs_ie_get_vcn(ie));
+
+ memcpy(ie, ie_last, le16_to_cpu(ie_last->length));
+
+ src->index.index_length = cpu_to_le32(((char *)ie - ies_start) +
+ le16_to_cpu(ie->length) +
+ le32_to_cpu(src->index.entries_offset));
+
+ if (ntfs_ib_write(icx, icx->parent_vcn[icx->pindex + 1], src))
+ return STATUS_ERROR;
+
+ return STATUS_OK;
+}
+
+static int ntfs_ia_add(ntfs_index_context *icx)
+{
+ ntfs_log_trace("Entering.\n");
+
+ if (ntfs_ibm_add(icx))
+ return -1;
+
+ if (!ntfs_attr_exist(icx->ni, AT_INDEX_ALLOCATION, icx->name,
+ icx->name_len)) {
+ if (ntfs_attr_add(icx->ni, AT_INDEX_ALLOCATION, icx->name,
+ icx->name_len, NULL, 0)) {
+ ntfs_log_perror("Failed to add AT_INDEX_ALLOCATION");
+ return -1;
+ }
+ }
+
+ icx->ia_na = ntfs_ia_open(icx, icx->ni);
+ if (!icx->ia_na)
+ return -1;
+ return 0;
+}
+
+static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len)
+{
+ ntfs_attr_search_ctx *ctx;
+ INDEX_ROOT *ir;
+
+ ir = ntfs_ir_lookup(ni, name, len, &ctx);
+ if (ir)
+ ntfs_attr_put_search_ctx(ctx);
+ return ir;
+}
+
+static int ntfs_ir_reparent(ntfs_index_context *icx)
+{
+ ntfs_attr_search_ctx *ctx;
+ INDEX_ROOT *ir;
+ INDEX_ENTRY *ie;
+ INDEX_BLOCK *ib = NULL;
+ VCN new_ib_vcn;
+ int ret = STATUS_ERROR;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!(icx->ia_na))
+ if (ntfs_ia_add(icx))
+ return -1;
+
+ ir = ntfs_ir_lookup(icx->ni, icx->name, icx->name_len, &ctx);
+ if (!ir)
+ return -1;
+
+ new_ib_vcn = ntfs_ibm_get_free(icx);
+ if (new_ib_vcn == -1)
+ goto err_out;
+
+ ib = ntfs_ir_to_ib(ir, new_ib_vcn);
+ if (ib == NULL) {
+ ntfs_log_perror("Failed to move index root to index block");
+ goto clear_bmp;
+ }
+
+ if (ntfs_ib_write(icx, new_ib_vcn, ib))
+ goto clear_bmp;
+
+ ntfs_ir_nill(ir);
+
+ ie = ntfs_ie_get_first(&ir->index);
+ ie->flags |= INDEX_ENTRY_NODE;
+ ie->length = cpu_to_le16(sizeof(INDEX_ENTRY_HEADER) + sizeof(VCN));
+ ntfs_ie_set_vcn(ie, new_ib_vcn);
+
+ ir->index.flags = LARGE_INDEX;
+ ir->index.index_length = cpu_to_le32(le32_to_cpu(
+ ir->index.entries_offset) + le16_to_cpu(ie->length));
+ ir->index.allocated_size = ir->index.index_length;
+
+ if (ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr,
+ sizeof(INDEX_ROOT) - sizeof(INDEX_HEADER) +
+ le32_to_cpu(ir->index.allocated_size)))
+ /* FIXME: revert bitmap, index root */
+ goto err_out;
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+
+ ret = STATUS_OK;
+err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ free(ib);
+ return ret;
+clear_bmp:
+ ntfs_ibm_clear(icx, new_ib_vcn);
+ goto err_out;
+}
+
+/**
+ * ntfs_ir_truncate - Truncate index root attribute
+ *
+ * Returns STATUS_OK, STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT or STATUS_ERROR.
+ */
+static int ntfs_ir_truncate(ntfs_index_context *icx, int data_size)
+{
+ ntfs_attr *na;
+ int ret;
+
+ ntfs_log_trace("Entering.\n");
+
+ na = ntfs_attr_open(icx->ni, AT_INDEX_ROOT, icx->name, icx->name_len);
+ if (!na) {
+ ntfs_log_perror("Failed to open INDEX_ROOT");
+ return STATUS_ERROR;
+ }
+ /*
+ * INDEX_ROOT must be resident and its entries can be moved to
+ * INDEX_BLOCK, so ENOSPC isn't a real error.
+ */
+ ret = ntfs_attr_truncate(na, data_size + offsetof(INDEX_ROOT, index));
+ if (ret == STATUS_OK) {
+ icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len);
+ if (!icx->ir)
+ return STATUS_ERROR;
+
+ icx->ir->index.allocated_size = cpu_to_le32(data_size);
+ } else {
+ if (errno != ENOSPC && errno != EOVERFLOW)
+ ntfs_log_trace("Failed to truncate INDEX_ROOT");
+ if (errno == EOVERFLOW)
+ ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT;
+ }
+
+ ntfs_attr_close(na);
+ return ret;
+}
+
+/**
+ * ntfs_ir_make_space - Make more space for the index root attribute
+ *
+ * On success return STATUS_OK or STATUS_KEEP_SEARCHING.
+ * On error return STATUS_ERROR.
+ */
+static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size)
+{
+ int ret;
+
+ ntfs_log_trace("Entering.\n");
+
+ ret = ntfs_ir_truncate(icx, data_size);
+ if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) {
+ ret = ntfs_ir_reparent(icx);
+ if (ret == STATUS_OK)
+ ret = STATUS_KEEP_SEARCHING;
+ else
+ ntfs_log_perror("Failed to nodify INDEX_ROOT");
+ }
+ return ret;
+}
+
+/*
+ * NOTE: 'ie' must be a copy of a real index entry.
+ */
+static int ntfs_ie_add_vcn(INDEX_ENTRY **ie)
+{
+ INDEX_ENTRY *p, *old = *ie;
+
+ old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN));
+ p = realloc(old, le16_to_cpu(old->length));
+ if (!p)
+ return STATUS_ERROR;
+
+ p->flags |= INDEX_ENTRY_NODE;
+ *ie = p;
+ return STATUS_OK;
+}
+
+static int ntfs_ih_insert(INDEX_HEADER *ih, INDEX_ENTRY *orig_ie, VCN new_vcn,
+ int pos)
+{
+ INDEX_ENTRY *ie_node, *ie;
+ int ret = STATUS_ERROR;
+ VCN old_vcn;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ntfs_ie_dup(orig_ie);
+ if (!ie)
+ return STATUS_ERROR;
+
+ if (!(ie->flags & INDEX_ENTRY_NODE))
+ if (ntfs_ie_add_vcn(&ie))
+ goto out;
+
+ ie_node = ntfs_ie_get_by_pos(ih, pos);
+ old_vcn = ntfs_ie_get_vcn(ie_node);
+ ntfs_ie_set_vcn(ie_node, new_vcn);
+
+ ntfs_ie_insert(ih, ie, ie_node);
+ ntfs_ie_set_vcn(ie_node, old_vcn);
+ ret = STATUS_OK;
+out:
+ free(ie);
+ return ret;
+}
+
+static VCN ntfs_icx_parent_vcn(ntfs_index_context *icx)
+{
+ return icx->parent_vcn[icx->pindex];
+}
+
+static VCN ntfs_icx_parent_pos(ntfs_index_context *icx)
+{
+ return icx->parent_pos[icx->pindex];
+}
+
+static int ntfs_ir_insert_median(ntfs_index_context *icx, INDEX_ENTRY *median,
+ VCN new_vcn)
+{
+ u32 new_size;
+ int ret;
+
+ ntfs_log_trace("Entering.\n");
+
+ icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len);
+ if (!icx->ir)
+ return STATUS_ERROR;
+
+ new_size = le32_to_cpu(icx->ir->index.index_length) +
+ le16_to_cpu(median->length);
+ if (!(median->flags & INDEX_ENTRY_NODE))
+ new_size += sizeof(VCN);
+
+ ret = ntfs_ir_make_space(icx, new_size);
+ if (ret != STATUS_OK)
+ return ret;
+
+ icx->ir = ntfs_ir_lookup2(icx->ni, icx->name, icx->name_len);
+ if (!icx->ir)
+ return STATUS_ERROR;
+
+ return ntfs_ih_insert(&icx->ir->index, median, new_vcn,
+ ntfs_icx_parent_pos(icx));
+}
+
+static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib);
+
+/**
+ * ntfs_ib_insert - insert an index block to an index context.
+ *
+ * On success return STATUS_OK or STATUS_KEEP_SEARCHING.
+ * On error return STATUS_ERROR.
+ */
+static int ntfs_ib_insert(ntfs_index_context *icx, INDEX_ENTRY *ie, VCN new_vcn)
+{
+ INDEX_BLOCK *ib;
+ u32 idx_size, allocated_size;
+ int err = STATUS_ERROR;
+ VCN old_vcn;
+
+ ntfs_log_trace("Entering.\n");
+
+ ib = ntfs_malloc(icx->block_size);
+ if (!ib)
+ return -1;
+
+ old_vcn = ntfs_icx_parent_vcn(icx);
+
+ if (ntfs_ib_read(icx, old_vcn, ib))
+ goto err_out;
+
+ idx_size = le32_to_cpu(ib->index.index_length);
+ allocated_size = le32_to_cpu(ib->index.allocated_size);
+ /* FIXME: sizeof(VCN) should be included only if ie has no VCN */
+ if (idx_size + le16_to_cpu(ie->length) + sizeof(VCN) > allocated_size) {
+ err = ntfs_ib_split(icx, ib);
+ if (err == STATUS_OK)
+ err = STATUS_KEEP_SEARCHING;
+ goto err_out;
+ }
+
+ if (ntfs_ih_insert(&ib->index, ie, new_vcn, ntfs_icx_parent_pos(icx)))
+ goto err_out;
+
+ if (ntfs_ib_write(icx, old_vcn, ib))
+ goto err_out;
+
+ err = STATUS_OK;
+err_out:
+ free(ib);
+ return err;
+}
+
+/**
+ * ntfs_ib_split - Split index allocation attribute
+ *
+ * On success return STATUS_OK or STATUS_KEEP_SEARCHING.
+ * On error return is STATUS_ERROR.
+ */
+static int ntfs_ib_split(ntfs_index_context *icx, INDEX_BLOCK *ib)
+{
+ INDEX_ENTRY *median;
+ VCN new_vcn;
+ int ret;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (ntfs_icx_parent_dec(icx))
+ return STATUS_ERROR;
+
+ median = ntfs_ie_get_median(&ib->index);
+ new_vcn = ntfs_ibm_get_free(icx);
+ if (new_vcn == -1)
+ return STATUS_ERROR;
+
+ if (ntfs_ib_copy_tail(icx, ib, median, new_vcn)) {
+ ntfs_ibm_clear(icx, new_vcn);
+ return STATUS_ERROR;
+ }
+
+ if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT)
+ ret = ntfs_ir_insert_median(icx, median, new_vcn);
+ else
+ ret = ntfs_ib_insert(icx, median, new_vcn);
+
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+
+ if (ret != STATUS_OK) {
+ ntfs_ibm_clear(icx, new_vcn);
+ return ret;
+ }
+
+ ret = ntfs_ib_cut_tail(icx, ib, median);
+ return ret;
+}
+
+static int ntfs_ie_add(ntfs_index_context *icx, INDEX_ENTRY *ie)
+{
+ INDEX_HEADER *ih;
+ int allocated_size, new_size;
+ int ret = STATUS_ERROR;
+
+#ifdef DEBUG
+ char *fn;
+ fn = ntfs_ie_filename_get(ie);
+ ntfs_log_trace("file: '%s'\n", fn);
+ free(fn);
+#endif
+
+ while (1) {
+ if (!ntfs_index_lookup(&ie->key, le16_to_cpu(ie->key_length),
+ icx)) {
+ errno = EEXIST;
+ ntfs_log_error("Index already have such entry.\n");
+ goto err_out;
+ }
+ if (errno != ENOENT) {
+ ntfs_log_perror("Failed to find place for new entry");
+ goto err_out;
+ }
+
+ if (icx->is_in_root)
+ ih = &icx->ir->index;
+ else
+ ih = &icx->ib->index;
+
+ allocated_size = le32_to_cpu(ih->allocated_size);
+ new_size = le32_to_cpu(ih->index_length) +
+ le16_to_cpu(ie->length);
+
+ if (new_size <= allocated_size)
+ break;
+
+ ntfs_log_trace("index block sizes: allocated: %d needed: %d\n",
+ allocated_size, new_size);
+
+ if (icx->is_in_root) {
+ if (ntfs_ir_make_space(icx, new_size) == STATUS_ERROR)
+ goto err_out;
+ } else {
+ if (ntfs_ib_split(icx, icx->ib) == STATUS_ERROR)
+ goto err_out;
+ }
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ ntfs_index_ctx_reinit(icx);
+ }
+
+ ntfs_ie_insert(ih, ie, icx->entry);
+ ntfs_index_entry_mark_dirty(icx);
+
+ ret = STATUS_OK;
+err_out:
+ ntfs_log_trace("%s\n", ret ? "Failed" : "Done");
+ return ret;
+}
+
+/**
+ * ntfs_index_add_filename - add filename to directory index
+ * @ni: ntfs inode describing directory to which index add filename
+ * @fn: FILE_NAME attribute to add
+ * @mref: reference of the inode which @fn describes
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_index_add_filename(ntfs_inode *ni, FILE_NAME_ATTR *fn, MFT_REF mref)
+{
+ INDEX_ENTRY *ie;
+ ntfs_index_context *icx;
+ int fn_size, ie_size, ret = -1, err;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!ni || !fn) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ fn_size = (fn->file_name_length * sizeof(ntfschar)) +
+ sizeof(FILE_NAME_ATTR);
+ ie_size = (sizeof(INDEX_ENTRY_HEADER) + fn_size + 7) & ~7;
+
+ ie = ntfs_calloc(ie_size);
+ if (!ie)
+ return -1;
+
+ ie->u.indexed_file = cpu_to_le64(mref);
+ ie->length = cpu_to_le16(ie_size);
+ ie->key_length = cpu_to_le16(fn_size);
+ memcpy(&ie->key, fn, fn_size);
+
+ icx = ntfs_index_ctx_get(ni, NTFS_INDEX_I30, 4);
+ if (!icx)
+ goto out;
+
+ err = errno;
+ ret = ntfs_ie_add(icx, ie);
+ errno = err;
+
+ ntfs_index_ctx_put(icx);
+out:
+ free(ie);
+ return ret;
+}
+
+static int ntfs_ih_takeout(ntfs_index_context *icx, INDEX_HEADER *ih,
+ INDEX_ENTRY *ie, INDEX_BLOCK *ib)
+{
+ INDEX_ENTRY *ie_roam;
+ int ret = STATUS_ERROR;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie_roam = ntfs_ie_dup_novcn(ie);
+ if (!ie_roam)
+ return STATUS_ERROR;
+
+ ntfs_ie_delete(ih, ie);
+
+ if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT)
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ else
+ if (ntfs_ib_write(icx, ntfs_icx_parent_vcn(icx), ib))
+ goto out;
+
+ ntfs_index_ctx_reinit(icx);
+
+ ret = ntfs_ie_add(icx, ie_roam);
+out:
+ free(ie_roam);
+ return ret;
+}
+
+/**
+ * ntfs_ir_leafify -
+ *
+ * Used if an empty index block to be deleted has END entry as the parent
+ * in the INDEX_ROOT which is the only one there.
+ */
+static void ntfs_ir_leafify(ntfs_index_context *icx, INDEX_HEADER *ih)
+{
+ INDEX_ENTRY *ie;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ntfs_ie_get_first(ih);
+ ie->flags &= ~INDEX_ENTRY_NODE;
+ ie->length = cpu_to_le16(le16_to_cpu(ie->length) - sizeof(VCN));
+
+ ih->index_length = cpu_to_le32(le32_to_cpu(ih->index_length) -
+ sizeof(VCN));
+ ih->flags &= ~LARGE_INDEX;
+
+ /* Not fatal error */
+ ntfs_ir_truncate(icx, le32_to_cpu(ih->index_length));
+
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ ntfs_index_ctx_reinit(icx);
+}
+
+/**
+ * ntfs_ih_reparent_end -
+ *
+ * Used if an empty index block to be deleted has END entry as the parent
+ * in the INDEX_ROOT which is not the only one there.
+ */
+static int ntfs_ih_reparent_end(ntfs_index_context *icx, INDEX_HEADER *ih,
+ INDEX_BLOCK *ib)
+{
+ INDEX_ENTRY *ie, *ie_prev;
+
+ ntfs_log_trace("Entering.\n");
+
+ ie = ntfs_ie_get_by_pos(ih, ntfs_icx_parent_pos(icx));
+ ie_prev = ntfs_ie_prev(ih, ie);
+
+ ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(ie_prev));
+ return ntfs_ih_takeout(icx, ih, ie_prev, ib);
+}
+
+static int ntfs_index_rm_leaf(ntfs_index_context *icx)
+{
+ INDEX_BLOCK *ib = NULL;
+ INDEX_HEADER *parent_ih;
+ INDEX_ENTRY *ie;
+ int ret = STATUS_ERROR;
+
+ ntfs_log_trace("pindex: %d\n", icx->pindex);
+
+ if (ntfs_icx_parent_dec(icx))
+ return STATUS_ERROR;
+
+ if (ntfs_ibm_clear(icx, icx->parent_vcn[icx->pindex + 1]))
+ return STATUS_ERROR;
+
+ if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT)
+ parent_ih = &icx->ir->index;
+ else {
+ ib = ntfs_malloc(icx->block_size);
+ if (!ib)
+ return STATUS_ERROR;
+
+ if (ntfs_ib_read(icx, ntfs_icx_parent_vcn(icx), ib))
+ goto out;
+
+ parent_ih = &ib->index;
+ }
+
+ ie = ntfs_ie_get_by_pos(parent_ih, ntfs_icx_parent_pos(icx));
+ if (!ntfs_ie_end(ie)) {
+ ret = ntfs_ih_takeout(icx, parent_ih, ie, ib);
+ goto out;
+ }
+
+ if (ntfs_ih_zero_entry(parent_ih)) {
+
+ if (ntfs_icx_parent_vcn(icx) == VCN_INDEX_ROOT_PARENT) {
+ ntfs_ir_leafify(icx, parent_ih);
+ goto ok;
+ }
+
+ ret = ntfs_index_rm_leaf(icx);
+ goto out;
+ }
+
+ if (ntfs_ih_reparent_end(icx, parent_ih, ib))
+ goto out;
+ok:
+ ret = STATUS_OK;
+out:
+ free(ib);
+ return ret;
+}
+
+static int ntfs_index_rm_node(ntfs_index_context *icx)
+{
+ int entry_pos;
+ VCN vcn;
+ INDEX_BLOCK *ib = NULL;
+ INDEX_ENTRY *ie_succ, *ie, *entry = icx->entry;
+ INDEX_HEADER *ih;
+ u32 new_size;
+ int delta, ret = STATUS_ERROR;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!icx->ia_na) {
+ icx->ia_na = ntfs_ia_open(icx, icx->ni);
+ if (!icx->ia_na)
+ return STATUS_ERROR;
+ }
+
+ ib = ntfs_malloc(icx->block_size);
+ if (!ib)
+ return STATUS_ERROR;
+
+ ie_succ = ntfs_ie_get_next(icx->entry);
+ entry_pos = icx->parent_pos[icx->pindex]++;
+descend:
+ vcn = ntfs_ie_get_vcn(ie_succ);
+ if (ntfs_ib_read(icx, vcn, ib))
+ goto out;
+
+ ie_succ = ntfs_ie_get_first(&ib->index);
+
+ if (ntfs_icx_parent_inc(icx))
+ goto out;
+
+ icx->parent_vcn[icx->pindex] = vcn;
+ icx->parent_pos[icx->pindex] = 0;
+
+ if ((ib->index.flags & NODE_MASK) == INDEX_NODE)
+ goto descend;
+
+ if (ntfs_ih_zero_entry(&ib->index)) {
+ errno = EOPNOTSUPP;
+ ntfs_log_perror("Failed to find any entry in an index block. "
+ "Please run chkdsk.");
+ goto out;
+ }
+
+ ie = ntfs_ie_dup(ie_succ);
+ if (!ie)
+ goto out;
+
+ if (ntfs_ie_add_vcn(&ie))
+ goto out2;
+
+ ntfs_ie_set_vcn(ie, ntfs_ie_get_vcn(icx->entry));
+
+ if (icx->is_in_root)
+ ih = &icx->ir->index;
+ else
+ ih = &icx->ib->index;
+
+ delta = le16_to_cpu(ie->length) - le16_to_cpu(icx->entry->length);
+ new_size = le32_to_cpu(ih->index_length) + delta;
+ if (delta > 0) {
+ if (icx->is_in_root) {
+ if (ntfs_ir_truncate(icx, new_size)) {
+ errno = EOPNOTSUPP;
+ ntfs_log_perror("Denied to truncate INDEX_ROOT"
+ " during entry removal");
+ goto out2;
+ }
+ ih = &icx->ir->index;
+ entry = ntfs_ie_get_by_pos(ih, entry_pos);
+ } else if (new_size > le32_to_cpu(ih->allocated_size)) {
+ errno = EOPNOTSUPP;
+ ntfs_log_perror("Denied to split INDEX_BLOCK during "
+ "entry removal");
+ goto out2;
+ }
+ }
+
+ ntfs_ie_delete(ih, entry);
+ ntfs_ie_insert(ih, ie, entry);
+
+ if (icx->is_in_root) {
+ if (ntfs_ir_truncate(icx, new_size))
+ goto out2;
+ ntfs_inode_mark_dirty(icx->actx->ntfs_ino);
+ } else
+ if (ntfs_icx_ib_write(icx))
+ goto out2;
+
+ ntfs_ie_delete(&ib->index, ie_succ);
+
+ if (ntfs_ih_zero_entry(&ib->index)) {
+ if (ntfs_index_rm_leaf(icx))
+ goto out2;
+ } else
+ if (ntfs_ib_write(icx, vcn, ib))
+ goto out2;
+
+ ret = STATUS_OK;
+out2:
+ free(ie);
+out:
+ free(ib);
+ return ret;
+}
+
+/**
+ * ntfs_index_rm - remove entry from the index
+ * @icx: index context describing entry to delete
+ *
+ * Delete entry described by @icx from the index. Index context is always
+ * reinitialized after use of this function, so it can be used for index
+ * lookup once again.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_index_rm(ntfs_index_context *icx)
+{
+ INDEX_HEADER *ih;
+ int err;
+
+ ntfs_log_trace("Entering.\n");
+
+ if (!icx || (!icx->ib && !icx->ir) || ntfs_ie_end(icx->entry)) {
+ ntfs_log_error("Invalid arguments.\n");
+ errno = EINVAL;
+ goto err_out;
+ }
+ if (icx->is_in_root)
+ ih = &icx->ir->index;
+ else
+ ih = &icx->ib->index;
+
+ if (icx->entry->flags & INDEX_ENTRY_NODE) {
+
+ if (ntfs_index_rm_node(icx))
+ goto err_out;
+
+ } else if (icx->is_in_root || !ntfs_ih_one_entry(ih)) {
+
+ ntfs_ie_delete(ih, icx->entry);
+
+ if (icx->is_in_root) {
+ err = ntfs_ir_truncate(icx,
+ le32_to_cpu(ih->index_length));
+ if (err != STATUS_OK)
+ goto err_out;
+ } else
+ if (ntfs_icx_ib_write(icx))
+ goto err_out;
+ } else {
+ if (ntfs_index_rm_leaf(icx))
+ goto err_out;
+ }
+
+ ntfs_index_ctx_reinit(icx);
+ ntfs_log_trace("Done.\n");
+ return 0;
+err_out:
+ err = errno;
+ ntfs_index_ctx_reinit(icx);
+ errno = err;
+ ntfs_log_trace("Failed.\n");
+ return -1;
+}
+
+/**
+ * ntfs_index_root_get - read the index root of an attribute
+ * @ni: open ntfs inode in which the ntfs attribute resides
+ * @attr: attribute for which we want its index root
+ *
+ * This function will read the related index root an ntfs attribute.
+ *
+ * On success a buffer is allocated with the content of the index root
+ * and which needs to be freed when it's not needed anymore.
+ *
+ * On error NULL is returned with errno set to the error code.
+ */
+INDEX_ROOT *ntfs_index_root_get(ntfs_inode *ni, ATTR_RECORD *attr)
+{
+ ntfs_attr_search_ctx *ctx;
+ ntfschar *name;
+ INDEX_ROOT *root = NULL;
+
+ name = (ntfschar *)((u8 *)attr + le16_to_cpu(attr->name_offset));
+
+ if (!ntfs_ir_lookup(ni, name, attr->name_length, &ctx))
+ return NULL;
+
+ root = ntfs_malloc(sizeof(INDEX_ROOT));
+ if (!root)
+ goto out;
+
+ *root = *((INDEX_ROOT *)((u8 *)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset)));
+out:
+ ntfs_attr_put_search_ctx(ctx);
+ return root;
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/inode.c b/usr/src/lib/libntfs/common/libntfs/inode.c
new file mode 100644
index 0000000000..9ea1d34524
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/inode.c
@@ -0,0 +1,1200 @@
+/**
+ * inode.c - Inode handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2005 Anton Altaparmakov
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "types.h"
+#include "attrib.h"
+#include "inode.h"
+#include "debug.h"
+#include "mft.h"
+#include "attrlist.h"
+#include "runlist.h"
+#include "lcnalloc.h"
+#include "index.h"
+#include "dir.h"
+#include "ntfstime.h"
+#include "logging.h"
+
+/**
+ * __ntfs_inode_allocate - Create and initialise an NTFS inode object
+ * @vol:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static ntfs_inode *__ntfs_inode_allocate(ntfs_volume *vol)
+{
+ ntfs_inode *ni;
+
+ ni = (ntfs_inode*)calloc(1, sizeof(ntfs_inode));
+ if (ni) {
+ ni->vol = vol;
+ INIT_LIST_HEAD(&ni->attr_cache);
+ }
+ return ni;
+}
+
+/**
+ * ntfs_inode_allocate - Create an NTFS inode object
+ * @vol:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol)
+{
+ return __ntfs_inode_allocate(vol);
+}
+
+/**
+ * __ntfs_inode_release - Destroy an NTFS inode object
+ * @ni:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int __ntfs_inode_release(ntfs_inode *ni)
+{
+ if (NInoDirty(ni))
+ ntfs_log_debug("Eeek. Discarding dirty inode!\n");
+ if (NInoAttrList(ni) && ni->attr_list)
+ free(ni->attr_list);
+ free(ni->mrec);
+ free(ni);
+ return 0;
+}
+
+/**
+ * __ntfs_inode_add_to_cache - do not use me! Only for internal library use.
+ */
+void __ntfs_inode_add_to_cache(ntfs_inode *ni)
+{
+ list_add_tail(&ni->list_entry, &ni->vol->inode_cache[
+ ni->mft_no & NTFS_INODE_CACHE_SIZE_BITS]);
+ ni->nr_references = 1;
+}
+
+/**
+ * ntfs_inode_open - open an inode ready for access
+ * @vol: volume to get the inode from
+ * @mref: inode number / mft record number to open
+ *
+ * Allocate an ntfs_inode structure and initialize it for the given inode
+ * specified by @mref. @mref specifies the inode number / mft record to read,
+ * including the sequence number, which can be 0 if no sequence number checking
+ * is to be performed.
+ *
+ * Then, allocate a buffer for the mft record, read the mft record from the
+ * volume @vol, and attach it to the ntfs_inode structure (->mrec). The
+ * mft record is mst deprotected and sanity checked for validity and we abort
+ * if deprotection or checks fail.
+ *
+ * Finally, search for an attribute list attribute in the mft record and if one
+ * is found, load the attribute list attribute value and attach it to the
+ * ntfs_inode structure (->attr_list). Also set the NI_AttrList bit to indicate
+ * this.
+ *
+ * Return a pointer to the ntfs_inode structure on success or NULL on error,
+ * with errno set to the error code.
+ */
+ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref)
+{
+ s64 l;
+ ntfs_inode *ni;
+ ntfs_attr_search_ctx *ctx;
+ int err = 0;
+ STANDARD_INFORMATION *std_info;
+ struct list_head *pos;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref));
+ if (!vol) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* Check cache, maybe this inode already opened? */
+ list_for_each(pos, &vol->inode_cache[MREF(mref) &
+ NTFS_INODE_CACHE_SIZE_BITS]) {
+ ntfs_inode *tmp_ni;
+
+ tmp_ni = list_entry(pos, ntfs_inode, list_entry);
+ if (tmp_ni->mft_no == MREF(mref)) {
+ ntfs_log_trace("Found this inode in cache, increment "
+ "reference count and return it.\n");
+ tmp_ni->nr_references++;
+ return tmp_ni;
+ }
+ }
+ /* Search failed. Properly open inode. */
+ ni = __ntfs_inode_allocate(vol);
+ if (!ni)
+ return NULL;
+ if (ntfs_file_record_read(vol, mref, &ni->mrec, NULL))
+ goto err_out;
+ if (!(ni->mrec->flags & MFT_RECORD_IN_USE)) {
+ err = ENOENT;
+ goto err_out;
+ }
+ ni->mft_no = MREF(mref);
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ goto err_out;
+ /* Receive some basic information about inode. */
+ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
+ 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ err = errno;
+ ntfs_log_trace("Failed to receive STANDARD_INFORMATION "
+ "attribute.\n");
+ goto put_err_out;
+ }
+ std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ ni->flags = std_info->file_attributes;
+ ni->creation_time = ntfs2utc(std_info->creation_time);
+ ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time);
+ ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time);
+ ni->last_access_time = ntfs2utc(std_info->last_access_time);
+ /* Set attribute list information. */
+ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
+ ctx)) {
+ if (errno != ENOENT)
+ goto put_err_out;
+ /* Attribute list attribute does not present. */
+ goto get_size;
+ }
+ NInoSetAttrList(ni);
+ l = ntfs_get_attribute_value_length(ctx->attr);
+ if (!l)
+ goto put_err_out;
+ if (l > 0x40000) {
+ err = EIO;
+ goto put_err_out;
+ }
+ ni->attr_list_size = l;
+ ni->attr_list = ntfs_malloc(ni->attr_list_size);
+ if (!ni->attr_list)
+ goto put_err_out;
+ l = ntfs_get_attribute_value(vol, ctx->attr, ni->attr_list);
+ if (!l)
+ goto put_err_out;
+ if (l != ni->attr_list_size) {
+ err = EIO;
+ goto put_err_out;
+ }
+get_size:
+ if (ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) {
+ if (errno != ENOENT)
+ goto put_err_out;
+ /* Directory or special file. */
+ ni->data_size = ni->allocated_size = 0;
+ } else {
+ if (ctx->attr->non_resident) {
+ ni->data_size = sle64_to_cpu(ctx->attr->u.nonres.data_size);
+ if (ctx->attr->flags &
+ (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE))
+ ni->allocated_size = sle64_to_cpu(
+ ctx->attr->u.nonres.compressed_size);
+ else
+ ni->allocated_size = sle64_to_cpu(
+ ctx->attr->u.nonres.allocated_size);
+ } else {
+ ni->data_size = le32_to_cpu(ctx->attr->u.res.value_length);
+ ni->allocated_size = (ni->data_size + 7) & ~7;
+ }
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ __ntfs_inode_add_to_cache(ni);
+ return ni;
+put_err_out:
+ if (!err)
+ err = errno;
+ ntfs_attr_put_search_ctx(ctx);
+err_out:
+ if (!err)
+ err = errno;
+ __ntfs_inode_release(ni);
+ errno = err;
+ return NULL;
+}
+
+/**
+ * ntfs_inode_close - close an ntfs inode and free all associated memory
+ * @ni: ntfs inode to close
+ *
+ * Make sure the ntfs inode @ni is clean.
+ *
+ * If the ntfs inode @ni is a base inode, close all associated extent inodes,
+ * then deallocate all memory attached to it, and finally free the ntfs inode
+ * structure itself.
+ *
+ * If it is an extent inode, we disconnect it from its base inode before we
+ * destroy it.
+ *
+ * It is OK to pass NULL to this function, it is just noop in this case.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code. On
+ * error, @ni has not been freed. The user should attempt to handle the error
+ * and call ntfs_inode_close() again. The following error codes are defined:
+ *
+ * EBUSY @ni and/or its attribute list runlist is/are dirty and the
+ * attempt to write it/them to disk failed.
+ * EINVAL @ni is invalid (probably it is an extent inode).
+ * EIO I/O error while trying to write inode to disk.
+ */
+int ntfs_inode_close(ntfs_inode *ni)
+{
+ if (!ni)
+ return 0;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ /* Decrement number of users. If there are left then just return. */
+ if (ni->nr_extents != -1) {
+ ni->nr_references--;
+ if (ni->nr_references) {
+ ntfs_log_trace("There are %d more references left to "
+ "this inode.\n",
+ ni->nr_references);
+ return 0;
+ } else
+ ntfs_log_trace("There are no more references left to "
+ "this inode.\n");
+ }
+ /* Check whether all attributes of this inode are closed. */
+ if (!list_empty(&ni->attr_cache))
+ ntfs_log_error("%s(): Not all attributes are closed. "
+ "We definitely have memory leak. "
+ "Continue anyway.\n", "ntfs_inode_close");
+ /* If we have dirty metadata, write it out. */
+ if (NInoDirty(ni) || NInoAttrListDirty(ni)) {
+ if (ntfs_inode_sync(ni)) {
+ if (errno != EIO)
+ errno = EBUSY;
+ return -1;
+ }
+ }
+ /* Is this a base inode with mapped extent inodes? */
+ if (ni->nr_extents > 0) {
+ while (ni->nr_extents > 0) {
+ if (ntfs_inode_close(ni->u.extent_nis[0])) {
+ if (errno != EIO)
+ errno = EBUSY;
+ return -1;
+ }
+ }
+ } else if (ni->nr_extents == -1) {
+ ntfs_inode **tmp_nis;
+ ntfs_inode *base_ni;
+ s32 i;
+
+ /*
+ * If the inode is an extent inode, disconnect it from the
+ * base inode before destroying it.
+ */
+ base_ni = ni->u.base_ni;
+ for (i = 0; i < base_ni->nr_extents; ++i) {
+ tmp_nis = base_ni->u.extent_nis;
+ if (tmp_nis[i] != ni)
+ continue;
+ /* Found it. Disconnect. */
+ memmove(tmp_nis + i, tmp_nis + i + 1,
+ (base_ni->nr_extents - i - 1) *
+ sizeof(ntfs_inode *));
+ /* Buffer should be for multiple of four extents. */
+ if ((--base_ni->nr_extents) & 3) {
+ i = -1;
+ break;
+ }
+ /*
+ * ElectricFence is unhappy with realloc(x,0) as free(x)
+ * thus we explicitly separate these two cases.
+ */
+ if (base_ni->nr_extents) {
+ /* Resize the memory buffer. */
+ tmp_nis = realloc(tmp_nis, base_ni->nr_extents *
+ sizeof(ntfs_inode *));
+ /* Ignore errors, they don't really matter. */
+ if (tmp_nis)
+ base_ni->u.extent_nis = tmp_nis;
+ } else if (tmp_nis)
+ free(tmp_nis);
+ /* Allow for error checking. */
+ i = -1;
+ break;
+ }
+ if (i != -1)
+ ntfs_log_debug("Extent inode was not attached to base "
+ "inode! Continuing regardless.\n");
+ }
+ /* Remove inode from the list of opened inodes. */
+ if (ni->nr_extents != -1)
+ list_del(&ni->list_entry);
+ return __ntfs_inode_release(ni);
+}
+
+/**
+ * ntfs_extent_inode_open - load an extent inode and attach it to its base
+ * @base_ni: base ntfs inode
+ * @mref: mft reference of the extent inode to load (in little endian)
+ *
+ * First check if the extent inode @mref is already attached to the base ntfs
+ * inode @base_ni, and if so, return a pointer to the attached extent inode.
+ *
+ * If the extent inode is not already attached to the base inode, allocate an
+ * ntfs_inode structure and initialize it for the given inode @mref. @mref
+ * specifies the inode number / mft record to read, including the sequence
+ * number, which can be 0 if no sequence number checking is to be performed.
+ *
+ * Then, allocate a buffer for the mft record, read the mft record from the
+ * volume @base_ni->vol, and attach it to the ntfs_inode structure (->mrec).
+ * The mft record is mst deprotected and sanity checked for validity and we
+ * abort if deprotection or checks fail.
+ *
+ * Finally attach the ntfs inode to its base inode @base_ni and return a
+ * pointer to the ntfs_inode structure on success or NULL on error, with errno
+ * set to the error code.
+ *
+ * Note, extent inodes are never closed directly. They are automatically
+ * disposed off by the closing of the base inode.
+ */
+ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const leMFT_REF mref)
+{
+ u64 mft_no = MREF_LE(mref);
+ ntfs_inode *ni;
+ ntfs_inode **extent_nis;
+ int i;
+
+ if (!base_ni) {
+ errno = EINVAL;
+ return NULL;
+ }
+ ntfs_log_trace("Opening extent inode 0x%llx "
+ "(base MFT record 0x%llx).\n",
+ (unsigned long long)mft_no,
+ (unsigned long long)base_ni->mft_no);
+ /* Is the extent inode already open and attached to the base inode? */
+ if (base_ni->nr_extents > 0) {
+ extent_nis = base_ni->u.extent_nis;
+ for (i = 0; i < base_ni->nr_extents; i++) {
+ u16 seq_no;
+
+ ni = extent_nis[i];
+ if (mft_no != ni->mft_no)
+ continue;
+ /* Verify the sequence number if given. */
+ seq_no = MSEQNO_LE(mref);
+ if (seq_no && seq_no != le16_to_cpu(
+ ni->mrec->sequence_number)) {
+ ntfs_log_debug("Found stale extent mft "
+ "reference! Corrupt file "
+ "system. Run chkdsk.\n");
+ errno = EIO;
+ return NULL;
+ }
+ /* We are done, return the extent inode. */
+ return ni;
+ }
+ }
+ /* Wasn't there, we need to load the extent inode. */
+ ni = __ntfs_inode_allocate(base_ni->vol);
+ if (!ni)
+ return NULL;
+ if (ntfs_file_record_read(base_ni->vol, le64_to_cpu(mref), &ni->mrec,
+ NULL))
+ goto err_out;
+ ni->mft_no = mft_no;
+ ni->nr_extents = -1;
+ ni->u.base_ni = base_ni;
+ /* Attach extent inode to base inode, reallocating memory if needed. */
+ if (!(base_ni->nr_extents & 3)) {
+ i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
+
+ extent_nis = (ntfs_inode**)ntfs_malloc(i);
+ if (!extent_nis)
+ goto err_out;
+ if (base_ni->nr_extents) {
+ memcpy(extent_nis, base_ni->u.extent_nis,
+ i - 4 * sizeof(ntfs_inode *));
+ free(base_ni->u.extent_nis);
+ }
+ base_ni->u.extent_nis = extent_nis;
+ }
+ base_ni->u.extent_nis[base_ni->nr_extents++] = ni;
+ return ni;
+err_out:
+ i = errno;
+ __ntfs_inode_release(ni);
+ errno = i;
+ ntfs_log_perror("Failed to open extent inode");
+ return NULL;
+}
+
+/**
+ * ntfs_inode_attach_all_extents - attach all extents for target inode
+ * @ni: opened ntfs inode for which perform attach
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_inode_attach_all_extents(ntfs_inode *ni)
+{
+ ATTR_LIST_ENTRY *ale;
+ u64 prev_attached = 0;
+
+ if (!ni) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (ni->nr_extents == -1)
+ ni = ni->u.base_ni;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ /* Inode haven't got attribute list, thus nothing to attach. */
+ if (!NInoAttrList(ni))
+ return 0;
+
+ if (!ni->attr_list) {
+ ntfs_log_trace("Corrupted in-memory structure.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Walk through attribute list and attach all extents. */
+ errno = 0;
+ ale = (ATTR_LIST_ENTRY *)ni->attr_list;
+ while ((u8*)ale < ni->attr_list + ni->attr_list_size) {
+ if (ni->mft_no != MREF_LE(ale->mft_reference) &&
+ prev_attached != MREF_LE(ale->mft_reference)) {
+ if (!ntfs_extent_inode_open(ni, ale->mft_reference)) {
+ ntfs_log_trace("Couldn't attach extent "
+ "inode (attr type 0x%x "
+ "references to it).\n",
+ le32_to_cpu(ale->type));
+ return -1;
+ }
+ prev_attached = MREF_LE(ale->mft_reference);
+ }
+ ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length));
+ }
+ return 0;
+}
+
+/**
+ * ntfs_inode_sync_standard_information - update standard information attribute
+ * @ni: ntfs inode to update standard information
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
+{
+ ntfs_attr_search_ctx *ctx;
+ STANDARD_INFORMATION *std_info;
+ int err;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx)
+ return -1;
+ if (ntfs_attr_lookup(AT_STANDARD_INFORMATION, AT_UNNAMED,
+ 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ err = errno;
+ ntfs_log_trace("Failed to receive STANDARD_INFORMATION "
+ "attribute.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+ }
+ std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ std_info->file_attributes = ni->flags;
+ std_info->creation_time = utc2ntfs(ni->creation_time);
+ std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time);
+ std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
+ std_info->last_access_time = utc2ntfs(ni->last_access_time);
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+}
+
+/**
+ * ntfs_inode_sync_file_name - update FILE_NAME attributes
+ * @ni: ntfs inode to update FILE_NAME attributes
+ *
+ * Update all FILE_NAME attributes for inode @ni in the index.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+static int ntfs_inode_sync_file_name(ntfs_inode *ni)
+{
+ ntfs_attr_search_ctx *ctx = NULL;
+ ntfs_index_context *ictx;
+ ntfs_inode *index_ni;
+ FILE_NAME_ATTR *fn;
+ int err = 0;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = errno;
+ ntfs_log_trace("Failed to get attribute search context.\n");
+ goto err_out;
+ }
+ /* Walk through all FILE_NAME attributes and update them. */
+ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr +
+ le16_to_cpu(ctx->attr->u.res.value_offset));
+ if (MREF_LE(fn->parent_directory) == ni->mft_no) {
+ /*
+ * WARNING: We cheater here and obtain 2 attribute
+ * search contexts for one inode (first we obtained
+ * above, second will be obtained inside
+ * ntfs_index_lookup), it's acceptable for library,
+ * but will lock kernel.
+ */
+ index_ni = ni;
+ } else
+ index_ni = ntfs_inode_open(ni->vol,
+ le64_to_cpu(fn->parent_directory));
+ if (!index_ni) {
+ if (!err)
+ err = errno;
+ ntfs_log_trace("Failed to open inode with index.\n");
+ continue;
+ }
+ ictx = ntfs_index_ctx_get(index_ni, NTFS_INDEX_I30, 4);
+ if (!ictx) {
+ if (!err)
+ err = errno;
+ ntfs_log_trace("Failed to get index context.\n");
+ ntfs_inode_close(index_ni);
+ continue;
+ }
+ if (ntfs_index_lookup(fn, sizeof(FILE_NAME_ATTR), ictx)) {
+ if (!err) {
+ if (errno == ENOENT)
+ err = EIO;
+ else
+ err = errno;
+ }
+ ntfs_log_trace("Index lookup failed.\n");
+ ntfs_index_ctx_put(ictx);
+ ntfs_inode_close(index_ni);
+ continue;
+ }
+ /* Update flags and file size. */
+ fn = (FILE_NAME_ATTR *)ictx->data;
+ fn->file_attributes =
+ (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) |
+ (ni->flags & FILE_ATTR_VALID_FLAGS);
+ fn->allocated_size = cpu_to_sle64(ni->allocated_size);
+ fn->data_size = cpu_to_sle64(ni->data_size);
+ fn->creation_time = utc2ntfs(ni->creation_time);
+ fn->last_data_change_time = utc2ntfs(ni->last_data_change_time);
+ fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time);
+ fn->last_access_time = utc2ntfs(ni->last_access_time);
+ ntfs_index_entry_mark_dirty(ictx);
+ ntfs_index_ctx_put(ictx);
+ if (ni != index_ni)
+ ntfs_inode_close(index_ni);
+ }
+ /* Check for real error occurred. */
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto err_out;
+ }
+ ntfs_attr_put_search_ctx(ctx);
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+err_out:
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_inode_sync - write the inode (and its dirty extents) to disk
+ * @ni: ntfs inode to write
+ *
+ * Write the inode @ni to disk as well as its dirty extent inodes if such
+ * exist and @ni is a base inode. If @ni is an extent inode, only @ni is
+ * written completely disregarding its base inode and any other extent inodes.
+ *
+ * For a base inode with dirty extent inodes if any writes fail for whatever
+ * reason, the failing inode is skipped and the sync process is continued. At
+ * the end the error condition that brought about the failure is returned. Thus
+ * the smallest amount of data loss possible occurs.
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ * The following error codes are defined:
+ * EINVAL - Invalid arguments were passed to the function.
+ * EBUSY - Inode and/or one of its extents is busy, try again later.
+ * EIO - I/O error while writing the inode (or one of its extents).
+ */
+int ntfs_inode_sync(ntfs_inode *ni)
+{
+ int err = 0;
+
+ if (!ni) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ /* Update FILE_NAME's in the index. */
+ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
+ NInoFileNameTestAndClearDirty(ni) &&
+ ntfs_inode_sync_file_name(ni)) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ }
+ ntfs_log_trace("Failed to sync FILE_NAME attributes.\n");
+ NInoFileNameSetDirty(ni);
+ }
+
+ /* Write out attribute list from cache to disk. */
+ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 &&
+ NInoAttrList(ni) && NInoAttrListTestAndClearDirty(ni)) {
+ ntfs_attr *na;
+
+ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
+ if (!na) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ ntfs_log_trace("Attribute list sync failed "
+ "(open failed).\n");
+ }
+ NInoAttrListSetDirty(ni);
+ } else {
+ if (na->data_size == ni->attr_list_size) {
+ if (ntfs_attr_pwrite(na, 0, ni->attr_list_size,
+ ni->attr_list) !=
+ ni->attr_list_size) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ ntfs_log_trace("Attribute list "
+ "sync failed "
+ "(write).\n");
+ }
+ NInoAttrListSetDirty(ni);
+ }
+ } else {
+ err = EIO;
+ ntfs_log_trace("Attribute list sync failed "
+ "(invalid size).\n");
+ NInoAttrListSetDirty(ni);
+ }
+ ntfs_attr_close(na);
+ }
+ }
+
+ /* Write this inode out to the $MFT (and $MFTMirr if applicable). */
+ if (NInoTestAndClearDirty(ni)) {
+ /* Update STANDARD_INFORMATION. */
+ if ((ni->mrec->flags & MFT_RECORD_IN_USE) &&
+ ni->nr_extents != -1 &&
+ ntfs_inode_sync_standard_information(ni)) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ }
+ ntfs_log_trace("Failed to sync standard "
+ "information.\n");
+ }
+ /* Write MFT record. */
+ if (ntfs_mft_record_write(ni->vol, ni->mft_no, ni->mrec)) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ }
+ NInoSetDirty(ni);
+ ntfs_log_trace("Base MFT record sync failed.\n");
+ }
+ }
+
+ /* If this is a base inode with extents write all dirty extents, too. */
+ if (ni->nr_extents > 0) {
+ s32 i;
+
+ for (i = 0; i < ni->nr_extents; ++i) {
+ ntfs_inode *eni;
+
+ eni = ni->u.extent_nis[i];
+ if (NInoTestAndClearDirty(eni)) {
+ if (ntfs_mft_record_write(eni->vol, eni->mft_no,
+ eni->mrec)) {
+ if (!err || errno == EIO) {
+ err = errno;
+ if (err != EIO)
+ err = EBUSY;
+ }
+ NInoSetDirty(eni);
+ ntfs_log_trace("Extent MFT record sync "
+ "failed.\n");
+ }
+ }
+ }
+ }
+
+ if (!err)
+ return 0;
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_inode_add_attrlist - add attribute list to inode and fill it
+ * @ni: opened ntfs inode to which add attribute list
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ * The following error codes are defined:
+ * EINVAL - Invalid arguments were passed to the function.
+ * EEXIST - Attribute list already exist.
+ * EIO - Input/Ouput error occurred.
+ * ENOMEM - Not enough memory to perform add.
+ */
+int ntfs_inode_add_attrlist(ntfs_inode *ni)
+{
+ int err;
+ ntfs_attr_search_ctx *ctx;
+ u8 *al, *aln;
+ int al_len, al_allocated;
+ ATTR_LIST_ENTRY *ale;
+ ntfs_attr *na;
+
+ if (!ni) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ if (NInoAttrList(ni) || ni->nr_extents) {
+ ntfs_log_trace("Inode already has got attribute list.\n");
+ errno = EEXIST;
+ return -1;
+ }
+
+ al_allocated = 0x40;
+ al_len = 0;
+ al = malloc(al_allocated);
+ NTFS_ON_DEBUG(memset(al, 0, 0x40)); /* Valgrind. */
+ ale = (ATTR_LIST_ENTRY *) al;
+ if (!al) {
+ ntfs_log_trace("Not enough memory.\n");
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Form attribute list. */
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ err = errno;
+ ntfs_log_trace("Couldn't get search context.\n");
+ goto err_out;
+ }
+ /* Walk through all attributes. */
+ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) {
+ if (ctx->attr->type == AT_ATTRIBUTE_LIST) {
+ err = EIO;
+ ntfs_log_trace("Attribute list already present.\n");
+ goto put_err_out;
+ }
+ /* Calculate new length of attribute list. */
+ al_len += (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) *
+ ctx->attr->name_length + 7) & ~7;
+ /* Allocate more memory if needed. */
+ while (al_len > al_allocated) {
+ al_allocated += 0x40;
+ aln = realloc(al, al_allocated);
+ NTFS_ON_DEBUG(memset(aln + al_allocated - 0x40, 0,
+ 0x40)); /* Valgrind. */
+ if (!aln) {
+ ntfs_log_trace("Not enough memory.\n");
+ err = ENOMEM;
+ goto put_err_out;
+ }
+ ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al));
+ al = aln;
+ }
+ /* Add attribute to attribute list. */
+ ale->type = ctx->attr->type;
+ ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) +
+ sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7);
+ ale->name_length = ctx->attr->name_length;
+ ale->name_offset = (u8 *)ale->name - (u8 *)ale;
+ if (ctx->attr->non_resident)
+ ale->lowest_vcn = ctx->attr->u.nonres.lowest_vcn;
+ else
+ ale->lowest_vcn = 0;
+ ale->mft_reference = MK_LE_MREF(ni->mft_no,
+ le16_to_cpu(ni->mrec->sequence_number));
+ ale->instance = ctx->attr->instance;
+ memcpy(ale->name, (u8 *)ctx->attr +
+ le16_to_cpu(ctx->attr->name_offset),
+ ctx->attr->name_length * sizeof(ntfschar));
+ ale = (ATTR_LIST_ENTRY *)(al + al_len);
+ }
+ /* Check for real error occurred. */
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+ /* Deallocate trailing memory. */
+ aln = realloc(al, al_len);
+ if (!aln) {
+ err = errno;
+ ntfs_log_trace("realloc() failed.\n");
+ goto put_err_out;
+ }
+ al = aln;
+
+ /* Set in-memory attribute list. */
+ ni->attr_list = al;
+ ni->attr_list_size = al_len;
+ NInoSetAttrList(ni);
+ NInoAttrListSetDirty(ni);
+
+ /* Free space if there is not enough it for $ATTRIBUTE_LIST. */
+ if (le32_to_cpu(ni->mrec->bytes_allocated) -
+ le32_to_cpu(ni->mrec->bytes_in_use) <
+ offsetof(ATTR_RECORD, u.res.resident_end)) {
+ if (ntfs_inode_free_space(ni,
+ offsetof(ATTR_RECORD, u.res.resident_end))) {
+ /* Failed to free space. */
+ err = errno;
+ ntfs_log_trace("Failed to free space for "
+ "$ATTRIBUTE_LIST.\n");
+ goto rollback;
+ }
+ }
+
+ /* Add $ATTRIBUTE_LIST to mft record. */
+ if (ntfs_resident_attr_record_add(ni,
+ AT_ATTRIBUTE_LIST, NULL, 0, NULL, 0, 0) < 0) {
+ err = errno;
+ ntfs_log_trace("Couldn't add $ATTRIBUTE_LIST to MFT record.\n");
+ goto rollback;
+ }
+
+ /* Resize it. */
+ na = ntfs_attr_open(ni, AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
+ if (!na) {
+ err = errno;
+ ntfs_log_trace("Failed to open just added $ATTRIBUTE_LIST.\n");
+ goto remove_attrlist_record;
+ }
+ if (ntfs_attr_truncate(na, al_len)) {
+ err = errno;
+ ntfs_log_trace("Failed to resize just added $ATTRIBUTE_LIST.\n");
+ ntfs_attr_close(na);
+ goto remove_attrlist_record;;
+ }
+ /* Done! */
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_attr_close(na);
+ return 0;
+remove_attrlist_record:
+ /* Prevent ntfs_attr_recorm_rm from freeing attribute list. */
+ ni->attr_list = NULL;
+ NInoClearAttrList(ni);
+ /* Remove $ATTRIBUTE_LIST record. */
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (!ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0,
+ CASE_SENSITIVE, 0, NULL, 0, ctx)) {
+ if (ntfs_attr_record_rm(ctx))
+ ntfs_log_trace("Rollback failed. Failed to remove attribute "
+ "list record.\n");
+ } else
+ ntfs_log_trace("Rollback failed. Couldn't find attribute list "
+ "record.\n");
+ /* Setup back in-memory runlist. */
+ ni->attr_list = al;
+ ni->attr_list_size = al_len;
+ NInoSetAttrList(ni);
+rollback:
+ /*
+ * Scan attribute list for attributes that placed not in the base MFT
+ * record and move them to it.
+ */
+ ntfs_attr_reinit_search_ctx(ctx);
+ ale = (ATTR_LIST_ENTRY*)al;
+ while ((u8*)ale < al + al_len) {
+ if (MREF_LE(ale->mft_reference) != ni->mft_no) {
+ if (!ntfs_attr_lookup(ale->type, ale->name,
+ ale->name_length,
+ CASE_SENSITIVE,
+ sle64_to_cpu(ale->lowest_vcn),
+ NULL, 0, ctx)) {
+ if (ntfs_attr_record_move_to(ctx, ni))
+ ntfs_log_trace("Rollback failed. Couldn't "
+ "back attribute to base MFT record.\n");
+ } else
+ ntfs_log_trace("Rollback failed. ntfs_attr_lookup "
+ "failed.\n");
+ ntfs_attr_reinit_search_ctx(ctx);
+ }
+ ale = (ATTR_LIST_ENTRY*)((u8*)ale + le16_to_cpu(ale->length));
+ }
+ /* Remove in-memory attribute list. */
+ ni->attr_list = NULL;
+ ni->attr_list_size = 0;
+ NInoClearAttrList(ni);
+ NInoAttrListClearDirty(ni);
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+err_out:
+ free(al);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_inode_free_space - free space in the MFT record of inode
+ * @ni: ntfs inode in which MFT record free space
+ * @size: amount of space needed to free
+ *
+ * Return 0 on success or -1 on error with errno set to the error code.
+ */
+int ntfs_inode_free_space(ntfs_inode *ni, int size)
+{
+ ntfs_attr_search_ctx *ctx;
+ int freed, err;
+
+ if (!ni || size < 0) {
+ ntfs_log_trace("Invalid arguments.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ ntfs_log_trace("Entering for inode 0x%llx, size %d.\n",
+ (long long) ni->mft_no, size);
+
+ freed = (le32_to_cpu(ni->mrec->bytes_allocated) -
+ le32_to_cpu(ni->mrec->bytes_in_use));
+
+ if (size <= freed)
+ return 0;
+
+ ctx = ntfs_attr_get_search_ctx(ni, NULL);
+ if (!ctx) {
+ ntfs_log_trace("Failed to get attribute search context.\n");
+ return -1;
+ }
+
+ /*
+ * Chkdsk complain if $STANDARD_INFORMATION is not in the base MFT
+ * record. FIXME: I'm not sure in this, need to recheck. For now simply
+ * do not move $STANDARD_INFORMATION at all.
+ *
+ * Also we can't move $ATTRIBUTE_LIST from base MFT_RECORD, so position
+ * search context on first attribute after $STANDARD_INFORMATION and
+ * $ATTRIBUTE_LIST.
+ *
+ * Why we reposition instead of simply skip this attributes during
+ * enumeration? Because in case we have got only in-memory attribute
+ * list ntfs_attr_lookup will fail when it will try to find
+ * $ATTRIBUTE_LIST.
+ */
+ if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0, NULL,
+ 0, ctx)) {
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ goto put_err_out;
+ }
+ if (ctx->attr->type == AT_END) {
+ err = ENOSPC;
+ goto put_err_out;
+ }
+ }
+
+ while (1) {
+ int record_size;
+
+ /*
+ * Check whether attribute is from different MFT record. If so,
+ * find next, because we don't need such.
+ */
+ while (ctx->ntfs_ino->mft_no != ni->mft_no) {
+ if (ntfs_attr_lookup(AT_UNUSED, NULL, 0, CASE_SENSITIVE,
+ 0, NULL, 0, ctx)) {
+ err = errno;
+ if (errno != ENOENT) {
+ ntfs_log_trace("Attribute lookup failed.\n");
+ } else
+ err = ENOSPC;
+ goto put_err_out;
+ }
+ }
+
+ record_size = le32_to_cpu(ctx->attr->length);
+
+ /* Move away attribute. */
+ if (ntfs_attr_record_move_away(ctx, 0)) {
+ err = errno;
+ ntfs_log_trace("Failed to move out attribute.\n");
+ break;
+ }
+ freed += record_size;
+
+ /* Check whether we done. */
+ if (size <= freed) {
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+ }
+
+ /*
+ * Reposition to first attribute after $STANDARD_INFORMATION and
+ * $ATTRIBUTE_LIST (see comments upwards).
+ */
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, CASE_SENSITIVE, 0,
+ NULL, 0, ctx)) {
+ if (errno != ENOENT) {
+ err = errno;
+ ntfs_log_trace("Attribute lookup failed.\n");
+ break;
+ }
+ if (ctx->attr->type == AT_END) {
+ err = ENOSPC;
+ break;
+ }
+ }
+ }
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ if (err == ENOSPC)
+ ntfs_log_trace("No attributes left that can be moved out.\n");
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_inode_update_times - update selected time fields for ntfs inode
+ * @ni: ntfs inode for which update time fields
+ * @mask: select which time fields should be updated
+ *
+ * This function updates time fields to current time. Fields to update are
+ * selected using @mask (see enum @ntfs_time_update_flags for posssible values).
+ */
+void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask)
+{
+ time_t now;
+
+ if (!ni) {
+ ntfs_log_error("%s(): Invalid arguments.\n", "ntfs_inode_update_times");
+ return;
+ }
+ if ((ni->mft_no < FILE_first_user && ni->mft_no != FILE_root) ||
+ NVolReadOnly(ni->vol) || !mask)
+ return;
+
+ now = time(NULL);
+ if (mask & NTFS_UPDATE_ATIME)
+ ni->last_access_time = now;
+ if (mask & NTFS_UPDATE_MTIME)
+ ni->last_data_change_time = now;
+ if (mask & NTFS_UPDATE_CTIME)
+ ni->last_mft_change_time = now;
+ NInoFileNameSetDirty(ni);
+ NInoSetDirty(ni);
+}
+
+/**
+ * ntfs_inode_badclus_bad - check for $Badclus:$Bad data attribute
+ * @mft_no: mft record number where @attr is present
+ * @attr: attribute record used to check for the $Bad attribute
+ *
+ * Check if the mft record given by @mft_no and @attr contains the bad sector
+ * list. Please note that mft record numbers describing $Badclus extent inodes
+ * will not match the current $Badclus:$Bad check.
+ *
+ * On success return 1 if the file is $Badclus:$Bad, otherwise return 0.
+ * On error return -1 with errno set to the error code.
+ */
+int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr)
+{
+ int len, ret = 0;
+ ntfschar *ustr;
+
+ if (!attr) {
+ ntfs_log_error("Invalid argument.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (mft_no != FILE_BadClus)
+ return 0;
+
+ if (attr->type != AT_DATA)
+ return 0;
+
+ if ((ustr = ntfs_str2ucs("$Bad", &len)) == NULL) {
+ ntfs_log_perror("Couldn't convert '$Bad' to Unicode");
+ return -1;
+ }
+
+ if (ustr && ntfs_names_are_equal(ustr, len,
+ (ntfschar *)((u8 *)attr + le16_to_cpu(
+ attr->name_offset)), attr->name_length, 0, NULL, 0))
+ ret = 1;
+
+ ntfs_ucsfree(ustr);
+
+ return ret;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/lcnalloc.c b/usr/src/lib/libntfs/common/libntfs/lcnalloc.c
new file mode 100644
index 0000000000..46698642af
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/lcnalloc.c
@@ -0,0 +1,857 @@
+/**
+ * lcnalloc.c - Cluster (de)allocation code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2004 Anton Altaparmakov
+ * Copyright (c) 2004 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "types.h"
+#include "attrib.h"
+#include "bitmap.h"
+#include "debug.h"
+#include "runlist.h"
+#include "volume.h"
+#include "lcnalloc.h"
+#include "logging.h"
+
+/**
+ * ntfs_cluster_alloc - allocate clusters on an ntfs volume
+ * @vol: mounted ntfs volume on which to allocate the clusters
+ * @start_vcn: vcn to use for the first allocated cluster
+ * @count: number of clusters to allocate
+ * @start_lcn: starting lcn at which to allocate the clusters (or -1 if none)
+ * @zone: zone from which to allocate the clusters
+ *
+ * Allocate @count clusters preferably starting at cluster @start_lcn or at the
+ * current allocator position if @start_lcn is -1, on the mounted ntfs volume
+ * @vol. @zone is either DATA_ZONE for allocation of normal clusters and
+ * MFT_ZONE for allocation of clusters for the master file table, i.e. the
+ * $MFT/$DATA attribute.
+ *
+ * On success return a runlist describing the allocated cluster(s).
+ *
+ * On error return NULL with errno set to the error code.
+ *
+ * Notes on the allocation algorithm
+ * =================================
+ *
+ * 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 NTFS 1.x
+ * volumes, the second data zone doesn't exist due to the mft zone being
+ * expanded to cover the start of the 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 up to 8192 bytes at a time, so we
+ * need to cope with crossing over boundaries of two buffers. 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: 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 it might even be correct not to do 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)
+ */
+runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
+ LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone)
+{
+ LCN zone_start, zone_end, bmp_pos, bmp_initial_pos, last_read_pos, lcn;
+ LCN prev_lcn = 0, prev_run_len = 0, mft_zone_size;
+ s64 clusters, br;
+ runlist *rl = NULL, *trl;
+ u8 *buf, *byte;
+ int err = 0, rlpos, rlsize, buf_size;
+ u8 pass, done_zones, search_zone, need_writeback, bit;
+
+ ntfs_log_trace("Entering with count = 0x%llx, start_lcn = 0x%llx, zone = "
+ "%s_ZONE.\n", (long long)count, (long long)start_lcn,
+ zone == MFT_ZONE ? "MFT" : "DATA");
+ if (!vol || count < 0 || start_lcn < -1 || !vol->lcnbmp_na ||
+ (s8)zone < FIRST_ZONE || zone > LAST_ZONE) {
+ ntfs_log_trace("Invalid arguments!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Return empty runlist if @count == 0 */
+ if (!count) {
+ rl = ntfs_malloc(0x1000);
+ if (!rl)
+ return NULL;
+ rl[0].vcn = start_vcn;
+ rl[0].lcn = LCN_RL_NOT_MAPPED;
+ rl[0].length = 0;
+ return rl;
+ }
+
+ /* Allocate memory. */
+ buf = (u8*)ntfs_malloc(8192);
+ if (!buf)
+ return NULL;
+ /*
+ * If no specific @start_lcn was requested, use the current data zone
+ * position, otherwise use the requested @start_lcn 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,
+ * we either start at the current position within the mft zone or at
+ * the specified position. 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 = start_lcn;
+ 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 == DATA_ZONE && zone_start >= vol->mft_zone_start &&
+ zone_start < vol->mft_zone_end) {
+ zone_start = vol->mft_zone_end;
+ /*
+ * Starting at beginning of data1_zone which means a single
+ * pass in this zone is sufficient.
+ */
+ pass = 2;
+ } else if (zone == MFT_ZONE && (zone_start < vol->mft_zone_start ||
+ zone_start >= vol->mft_zone_end)) {
+ zone_start = vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ zone_start = 0;
+ /*
+ * Starting at beginning of volume which means a single pass
+ * is sufficient.
+ */
+ pass = 2;
+ }
+ if (zone == MFT_ZONE) {
+ zone_end = vol->mft_zone_end;
+ search_zone = 1;
+ } else /* 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;
+ }
+ }
+ /*
+ * bmp_pos is the current bit position inside the bitmap. We use
+ * bmp_initial_pos to determine whether or not to do a zone switch.
+ */
+ bmp_pos = bmp_initial_pos = zone_start;
+
+ /* Loop until all clusters are allocated, i.e. clusters == 0. */
+ clusters = count;
+ rlpos = rlsize = 0;
+ while (1) {
+ ntfs_log_trace("Start of outer while loop: done_zones = 0x%x, "
+ "search_zone = %i, pass = %i, zone_start = "
+ "0x%llx, zone_end = 0x%llx, bmp_initial_pos = "
+ "0x%llx, bmp_pos = 0x%llx, rlpos = %i, rlsize = "
+ "%i.\n", done_zones, search_zone, pass,
+ (long long)zone_start, (long long)zone_end,
+ (long long)bmp_initial_pos, (long long)bmp_pos,
+ rlpos, rlsize);
+ /* Loop until we run out of free clusters. */
+ last_read_pos = bmp_pos >> 3;
+ ntfs_log_trace("last_read_pos = 0x%llx.\n", (long long)last_read_pos);
+ br = ntfs_attr_pread(vol->lcnbmp_na, last_read_pos, 8192, buf);
+ if (br <= 0) {
+ if (!br) {
+ /* Reached end of attribute. */
+ ntfs_log_trace("End of attribute reached. Skipping "
+ "to zone_pass_done.\n");
+ goto zone_pass_done;
+ }
+ err = errno;
+ ntfs_log_trace("ntfs_attr_pread() failed. Aborting.\n");
+ goto err_ret;
+ }
+ /*
+ * We might have read less than 8192 bytes if we are close to
+ * the end of the attribute.
+ */
+ buf_size = (int)br << 3;
+ lcn = bmp_pos & 7;
+ bmp_pos &= ~7;
+ need_writeback = 0;
+ ntfs_log_trace("Before inner while loop: buf_size = %i, lcn = "
+ "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
+ buf_size, (long long)lcn, (long long)bmp_pos,
+ need_writeback);
+ while (lcn < buf_size && lcn + bmp_pos < zone_end) {
+ byte = buf + (lcn >> 3);
+ ntfs_log_trace("In inner while loop: buf_size = %i, lcn = "
+ "0x%llx, bmp_pos = 0x%llx, "
+ "need_writeback = %i, byte ofs = 0x%x, "
+ "*byte = 0x%x.\n", buf_size,
+ (long long)lcn, (long long)bmp_pos,
+ need_writeback, (unsigned int)(lcn >> 3),
+ (unsigned int)*byte);
+ /* Skip full bytes. */
+ if (*byte == 0xff) {
+ lcn = (lcn + 8) & ~7;
+ ntfs_log_trace("continuing while loop 1.\n");
+ continue;
+ }
+ bit = 1 << (lcn & 7);
+ ntfs_log_trace("bit = %i.\n", bit);
+ /* If the bit is already set, go onto the next one. */
+ if (*byte & bit) {
+ lcn++;
+ ntfs_log_trace("continuing while loop 2.\n");
+ continue;
+ }
+ /* Reallocate memory if necessary. */
+ if ((rlpos + 2) * (int)sizeof(runlist) >= rlsize) {
+ ntfs_log_trace("Reallocating space.\n");
+ if (!rl)
+ ntfs_log_trace("First free bit is at LCN = "
+ "0x%llx.\n", (long long)(lcn + bmp_pos));
+ rlsize += 4096;
+ trl = (runlist*)realloc(rl, rlsize);
+ if (!trl) {
+ err = ENOMEM;
+ ntfs_log_trace("Failed to allocate memory, "
+ "going to wb_err_ret.\n");
+ goto wb_err_ret;
+ }
+ rl = trl;
+ ntfs_log_trace("Reallocated memory, rlsize = "
+ "0x%x.\n", rlsize);
+ }
+ /* Allocate the bitmap bit. */
+ *byte |= bit;
+ vol->nr_free_clusters--;
+ /* We need to write this bitmap buffer back to disk! */
+ need_writeback = 1;
+ ntfs_log_trace("*byte = 0x%x, need_writeback is set.\n",
+ (unsigned int)*byte);
+ /*
+ * Coalesce with previous run if adjacent LCNs.
+ * Otherwise, append a new run.
+ */
+ ntfs_log_trace("Adding run (lcn 0x%llx, len 0x%llx), "
+ "prev_lcn = 0x%llx, lcn = 0x%llx, "
+ "bmp_pos = 0x%llx, prev_run_len = "
+ "0x%llx, rlpos = %i.\n",
+ (long long)(lcn + bmp_pos), 1LL,
+ (long long)prev_lcn, (long long)lcn,
+ (long long)bmp_pos,
+ (long long)prev_run_len, rlpos);
+ if (prev_lcn == lcn + bmp_pos - prev_run_len && rlpos) {
+ ntfs_log_trace("Coalescing to run (lcn 0x%llx, len "
+ "0x%llx).\n",
+ (long long)rl[rlpos - 1].lcn,
+ (long long) rl[rlpos - 1].length);
+ rl[rlpos - 1].length = ++prev_run_len;
+ ntfs_log_trace("Run now (lcn 0x%llx, len 0x%llx), "
+ "prev_run_len = 0x%llx.\n",
+ (long long)rl[rlpos - 1].lcn,
+ (long long)rl[rlpos - 1].length,
+ (long long)prev_run_len);
+ } else {
+ if (rlpos) {
+ ntfs_log_trace("Adding new run, (previous "
+ "run lcn 0x%llx, len 0x%llx).\n",
+ (long long) rl[rlpos - 1].lcn,
+ (long long) rl[rlpos - 1].length);
+ rl[rlpos].vcn = rl[rlpos - 1].vcn +
+ prev_run_len;
+ } else {
+ ntfs_log_trace("Adding new run, is first run.\n");
+ rl[rlpos].vcn = start_vcn;
+ }
+ rl[rlpos].lcn = prev_lcn = lcn + bmp_pos;
+ rl[rlpos].length = prev_run_len = 1;
+ rlpos++;
+ }
+ /* Done? */
+ if (!--clusters) {
+ LCN tc;
+ /*
+ * Update the current zone position. Positions
+ * of already scanned zones have been updated
+ * during the respective zone switches.
+ */
+ tc = lcn + bmp_pos + 1;
+ ntfs_log_trace("Done. Updating current zone "
+ "position, tc = 0x%llx, search_zone = %i.\n",
+ (long long)tc, search_zone);
+ switch (search_zone) {
+ case 1:
+ ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n",
+ (long long) 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 = 0;
+ } else if ((bmp_initial_pos >=
+ vol->mft_zone_pos ||
+ tc > vol->mft_zone_pos)
+ && tc >= vol->mft_lcn)
+ vol->mft_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n",
+ (long long) vol->mft_zone_pos);
+ break;
+ case 2:
+ ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n",
+ (long long) vol->data1_zone_pos);
+ if (tc >= vol->nr_clusters)
+ vol->data1_zone_pos =
+ vol->mft_zone_end;
+ else if ((bmp_initial_pos >=
+ vol->data1_zone_pos ||
+ tc > vol->data1_zone_pos)
+ && tc >= vol->mft_zone_end)
+ vol->data1_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n",
+ (long long) vol->data1_zone_pos);
+ break;
+ case 4:
+ ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n",
+ (long long) vol->data2_zone_pos);
+ if (tc >= vol->mft_zone_start)
+ vol->data2_zone_pos = 0;
+ else if (bmp_initial_pos >=
+ vol->data2_zone_pos ||
+ tc > vol->data2_zone_pos)
+ vol->data2_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n",
+ (long long) vol->data2_zone_pos);
+ break;
+ default:
+ free(rl);
+ free(buf);
+ NTFS_BUG("switch (search_zone) 1");
+ return NULL;
+ }
+ ntfs_log_trace("Going to done_ret.\n");
+ goto done_ret;
+ }
+ lcn++;
+ }
+ bmp_pos += buf_size;
+ ntfs_log_trace("After inner while loop: buf_size = 0x%x, lcn = "
+ "0x%llx, bmp_pos = 0x%llx, need_writeback = %i.\n",
+ buf_size, (long long)lcn,
+ (long long)bmp_pos, need_writeback);
+ if (need_writeback) {
+ s64 bw;
+ ntfs_log_trace("Writing back.\n");
+ need_writeback = 0;
+ bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos,
+ br, buf);
+ if (bw != br) {
+ if (bw == -1)
+ err = errno;
+ else
+ err = EIO;
+ ntfs_log_trace("Bitmap writeback failed in read next "
+ "buffer code path with error code %i.\n", err);
+ goto err_ret;
+ }
+ }
+ if (bmp_pos < zone_end) {
+ ntfs_log_trace("Continuing outer while loop, bmp_pos = "
+ "0x%llx, zone_end = 0x%llx.\n",
+ (long long)bmp_pos,
+ (long long)zone_end);
+ continue;
+ }
+zone_pass_done: /* Finished with the current zone pass. */
+ ntfs_log_trace("At zone_pass_done, pass = %i.\n", 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 = 0;
+ break;
+ default:
+ NTFS_BUG("switch (search_zone) 2");
+ }
+ /* Sanity check. */
+ if (zone_end < zone_start)
+ zone_end = zone_start;
+ bmp_pos = zone_start;
+ ntfs_log_trace("Continuing outer while loop, pass = 2, "
+ "zone_start = 0x%llx, zone_end = "
+ "0x%llx, bmp_pos = 0x%llx.\n",
+ zone_start, zone_end, bmp_pos);
+ continue;
+ } /* pass == 2 */
+done_zones_check:
+ ntfs_log_trace("At done_zones_check, search_zone = %i, done_zones "
+ "before = 0x%x, done_zones after = 0x%x.\n",
+ search_zone, done_zones, done_zones | search_zone);
+ done_zones |= search_zone;
+ if (done_zones < 7) {
+ ntfs_log_trace("Switching zone.\n");
+ /* Now switch to the next zone we haven't done yet. */
+ pass = 1;
+ switch (search_zone) {
+ case 1:
+ ntfs_log_trace("Switching from mft zone to data1 "
+ "zone.\n");
+ /* Update mft zone position. */
+ if (rlpos) {
+ LCN tc;
+ ntfs_log_trace("Before checks, vol->mft_zone_pos = 0x%llx.\n",
+ (long long) vol->mft_zone_pos);
+ tc = rl[rlpos - 1].lcn +
+ rl[rlpos - 1].length;
+ if (tc >= vol->mft_zone_end) {
+ vol->mft_zone_pos =
+ vol->mft_lcn;
+ if (!vol->mft_zone_end)
+ vol->mft_zone_pos = 0;
+ } else if ((bmp_initial_pos >=
+ vol->mft_zone_pos ||
+ tc > vol->mft_zone_pos)
+ && tc >= vol->mft_lcn)
+ vol->mft_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->mft_zone_pos = 0x%llx.\n",
+ (long long) vol->mft_zone_pos);
+ }
+ /* Switch from mft zone to data1 zone. */
+switch_to_data1_zone: search_zone = 2;
+ zone_start = bmp_initial_pos =
+ 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_log_trace("Switching from data1 zone to data2 "
+ "zone.\n");
+ /* Update data1 zone position. */
+ if (rlpos) {
+ LCN tc;
+ ntfs_log_trace("Before checks, vol->data1_zone_pos = 0x%llx.\n",
+ (long long) vol->data1_zone_pos);
+ tc = rl[rlpos - 1].lcn +
+ rl[rlpos - 1].length;
+ if (tc >= vol->nr_clusters)
+ vol->data1_zone_pos =
+ vol->mft_zone_end;
+ else if ((bmp_initial_pos >=
+ vol->data1_zone_pos ||
+ tc > vol->data1_zone_pos)
+ && tc >= vol->mft_zone_end)
+ vol->data1_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->data1_zone_pos = 0x%llx.\n",
+ (long long) vol->data1_zone_pos);
+ }
+ /* Switch from data1 zone to data2 zone. */
+ search_zone = 4;
+ zone_start = bmp_initial_pos =
+ 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 =
+ bmp_initial_pos = 0;
+ pass = 2;
+ }
+ break;
+ case 4:
+ ntfs_log_debug("Switching from data2 zone to data1 "
+ "zone.\n");
+ /* Update data2 zone position. */
+ if (rlpos) {
+ LCN tc;
+ ntfs_log_trace("Before checks, vol->data2_zone_pos = 0x%llx.\n",
+ (long long) vol->data2_zone_pos);
+ tc = rl[rlpos - 1].lcn +
+ rl[rlpos - 1].length;
+ if (tc >= vol->mft_zone_start)
+ vol->data2_zone_pos = 0;
+ else if (bmp_initial_pos >=
+ vol->data2_zone_pos ||
+ tc > vol->data2_zone_pos)
+ vol->data2_zone_pos = tc;
+ ntfs_log_trace("After checks, vol->data2_zone_pos = 0x%llx.\n",
+ (long long) vol->data2_zone_pos);
+ }
+ /* Switch from data2 zone to data1 zone. */
+ goto switch_to_data1_zone; /* See above. */
+ default:
+ NTFS_BUG("switch (search_zone) 3");
+ }
+ ntfs_log_trace("After zone switch, search_zone = %i, pass = "
+ "%i, bmp_initial_pos = 0x%llx, "
+ "zone_start = 0x%llx, zone_end = "
+ "0x%llx.\n", search_zone, pass,
+ (long long)bmp_initial_pos,
+ (long long)zone_start,
+ (long long)zone_end);
+ bmp_pos = zone_start;
+ if (zone_start == zone_end) {
+ ntfs_log_trace("Empty zone, going to "
+ "done_zones_check.\n");
+ /* Empty zone. Don't bother searching it. */
+ goto done_zones_check;
+ }
+ ntfs_log_trace("Continuing outer while loop.\n");
+ continue;
+ } /* done_zones == 7 */
+ ntfs_log_trace("All zones are finished.\n");
+ /*
+ * 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_log_trace("vol->mft_zone_start = 0x%llx, vol->mft_zone_end = "
+ "0x%llx, mft_zone_size = 0x%llx.\n",
+ (long long)vol->mft_zone_start,
+ (long long)vol->mft_zone_end,
+ (long long)mft_zone_size);
+ if (zone == MFT_ZONE || mft_zone_size <= 0) {
+ ntfs_log_trace("No free clusters left, going to err_ret.\n");
+ /* Really no more space left on device. */
+ err = ENOSPC;
+ goto err_ret;
+ } /* zone == DATA_ZONE && mft_zone_size > 0 */
+ ntfs_log_trace("Shrinking mft zone.\n");
+ zone_end = vol->mft_zone_end;
+ mft_zone_size >>= 1;
+ if (mft_zone_size > 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 = 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 = 0;
+ }
+ bmp_pos = zone_start = bmp_initial_pos =
+ vol->data1_zone_pos = vol->mft_zone_end;
+ search_zone = 2;
+ pass = 2;
+ done_zones &= ~2;
+ ntfs_log_trace("After shrinking mft zone, mft_zone_size = 0x%llx, "
+ "vol->mft_zone_start = 0x%llx, "
+ "vol->mft_zone_end = 0x%llx, vol->mft_zone_pos "
+ "= 0x%llx, search_zone = 2, pass = 2, "
+ "dones_zones = 0x%x, zone_start = 0x%llx, "
+ "zone_end = 0x%llx, vol->data1_zone_pos = "
+ "0x%llx, continuing outer while loop.\n",
+ (long long)mft_zone_size,
+ (long long)vol->mft_zone_start,
+ (long long)vol->mft_zone_end,
+ (long long)vol->mft_zone_pos,
+ done_zones,
+ (long long)zone_start,
+ (long long)zone_end,
+ (long long)vol->data1_zone_pos);
+ }
+ ntfs_log_debug("After outer while loop.\n");
+done_ret:
+ ntfs_log_debug("At done_ret.\n");
+ /* Add runlist terminator element. */
+ rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
+ rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+ rl[rlpos].length = 0;
+ if (need_writeback) {
+ s64 bw;
+ ntfs_log_trace("Writing back.\n");
+ need_writeback = 0;
+ bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
+ if (bw != br) {
+ if (bw < 0)
+ err = errno;
+ else
+ err = EIO;
+ ntfs_log_trace("Bitmap writeback failed in done code path "
+ "with error code %i.\n", err);
+ goto err_ret;
+ }
+ }
+done_err_ret:
+ ntfs_log_debug("At done_err_ret (follows done_ret).\n");
+ free(buf);
+ /* Done! */
+ if (!err)
+ return rl;
+ ntfs_log_trace("Failed to allocate clusters. Returning with error code "
+ "%i.\n", err);
+ errno = err;
+ return NULL;
+wb_err_ret:
+ ntfs_log_trace("At wb_err_ret.\n");
+ if (need_writeback) {
+ s64 bw;
+ ntfs_log_trace("Writing back.\n");
+ need_writeback = 0;
+ bw = ntfs_attr_pwrite(vol->lcnbmp_na, last_read_pos, br, buf);
+ if (bw != br) {
+ if (bw < 0)
+ err = errno;
+ else
+ err = EIO;
+ ntfs_log_trace("Bitmap writeback failed in error code path "
+ "with error code %i.\n", err);
+ }
+ }
+err_ret:
+ ntfs_log_trace("At err_ret.\n");
+ if (rl) {
+ if (err == ENOSPC) {
+ ntfs_log_trace("err = ENOSPC, first free lcn = 0x%llx, could "
+ "allocate up to = 0x%llx clusters.\n",
+ (long long)rl[0].lcn,
+ (long long)count - clusters);
+ }
+ /* Add runlist terminator element. */
+ rl[rlpos].vcn = rl[rlpos - 1].vcn + rl[rlpos - 1].length;
+ rl[rlpos].lcn = LCN_RL_NOT_MAPPED;
+ rl[rlpos].length = 0;
+ /* Deallocate all allocated clusters. */
+ ntfs_log_trace("Deallocating allocated clusters.\n");
+ ntfs_cluster_free_from_rl(vol, rl);
+ /* Free the runlist. */
+ free(rl);
+ rl = NULL;
+ } else {
+ if (err == ENOSPC) {
+ ntfs_log_trace("No space left at all, err = ENOSPC, first "
+ "free lcn = 0x%llx.\n",
+ (long long)vol->data1_zone_pos);
+ }
+ }
+ ntfs_log_trace("rl = NULL, going to done_err_ret.\n");
+ goto done_err_ret;
+}
+
+/**
+ * ntfs_cluster_free_from_rl - free clusters from runlist
+ * @vol: mounted ntfs volume on which to free the clusters
+ * @rl: runlist from which deallocate clusters
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
+{
+ ntfs_log_trace("Entering.\n");
+
+ for (; rl->length; rl++) {
+
+ ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n",
+ (long long)rl->lcn, (long long)rl->length);
+
+ if (rl->lcn >= 0 && ntfs_bitmap_clear_run(vol->lcnbmp_na,
+ rl->lcn, rl->length)) {
+ int eo = errno;
+ ntfs_log_trace("Eeek! Deallocation of clusters failed.\n");
+ errno = eo;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ntfs_cluster_free - free clusters on an ntfs volume
+ * @vol: mounted ntfs volume on which to free the clusters
+ * @na: attribute whose runlist describes the clusters to free
+ * @start_vcn: vcn in @rl at which to start freeing clusters
+ * @count: number of clusters to free or -1 for all clusters
+ *
+ * Free @count clusters starting at the cluster @start_vcn in the runlist
+ * described by the attribute @na from the mounted ntfs volume @vol.
+ *
+ * If @count is -1, all clusters from @start_vcn to the end of the runlist
+ * are deallocated.
+ *
+ * On success return the number of deallocated clusters (not counting sparse
+ * clusters) and on error return -1 with errno set to the error code.
+ */
+int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count)
+{
+ runlist *rl;
+ s64 nr_freed, delta, to_free;
+
+ if (!vol || !vol->lcnbmp_na || !na || start_vcn < 0 ||
+ (count < 0 && count != -1)) {
+ ntfs_log_trace("Invalid arguments!\n");
+ errno = EINVAL;
+ return -1;
+ }
+ ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, count 0x%llx, "
+ "vcn 0x%llx.\n", (unsigned long long)na->ni->mft_no,
+ na->type, (long long)count, (long long)start_vcn);
+
+ rl = ntfs_attr_find_vcn(na, start_vcn);
+ if (!rl) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return -1;
+ }
+
+ if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
+ errno = EIO;
+ return -1;
+ }
+
+ /* Find the starting cluster inside the run that needs freeing. */
+ delta = start_vcn - rl->vcn;
+
+ /* The number of clusters in this run that need freeing. */
+ to_free = rl->length - delta;
+ if (count >= 0 && to_free > count)
+ to_free = count;
+
+ if (rl->lcn != LCN_HOLE) {
+ /* Do the actual freeing of the clusters in this run. */
+ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn + delta,
+ to_free))
+ return -1;
+ /* We have freed @to_free real clusters. */
+ nr_freed = to_free;
+ } else {
+ /* No real clusters were freed. */
+ nr_freed = 0;
+ }
+
+ /* Go to the next run and adjust the number of clusters left to free. */
+ ++rl;
+ if (count >= 0)
+ count -= to_free;
+
+ /*
+ * Loop over the remaining runs, using @count as a capping value, and
+ * free them.
+ */
+ for (; rl->length && count != 0; ++rl) {
+ // FIXME: Need to try ntfs_attr_map_runlist() for attribute
+ // list support! (AIA)
+ if (rl->lcn < 0 && rl->lcn != LCN_HOLE) {
+ // FIXME: Eeek! We need rollback! (AIA)
+ ntfs_log_trace("Eeek! invalid lcn (= %lli). Should attempt "
+ "to map runlist! Leaving inconsistent "
+ "metadata!\n", (long long)rl->lcn);
+ errno = EIO;
+ return -1;
+ }
+
+ /* The number of clusters in this run that need freeing. */
+ to_free = rl->length;
+ if (count >= 0 && to_free > count)
+ to_free = count;
+
+ if (rl->lcn != LCN_HOLE) {
+ /* Do the actual freeing of the clusters in the run. */
+ if (ntfs_bitmap_clear_run(vol->lcnbmp_na, rl->lcn,
+ to_free)) {
+ int eo = errno;
+
+ // FIXME: Eeek! We need rollback! (AIA)
+ ntfs_log_trace("Eeek! bitmap clear run failed. "
+ "Leaving inconsistent metadata!\n");
+ errno = eo;
+ return -1;
+ }
+ /* We have freed @to_free real clusters. */
+ nr_freed += to_free;
+ }
+
+ if (count >= 0)
+ count -= to_free;
+ }
+
+ if (count != -1 && count != 0) {
+ // FIXME: Eeek! BUG()
+ ntfs_log_trace("Eeek! count still not zero (= %lli). Leaving "
+ "inconsistent metadata!\n", (long long)count);
+ errno = EIO;
+ return -1;
+ }
+
+ /* Done. Return the number of actual clusters that were freed. */
+ return nr_freed;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/logfile.c b/usr/src/lib/libntfs/common/libntfs/logfile.c
new file mode 100644
index 0000000000..054bd2f088
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/logfile.c
@@ -0,0 +1,769 @@
+/**
+ * logfile.c - NTFS journal handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2005 Anton Altaparmakov
+ * Copyright (c) 2005 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "attrib.h"
+#include "debug.h"
+#include "logfile.h"
+#include "volume.h"
+#include "mst.h"
+#include "logging.h"
+
+/**
+ * ntfs_check_restart_page_header - check the page header for consistency
+ * @rp: restart page header to check
+ * @pos: position in logfile at which the restart page header resides
+ *
+ * Check the restart page header @rp for consistency and return TRUE if it is
+ * consistent and FALSE otherwise.
+ *
+ * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
+ * require the full restart page.
+ */
+static BOOL ntfs_check_restart_page_header(RESTART_PAGE_HEADER *rp, s64 pos)
+{
+ u32 logfile_system_page_size, logfile_log_page_size;
+ u16 ra_ofs, usa_count, usa_ofs, usa_end = 0;
+ BOOL have_usa = TRUE;
+
+ ntfs_log_trace("Entering.\n");
+ /*
+ * If the system or log page sizes are smaller than the ntfs block size
+ * or either is not a power of 2 we cannot handle this log file.
+ */
+ logfile_system_page_size = le32_to_cpu(rp->system_page_size);
+ logfile_log_page_size = le32_to_cpu(rp->log_page_size);
+ if (logfile_system_page_size < NTFS_BLOCK_SIZE ||
+ logfile_log_page_size < NTFS_BLOCK_SIZE ||
+ logfile_system_page_size &
+ (logfile_system_page_size - 1) ||
+ logfile_log_page_size & (logfile_log_page_size - 1)) {
+ ntfs_log_error("$LogFile uses unsupported page size.\n");
+ return FALSE;
+ }
+ /*
+ * We must be either at !pos (1st restart page) or at pos = system page
+ * size (2nd restart page).
+ */
+ if (pos && pos != logfile_system_page_size) {
+ ntfs_log_error("Found restart area in incorrect "
+ "position in $LogFile.\n");
+ return FALSE;
+ }
+ /* We only know how to handle version 1.1. */
+ if (sle16_to_cpu(rp->major_ver) != 1 ||
+ sle16_to_cpu(rp->minor_ver) != 1) {
+ ntfs_log_error("$LogFile version %i.%i is not "
+ "supported. (This driver supports version "
+ "1.1 only.)\n", (int)sle16_to_cpu(rp->major_ver),
+ (int)sle16_to_cpu(rp->minor_ver));
+ return FALSE;
+ }
+ /*
+ * If chkdsk has been run the restart page may not be protected by an
+ * update sequence array.
+ */
+ if (ntfs_is_chkd_record(rp->magic) && !le16_to_cpu(rp->usa_count)) {
+ have_usa = FALSE;
+ goto skip_usa_checks;
+ }
+ /* Verify the size of the update sequence array. */
+ usa_count = 1 + (logfile_system_page_size >> NTFS_BLOCK_SIZE_BITS);
+ if (usa_count != le16_to_cpu(rp->usa_count)) {
+ ntfs_log_error("$LogFile restart page specifies "
+ "inconsistent update sequence array count.\n");
+ return FALSE;
+ }
+ /* Verify the position of the update sequence array. */
+ usa_ofs = le16_to_cpu(rp->usa_ofs);
+ usa_end = usa_ofs + usa_count * sizeof(u16);
+ if (usa_ofs < sizeof(RESTART_PAGE_HEADER) ||
+ usa_end > NTFS_BLOCK_SIZE - sizeof(u16)) {
+ ntfs_log_error("$LogFile restart page specifies "
+ "inconsistent update sequence array offset.\n");
+ return FALSE;
+ }
+skip_usa_checks:
+ /*
+ * Verify the position of the restart area. It must be:
+ * - aligned to 8-byte boundary,
+ * - after the update sequence array, and
+ * - within the system page size.
+ */
+ ra_ofs = le16_to_cpu(rp->restart_area_offset);
+ if (ra_ofs & 7 || (have_usa ? ra_ofs < usa_end :
+ ra_ofs < sizeof(RESTART_PAGE_HEADER)) ||
+ ra_ofs > logfile_system_page_size) {
+ ntfs_log_error("$LogFile restart page specifies "
+ "inconsistent restart area offset.\n");
+ return FALSE;
+ }
+ /*
+ * Only restart pages modified by chkdsk are allowed to have chkdsk_lsn
+ * set.
+ */
+ if (!ntfs_is_chkd_record(rp->magic) && sle64_to_cpu(rp->chkdsk_lsn)) {
+ ntfs_log_error("$LogFile restart page is not modified "
+ "by chkdsk but a chkdsk LSN is specified.\n");
+ return FALSE;
+ }
+ ntfs_log_trace("Done.\n");
+ return TRUE;
+}
+
+/**
+ * ntfs_check_restart_area - check the restart area for consistency
+ * @rp: restart page whose restart area to check
+ *
+ * Check the restart area of the restart page @rp for consistency and return
+ * TRUE if it is consistent and FALSE otherwise.
+ *
+ * This function assumes that the restart page header has already been
+ * consistency checked.
+ *
+ * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
+ * require the full restart page.
+ */
+static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp)
+{
+ u64 file_size;
+ RESTART_AREA *ra;
+ u16 ra_ofs, ra_len, ca_ofs;
+ u8 fs_bits;
+
+ ntfs_log_trace("Entering.\n");
+ ra_ofs = le16_to_cpu(rp->restart_area_offset);
+ ra = (RESTART_AREA*)((u8*)rp + ra_ofs);
+ /*
+ * Everything before ra->file_size must be before the first word
+ * protected by an update sequence number. This ensures that it is
+ * safe to access ra->client_array_offset.
+ */
+ if (ra_ofs + offsetof(RESTART_AREA, file_size) >
+ NTFS_BLOCK_SIZE - sizeof(u16)) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "inconsistent file offset.\n");
+ return FALSE;
+ }
+ /*
+ * Now that we can access ra->client_array_offset, make sure everything
+ * up to the log client array is before the first word protected by an
+ * update sequence number. This ensures we can access all of the
+ * restart area elements safely. Also, the client array offset must be
+ * aligned to an 8-byte boundary.
+ */
+ ca_ofs = le16_to_cpu(ra->client_array_offset);
+ if (((ca_ofs + 7) & ~7) != ca_ofs ||
+ ra_ofs + ca_ofs > (u16)(NTFS_BLOCK_SIZE -
+ sizeof(u16))) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "inconsistent client array offset.\n");
+ return FALSE;
+ }
+ /*
+ * The restart area must end within the system page size both when
+ * calculated manually and as specified by ra->restart_area_length.
+ * Also, the calculated length must not exceed the specified length.
+ */
+ ra_len = ca_ofs + le16_to_cpu(ra->log_clients) *
+ sizeof(LOG_CLIENT_RECORD);
+ if ((u32)(ra_ofs + ra_len) > le32_to_cpu(rp->system_page_size) ||
+ (u32)(ra_ofs + le16_to_cpu(ra->restart_area_length)) >
+ le32_to_cpu(rp->system_page_size) ||
+ ra_len > le16_to_cpu(ra->restart_area_length)) {
+ ntfs_log_error("$LogFile restart area is out of bounds "
+ "of the system page size specified by the "
+ "restart page header and/or the specified "
+ "restart area length is inconsistent.\n");
+ return FALSE;
+ }
+ /*
+ * The ra->client_free_list and ra->client_in_use_list must be either
+ * LOGFILE_NO_CLIENT or less than ra->log_clients or they are
+ * overflowing the client array.
+ */
+ if ((ra->client_free_list != LOGFILE_NO_CLIENT &&
+ le16_to_cpu(ra->client_free_list) >=
+ le16_to_cpu(ra->log_clients)) ||
+ (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
+ le16_to_cpu(ra->client_in_use_list) >=
+ le16_to_cpu(ra->log_clients))) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "overflowing client free and/or in use lists.\n");
+ return FALSE;
+ }
+ /*
+ * Check ra->seq_number_bits against ra->file_size for consistency.
+ * We cannot just use ffs() because the file size is not a power of 2.
+ */
+ file_size = (u64)sle64_to_cpu(ra->file_size);
+ fs_bits = 0;
+ while (file_size) {
+ file_size >>= 1;
+ fs_bits++;
+ }
+ if (le32_to_cpu(ra->seq_number_bits) != (u32)(67 - fs_bits)) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "inconsistent sequence number bits.\n");
+ return FALSE;
+ }
+ /* The log record header length must be a multiple of 8. */
+ if (((le16_to_cpu(ra->log_record_header_length) + 7) & ~7) !=
+ le16_to_cpu(ra->log_record_header_length)) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "inconsistent log record header length.\n");
+ return FALSE;
+ }
+ /* Ditto for the log page data offset. */
+ if (((le16_to_cpu(ra->log_page_data_offset) + 7) & ~7) !=
+ le16_to_cpu(ra->log_page_data_offset)) {
+ ntfs_log_error("$LogFile restart area specifies "
+ "inconsistent log page data offset.\n");
+ return FALSE;
+ }
+ ntfs_log_trace("Done.\n");
+ return TRUE;
+}
+
+/**
+ * ntfs_check_log_client_array - check the log client array for consistency
+ * @rp: restart page whose log client array to check
+ *
+ * Check the log client array of the restart page @rp for consistency and
+ * return TRUE if it is consistent and FALSE otherwise.
+ *
+ * This function assumes that the restart page header and the restart area have
+ * already been consistency checked.
+ *
+ * Unlike ntfs_check_restart_page_header() and ntfs_check_restart_area(), this
+ * function needs @rp->system_page_size bytes in @rp, i.e. it requires the full
+ * restart page and the page must be multi sector transfer deprotected.
+ */
+static BOOL ntfs_check_log_client_array(RESTART_PAGE_HEADER *rp)
+{
+ RESTART_AREA *ra;
+ LOG_CLIENT_RECORD *ca, *cr;
+ u16 nr_clients, idx;
+ BOOL in_free_list, idx_is_first;
+
+ ntfs_log_trace("Entering.\n");
+ ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
+ ca = (LOG_CLIENT_RECORD*)((u8*)ra +
+ le16_to_cpu(ra->client_array_offset));
+ /*
+ * Check the ra->client_free_list first and then check the
+ * ra->client_in_use_list. Check each of the log client records in
+ * each of the lists and check that the array does not overflow the
+ * ra->log_clients value. Also keep track of the number of records
+ * visited as there cannot be more than ra->log_clients records and
+ * that way we detect eventual loops in within a list.
+ */
+ nr_clients = le16_to_cpu(ra->log_clients);
+ idx = le16_to_cpu(ra->client_free_list);
+ in_free_list = TRUE;
+check_list:
+ for (idx_is_first = TRUE; idx != LOGFILE_NO_CLIENT_CPU; nr_clients--,
+ idx = le16_to_cpu(cr->next_client)) {
+ if (!nr_clients || idx >= le16_to_cpu(ra->log_clients))
+ goto err_out;
+ /* Set @cr to the current log client record. */
+ cr = ca + idx;
+ /* The first log client record must not have a prev_client. */
+ if (idx_is_first) {
+ if (cr->prev_client != LOGFILE_NO_CLIENT)
+ goto err_out;
+ idx_is_first = FALSE;
+ }
+ }
+ /* Switch to and check the in use list if we just did the free list. */
+ if (in_free_list) {
+ in_free_list = FALSE;
+ idx = le16_to_cpu(ra->client_in_use_list);
+ goto check_list;
+ }
+ ntfs_log_trace("Done.\n");
+ return TRUE;
+err_out:
+ ntfs_log_error("$LogFile log client array is corrupt.\n");
+ return FALSE;
+}
+
+/**
+ * ntfs_check_and_load_restart_page - check the restart page for consistency
+ * @log_na: opened ntfs attribute for journal $LogFile
+ * @rp: restart page to check
+ * @pos: position in @log_na at which the restart page resides
+ * @wrp: [OUT] copy of the multi sector transfer deprotected restart page
+ * @lsn: [OUT] set to the current logfile lsn on success
+ *
+ * Check the restart page @rp for consistency and return 0 if it is consistent
+ * and errno otherwise. The restart page may have been modified by chkdsk in
+ * which case its magic is CHKD instead of RSTR.
+ *
+ * This function only needs NTFS_BLOCK_SIZE bytes in @rp, i.e. it does not
+ * require the full restart page.
+ *
+ * If @wrp is not NULL, on success, *@wrp will point to a buffer containing a
+ * copy of the complete multi sector transfer deprotected page. On failure,
+ * *@wrp is undefined.
+ *
+ * Similarly, if @lsn is not NULL, on success *@lsn will be set to the current
+ * logfile lsn according to this restart page. On failure, *@lsn is undefined.
+ *
+ * The following error codes are defined:
+ * EINVAL - The restart page is inconsistent.
+ * ENOMEM - Not enough memory to load the restart page.
+ * EIO - Failed to reading from $LogFile.
+ */
+static int ntfs_check_and_load_restart_page(ntfs_attr *log_na,
+ RESTART_PAGE_HEADER *rp, s64 pos, RESTART_PAGE_HEADER **wrp,
+ LSN *lsn)
+{
+ RESTART_AREA *ra;
+ RESTART_PAGE_HEADER *trp;
+ int err;
+
+ ntfs_log_trace("Entering.\n");
+ /* Check the restart page header for consistency. */
+ if (!ntfs_check_restart_page_header(rp, pos)) {
+ /* Error output already done inside the function. */
+ return EINVAL;
+ }
+ /* Check the restart area for consistency. */
+ if (!ntfs_check_restart_area(rp)) {
+ /* Error output already done inside the function. */
+ return EINVAL;
+ }
+ ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
+ /*
+ * Allocate a buffer to store the whole restart page so we can multi
+ * sector transfer deprotect it.
+ */
+ trp = ntfs_malloc(le32_to_cpu(rp->system_page_size));
+ if (!trp)
+ return ENOMEM;
+ /*
+ * Read the whole of the restart page into the buffer. If it fits
+ * completely inside @rp, just copy it from there. Otherwise read it
+ * from disk.
+ */
+ if (le32_to_cpu(rp->system_page_size) <= NTFS_BLOCK_SIZE)
+ memcpy(trp, rp, le32_to_cpu(rp->system_page_size));
+ else if (ntfs_attr_pread(log_na, pos,
+ le32_to_cpu(rp->system_page_size), trp) !=
+ le32_to_cpu(rp->system_page_size)) {
+ err = errno;
+ ntfs_log_error("Failed to read whole restart page into the "
+ "buffer.\n");
+ if (err != ENOMEM)
+ err = EIO;
+ goto err_out;
+ }
+ /*
+ * Perform the multi sector transfer deprotection on the buffer if the
+ * restart page is protected.
+ */
+ if ((!ntfs_is_chkd_record(trp->magic) || le16_to_cpu(trp->usa_count))
+ && ntfs_mst_post_read_fixup((NTFS_RECORD*)trp,
+ le32_to_cpu(rp->system_page_size))) {
+ /*
+ * A multi sector tranfer error was detected. We only need to
+ * abort if the restart page contents exceed the multi sector
+ * transfer fixup of the first sector.
+ */
+ if (le16_to_cpu(rp->restart_area_offset) +
+ le16_to_cpu(ra->restart_area_length) >
+ NTFS_BLOCK_SIZE - (int)sizeof(u16)) {
+ ntfs_log_error("Multi sector transfer error "
+ "detected in $LogFile restart page.\n");
+ err = EINVAL;
+ goto err_out;
+ }
+ }
+ /*
+ * If the restart page is modified by chkdsk or there are no active
+ * logfile clients, the logfile is consistent. Otherwise, need to
+ * check the log client records for consistency, too.
+ */
+ err = 0;
+ if (ntfs_is_rstr_record(rp->magic) &&
+ ra->client_in_use_list != LOGFILE_NO_CLIENT) {
+ if (!ntfs_check_log_client_array(trp)) {
+ err = EINVAL;
+ goto err_out;
+ }
+ }
+ if (lsn) {
+ if (ntfs_is_rstr_record(rp->magic))
+ *lsn = sle64_to_cpu(ra->current_lsn);
+ else /* if (ntfs_is_chkd_record(rp->magic)) */
+ *lsn = sle64_to_cpu(rp->chkdsk_lsn);
+ }
+ ntfs_log_trace("Done.\n");
+ if (wrp)
+ *wrp = trp;
+ else {
+err_out:
+ free(trp);
+ }
+ return err;
+}
+
+/**
+ * ntfs_check_logfile - check in the journal if the volume is consistent
+ * @log_na: ntfs attribute of loaded journal $LogFile to check
+ * @rp: [OUT] on success this is a copy of the current restart page
+ *
+ * Check the $LogFile journal for consistency and return TRUE if it is
+ * consistent and FALSE if not. On success, the current restart page is
+ * returned in *@rp. Caller must call ntfs_free(*@rp) when finished with it.
+ *
+ * At present we only check the two restart pages and ignore the log record
+ * pages.
+ *
+ * Note that the MstProtected flag is not set on the $LogFile inode and hence
+ * when reading pages they are not deprotected. This is because we do not know
+ * if the $LogFile was created on a system with a different page size to ours
+ * yet and mst deprotection would fail if our page size is smaller.
+ */
+BOOL ntfs_check_logfile(ntfs_attr *log_na, RESTART_PAGE_HEADER **rp)
+{
+ s64 size, pos;
+ LSN rstr1_lsn, rstr2_lsn;
+ ntfs_volume *vol = log_na->ni->vol;
+ u8 *kaddr = NULL;
+ RESTART_PAGE_HEADER *rstr1_ph = NULL;
+ RESTART_PAGE_HEADER *rstr2_ph = NULL;
+ int log_page_size, log_page_mask, err;
+ BOOL logfile_is_empty = TRUE;
+ u8 log_page_bits;
+
+ ntfs_log_trace("Entering.\n");
+ /* An empty $LogFile must have been clean before it got emptied. */
+ if (NVolLogFileEmpty(vol))
+ goto is_empty;
+ size = log_na->data_size;
+ /* Make sure the file doesn't exceed the maximum allowed size. */
+ if (size > (s64)MaxLogFileSize)
+ size = MaxLogFileSize;
+ log_page_size = DefaultLogPageSize;
+ log_page_mask = log_page_size - 1;
+ /*
+ * Use generic_ffs() instead of ffs() to enable the compiler to
+ * optimize log_page_size and log_page_bits into constants.
+ */
+ log_page_bits = ffs(log_page_size) - 1;
+ size &= ~(log_page_size - 1);
+
+ /*
+ * Ensure the log file is big enough to store at least the two restart
+ * pages and the minimum number of log record pages.
+ */
+ if (size < log_page_size * 2 || (size - log_page_size * 2) >>
+ log_page_bits < MinLogRecordPages) {
+ ntfs_log_error("$LogFile is too small.\n");
+ return FALSE;
+ }
+ /* Allocate memory for restart page. */
+ kaddr = ntfs_malloc(NTFS_BLOCK_SIZE);
+ if (!kaddr)
+ return FALSE;
+ /*
+ * Read through the file looking for a restart page. Since the restart
+ * page header is at the beginning of a page we only need to search at
+ * what could be the beginning of a page (for each page size) rather
+ * than scanning the whole file byte by byte. If all potential places
+ * contain empty and uninitialized records, the log file can be assumed
+ * to be empty.
+ */
+ for (pos = 0; pos < size; pos <<= 1) {
+ /*
+ * Read first NTFS_BLOCK_SIZE bytes of potential restart page.
+ */
+ if (ntfs_attr_pread(log_na, pos, NTFS_BLOCK_SIZE, kaddr) !=
+ NTFS_BLOCK_SIZE) {
+ ntfs_log_error("Failed to read first NTFS_BLOCK_SIZE "
+ "bytes of potential restart page.\n");
+ goto err_out;
+ }
+
+ /*
+ * A non-empty block means the logfile is not empty while an
+ * empty block after a non-empty block has been encountered
+ * means we are done.
+ */
+ if (!ntfs_is_empty_recordp((le32*)kaddr))
+ logfile_is_empty = FALSE;
+ else if (!logfile_is_empty)
+ break;
+ /*
+ * A log record page means there cannot be a restart page after
+ * this so no need to continue searching.
+ */
+ if (ntfs_is_rcrd_recordp((le32*)kaddr))
+ break;
+ /* If not a (modified by chkdsk) restart page, continue. */
+ if (!ntfs_is_rstr_recordp((le32*)kaddr) &&
+ !ntfs_is_chkd_recordp((le32*)kaddr)) {
+ if (!pos)
+ pos = NTFS_BLOCK_SIZE >> 1;
+ continue;
+ }
+ /*
+ * Check the (modified by chkdsk) restart page for consistency
+ * and get a copy of the complete multi sector transfer
+ * deprotected restart page.
+ */
+ err = ntfs_check_and_load_restart_page(log_na,
+ (RESTART_PAGE_HEADER*)kaddr, pos,
+ !rstr1_ph ? &rstr1_ph : &rstr2_ph,
+ !rstr1_ph ? &rstr1_lsn : &rstr2_lsn);
+ if (!err) {
+ /*
+ * If we have now found the first (modified by chkdsk)
+ * restart page, continue looking for the second one.
+ */
+ if (!pos) {
+ pos = NTFS_BLOCK_SIZE >> 1;
+ continue;
+ }
+ /*
+ * We have now found the second (modified by chkdsk)
+ * restart page, so we can stop looking.
+ */
+ break;
+ }
+ /*
+ * Error output already done inside the function. Note, we do
+ * not abort if the restart page was invalid as we might still
+ * find a valid one further in the file.
+ */
+ if (err != EINVAL)
+ goto err_out;
+ /* Continue looking. */
+ if (!pos)
+ pos = NTFS_BLOCK_SIZE >> 1;
+ }
+ if (kaddr) {
+ free(kaddr);
+ kaddr = NULL;
+ }
+ if (logfile_is_empty) {
+ NVolSetLogFileEmpty(vol);
+is_empty:
+ ntfs_log_trace("Done. ($LogFile is empty.)\n");
+ return TRUE;
+ }
+ if (!rstr1_ph) {
+ if (rstr2_ph)
+ ntfs_log_error("BUG: rstr2_ph isn't NULL!\n");
+ ntfs_log_error("Did not find any restart pages in "
+ "$LogFile and it was not empty.\n");
+ return FALSE;
+ }
+ /* If both restart pages were found, use the more recent one. */
+ if (rstr2_ph) {
+ /*
+ * If the second restart area is more recent, switch to it.
+ * Otherwise just throw it away.
+ */
+ if (rstr2_lsn > rstr1_lsn) {
+ ntfs_log_debug("Using second restart page as it is more "
+ "recent.\n");
+ free(rstr1_ph);
+ rstr1_ph = rstr2_ph;
+ /* rstr1_lsn = rstr2_lsn; */
+ } else {
+ ntfs_log_debug("Using first restart page as it is more "
+ "recent.\n");
+ free(rstr2_ph);
+ }
+ rstr2_ph = NULL;
+ }
+ /* All consistency checks passed. */
+ if (rp)
+ *rp = rstr1_ph;
+ else
+ free(rstr1_ph);
+ ntfs_log_trace("Done.\n");
+ return TRUE;
+err_out:
+ free(kaddr);
+ free(rstr1_ph);
+ free(rstr2_ph);
+ return FALSE;
+}
+
+/**
+ * ntfs_is_logfile_clean - check in the journal if the volume is clean
+ * @log_na: ntfs attribute of loaded journal $LogFile to check
+ * @rp: copy of the current restart page
+ *
+ * Analyze the $LogFile journal and return TRUE if it indicates the volume was
+ * shutdown cleanly and FALSE if not.
+ *
+ * At present we only look at the two restart pages and ignore the log record
+ * pages. This is a little bit crude in that there will be a very small number
+ * of cases where we think that a volume is dirty when in fact it is clean.
+ * This should only affect volumes that have not been shutdown cleanly but did
+ * not have any pending, non-check-pointed i/o, i.e. they were completely idle
+ * at least for the five seconds preceding the unclean shutdown.
+ *
+ * This function assumes that the $LogFile journal has already been consistency
+ * checked by a call to ntfs_check_logfile() and in particular if the $LogFile
+ * is empty this function requires that NVolLogFileEmpty() is true otherwise an
+ * empty volume will be reported as dirty.
+ */
+BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp)
+{
+ RESTART_AREA *ra;
+
+ ntfs_log_trace("Entering.\n");
+ /* An empty $LogFile must have been clean before it got emptied. */
+ if (NVolLogFileEmpty(log_na->ni->vol)) {
+ ntfs_log_trace("Done. ($LogFile is empty.)\n");
+ return TRUE;
+ }
+ if (!rp) {
+ ntfs_log_error("Restart page header is NULL.\n");
+ return FALSE;
+ }
+ if (!ntfs_is_rstr_record(rp->magic) &&
+ !ntfs_is_chkd_record(rp->magic)) {
+ ntfs_log_error("Restart page buffer is invalid. This is "
+ "probably a bug in that the $LogFile should "
+ "have been consistency checked before calling "
+ "this function.\n");
+ return FALSE;
+ }
+
+ ra = (RESTART_AREA*)((u8*)rp + le16_to_cpu(rp->restart_area_offset));
+ /*
+ * If the $LogFile has active clients, i.e. it is open, and we do not
+ * have the RESTART_VOLUME_IS_CLEAN bit set in the restart area flags,
+ * we assume there was an unclean shutdown.
+ */
+ if (ra->client_in_use_list != LOGFILE_NO_CLIENT &&
+ !(ra->flags & RESTART_VOLUME_IS_CLEAN)) {
+ ntfs_log_debug("Done. $LogFile indicates a dirty shutdown.\n");
+ return FALSE;
+ }
+ /* $LogFile indicates a clean shutdown. */
+ ntfs_log_trace("Done. $LogFile indicates a clean shutdown.\n");
+ return TRUE;
+}
+
+/**
+ * ntfs_empty_logfile - empty the contents of the $LogFile journal
+ * @na: ntfs attribute of journal $LogFile to empty
+ *
+ * Empty the contents of the $LogFile journal @na and return 0 on success and
+ * -1 on error.
+ *
+ * This function assumes that the $LogFile journal has already been consistency
+ * checked by a call to ntfs_check_logfile() and that ntfs_is_logfile_clean()
+ * has been used to ensure that the $LogFile is clean.
+ */
+int ntfs_empty_logfile(ntfs_attr *na)
+{
+ s64 len, pos, count;
+ char buf[NTFS_BUF_SIZE];
+ int err;
+
+ ntfs_log_trace("Entering.\n");
+ if (NVolLogFileEmpty(na->ni->vol))
+ goto done;
+
+ /* The $DATA attribute of the $LogFile has to be non-resident. */
+ if (!NAttrNonResident(na)) {
+ err = EIO;
+ ntfs_log_debug("$LogFile $DATA attribute is resident!?!\n");
+ goto io_error_exit;
+ }
+
+ /* Get length of $LogFile contents. */
+ len = na->data_size;
+ if (!len) {
+ ntfs_log_debug("$LogFile has zero length, no disk write "
+ "needed.\n");
+ return 0;
+ }
+
+ /* Read $LogFile until its end. We do this as a check for correct
+ length thus making sure we are decompressing the mapping pairs
+ array correctly and hence writing below is safe as well. */
+ pos = 0;
+ while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0)
+ pos += count;
+
+ if (count == -1 || pos != len) {
+ err = errno;
+ ntfs_log_debug("Amount of $LogFile data read does not "
+ "correspond to expected length!\n");
+ if (count != -1)
+ err = EIO;
+ goto io_error_exit;
+ }
+
+ /* Fill the buffer with 0xff's. */
+ memset(buf, -1, NTFS_BUF_SIZE);
+
+ /* Set the $DATA attribute. */
+ pos = 0;
+ while ((count = len - pos) > 0) {
+ if (count > NTFS_BUF_SIZE)
+ count = NTFS_BUF_SIZE;
+
+ if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) {
+ err = errno;
+ ntfs_log_debug("Failed to set the $LogFile attribute "
+ "value.\n");
+ if (count != -1)
+ err = EIO;
+ goto io_error_exit;
+ }
+ pos += count;
+ }
+
+ /* Set the flag so we do not have to do it again on remount. */
+ NVolSetLogFileEmpty(na->ni->vol);
+done:
+ ntfs_log_trace("Done.\n");
+ return 0;
+io_error_exit:
+ errno = err;
+ return -1;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/logging.c b/usr/src/lib/libntfs/common/libntfs/logging.c
new file mode 100644
index 0000000000..d61326dc14
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/logging.c
@@ -0,0 +1,643 @@
+/**
+ * logging.c - Centralised logging. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
+
+#include "logging.h"
+
+#ifndef PATH_SEP
+#define PATH_SEP '/'
+#endif
+
+/* Colour prefixes and a suffix */
+#ifdef __sun
+static const char *col_green = "\033[32m";
+static const char *col_cyan = "\033[36m";
+static const char *col_yellow = "\033[01;33m";
+static const char *col_red = "\033[01;31m";
+static const char *col_redinv = "\033[01;07;31m";
+static const char *col_end = "\033[0m";
+#else /* ! __sun */
+static const char *col_green = "\e[32m";
+static const char *col_cyan = "\e[36m";
+static const char *col_yellow = "\e[01;33m";
+static const char *col_red = "\e[01;31m";
+static const char *col_redinv = "\e[01;07;31m";
+static const char *col_end = "\e[0m";
+#endif /* __sun */
+
+/**
+ * struct ntfs_logging - Control info for the logging system
+ * @levels: Bitfield of logging levels
+ * @flags: Flags which affect the output style
+ * @handler: Function to perform the actual logging
+ */
+struct ntfs_logging {
+ u32 levels;
+ u32 flags;
+ ntfs_log_handler *handler;
+};
+
+/**
+ * ntfs_log - This struct controls all the logging in the library and tools.
+ */
+static struct ntfs_logging ntfs_log = {
+ .levels = NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET |
+ NTFS_LOG_LEVEL_WARNING | NTFS_LOG_LEVEL_ERROR |
+ NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL |
+ NTFS_LOG_LEVEL_PROGRESS |
+ 0,
+ .flags = NTFS_LOG_FLAG_ONLYNAME,
+ .handler = ntfs_log_handler_null,
+};
+
+
+/**
+ * ntfs_log_get_levels - Get a list of the current logging levels
+ *
+ * Find out which logging levels are enabled.
+ *
+ * Returns: Log levels in a 32-bit field
+ */
+u32 ntfs_log_get_levels(void)
+{
+ return ntfs_log.levels;
+}
+
+/**
+ * ntfs_log_set_levels - Enable extra logging levels
+ * @levels: 32-bit field of log levels to set
+ *
+ * Enable one or more logging levels.
+ * The logging levels are named: NTFS_LOG_LEVEL_*.
+ *
+ * Returns: Log levels that were enabled before the call
+ */
+u32 ntfs_log_set_levels(u32 levels)
+{
+ u32 old;
+ old = ntfs_log.levels;
+ ntfs_log.levels |= levels;
+ return old;
+}
+
+/**
+ * ntfs_log_clear_levels - Disable some logging levels
+ * @levels: 32-bit field of log levels to clear
+ *
+ * Disable one or more logging levels.
+ * The logging levels are named: NTFS_LOG_LEVEL_*.
+ *
+ * Returns: Log levels that were enabled before the call
+ */
+u32 ntfs_log_clear_levels(u32 levels)
+{
+ u32 old;
+ old = ntfs_log.levels;
+ ntfs_log.levels &= (~levels);
+ return old;
+}
+
+
+/**
+ * ntfs_log_get_flags - Get a list of logging style flags
+ *
+ * Find out which logging flags are enabled.
+ *
+ * Returns: Logging flags in a 32-bit field
+ */
+u32 ntfs_log_get_flags(void)
+{
+ return ntfs_log.flags;
+}
+
+/**
+ * ntfs_log_set_flags - Enable extra logging style flags
+ * @flags: 32-bit field of logging flags to set
+ *
+ * Enable one or more logging flags.
+ * The log flags are named: NTFS_LOG_LEVEL_*.
+ *
+ * Returns: Logging flags that were enabled before the call
+ */
+u32 ntfs_log_set_flags(u32 flags)
+{
+ u32 old;
+ old = ntfs_log.flags;
+ ntfs_log.flags |= flags;
+ return old;
+}
+
+/**
+ * ntfs_log_clear_flags - Disable some logging styles
+ * @flags: 32-bit field of logging flags to clear
+ *
+ * Disable one or more logging flags.
+ * The log flags are named: NTFS_LOG_LEVEL_*.
+ *
+ * Returns: Logging flags that were enabled before the call
+ */
+u32 ntfs_log_clear_flags(u32 flags)
+{
+ u32 old;
+ old = ntfs_log.flags;
+ ntfs_log.flags &= (~flags);
+ return old;
+}
+
+
+/**
+ * ntfs_log_get_stream - Default output streams for logging levels
+ * @level: Log level
+ *
+ * By default, urgent messages are sent to "stderr".
+ * Other messages are sent to "stdout".
+ *
+ * Returns: "string" Prefix to be used
+ */
+static FILE * ntfs_log_get_stream(u32 level)
+{
+ FILE *stream;
+
+ switch (level) {
+ case NTFS_LOG_LEVEL_INFO:
+ case NTFS_LOG_LEVEL_QUIET:
+ case NTFS_LOG_LEVEL_PROGRESS:
+ case NTFS_LOG_LEVEL_VERBOSE:
+ stream = stdout;
+ break;
+
+ case NTFS_LOG_LEVEL_DEBUG:
+ case NTFS_LOG_LEVEL_TRACE:
+ case NTFS_LOG_LEVEL_WARNING:
+ case NTFS_LOG_LEVEL_ERROR:
+ case NTFS_LOG_LEVEL_CRITICAL:
+ case NTFS_LOG_LEVEL_PERROR:
+ default:
+ stream = stderr;
+ break;
+ }
+
+ return stream;
+}
+
+/**
+ * ntfs_log_get_prefix - Default prefixes for logging levels
+ * @level: Log level to be prefixed
+ *
+ * Prefixing the logging output can make it easier to parse.
+ *
+ * Returns: "string" Prefix to be used
+ */
+static const char * ntfs_log_get_prefix(u32 level)
+{
+ const char *prefix;
+
+ switch (level) {
+ case NTFS_LOG_LEVEL_DEBUG:
+ prefix = "DEBUG: ";
+ break;
+ case NTFS_LOG_LEVEL_TRACE:
+ prefix = "TRACE: ";
+ break;
+ case NTFS_LOG_LEVEL_QUIET:
+ prefix = "QUIET: ";
+ break;
+ case NTFS_LOG_LEVEL_INFO:
+ prefix = "INFO: ";
+ break;
+ case NTFS_LOG_LEVEL_VERBOSE:
+ prefix = "VERBOSE: ";
+ break;
+ case NTFS_LOG_LEVEL_PROGRESS:
+ prefix = "PROGRESS: ";
+ break;
+ case NTFS_LOG_LEVEL_WARNING:
+ prefix = "WARNING: ";
+ break;
+ case NTFS_LOG_LEVEL_ERROR:
+ prefix = "ERROR: ";
+ break;
+ case NTFS_LOG_LEVEL_PERROR:
+ prefix = "ERROR: ";
+ break;
+ case NTFS_LOG_LEVEL_CRITICAL:
+ prefix = "CRITICAL: ";
+ break;
+ default:
+ prefix = "";
+ break;
+ }
+
+ return prefix;
+}
+
+
+/**
+ * ntfs_log_set_handler - Provide an alternate logging handler
+ * @handler: function to perform the logging
+ *
+ * This alternate handler will be called for all future logging requests.
+ * If no @handler is specified, logging will revert to the default handler.
+ */
+void ntfs_log_set_handler(ntfs_log_handler *handler)
+{
+ if (handler) {
+ ntfs_log.handler = handler;
+#ifdef HAVE_SYSLOG_H
+ if (handler == ntfs_log_handler_syslog)
+ openlog("libntfs", LOG_PID, LOG_USER);
+#endif
+ } else
+ ntfs_log.handler = ntfs_log_handler_null;
+}
+
+/**
+ * ntfs_log_redirect - Pass on the request to the real handler
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @...: Arguments to be formatted
+ *
+ * This is just a redirector function. The arguments are simply passed to the
+ * main logging handler (as defined in the global logging struct @ntfs_log).
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_redirect(const char *function, const char *file,
+ int line, u32 level, void *data, const char *format, ...)
+{
+ int olderr = errno;
+ int ret;
+ va_list args;
+
+ if (!(ntfs_log.levels & level)) /* Don't log this message */
+ return 0;
+
+ va_start(args, format);
+ errno = olderr;
+ ret = ntfs_log.handler(function, file, line, level, data, format, args);
+ va_end(args);
+
+ errno = olderr;
+ return ret;
+}
+
+
+#ifdef HAVE_SYSLOG_H
+/**
+ * ntfs_log_handler_syslog - syslog logging handler
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * A syslog logging handler. Ignores colors and truncates output after 512
+ * bytes.
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_handler_syslog(const char *function, const char *file, int line,
+ u32 level, void *data __attribute__((unused)),
+ const char *format, va_list args)
+{
+ char buffer[512];
+ int ret = 0, olderr = errno;
+
+ if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
+ (strchr(file, PATH_SEP))) /* Abbreviate the filename */
+ file = strrchr(file, PATH_SEP) + 1;
+
+ /* Prefix the output */
+ if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_PREFIX)
+ ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s",
+ ntfs_log_get_prefix(level));
+
+ /* Source filename */
+ if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_FILENAME)
+ ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s ",
+ file);
+
+ /* Source line number */
+ if (ret < sizeof(buffer) && ntfs_log.flags & NTFS_LOG_FLAG_LINE)
+ ret += snprintf(buffer + ret, sizeof(buffer) - ret, "(%d) ",
+ line);
+
+ /* Source function */
+ if (ret < sizeof(buffer) && ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION)
+ || (level & NTFS_LOG_LEVEL_TRACE)))
+ ret += snprintf(buffer + ret, sizeof(buffer) - ret, "%s(): ",
+ function);
+
+ /* Message itself */
+ if (ret < sizeof(buffer))
+ ret += vsnprintf(buffer + ret, sizeof(buffer) - ret, format,
+ args);
+
+ /* Append errno */
+ if (ret < sizeof(buffer) && level & NTFS_LOG_LEVEL_PERROR)
+ ret += snprintf(buffer + ret, sizeof(buffer) - ret, ": %s.\n",
+ strerror(olderr));
+
+ syslog(LOG_NOTICE, "%s", buffer);
+
+ errno = olderr;
+ return ret;
+}
+#endif
+
+/**
+ * ntfs_log_handler_fprintf - Basic logging handler
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * A simple logging handler. This is where the log line is finally displayed.
+ * It is more likely that you will want to set the handler to either
+ * ntfs_log_handler_outerr or ntfs_log_handler_stderr.
+ *
+ * Note: For this handler, @data is a pointer to a FILE output stream.
+ * If @data is NULL, nothing will be displayed.
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_handler_fprintf(const char *function, const char *file,
+ int line, u32 level, void *data, const char *format, va_list args)
+{
+ int ret = 0;
+ int olderr = errno;
+ FILE *stream;
+ const char *col_prefix = NULL;
+ const char *col_suffix = NULL;
+
+ if (!data) /* Interpret data as a FILE stream. */
+ return 0; /* If it's NULL, we can't do anything. */
+ stream = (FILE*)data;
+
+ if (ntfs_log.flags & NTFS_LOG_FLAG_COLOUR) {
+ /* Pick a colour determined by the log level */
+ switch (level) {
+ case NTFS_LOG_LEVEL_DEBUG:
+ col_prefix = col_green;
+ col_suffix = col_end;
+ break;
+ case NTFS_LOG_LEVEL_TRACE:
+ col_prefix = col_cyan;
+ col_suffix = col_end;
+ break;
+ case NTFS_LOG_LEVEL_WARNING:
+ col_prefix = col_yellow;
+ col_suffix = col_end;
+ break;
+ case NTFS_LOG_LEVEL_ERROR:
+ case NTFS_LOG_LEVEL_PERROR:
+ col_prefix = col_red;
+ col_suffix = col_end;
+ break;
+ case NTFS_LOG_LEVEL_CRITICAL:
+ col_prefix = col_redinv;
+ col_suffix = col_end;
+ break;
+ }
+ }
+
+ if (col_prefix)
+ ret += fprintf(stream, col_prefix);
+
+ if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) &&
+ (strchr(file, PATH_SEP))) /* Abbreviate the filename */
+ file = strrchr(file, PATH_SEP) + 1;
+
+ if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */
+ ret += fprintf(stream, "%s", ntfs_log_get_prefix(level));
+
+ if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */
+ ret += fprintf(stream, "%s ", file);
+
+ if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */
+ ret += fprintf(stream, "(%d) ", line);
+
+ if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */
+ (level & NTFS_LOG_LEVEL_TRACE))
+ ret += fprintf(stream, "%s(): ", function);
+
+ ret += vfprintf(stream, format, args);
+
+ if (level & NTFS_LOG_LEVEL_PERROR)
+ ret += fprintf(stream, ": %s.\n", strerror(olderr));
+
+ if (col_suffix)
+ ret += fprintf(stream, col_suffix);
+
+
+ fflush(stream);
+ errno = olderr;
+ return ret;
+}
+
+/**
+ * ntfs_log_handler_null - Null logging handler (no output)
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * This handler produces no output. It provides a way to temporarily disable
+ * logging, without having to change the levels and flags.
+ *
+ * Returns: 0 Message wasn't logged
+ */
+int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)),
+ int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)),
+ const char *format __attribute__((unused)), va_list args __attribute__((unused)))
+{
+ return 0;
+}
+
+/**
+ * ntfs_log_handler_stdout - All logs go to stdout
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * Display a log message to stdout.
+ *
+ * Note: For this handler, @data is a pointer to a FILE output stream.
+ * If @data is NULL, then stdout will be used.
+ *
+ * Note: This function calls ntfs_log_handler_fprintf to do the main work.
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_handler_stdout(const char *function, const char *file,
+ int line, u32 level, void *data, const char *format, va_list args)
+{
+ if (!data)
+ data = stdout;
+
+ return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
+}
+
+/**
+ * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * Display a log message. The output stream will be determined by the log
+ * level.
+ *
+ * Note: For this handler, @data is a pointer to a FILE output stream.
+ * If @data is NULL, the function ntfs_log_get_stream will be called
+ *
+ * Note: This function calls ntfs_log_handler_fprintf to do the main work.
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_handler_outerr(const char *function, const char *file,
+ int line, u32 level, void *data, const char *format, va_list args)
+{
+ if (!data)
+ data = ntfs_log_get_stream(level);
+
+ return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
+}
+
+/**
+ * ntfs_log_handler_stderr - All logs go to stderr
+ * @function: Function in which the log line occurred
+ * @file: File in which the log line occurred
+ * @line: Line number on which the log line occurred
+ * @level: Level at which the line is logged
+ * @data: User specified data, possibly specific to a handler
+ * @format: printf-style formatting string
+ * @args: Arguments to be formatted
+ *
+ * Display a log message to stderr.
+ *
+ * Note: For this handler, @data is a pointer to a FILE output stream.
+ * If @data is NULL, then stdout will be used.
+ *
+ * Note: This function calls ntfs_log_handler_fprintf to do the main work.
+ *
+ * Returns: -1 Error occurred
+ * 0 Message wasn't logged
+ * num Number of output characters
+ */
+int ntfs_log_handler_stderr(const char *function, const char *file,
+ int line, u32 level, void *data, const char *format, va_list args)
+{
+ if (!data)
+ data = stderr;
+
+ return ntfs_log_handler_fprintf(function, file, line, level, data, format, args);
+}
+
+
+/**
+ * ntfs_log_parse_option - Act upon command line options
+ * @option: Option flag
+ *
+ * Delegate some of the work of parsing the command line. All the options begin
+ * with "--log-". Options cause log levels to be enabled in @ntfs_log (the
+ * global logging structure).
+ *
+ * Note: The "colour" option changes the logging handler.
+ *
+ * Returns: TRUE Option understood
+ * FALSE Invalid log option
+ */
+BOOL ntfs_log_parse_option(const char *option)
+{
+ if (strcmp(option, "--log-debug") == 0) {
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG);
+ return TRUE;
+ } else if (strcmp(option, "--log-verbose") == 0) {
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE);
+ return TRUE;
+ } else if (strcmp(option, "--log-quiet") == 0) {
+ ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET);
+ return TRUE;
+ } else if (strcmp(option, "--log-trace") == 0) {
+ ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE);
+ return TRUE;
+ } else if ((strcmp(option, "--log-colour") == 0) ||
+ (strcmp(option, "--log-color") == 0)) {
+ ntfs_log_set_flags(NTFS_LOG_FLAG_COLOUR);
+ return TRUE;
+ }
+
+ ntfs_log_debug("Unknown logging option '%s'\n", option);
+ return FALSE;
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/mft.c b/usr/src/lib/libntfs/common/libntfs/mft.c
new file mode 100644
index 0000000000..49034707ba
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/mft.c
@@ -0,0 +1,1583 @@
+/**
+ * mft.c - Mft record handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ * Copyright (c) 2004-2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+
+#include "compat.h"
+#include "types.h"
+#include "device.h"
+#include "debug.h"
+#include "bitmap.h"
+#include "attrib.h"
+#include "inode.h"
+#include "volume.h"
+#include "layout.h"
+#include "lcnalloc.h"
+#include "mft.h"
+#include "logging.h"
+
+/**
+ * ntfs_mft_records_read - read records from the mft from disk
+ * @vol: volume to read from
+ * @mref: starting mft record number to read
+ * @count: number of mft records to read
+ * @b: output data buffer
+ *
+ * Read @count mft records starting at @mref from volume @vol into buffer
+ * @b. Return 0 on success or -1 on error, with errno set to the error
+ * code.
+ *
+ * If any of the records exceed the initialized size of the $MFT/$DATA
+ * attribute, i.e. they cannot possibly be allocated mft records, assume this
+ * is a bug and return error code ESPIPE.
+ *
+ * The read mft records are mst deprotected and are hence ready to use. The
+ * caller should check each record with is_baad_record() in case mst
+ * deprotection failed.
+ *
+ * NOTE: @b has to be at least of size @count * vol->mft_record_size.
+ */
+int ntfs_mft_records_read(const ntfs_volume *vol, const MFT_REF mref,
+ const s64 count, MFT_RECORD *b)
+{
+ s64 br;
+ VCN m;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref));
+ if (!vol || !vol->mft_na || !b || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ m = MREF(mref);
+ /* Refuse to read non-allocated mft records. */
+ if (m + count > vol->mft_na->initialized_size >>
+ vol->mft_record_size_bits) {
+ errno = ESPIPE;
+ return -1;
+ }
+ br = ntfs_attr_mst_pread(vol->mft_na, m << vol->mft_record_size_bits,
+ count, vol->mft_record_size, b);
+ if (br != count) {
+ if (br != -1)
+ errno = EIO;
+ if (br >= 0)
+ ntfs_log_debug("Error: partition is smaller than it should "
+ "be!\n");
+ else
+ ntfs_log_perror("Error reading $Mft record(s)");
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_mft_records_write - write mft records to disk
+ * @vol: volume to write to
+ * @mref: starting mft record number to write
+ * @count: number of mft records to write
+ * @b: data buffer containing the mft records to write
+ *
+ * Write @count mft records starting at @mref from data buffer @b to volume
+ * @vol. Return 0 on success or -1 on error, with errno set to the error code.
+ *
+ * If any of the records exceed the initialized size of the $MFT/$DATA
+ * attribute, i.e. they cannot possibly be allocated mft records, assume this
+ * is a bug and return error code ESPIPE.
+ *
+ * Before the mft records are written, they are mst protected. After the write,
+ * they are deprotected again, thus resulting in an increase in the update
+ * sequence number inside the data buffer @b.
+ *
+ * If any mft records are written which are also represented in the mft mirror
+ * $MFTMirr, we make a copy of the relevant parts of the data buffer @b into a
+ * temporary buffer before we do the actual write. Then if at least one mft
+ * record was successfully written, we write the appropriate mft records from
+ * the copied buffer to the mft mirror, too.
+ */
+int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
+ const s64 count, MFT_RECORD *b)
+{
+ s64 bw;
+ VCN m;
+ void *bmirr = NULL;
+ int cnt = 0, res = 0;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", MREF(mref));
+ if (!vol || !vol->mft_na || vol->mftmirr_size <= 0 || !b || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ m = MREF(mref);
+ /* Refuse to write non-allocated mft records. */
+ if (m + count > vol->mft_na->initialized_size >>
+ vol->mft_record_size_bits) {
+ errno = ESPIPE;
+ return -1;
+ }
+ if (m < vol->mftmirr_size) {
+ if (!vol->mftmirr_na) {
+ errno = EINVAL;
+ return -1;
+ }
+ cnt = vol->mftmirr_size - m;
+ if (cnt > count)
+ cnt = count;
+ bmirr = ntfs_malloc(cnt * vol->mft_record_size);
+ if (!bmirr)
+ return -1;
+ memcpy(bmirr, b, cnt * vol->mft_record_size);
+ }
+ bw = ntfs_attr_mst_pwrite(vol->mft_na, m << vol->mft_record_size_bits,
+ count, vol->mft_record_size, b);
+ if (bw != count) {
+ if (bw != -1)
+ errno = EIO;
+ if (bw >= 0)
+ ntfs_log_error("Partial write while writing $Mft "
+ "record(s)!\n");
+ else
+ ntfs_log_perror("Error writing $Mft record(s)");
+ res = errno;
+ }
+ if (bmirr && bw > 0) {
+ if (bw < cnt)
+ cnt = bw;
+ bw = ntfs_attr_mst_pwrite(vol->mftmirr_na,
+ m << vol->mft_record_size_bits, cnt,
+ vol->mft_record_size, bmirr);
+ if (bw != cnt) {
+ if (bw != -1)
+ errno = EIO;
+ ntfs_log_debug("Error: failed to sync $MFTMirr! Run "
+ "chkdsk.\n");
+ res = errno;
+ }
+ }
+ free(bmirr);
+ if (!res)
+ return res;
+ errno = res;
+ return -1;
+}
+
+/**
+ * ntfs_file_record_read - read a FILE record from the mft from disk
+ * @vol: volume to read from
+ * @mref: mft reference specifying mft record to read
+ * @mrec: address of pointer in which to return the mft record
+ * @attr: address of pointer in which to return the first attribute
+ *
+ * Read a FILE record from the mft of @vol from the storage medium. @mref
+ * specifies the mft record to read, including the sequence number, which can
+ * be 0 if no sequence number checking is to be performed.
+ *
+ * The function allocates a buffer large enough to hold the mft record and
+ * reads the record into the buffer (mst deprotecting it in the process).
+ * *@mrec is then set to point to the buffer.
+ *
+ * If @attr is not NULL, *@attr is set to point to the first attribute in the
+ * mft record, i.e. *@attr is a pointer into *@mrec.
+ *
+ * Return 0 on success, or -1 on error, with errno set to the error code.
+ *
+ * The read mft record is checked for having the magic FILE,
+ * and for having a matching sequence number (if MSEQNO(*@mref) != 0).
+ * If either of these fails, -1 is returned and errno is set to EIO. If you get
+ * this, but you still want to read the mft record (e.g. in order to correct
+ * it), use ntfs_mft_record_read() directly.
+ *
+ * Note: Caller has to free *@mrec when finished.
+ *
+ * Note: We do not check if the mft record is flagged in use. The caller can
+ * check if desired.
+ */
+int ntfs_file_record_read(const ntfs_volume *vol, const MFT_REF mref,
+ MFT_RECORD **mrec, ATTR_RECORD **attr)
+{
+ MFT_RECORD *m;
+ ATTR_RECORD *a;
+ int err;
+
+ if (!vol || !mrec) {
+ errno = EINVAL;
+ return -1;
+ }
+ m = *mrec;
+ if (!m) {
+ m = (MFT_RECORD*)ntfs_malloc(vol->mft_record_size);
+ if (!m)
+ return -1;
+ }
+ if (ntfs_mft_record_read(vol, mref, m)) {
+ err = errno;
+ goto read_failed;
+ }
+ if (!ntfs_is_file_record(m->magic))
+ goto file_corrupt;
+ if (MSEQNO(mref) && MSEQNO(mref) != le16_to_cpu(m->sequence_number))
+ goto file_corrupt;
+ a = (ATTR_RECORD*)((char*)m + le16_to_cpu(m->attrs_offset));
+ if (p2n(a) < p2n(m) || (char*)a > (char*)m + vol->mft_record_size)
+ goto file_corrupt;
+ *mrec = m;
+ if (attr)
+ *attr = a;
+ return 0;
+file_corrupt:
+ ntfs_log_debug("ntfs_file_record_read(): file is corrupt.\n");
+ err = EIO;
+read_failed:
+ if (m != *mrec)
+ free(m);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_mft_record_layout - layout an mft record into a memory buffer
+ * @vol: volume to which the mft record will belong
+ * @mref: mft reference specifying the mft record number
+ * @mrec: destination buffer of size >= @vol->mft_record_size bytes
+ *
+ * Layout an empty, unused mft record with the mft reference @mref into the
+ * buffer @m. The volume @vol is needed because the mft record structure was
+ * modified in NTFS 3.1 so we need to know which volume version this mft record
+ * will be used on.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_mft_record_layout(const ntfs_volume *vol, const MFT_REF mref,
+ MFT_RECORD *mrec)
+{
+ ATTR_RECORD *a;
+
+ if (!vol || !mrec) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Aligned to 2-byte boundary. */
+ if (vol->major_ver < 3 || (vol->major_ver == 3 && !vol->minor_ver))
+ mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD_OLD) + 1) & ~1);
+ else {
+ /* Abort if mref is > 32 bits. */
+ if (MREF(mref) & 0x0000ffff00000000ull) {
+ ntfs_log_debug("Mft reference exceeds 32 bits!\n");
+ errno = ERANGE;
+ return -1;
+ }
+ mrec->usa_ofs = cpu_to_le16((sizeof(MFT_RECORD) + 1) & ~1);
+ /*
+ * Set the NTFS 3.1+ specific fields while we know that the
+ * volume version is 3.1+.
+ */
+ mrec->reserved = cpu_to_le16(0);
+ mrec->mft_record_number = cpu_to_le32(MREF(mref));
+ }
+ mrec->magic = magic_FILE;
+ if (vol->mft_record_size >= NTFS_BLOCK_SIZE)
+ mrec->usa_count = cpu_to_le16(vol->mft_record_size /
+ NTFS_BLOCK_SIZE + 1);
+ else {
+ mrec->usa_count = cpu_to_le16(1);
+ ntfs_log_error("Sector size is bigger than MFT record size. "
+ "Setting usa_count to 1. If Windows chkdsk "
+ "reports this as corruption, please email %s "
+ "stating that you saw this message and that "
+ "the file system created was corrupt. "
+ "Thank you.\n", NTFS_DEV_LIST);
+ }
+ /* Set the update sequence number to 1. */
+ *(le16*)((u8*)mrec + le16_to_cpu(mrec->usa_ofs)) = cpu_to_le16(1);
+ mrec->lsn = 0;
+ mrec->sequence_number = cpu_to_le16(1);
+ mrec->link_count = cpu_to_le16(0);
+ /* Aligned to 8-byte boundary. */
+ mrec->attrs_offset = cpu_to_le16((le16_to_cpu(mrec->usa_ofs) +
+ (le16_to_cpu(mrec->usa_count) << 1) + 7) & ~7);
+ mrec->flags = cpu_to_le16(0);
+ /*
+ * Using attrs_offset plus eight bytes (for the termination attribute),
+ * aligned to 8-byte boundary.
+ */
+ mrec->bytes_in_use = cpu_to_le32((le16_to_cpu(mrec->attrs_offset) + 8 +
+ 7) & ~7);
+ mrec->bytes_allocated = cpu_to_le32(vol->mft_record_size);
+ mrec->base_mft_record = cpu_to_le64((MFT_REF)0);
+ mrec->next_attr_instance = cpu_to_le16(0);
+ a = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset));
+ a->type = AT_END;
+ a->length = cpu_to_le32(0);
+ /* Finally, clear the unused part of the mft record. */
+ memset((u8*)a + 8, 0, vol->mft_record_size - ((u8*)a + 8 - (u8*)mrec));
+ return 0;
+}
+
+/**
+ * ntfs_mft_record_format - format an mft record on an ntfs volume
+ * @vol: volume on which to format the mft record
+ * @mref: mft reference specifying mft record to format
+ *
+ * Format the mft record with the mft reference @mref in $MFT/$DATA, i.e. lay
+ * out an empty, unused mft record in memory and write it to the volume @vol.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_mft_record_format(const ntfs_volume *vol, const MFT_REF mref)
+{
+ MFT_RECORD *m;
+ int err;
+
+ if (!vol || !vol->mft_na) {
+ errno = EINVAL;
+ return -1;
+ }
+ m = ntfs_calloc(vol->mft_record_size);
+ if (!m)
+ return -1;
+ if (ntfs_mft_record_layout(vol, mref, m)) {
+ err = errno;
+ free(m);
+ errno = err;
+ return -1;
+ }
+ if (ntfs_mft_record_write(vol, mref, m)) {
+ err = errno;
+ free(m);
+ errno = err;
+ return -1;
+ }
+ free(m);
+ return 0;
+}
+
+static const char *es = " Leaving inconsistent metadata. Run chkdsk.";
+
+/**
+ * ntfs_ffz - Find the first unset (zero) bit in a word
+ * @word:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static inline unsigned int ntfs_ffz(unsigned int word)
+{
+ return ffs(~word) - 1;
+}
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+/**
+ * ntfs_mft_bitmap_find_free_rec - find a free mft record in the mft bitmap
+ * @vol: volume on which to search for a free mft record
+ * @base_ni: open base inode if allocating an extent mft record or NULL
+ *
+ * Search for a free mft record in the mft bitmap attribute on the ntfs volume
+ * @vol.
+ *
+ * If @base_ni is NULL start the search at the default allocator position.
+ *
+ * If @base_ni is not NULL start the search at the mft record after the base
+ * mft record @base_ni.
+ *
+ * Return the free mft record on success and -1 on error with errno set to the
+ * error code. An error code of ENOSPC means that there are no free mft
+ * records in the currently initialized mft bitmap.
+ */
+static int ntfs_mft_bitmap_find_free_rec(ntfs_volume *vol, ntfs_inode *base_ni)
+{
+ s64 pass_end, ll, data_pos, pass_start, ofs, bit;
+ ntfs_attr *mftbmp_na;
+ u8 *buf, *byte;
+ unsigned int size;
+ u8 pass, b;
+
+ mftbmp_na = vol->mftbmp_na;
+ /*
+ * Set the end of the pass making sure we do not overflow the mft
+ * bitmap.
+ */
+ size = PAGE_SIZE;
+ pass_end = vol->mft_na->allocated_size >> vol->mft_record_size_bits;
+ ll = mftbmp_na->initialized_size << 3;
+ if (pass_end > ll)
+ pass_end = ll;
+ pass = 1;
+ if (!base_ni)
+ data_pos = vol->mft_data_pos;
+ else
+ data_pos = base_ni->mft_no + 1;
+ if (data_pos < 24)
+ data_pos = 24;
+ if (data_pos >= pass_end) {
+ data_pos = 24;
+ pass = 2;
+ /* This happens on a freshly formatted volume. */
+ if (data_pos >= pass_end) {
+ errno = ENOSPC;
+ return -1;
+ }
+ }
+ pass_start = data_pos;
+ buf = (u8*)ntfs_malloc(PAGE_SIZE);
+ if (!buf)
+ return -1;
+
+ ntfs_log_debug("Starting bitmap search: pass %u, pass_start 0x%llx, "
+ "pass_end 0x%llx, data_pos 0x%llx.\n", pass,
+ (long long)pass_start, (long long)pass_end,
+ (long long)data_pos);
+#ifdef DEBUG
+ byte = NULL;
+ b = 0;
+#endif
+ /* Loop until a free mft record is found. */
+ for (; pass <= 2; size = PAGE_SIZE) {
+ /* Cap size to pass_end. */
+ ofs = data_pos >> 3;
+ ll = ((pass_end + 7) >> 3) - ofs;
+ if (size > ll)
+ size = ll;
+ ll = ntfs_attr_pread(mftbmp_na, ofs, size, buf);
+ if (ll < 0) {
+ ntfs_log_error("Failed to read mft bitmap "
+ "attribute, aborting.\n");
+ free(buf);
+ return -1;
+ }
+ ntfs_log_debug("Read 0x%llx bytes.\n", (long long)ll);
+ /* If we read at least one byte, search @buf for a zero bit. */
+ if (ll) {
+ size = ll << 3;
+ bit = data_pos & 7;
+ data_pos &= ~7ull;
+ ntfs_log_debug("Before inner for loop: size 0x%x, "
+ "data_pos 0x%llx, bit 0x%llx, "
+ "*byte 0x%hhx, b %u.\n", size,
+ (long long)data_pos, (long long)bit,
+ byte ? *byte : -1, b);
+ for (; bit < size && data_pos + bit < pass_end;
+ bit &= ~7ull, bit += 8) {
+ byte = buf + (bit >> 3);
+ if (*byte == 0xff)
+ continue;
+ /* Note: ffz() result must be zero based. */
+ b = ntfs_ffz((unsigned long)*byte);
+ if (b < 8 && b >= (bit & 7)) {
+ free(buf);
+ return data_pos + (bit & ~7ull) + b;
+ }
+ }
+ ntfs_log_debug("After inner for loop: size 0x%x, "
+ "data_pos 0x%llx, bit 0x%llx, "
+ "*byte 0x%hhx, b %u.\n", size,
+ (long long)data_pos, (long long)bit,
+ byte ? *byte : -1, b);
+ data_pos += size;
+ /*
+ * If the end of the pass has not been reached yet,
+ * continue searching the mft bitmap for a zero bit.
+ */
+ if (data_pos < pass_end)
+ continue;
+ }
+ /* Do the next pass. */
+ pass++;
+ if (pass == 2) {
+ /*
+ * Starting the second pass, in which we scan the first
+ * part of the zone which we omitted earlier.
+ */
+ pass_end = pass_start;
+ data_pos = pass_start = 24;
+ ntfs_log_debug("pass %i, pass_start 0x%llx, pass_end "
+ "0x%llx.\n", pass, (long long)pass_start,
+ (long long)pass_end);
+ if (data_pos >= pass_end)
+ break;
+ }
+ }
+ /* No free mft records in currently initialized mft bitmap. */
+ free(buf);
+ errno = ENOSPC;
+ return -1;
+}
+
+/**
+ * ntfs_mft_bitmap_extend_allocation - extend mft bitmap attribute by a cluster
+ * @vol: volume on which to extend the mft bitmap attribute
+ *
+ * Extend the mft bitmap attribute on the ntfs volume @vol by one cluster.
+ *
+ * Note: Only changes allocated_size, i.e. does not touch initialized_size or
+ * data_size.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+static int ntfs_mft_bitmap_extend_allocation(ntfs_volume *vol)
+{
+ LCN lcn;
+ s64 ll = 0; /* silence compiler warning */
+ ntfs_attr *mftbmp_na, *lcnbmp_na;
+ runlist_element *rl, *rl2 = NULL; /* silence compiler warning */
+ ntfs_attr_search_ctx *ctx;
+ MFT_RECORD *m = NULL; /* silence compiler warning */
+ ATTR_RECORD *a = NULL; /* silence compiler warning */
+ int ret, mp_size;
+ u32 old_alen = 0; /* silence compiler warning */
+ u8 b, tb;
+ struct {
+ u8 added_cluster:1;
+ u8 added_run:1;
+ u8 mp_rebuilt:1;
+ } status = { 0, 0, 0 };
+
+ mftbmp_na = vol->mftbmp_na;
+ lcnbmp_na = vol->lcnbmp_na;
+ /*
+ * Determine the last lcn of the mft bitmap. The allocated size of the
+ * mft bitmap cannot be zero so we are ok to do this.
+ */
+ rl = ntfs_attr_find_vcn(mftbmp_na, (mftbmp_na->allocated_size - 1) >>
+ vol->cluster_size_bits);
+ if (!rl || !rl->length || rl->lcn < 0) {
+ ntfs_log_error("Failed to determine last allocated "
+ "cluster of mft bitmap attribute.\n");
+ if (rl)
+ errno = EIO;
+ return -1;
+ }
+ lcn = rl->lcn + rl->length;
+ /*
+ * Attempt to get the cluster following the last allocated cluster by
+ * hand as it may be in the MFT zone so the allocator would not give it
+ * to us.
+ */
+ ret = (int)ntfs_attr_pread(lcnbmp_na, lcn >> 3, 1, &b);
+ if (ret < 0) {
+ ntfs_log_error("Failed to read from lcn bitmap.\n");
+ return -1;
+ }
+ ntfs_log_debug("Read %i byte%s.\n", ret, ret == 1 ? "" : "s");
+ tb = 1 << (lcn & 7ull);
+ if (ret == 1 && b != 0xff && !(b & tb)) {
+ /* Next cluster is free, allocate it. */
+ b |= tb;
+ ret = (int)ntfs_attr_pwrite(lcnbmp_na, lcn >> 3, 1, &b);
+ if (ret < 1) {
+ ntfs_log_error("Failed to write to lcn "
+ "bitmap.\n");
+ if (!ret)
+ errno = EIO;
+ return -1;
+ }
+ vol->nr_free_clusters--;
+ /* Update the mft bitmap runlist. */
+ rl->length++;
+ rl[1].vcn++;
+ status.added_cluster = 1;
+ ntfs_log_debug("Appending one cluster to mft bitmap.\n");
+ } else {
+ /* Allocate a cluster from the DATA_ZONE. */
+ rl2 = ntfs_cluster_alloc(vol, rl[1].vcn, 1, lcn, DATA_ZONE);
+ if (!rl2) {
+ ntfs_log_error("Failed to allocate a cluster for "
+ "the mft bitmap.\n");
+ return -1;
+ }
+ rl = ntfs_runlists_merge(mftbmp_na->rl, rl2);
+ if (!rl) {
+ ret = errno;
+ ntfs_log_error("Failed to merge runlists for mft "
+ "bitmap.\n");
+ if (ntfs_cluster_free_from_rl(vol, rl2))
+ ntfs_log_error("Failed to deallocate "
+ "cluster.%s\n", es);
+ free(rl2);
+ errno = ret;
+ return -1;
+ }
+ mftbmp_na->rl = rl;
+ status.added_run = 1;
+ ntfs_log_debug("Adding one run to mft bitmap.\n");
+ /* Find the last run in the new runlist. */
+ for (; rl[1].length; rl++)
+ ;
+ }
+ /*
+ * Update the attribute record as well. Note: @rl is the last
+ * (non-terminator) runlist element of mft bitmap.
+ */
+ ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_error("Failed to get search context.\n");
+ goto undo_alloc;
+ }
+ if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
+ mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find last attribute extent of "
+ "mft bitmap attribute.\n");
+ goto undo_alloc;
+ }
+ m = ctx->mrec;
+ a = ctx->attr;
+ ll = sle64_to_cpu(a->u.nonres.lowest_vcn);
+ rl2 = ntfs_attr_find_vcn(mftbmp_na, ll);
+ if (!rl2 || !rl2->length) {
+ ntfs_log_error("Failed to determine previous last "
+ "allocated cluster of mft bitmap attribute.\n");
+ if (rl2)
+ errno = EIO;
+ goto undo_alloc;
+ }
+ /* Get the size for the new mapping pairs array for this extent. */
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
+ if (mp_size <= 0) {
+ ntfs_log_error("Get size for mapping pairs failed for "
+ "mft bitmap attribute extent.\n");
+ goto undo_alloc;
+ }
+ /* Expand the attribute record if necessary. */
+ old_alen = le32_to_cpu(a->length);
+ if (ntfs_attr_record_resize(m, a, mp_size +
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset))) {
+ if (errno != ENOSPC) {
+ ntfs_log_error("Failed to resize attribute "
+ "record for mft bitmap attribute.\n");
+ goto undo_alloc;
+ }
+ // TODO: Deal with this by moving this extent to a new mft
+ // record or by starting a new extent in a new mft record.
+ ntfs_log_error("Not enough space in this mft record to "
+ "accommodate extended mft bitmap attribute "
+ "extent. Cannot handle this yet.\n");
+ errno = EOPNOTSUPP;
+ goto undo_alloc;
+ }
+ status.mp_rebuilt = 1;
+ /* Generate the mapping pairs array directly into the attr record. */
+ if (ntfs_mapping_pairs_build(vol, (u8*)a +
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp_size, rl2, ll,
+ NULL)) {
+ ntfs_log_error("Failed to build mapping pairs array for "
+ "mft bitmap attribute.\n");
+ errno = EIO;
+ goto undo_alloc;
+ }
+ /* Update the highest_vcn. */
+ a->u.nonres.highest_vcn = cpu_to_sle64(rl[1].vcn - 1);
+ /*
+ * We now have extended the mft bitmap allocated_size by one cluster.
+ * Reflect this in the ntfs_attr structure and the attribute record.
+ */
+ if (a->u.nonres.lowest_vcn) {
+ /*
+ * We are not in the first attribute extent, switch to it, but
+ * first ensure the changes will make it to disk later.
+ */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
+ mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find first attribute "
+ "extent of mft bitmap attribute.\n");
+ goto restore_undo_alloc;
+ }
+ a = ctx->attr;
+ }
+ mftbmp_na->allocated_size += vol->cluster_size;
+ a->u.nonres.allocated_size = cpu_to_sle64(mftbmp_na->allocated_size);
+ /* Ensure the changes make it to disk. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+restore_undo_alloc:
+ ret = errno;
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
+ mftbmp_na->name_len, 0, rl[1].vcn, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find last attribute extent of "
+ "mft bitmap attribute.%s\n", es);
+ ntfs_attr_put_search_ctx(ctx);
+ mftbmp_na->allocated_size += vol->cluster_size;
+ /*
+ * The only thing that is now wrong is ->allocated_size of the
+ * base attribute extent which chkdsk should be able to fix.
+ */
+ errno = ret;
+ return -1;
+ }
+ m = ctx->mrec;
+ a = ctx->attr;
+ a->u.nonres.highest_vcn = cpu_to_sle64(rl[1].vcn - 2);
+ errno = ret;
+undo_alloc:
+ ret = errno;
+ if (status.added_cluster) {
+ /* Truncate the last run in the runlist by one cluster. */
+ rl->length--;
+ rl[1].vcn--;
+ } else if (status.added_run) {
+ lcn = rl->lcn;
+ /* Remove the last run from the runlist. */
+ rl->lcn = rl[1].lcn;
+ rl->length = 0;
+ }
+ /* Deallocate the cluster. */
+ if (ntfs_bitmap_clear_bit(lcnbmp_na, lcn))
+ ntfs_log_error("Failed to free cluster.%s\n", es);
+ if (status.mp_rebuilt) {
+ if (ntfs_mapping_pairs_build(vol, (u8*)a +
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset),
+ old_alen - le16_to_cpu(a->u.nonres.mapping_pairs_offset),
+ rl2, ll, NULL))
+ ntfs_log_error("Failed to restore mapping "
+ "pairs array.%s\n", es);
+ if (ntfs_attr_record_resize(m, a, old_alen))
+ ntfs_log_error("Failed to restore attribute "
+ "record.%s\n", es);
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ }
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ errno = ret;
+ return -1;
+}
+
+/**
+ * ntfs_mft_bitmap_extend_initialized - extend mft bitmap initialized data
+ * @vol: volume on which to extend the mft bitmap attribute
+ *
+ * Extend the initialized portion of the mft bitmap attribute on the ntfs
+ * volume @vol by 8 bytes.
+ *
+ * Note: Only changes initialized_size and data_size, i.e. requires that
+ * allocated_size is big enough to fit the new initialized_size.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+static int ntfs_mft_bitmap_extend_initialized(ntfs_volume *vol)
+{
+ s64 old_data_size, old_initialized_size, ll;
+ ntfs_attr *mftbmp_na;
+ ntfs_attr_search_ctx *ctx;
+ ATTR_RECORD *a;
+ int err;
+
+ mftbmp_na = vol->mftbmp_na;
+ ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_error("Failed to get search context.\n");
+ return -1;
+ }
+ if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
+ mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find first attribute extent of "
+ "mft bitmap attribute.\n");
+ err = errno;
+ goto put_err_out;
+ }
+ a = ctx->attr;
+ old_data_size = mftbmp_na->data_size;
+ old_initialized_size = mftbmp_na->initialized_size;
+ mftbmp_na->initialized_size += 8;
+ a->u.nonres.initialized_size = cpu_to_sle64(mftbmp_na->initialized_size);
+ if (mftbmp_na->initialized_size > mftbmp_na->data_size) {
+ mftbmp_na->data_size = mftbmp_na->initialized_size;
+ a->u.nonres.data_size = cpu_to_sle64(mftbmp_na->data_size);
+ }
+ /* Ensure the changes make it to disk. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ /* Initialize the mft bitmap attribute value with zeroes. */
+ ll = 0;
+ ll = ntfs_attr_pwrite(mftbmp_na, old_initialized_size, 8, &ll);
+ if (ll == 8) {
+ ntfs_log_debug("Wrote eight initialized bytes to mft bitmap.\n");
+ return 0;
+ }
+ vol->nr_free_mft_records += 64; /* 8 bytes x 8 bits each. */
+ ntfs_log_error("Failed to write to mft bitmap.\n");
+ err = errno;
+ if (ll >= 0)
+ err = EIO;
+ /* Try to recover from the error. */
+ ctx = ntfs_attr_get_search_ctx(mftbmp_na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_error("Failed to get search context.%s\n", es);
+ goto err_out;
+ }
+ if (ntfs_attr_lookup(mftbmp_na->type, mftbmp_na->name,
+ mftbmp_na->name_len, 0, 0, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find first attribute extent of "
+ "mft bitmap attribute.%s\n", es);
+put_err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ goto err_out;
+ }
+ a = ctx->attr;
+ mftbmp_na->initialized_size = old_initialized_size;
+ a->u.nonres.initialized_size = cpu_to_sle64(old_initialized_size);
+ if (mftbmp_na->data_size != old_data_size) {
+ mftbmp_na->data_size = old_data_size;
+ a->u.nonres.data_size = cpu_to_sle64(old_data_size);
+ }
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_log_debug("Restored status of mftbmp: allocated_size 0x%llx, "
+ "data_size 0x%llx, initialized_size 0x%llx.\n",
+ (long long)mftbmp_na->allocated_size,
+ (long long)mftbmp_na->data_size,
+ (long long)mftbmp_na->initialized_size);
+err_out:
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_mft_data_extend_allocation - extend mft data attribute
+ * @vol: volume on which to extend the mft data attribute
+ *
+ * Extend the mft data attribute on the ntfs volume @vol by 16 mft records
+ * worth of clusters or if not enough space for this by one mft record worth
+ * of clusters.
+ *
+ * Note: Only changes allocated_size, i.e. does not touch initialized_size or
+ * data_size.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+static int ntfs_mft_data_extend_allocation(ntfs_volume *vol)
+{
+ LCN lcn;
+ VCN old_last_vcn;
+ s64 min_nr, nr, ll = 0; /* silence compiler warning */
+ ntfs_attr *mft_na;
+ runlist_element *rl, *rl2;
+ ntfs_attr_search_ctx *ctx;
+ MFT_RECORD *m = NULL; /* silence compiler warning */
+ ATTR_RECORD *a = NULL; /* silence compiler warning */
+ int err, mp_size;
+ u32 old_alen = 0; /* silence compiler warning */
+ BOOL mp_rebuilt = FALSE;
+
+ ntfs_log_debug("Extending mft data allocation.\n");
+ mft_na = vol->mft_na;
+ /*
+ * Determine the preferred allocation location, i.e. the last lcn of
+ * the mft data attribute. The allocated size of the mft data
+ * attribute cannot be zero so we are ok to do this.
+ */
+ rl = ntfs_attr_find_vcn(mft_na,
+ (mft_na->allocated_size - 1) >> vol->cluster_size_bits);
+ if (!rl || !rl->length || rl->lcn < 0) {
+ ntfs_log_error("Failed to determine last allocated "
+ "cluster of mft data attribute.\n");
+ if (rl)
+ errno = EIO;
+ return -1;
+ }
+ lcn = rl->lcn + rl->length;
+ ntfs_log_debug("Last lcn of mft data attribute is 0x%llx.\n", (long long)lcn);
+ /* Minimum allocation is one mft record worth of clusters. */
+ min_nr = vol->mft_record_size >> vol->cluster_size_bits;
+ if (!min_nr)
+ min_nr = 1;
+ /* Want to allocate 16 mft records worth of clusters. */
+ nr = vol->mft_record_size << 4 >> vol->cluster_size_bits;
+ if (!nr)
+ nr = min_nr;
+ ntfs_log_debug("Trying mft data allocation with default cluster count "
+ "%lli.\n", (long long)nr);
+ old_last_vcn = rl[1].vcn;
+ do {
+ rl2 = ntfs_cluster_alloc(vol, old_last_vcn, nr, lcn, MFT_ZONE);
+ if (rl2)
+ break;
+ if (errno != ENOSPC || nr == min_nr) {
+ ntfs_log_error("Failed to allocate the minimal "
+ "number of clusters (%lli) for the "
+ "mft data attribute.\n", (long long)nr);
+ return -1;
+ }
+ /*
+ * There is not enough space to do the allocation, but there
+ * might be enough space to do a minimal allocation so try that
+ * before failing.
+ */
+ nr = min_nr;
+ ntfs_log_debug("Retrying mft data allocation with minimal cluster "
+ "count %lli.\n", (long long)nr);
+ } while (1);
+ rl = ntfs_runlists_merge(mft_na->rl, rl2);
+ if (!rl) {
+ err = errno;
+ ntfs_log_error("Failed to merge runlists for mft data "
+ "attribute.\n");
+ if (ntfs_cluster_free_from_rl(vol, rl2))
+ ntfs_log_error("Failed to deallocate clusters "
+ "from the mft data attribute.%s\n", es);
+ free(rl2);
+ errno = err;
+ return -1;
+ }
+ mft_na->rl = rl;
+ ntfs_log_debug("Allocated %lli clusters.\n", nr);
+ /* Find the last run in the new runlist. */
+ for (; rl[1].length; rl++)
+ ;
+ /* Update the attribute record as well. */
+ ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_error("Failed to get search context.\n");
+ goto undo_alloc;
+ }
+ if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
+ rl[1].vcn, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find last attribute extent of "
+ "mft data attribute.\n");
+ goto undo_alloc;
+ }
+ m = ctx->mrec;
+ a = ctx->attr;
+ ll = sle64_to_cpu(a->u.nonres.lowest_vcn);
+ rl2 = ntfs_attr_find_vcn(mft_na, ll);
+ if (!rl2 || !rl2->length) {
+ ntfs_log_error("Failed to determine previous last "
+ "allocated cluster of mft data attribute.\n");
+ if (rl2)
+ errno = EIO;
+ goto undo_alloc;
+ }
+ /* Get the size for the new mapping pairs array for this extent. */
+ mp_size = ntfs_get_size_for_mapping_pairs(vol, rl2, ll);
+ if (mp_size <= 0) {
+ ntfs_log_error("Get size for mapping pairs failed for "
+ "mft data attribute extent.\n");
+ goto undo_alloc;
+ }
+ /* Expand the attribute record if necessary. */
+ old_alen = le32_to_cpu(a->length);
+ if (ntfs_attr_record_resize(m, a,
+ mp_size + le16_to_cpu(a->u.nonres.mapping_pairs_offset))) {
+ if (errno != ENOSPC) {
+ ntfs_log_error("Failed to resize attribute "
+ "record for mft data attribute.\n");
+ goto undo_alloc;
+ }
+ // TODO: Deal with this by moving this extent to a new mft
+ // record or by starting a new extent in a new mft record.
+ // Note: Use the special reserved mft records and ensure that
+ // this extent is not required to find the mft record in
+ // question.
+ ntfs_log_error("Not enough space in this mft record to "
+ "accommodate extended mft data attribute "
+ "extent. Cannot handle this yet.\n");
+ errno = EOPNOTSUPP;
+ goto undo_alloc;
+ }
+ mp_rebuilt = TRUE;
+ /*
+ * Generate the mapping pairs array directly into the attribute record.
+ */
+ if (ntfs_mapping_pairs_build(vol,
+ (u8*)a + le16_to_cpu(a->u.nonres.mapping_pairs_offset), mp_size,
+ rl2, ll, NULL)) {
+ ntfs_log_error("Failed to build mapping pairs array of "
+ "mft data attribute.\n");
+ errno = EIO;
+ goto undo_alloc;
+ }
+ /* Update the highest_vcn. */
+ a->u.nonres.highest_vcn = cpu_to_sle64(rl[1].vcn - 1);
+ /*
+ * We now have extended the mft data allocated_size by nr clusters.
+ * Reflect this in the ntfs_attr structure and the attribute record.
+ * @rl is the last (non-terminator) runlist element of mft data
+ * attribute.
+ */
+ if (a->u.nonres.lowest_vcn) {
+ /*
+ * We are not in the first attribute extent, switch to it, but
+ * first ensure the changes will make it to disk later.
+ */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(mft_na->type, mft_na->name,
+ mft_na->name_len, 0, 0, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find first attribute "
+ "extent of mft data attribute.\n");
+ goto restore_undo_alloc;
+ }
+ a = ctx->attr;
+ }
+ mft_na->allocated_size += nr << vol->cluster_size_bits;
+ a->u.nonres.allocated_size = cpu_to_sle64(mft_na->allocated_size);
+ /* Ensure the changes make it to disk. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ return 0;
+restore_undo_alloc:
+ err = errno;
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
+ rl[1].vcn, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find last attribute extent of "
+ "mft data attribute.%s\n", es);
+ ntfs_attr_put_search_ctx(ctx);
+ mft_na->allocated_size += nr << vol->cluster_size_bits;
+ /*
+ * The only thing that is now wrong is ->allocated_size of the
+ * base attribute extent which chkdsk should be able to fix.
+ */
+ errno = err;
+ return -1;
+ }
+ m = ctx->mrec;
+ a = ctx->attr;
+ a->u.nonres.highest_vcn = cpu_to_sle64(old_last_vcn - 1);
+ errno = err;
+undo_alloc:
+ err = errno;
+ if (ntfs_cluster_free(vol, mft_na, old_last_vcn, -1) < 0)
+ ntfs_log_error("Failed to free clusters from mft data "
+ "attribute.%s\n", es);
+ if (ntfs_rl_truncate(&mft_na->rl, old_last_vcn))
+ ntfs_log_error("Failed to truncate mft data attribute "
+ "runlist.%s\n", es);
+ if (mp_rebuilt) {
+ if (ntfs_mapping_pairs_build(vol, (u8*)a +
+ le16_to_cpu(a->u.nonres.mapping_pairs_offset),
+ old_alen - le16_to_cpu(a->u.nonres.mapping_pairs_offset),
+ rl2, ll, NULL))
+ ntfs_log_error("Failed to restore mapping pairs "
+ "array.%s\n", es);
+ if (ntfs_attr_record_resize(m, a, old_alen))
+ ntfs_log_error("Failed to restore attribute "
+ "record.%s\n", es);
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ }
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_mft_record_alloc - allocate an mft record on an ntfs volume
+ * @vol: volume on which to allocate the mft record
+ * @base_ni: open base inode if allocating an extent mft record or NULL
+ *
+ * Allocate an mft record in $MFT/$DATA of an open ntfs volume @vol.
+ *
+ * If @base_ni is NULL make the mft record a base mft record and allocate it at
+ * the default allocator position.
+ *
+ * If @base_ni is not NULL make the allocated mft record an extent record,
+ * allocate it starting at the mft record after the base mft record and attach
+ * the allocated and opened ntfs inode to the base inode @base_ni.
+ *
+ * On success return the now opened ntfs (extent) inode of the mft record.
+ *
+ * On error return NULL with errno set to the error code.
+ *
+ * To find a free mft record, we scan the mft bitmap for a zero bit. To
+ * optimize this we start scanning at the place specified by @base_ni or if
+ * @base_ni is NULL we start 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 the $DATA attribute of $MFT. 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, it does not 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 attribute 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
+ * is not sufficient space to do this, we try to extend by a single mft record
+ * or one cluster, if cluster size is above the 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
+ * accordingly, open an ntfs_inode for it and return it to the caller, 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
+ * superfluous bits are padded with zeroes.
+ *
+ * Thus, when we return successfully (return value non-zero), 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,
+ * - open an ntfs_inode for the allocated mft record, and we will
+ * - return the ntfs_inode.
+ *
+ * On error (return value zero), nothing will have changed. If we had changed
+ * anything before the error occurred, 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 consistent state when returning from
+ * this function.
+ *
+ * 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 does not 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.
+ */
+ntfs_inode *ntfs_mft_record_alloc(ntfs_volume *vol, ntfs_inode *base_ni)
+{
+ s64 ll, bit, old_data_initialized, old_data_size;
+ ntfs_attr *mft_na, *mftbmp_na;
+ ntfs_attr_search_ctx *ctx;
+ MFT_RECORD *m;
+ ATTR_RECORD *a;
+ ntfs_inode *ni;
+ int err;
+ le16 seq_no, usn;
+
+ if (base_ni)
+ ntfs_log_trace("Entering (allocating an extent mft record for "
+ "base mft record 0x%llx).\n",
+ (long long)base_ni->mft_no);
+ else
+ ntfs_log_trace("Entering (allocating a base mft record).\n");
+ if (!vol || !vol->mft_na || !vol->mftbmp_na) {
+ errno = EINVAL;
+ return NULL;
+ }
+ mft_na = vol->mft_na;
+ mftbmp_na = vol->mftbmp_na;
+ bit = ntfs_mft_bitmap_find_free_rec(vol, base_ni);
+ if (bit >= 0) {
+ ntfs_log_debug("Found free record (#1), bit 0x%llx.\n",
+ (long long)bit);
+ goto found_free_rec;
+ }
+ if (errno != ENOSPC)
+ return NULL;
+ /*
+ * No free mft records left. If the mft bitmap already covers more
+ * than the currently used mft records, the next records are all free,
+ * so we can simply allocate the first unused mft record.
+ * Note: We also have to make sure that the mft bitmap at least covers
+ * the first 24 mft records as they are special and whilst they may not
+ * be in use, we do not allocate from them.
+ */
+ ll = mft_na->initialized_size >> vol->mft_record_size_bits;
+ if (mftbmp_na->initialized_size << 3 > ll &&
+ mftbmp_na->initialized_size > 3) {
+ bit = ll;
+ if (bit < 24)
+ bit = 24;
+ ntfs_log_debug("Found free record (#2), bit 0x%llx.\n",
+ (long long)bit);
+ goto found_free_rec;
+ }
+ /*
+ * The mft bitmap needs to be expanded until it covers the first unused
+ * mft record that we can allocate.
+ * Note: The smallest mft record we allocate is mft record 24.
+ */
+ ntfs_log_debug("Status of mftbmp before extension: allocated_size 0x%llx, "
+ "data_size 0x%llx, initialized_size 0x%llx.\n",
+ (long long)mftbmp_na->allocated_size,
+ (long long)mftbmp_na->data_size,
+ (long long)mftbmp_na->initialized_size);
+ if (mftbmp_na->initialized_size + 8 > mftbmp_na->allocated_size) {
+ /* Need to extend bitmap by one more cluster. */
+ ntfs_log_debug("mftbmp: initialized_size + 8 > allocated_size.\n");
+ if (ntfs_mft_bitmap_extend_allocation(vol))
+ goto err_out;
+ ntfs_log_debug("Status of mftbmp after allocation extension: "
+ "allocated_size 0x%llx, data_size 0x%llx, "
+ "initialized_size 0x%llx.\n",
+ (long long)mftbmp_na->allocated_size,
+ (long long)mftbmp_na->data_size,
+ (long long)mftbmp_na->initialized_size);
+ }
+ /*
+ * We now have sufficient allocated space, extend the initialized_size
+ * as well as the data_size if necessary and fill the new space with
+ * zeroes.
+ */
+ bit = mftbmp_na->initialized_size << 3;
+ if (ntfs_mft_bitmap_extend_initialized(vol))
+ goto err_out;
+ ntfs_log_debug("Status of mftbmp after initialized extension: "
+ "allocated_size 0x%llx, data_size 0x%llx, "
+ "initialized_size 0x%llx.\n",
+ (long long)mftbmp_na->allocated_size,
+ (long long)mftbmp_na->data_size,
+ (long long)mftbmp_na->initialized_size);
+ ntfs_log_debug("Found free record (#3), bit 0x%llx.\n", (long long)bit);
+found_free_rec:
+ /* @bit is the found free mft record, allocate it in the mft bitmap. */
+ ntfs_log_debug("At found_free_rec.\n");
+ if (ntfs_bitmap_set_bit(mftbmp_na, bit)) {
+ ntfs_log_error("Failed to allocate bit in mft bitmap.\n");
+ goto err_out;
+ }
+ ntfs_log_debug("Set bit 0x%llx in mft bitmap.\n", (long long)bit);
+ /* The mft bitmap is now uptodate. Deal with mft data attribute now. */
+ ll = (bit + 1) << vol->mft_record_size_bits;
+ if (ll <= mft_na->initialized_size) {
+ ntfs_log_debug("Allocated mft record already initialized.\n");
+ goto mft_rec_already_initialized;
+ }
+ ntfs_log_debug("Initializing allocated mft record.\n");
+ /*
+ * The mft record is outside the initialized data. Extend the mft data
+ * attribute until it covers the allocated record. The loop is only
+ * actually traversed more than once when a freshly formatted volume is
+ * first written to so it optimizes away nicely in the common case.
+ */
+ ntfs_log_debug("Status of mft data before extension: "
+ "allocated_size 0x%llx, data_size 0x%llx, "
+ "initialized_size 0x%llx.\n",
+ (long long)mft_na->allocated_size,
+ (long long)mft_na->data_size,
+ (long long)mft_na->initialized_size);
+ while (ll > mft_na->allocated_size) {
+ if (ntfs_mft_data_extend_allocation(vol))
+ goto undo_mftbmp_alloc;
+ ntfs_log_debug("Status of mft data after allocation extension: "
+ "allocated_size 0x%llx, data_size 0x%llx, "
+ "initialized_size 0x%llx.\n",
+ (long long)mft_na->allocated_size,
+ (long long)mft_na->data_size,
+ (long long)mft_na->initialized_size);
+ }
+ old_data_initialized = mft_na->initialized_size;
+ old_data_size = mft_na->data_size;
+ /*
+ * Extend mft data initialized size (and data size of course) to reach
+ * the allocated mft record, formatting the mft records along the way.
+ * Note: We only modify the ntfs_attr structure as that is all that is
+ * needed by ntfs_mft_record_format(). We will update the attribute
+ * record itself in one fell swoop later on.
+ */
+ while (ll > mft_na->initialized_size) {
+ s64 ll2 = mft_na->initialized_size >> vol->mft_record_size_bits;
+ mft_na->initialized_size += vol->mft_record_size;
+ if (mft_na->initialized_size > mft_na->data_size)
+ mft_na->data_size = mft_na->initialized_size;
+ ntfs_log_debug("Initializing mft record 0x%llx.\n", (long long)ll2);
+ err = ntfs_mft_record_format(vol, ll2);
+ if (err) {
+ ntfs_log_error("Failed to format mft record.\n");
+ goto undo_data_init;
+ }
+ }
+ /* Update the mft data attribute record to reflect the new sizes. */
+ ctx = ntfs_attr_get_search_ctx(mft_na->ni, NULL);
+ if (!ctx) {
+ ntfs_log_error("Failed to get search context.\n");
+ goto undo_data_init;
+ }
+ if (ntfs_attr_lookup(mft_na->type, mft_na->name, mft_na->name_len, 0,
+ 0, NULL, 0, ctx)) {
+ ntfs_log_error("Failed to find first attribute extent of "
+ "mft data attribute.\n");
+ ntfs_attr_put_search_ctx(ctx);
+ goto undo_data_init;
+ }
+ a = ctx->attr;
+ a->u.nonres.initialized_size = cpu_to_sle64(mft_na->initialized_size);
+ a->u.nonres.data_size = cpu_to_sle64(mft_na->data_size);
+ /* Ensure the changes make it to disk. */
+ ntfs_inode_mark_dirty(ctx->ntfs_ino);
+ ntfs_attr_put_search_ctx(ctx);
+ ntfs_log_debug("Status of mft data after mft record initialization: "
+ "allocated_size 0x%llx, data_size 0x%llx, "
+ "initialized_size 0x%llx.\n",
+ (long long)mft_na->allocated_size,
+ (long long)mft_na->data_size,
+ (long long)mft_na->initialized_size);
+ /* Sanity checks. */
+ if (mft_na->data_size > mft_na->allocated_size ||
+ mft_na->initialized_size > mft_na->data_size)
+ NTFS_BUG("mft_na sanity checks failed");
+ /* Sync MFT to disk now in order to minimize data-loss. */
+ if (ntfs_inode_sync(mft_na->ni)) {
+ ntfs_log_debug("mft sync after extension failed. rolling back.");
+ goto undo_data_init;
+ }
+mft_rec_already_initialized:
+ /*
+ * We now have allocated and initialized the mft record. Need to read
+ * it from disk and re-format it, preserving the sequence number if it
+ * is not zero as well as the update sequence number if it is not zero
+ * or -1 (0xffff).
+ */
+ m = (MFT_RECORD*)ntfs_malloc(vol->mft_record_size);
+ if (!m)
+ goto undo_mftbmp_alloc;
+
+ if (ntfs_mft_record_read(vol, bit, m)) {
+ err = errno;
+ ntfs_log_error("Failed to read mft record.\n");
+ free(m);
+ errno = err;
+ goto undo_mftbmp_alloc;
+ }
+ /* Sanity check that the mft record is really not in use. */
+ if (ntfs_is_file_record(m->magic) && (m->flags & MFT_RECORD_IN_USE)) {
+ ntfs_log_error("Mft record 0x%llx was marked unused in "
+ "mft bitmap but is marked used itself. "
+ "Corrupt filesystem or library bug! "
+ "Run chkdsk immediately!\n", (long long)bit);
+ free(m);
+ errno = EIO;
+ goto undo_mftbmp_alloc;
+ }
+ seq_no = m->sequence_number;
+ usn = *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs));
+ if (ntfs_mft_record_layout(vol, bit, m)) {
+ err = errno;
+ ntfs_log_error("Failed to re-format mft record.\n");
+ free(m);
+ errno = err;
+ goto undo_mftbmp_alloc;
+ }
+ if (seq_no)
+ m->sequence_number = seq_no;
+ if (usn && le16_to_cpu(usn) != 0xffff)
+ *(le16*)((u8*)m + le16_to_cpu(m->usa_ofs)) = usn;
+ /* Set the mft record itself in use. */
+ m->flags |= MFT_RECORD_IN_USE;
+ /* Now need to open an ntfs inode for the mft record. */
+ ni = ntfs_inode_allocate(vol);
+ if (!ni) {
+ err = errno;
+ ntfs_log_error("Failed to allocate buffer for inode.\n");
+ free(m);
+ errno = err;
+ goto undo_mftbmp_alloc;
+ }
+ ni->mft_no = bit;
+ ni->mrec = m;
+ /*
+ * If we are allocating an extent mft record, make the opened inode an
+ * extent inode and attach it to the base inode. Also, set the base
+ * mft record reference in the extent inode.
+ */
+ if (base_ni) {
+ ni->nr_extents = -1;
+ ni->u.base_ni = base_ni;
+ m->base_mft_record = MK_LE_MREF(base_ni->mft_no,
+ le16_to_cpu(base_ni->mrec->sequence_number));
+ /*
+ * Attach the extent inode to the base inode, reallocating
+ * memory if needed.
+ */
+ if (!(base_ni->nr_extents & 3)) {
+ ntfs_inode **extent_nis;
+ int i;
+
+ i = (base_ni->nr_extents + 4) * sizeof(ntfs_inode *);
+ extent_nis = (ntfs_inode**)ntfs_malloc(i);
+ if (!extent_nis) {
+ err = errno;
+ free(m);
+ free(ni);
+ errno = err;
+ goto undo_mftbmp_alloc;
+ }
+ if (base_ni->u.extent_nis) {
+ memcpy(extent_nis, base_ni->u.extent_nis,
+ i - 4 * sizeof(ntfs_inode *));
+ free(base_ni->u.extent_nis);
+ }
+ base_ni->u.extent_nis = extent_nis;
+ }
+ base_ni->u.extent_nis[base_ni->nr_extents++] = ni;
+ }
+ /* Make sure the allocated inode is written out to disk later. */
+ ntfs_inode_mark_dirty(ni);
+ /* Initialize time, allocated and data size in ntfs_inode struct. */
+ ni->data_size = ni->allocated_size = 0;
+ ni->flags = 0;
+ ni->creation_time = ni->last_data_change_time =
+ ni->last_mft_change_time =
+ ni->last_access_time = time(NULL);
+ if (!base_ni) {
+ /* Update the default mft allocation position if it was used. */
+ vol->mft_data_pos = bit + 1;
+ /* Add inode to cache. */
+ __ntfs_inode_add_to_cache(ni);
+ }
+ /* Return the opened, allocated inode of the allocated mft record. */
+ ntfs_log_debug("Returning opened, allocated %sinode 0x%llx.\n",
+ base_ni ? "extent " : "", (long long)bit);
+ return ni;
+undo_data_init:
+ mft_na->initialized_size = old_data_initialized;
+ mft_na->data_size = old_data_size;
+undo_mftbmp_alloc:
+ err = errno;
+ if (ntfs_bitmap_clear_bit(mftbmp_na, bit))
+ ntfs_log_error("Failed to clear bit in mft bitmap.%s\n", es);
+ errno = err;
+err_out:
+ if (!errno)
+ errno = EIO;
+ return NULL;
+}
+
+/**
+ * ntfs_mft_record_free - free an mft record on an ntfs volume
+ * @vol: volume on which to free the mft record
+ * @ni: open ntfs inode of the mft record to free
+ *
+ * Free the mft record of the open inode @ni on the mounted ntfs volume @vol.
+ * Note that this function calls ntfs_inode_close() internally and hence you
+ * cannot use the pointer @ni any more after this function returns success.
+ *
+ * On success return 0 and on error return -1 with errno set to the error code.
+ */
+int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni)
+{
+ u64 mft_no;
+ int err;
+ u16 seq_no;
+ le16 old_seq_no;
+
+ ntfs_log_trace("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
+
+ if (!vol || !vol->mftbmp_na || !ni) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Cache the mft reference for later. */
+ mft_no = ni->mft_no;
+
+ /* Mark the mft record as not in use. */
+ ni->mrec->flags &= ~MFT_RECORD_IN_USE;
+
+ /* Increment the sequence number, skipping zero, if it is not zero. */
+ old_seq_no = ni->mrec->sequence_number;
+ seq_no = le16_to_cpu(old_seq_no);
+ if (seq_no == 0xffff)
+ seq_no = 1;
+ else if (seq_no)
+ seq_no++;
+ ni->mrec->sequence_number = cpu_to_le16(seq_no);
+
+ /* Set the inode dirty and write it out. */
+ ntfs_inode_mark_dirty(ni);
+ if (ntfs_inode_sync(ni)) {
+ err = errno;
+ goto sync_rollback;
+ }
+
+ /* Clear the bit in the $MFT/$BITMAP corresponding to this record. */
+ if (ntfs_bitmap_clear_bit(vol->mftbmp_na, mft_no)) {
+ err = errno;
+ // FIXME: If ntfs_bitmap_clear_run() guarantees rollback on
+ // error, this could be changed to goto sync_rollback;
+ goto bitmap_rollback;
+ }
+
+ /* Throw away the now freed inode. */
+ if (!ntfs_inode_close(ni))
+ return 0;
+ err = errno;
+
+ /* Rollback what we did... */
+bitmap_rollback:
+ if (ntfs_bitmap_set_bit(vol->mftbmp_na, mft_no))
+ ntfs_log_debug("Eeek! Rollback failed in ntfs_mft_record_free(). "
+ "Leaving inconsistent metadata!\n");
+sync_rollback:
+ ni->mrec->flags |= MFT_RECORD_IN_USE;
+ ni->mrec->sequence_number = old_seq_no;
+ ntfs_inode_mark_dirty(ni);
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_mft_usn_dec - Decrement USN by one
+ * @mrec: pointer to an mft record
+ *
+ * On success return 0 and on error return -1 with errno set.
+ */
+int ntfs_mft_usn_dec(MFT_RECORD *mrec)
+{
+ u16 usn;
+ le16 *usnp;
+
+ if (!mrec) {
+ errno = EINVAL;
+ return -1;
+ }
+ usnp = (le16 *)((char *)mrec + le16_to_cpu(mrec->usa_ofs));
+ usn = le16_to_cpup(usnp);
+ if (usn-- <= 1)
+ usn = 0xfffe;
+ *usnp = cpu_to_le16(usn);
+
+ return 0;
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/misc.c b/usr/src/lib/libntfs/common/libntfs/misc.c
new file mode 100644
index 0000000000..26a51c13c9
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/misc.c
@@ -0,0 +1,64 @@
+/**
+ * misc.c - Miscellaneous functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2006 Szabolcs Szakacsits
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "compat.h"
+#include "support.h"
+#include "logging.h"
+
+/**
+ * ntfs_calloc - A logging supported calloc(3)
+ *
+ * Return a pointer to the allocated memory or NULL if the request fails.
+ * Memory is initialized with zeros.
+ */
+void *ntfs_calloc(size_t size)
+{
+ void *p;
+
+ p = calloc(1, size);
+ if (!p)
+ ntfs_log_perror("Failed to calloc %lld bytes", (long long)size);
+ return p;
+}
+
+/**
+ * ntfs_malloc - A logging supported malloc(3)
+ *
+ * Return a pointer to the allocated memory or NULL if the request fails.
+ * Memory is uninitialized.
+ */
+void *ntfs_malloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (!p)
+ ntfs_log_perror("Failed to malloc %lld bytes", (long long)size);
+ return p;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/mst.c b/usr/src/lib/libntfs/common/libntfs/mst.c
new file mode 100644
index 0000000000..2b48e992c0
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/mst.c
@@ -0,0 +1,216 @@
+/**
+ * mst.c - Multi sector fixup handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2004 Anton Altaparmakov
+ * Copyright (c) 2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "mst.h"
+
+/**
+ * ntfs_mst_post_read_fixup - deprotect multi sector transfer protected data
+ * @b: pointer to the data to deprotect
+ * @size: size in bytes of @b
+ *
+ * Perform the necessary post read multi sector transfer fixups and detect the
+ * presence of incomplete multi sector transfers. - In that case, overwrite the
+ * magic of the ntfs record header being processed with "BAAD" (in memory only!)
+ * and abort processing.
+ *
+ * Return 0 on success and -1 on error, with errno set to the error code. The
+ * following error codes are defined:
+ * EINVAL Invalid arguments or invalid NTFS record in buffer @b.
+ * EIO Multi sector transfer error was detected. Magic of the NTFS
+ * record in @b will have been set to "BAAD".
+ */
+int ntfs_mst_post_read_fixup(NTFS_RECORD *b, const u32 size)
+{
+ u16 usa_ofs, usa_count, usn;
+ u16 *usa_pos, *data_pos;
+
+ /* Setup the variables. */
+ usa_ofs = le16_to_cpu(b->usa_ofs);
+ /* Decrement usa_count to get number of fixups. */
+ usa_count = le16_to_cpu(b->usa_count) - 1;
+ /* Size and alignment checks. */
+ if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
+ (u32)(usa_ofs + (usa_count * 2)) > size ||
+ (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Position of usn in update sequence array. */
+ usa_pos = (u16*)b + usa_ofs/sizeof(u16);
+ /*
+ * The update sequence number which has to be equal to each of the
+ * u16 values before they are fixed up. Note no need to care for
+ * endianness since we are comparing and moving data for on disk
+ * structures which means the data is consistent. - If it is
+ * consistency the wrong endianness it doesn't make any difference.
+ */
+ usn = *usa_pos;
+ /*
+ * Position in protected data of first u16 that needs fixing up.
+ */
+ data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+ /*
+ * Check for incomplete multi sector transfer(s).
+ */
+ while (usa_count--) {
+ if (*data_pos != usn) {
+ /*
+ * Incomplete multi sector transfer detected! )-:
+ * Set the magic to "BAAD" and return failure.
+ * Note that magic_BAAD is already converted to le32.
+ */
+ b->magic = magic_BAAD;
+ errno = EIO;
+ return -1;
+ }
+ data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+ }
+ /* Re-setup the variables. */
+ usa_count = le16_to_cpu(b->usa_count) - 1;
+ data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+ /* Fixup all sectors. */
+ while (usa_count--) {
+ /*
+ * Increment position in usa and restore original data from
+ * the usa into the data buffer.
+ */
+ *data_pos = *(++usa_pos);
+ /* Increment position in data as well. */
+ data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+ }
+ return 0;
+}
+
+/**
+ * ntfs_mst_pre_write_fixup - apply multi sector transfer protection
+ * @b: pointer to the data to protect
+ * @size: size in bytes of @b
+ *
+ * Perform the necessary pre write multi sector transfer fixup on the data
+ * pointer to by @b of @size.
+ *
+ * Return 0 if fixups applied successfully or -1 if no fixups were performed
+ * due to errors. In that case errno i set to the error code (EINVAL).
+ *
+ * NOTE: We consider the absence / invalidity of an update sequence array to
+ * mean error. This means that you have to create a valid update sequence
+ * array header in the ntfs record before calling this function, otherwise it
+ * will fail (the header needs to contain the position of the update sequence
+ * array together with the number of elements in the array). You also need to
+ * initialise the update sequence number before calling this function
+ * otherwise a random word will be used (whatever was in the record at that
+ * position at that time).
+ */
+int ntfs_mst_pre_write_fixup(NTFS_RECORD *b, const u32 size)
+{
+ u16 usa_ofs, usa_count, usn;
+ le16 *usa_pos, *data_pos, usnle;
+
+ /* Sanity check + only fixup if it makes sense. */
+ if (!b || ntfs_is_baad_record(b->magic) ||
+ ntfs_is_hole_record(b->magic)) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Setup the variables. */
+ usa_ofs = le16_to_cpu(b->usa_ofs);
+ /* Decrement usa_count to get number of fixups. */
+ usa_count = le16_to_cpu(b->usa_count) - 1;
+ /* Size and alignment checks. */
+ if (size & (NTFS_BLOCK_SIZE - 1) || usa_ofs & 1 ||
+ (u32)(usa_ofs + (usa_count * 2)) > size ||
+ (size >> NTFS_BLOCK_SIZE_BITS) != usa_count) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Position of usn in update sequence array. */
+ usa_pos = (le16*)((u8*)b + usa_ofs);
+ /*
+ * Cyclically increment the update sequence number
+ * (skipping 0 and -1, i.e. 0xffff).
+ */
+ usn = le16_to_cpup(usa_pos) + 1;
+ if (usn == 0xffff || !usn)
+ usn = 1;
+ usnle = cpu_to_le16(usn);
+ *usa_pos = usnle;
+ /* Position in data of first u16 that needs fixing up. */
+ data_pos = (le16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+ /* Fixup all sectors. */
+ while (usa_count--) {
+ /*
+ * Increment the position in the usa and save the
+ * original data from the data buffer into the usa.
+ */
+ *(++usa_pos) = *data_pos;
+ /* Apply fixup to data. */
+ *data_pos = usnle;
+ /* Increment position in data as well. */
+ data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+ }
+ return 0;
+}
+
+/**
+ * ntfs_mst_post_write_fixup - deprotect multi sector transfer protected data
+ * @b: pointer to the data to deprotect
+ *
+ * Perform the necessary post write multi sector transfer fixup, not checking
+ * for any errors, because we assume we have just used
+ * ntfs_mst_pre_write_fixup(), thus the data will be fine or we would never
+ * have gotten here.
+ */
+void ntfs_mst_post_write_fixup(NTFS_RECORD *b)
+{
+ u16 *usa_pos, *data_pos;
+
+ u16 usa_ofs = le16_to_cpu(b->usa_ofs);
+ u16 usa_count = le16_to_cpu(b->usa_count) - 1;
+
+ /* Position of usn in update sequence array. */
+ usa_pos = (u16*)b + usa_ofs/sizeof(u16);
+
+ /* Position in protected data of first u16 that needs fixing up. */
+ data_pos = (u16*)b + NTFS_BLOCK_SIZE/sizeof(u16) - 1;
+
+ /* Fixup all sectors. */
+ while (usa_count--) {
+ /*
+ * Increment position in usa and restore original data from
+ * the usa into the data buffer.
+ */
+ *data_pos = *(++usa_pos);
+
+ /* Increment position in data as well. */
+ data_pos += NTFS_BLOCK_SIZE/sizeof(u16);
+ }
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/runlist.c b/usr/src/lib/libntfs/common/libntfs/runlist.c
new file mode 100644
index 0000000000..d70b643a88
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/runlist.c
@@ -0,0 +1,2154 @@
+/**
+ * runlist.c - Run list handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2002-2005 Anton Altaparmakov
+ * Copyright (c) 2002-2005 Richard Russon
+ * Copyright (c) 2002-2006 Szabolcs Szakacsits
+ * Copyright (c) 2004-2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "compat.h"
+#include "types.h"
+#include "volume.h"
+#include "layout.h"
+#include "debug.h"
+#include "device.h"
+#include "logging.h"
+
+/**
+ * ntfs_rl_mm - runlist memmove
+ * @base:
+ * @dst:
+ * @src:
+ * @size:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static __inline__ void ntfs_rl_mm(runlist_element *base, int dst, int src,
+ int size)
+{
+ if ((dst != src) && (size > 0))
+ memmove(base + dst, base + src, size * sizeof(*base));
+}
+
+/**
+ * ntfs_rl_mc - runlist memory copy
+ * @dstbase:
+ * @dst:
+ * @srcbase:
+ * @src:
+ * @size:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static __inline__ void ntfs_rl_mc(runlist_element *dstbase, int dst,
+ runlist_element *srcbase, int src, int size)
+{
+ if (size > 0)
+ memcpy(dstbase + dst, srcbase + src, size * sizeof(*dstbase));
+}
+
+/**
+ * ntfs_rl_realloc - Reallocate memory for runlists
+ * @rl: original runlist
+ * @old_size: number of runlist elements in the original runlist @rl
+ * @new_size: number of runlist elements we need space for
+ *
+ * As the runlists grow, more memory will be required. To prevent large
+ * numbers of small reallocations of memory, this function returns a 4kiB block
+ * of memory.
+ *
+ * N.B. If the new allocation doesn't require a different number of 4kiB
+ * blocks in memory, the function will return the original pointer.
+ *
+ * On success, return a pointer to the newly allocated, or recycled, memory.
+ * On error, return NULL with errno set to the error code.
+ */
+static runlist_element *ntfs_rl_realloc(runlist_element *rl,
+ int old_size, int new_size)
+{
+ old_size = (old_size * sizeof(runlist_element) + 0xfff) & ~0xfff;
+ new_size = (new_size * sizeof(runlist_element) + 0xfff) & ~0xfff;
+ if (old_size == new_size)
+ return rl;
+ return realloc(rl, new_size);
+}
+
+/**
+ * ntfs_rl_are_mergeable - test if two runlists can be joined together
+ * @dst: original runlist
+ * @src: new runlist to test for mergeability with @dst
+ *
+ * Test if two runlists can be joined together. For this, their VCNs and LCNs
+ * must be adjacent.
+ *
+ * Return: TRUE Success, the runlists can be merged.
+ * FALSE Failure, the runlists cannot be merged.
+ */
+static BOOL ntfs_rl_are_mergeable(runlist_element *dst,
+ runlist_element *src)
+{
+ if (!dst || !src) {
+ ntfs_log_debug("Eeek. ntfs_rl_are_mergeable() invoked with NULL "
+ "pointer!\n");
+ return FALSE;
+ }
+
+ /* We can merge unmapped regions even if they are misaligned. */
+ if ((dst->lcn == LCN_RL_NOT_MAPPED) && (src->lcn == LCN_RL_NOT_MAPPED))
+ return TRUE;
+ /* If the runs are misaligned, we cannot merge them. */
+ if ((dst->vcn + dst->length) != src->vcn)
+ return FALSE;
+ /* If both runs are non-sparse and contiguous, we can merge them. */
+ if ((dst->lcn >= 0) && (src->lcn >= 0) &&
+ ((dst->lcn + dst->length) == src->lcn))
+ return TRUE;
+ /* If we are merging two holes, we can merge them. */
+ if ((dst->lcn == LCN_HOLE) && (src->lcn == LCN_HOLE))
+ return TRUE;
+ /* Cannot merge. */
+ return FALSE;
+}
+
+/**
+ * __ntfs_rl_merge - merge two runlists without testing if they can be merged
+ * @dst: original, destination runlist
+ * @src: new runlist to merge with @dst
+ *
+ * Merge the two runlists, writing into the destination runlist @dst. The
+ * caller must make sure the runlists can be merged or this will corrupt the
+ * destination runlist.
+ */
+static __inline__ void __ntfs_rl_merge(runlist_element *dst,
+ runlist_element *src)
+{
+ dst->length += src->length;
+}
+
+/**
+ * ntfs_rl_append - append a runlist after a given element
+ * @dst: original runlist to be worked on
+ * @dsize: number of elements in @dst (including end marker)
+ * @src: runlist to be inserted into @dst
+ * @ssize: number of elements in @src (excluding end marker)
+ * @loc: append the new runlist @src after this element in @dst
+ *
+ * Append the runlist @src after element @loc in @dst. Merge the right end of
+ * the new runlist, if necessary. Adjust the size of the hole before the
+ * appended runlist.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return NULL, with errno set to the error code. Both runlists are
+ * left unmodified.
+ */
+static runlist_element *ntfs_rl_append(runlist_element *dst,
+ int dsize, runlist_element *src, int ssize, int loc)
+{
+ BOOL right = FALSE; /* Right end of @src needs merging */
+ int marker; /* End of the inserted runs */
+
+ if (!dst || !src) {
+ ntfs_log_debug("Eeek. ntfs_rl_append() invoked with NULL "
+ "pointer!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* First, check if the right hand end needs merging. */
+ if ((loc + 1) < dsize)
+ right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1);
+
+ /* Space required: @dst size + @src size, less one if we merged. */
+ dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - right);
+ if (!dst)
+ return NULL;
+ /*
+ * We are guaranteed to succeed from here so can start modifying the
+ * original runlists.
+ */
+
+ /* First, merge the right hand end, if necessary. */
+ if (right)
+ __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
+
+ /* marker - First run after the @src runs that have been inserted */
+ marker = loc + ssize + 1;
+
+ /* Move the tail of @dst out of the way, then copy in @src. */
+ ntfs_rl_mm(dst, marker, loc + 1 + right, dsize - loc - 1 - right);
+ ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
+
+ /* Adjust the size of the preceding hole. */
+ dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn;
+
+ /* We may have changed the length of the file, so fix the end marker */
+ if (dst[marker].lcn == LCN_ENOENT)
+ dst[marker].vcn = dst[marker-1].vcn + dst[marker-1].length;
+
+ return dst;
+}
+
+/**
+ * ntfs_rl_insert - insert a runlist into another
+ * @dst: original runlist to be worked on
+ * @dsize: number of elements in @dst (including end marker)
+ * @src: new runlist to be inserted
+ * @ssize: number of elements in @src (excluding end marker)
+ * @loc: insert the new runlist @src before this element in @dst
+ *
+ * Insert the runlist @src before element @loc in the runlist @dst. Merge the
+ * left end of the new runlist, if necessary. Adjust the size of the hole
+ * after the inserted runlist.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return NULL, with errno set to the error code. Both runlists are
+ * left unmodified.
+ */
+static runlist_element *ntfs_rl_insert(runlist_element *dst,
+ int dsize, runlist_element *src, int ssize, int loc)
+{
+ BOOL left = FALSE; /* Left end of @src needs merging */
+ BOOL disc = FALSE; /* Discontinuity between @dst and @src */
+ int marker; /* End of the inserted runs */
+
+ if (!dst || !src) {
+ ntfs_log_debug("Eeek. ntfs_rl_insert() invoked with NULL "
+ "pointer!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* disc => Discontinuity between the end of @dst and the start of @src.
+ * This means we might need to insert a "notmapped" run.
+ */
+ if (loc == 0)
+ disc = (src[0].vcn > 0);
+ else {
+ s64 merged_length;
+
+ left = ntfs_rl_are_mergeable(dst + loc - 1, src);
+
+ merged_length = dst[loc - 1].length;
+ if (left)
+ merged_length += src->length;
+
+ disc = (src[0].vcn > dst[loc - 1].vcn + merged_length);
+ }
+
+ /* Space required: @dst size + @src size, less one if we merged, plus
+ * one if there was a discontinuity.
+ */
+ dst = ntfs_rl_realloc(dst, dsize, dsize + ssize - left + disc);
+ if (!dst)
+ return NULL;
+ /*
+ * We are guaranteed to succeed from here so can start modifying the
+ * original runlist.
+ */
+
+ if (left)
+ __ntfs_rl_merge(dst + loc - 1, src);
+
+ /*
+ * marker - First run after the @src runs that have been inserted
+ * Nominally: marker = @loc + @ssize (location + number of runs in @src)
+ * If "left", then the first run in @src has been merged with one in @dst.
+ * If "disc", then @dst and @src don't meet and we need an extra run to fill the gap.
+ */
+ marker = loc + ssize - left + disc;
+
+ /* Move the tail of @dst out of the way, then copy in @src. */
+ ntfs_rl_mm(dst, marker, loc, dsize - loc);
+ ntfs_rl_mc(dst, loc + disc, src, left, ssize - left);
+
+ /* Adjust the VCN of the first run after the insertion ... */
+ dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length;
+ /* ... and the length. */
+ if (dst[marker].lcn == LCN_HOLE || dst[marker].lcn == LCN_RL_NOT_MAPPED)
+ dst[marker].length = dst[marker + 1].vcn - dst[marker].vcn;
+
+ /* Writing beyond the end of the file and there's a discontinuity. */
+ if (disc) {
+ if (loc > 0) {
+ dst[loc].vcn = dst[loc - 1].vcn + dst[loc - 1].length;
+ dst[loc].length = dst[loc + 1].vcn - dst[loc].vcn;
+ } else {
+ dst[loc].vcn = 0;
+ dst[loc].length = dst[loc + 1].vcn;
+ }
+ dst[loc].lcn = LCN_RL_NOT_MAPPED;
+ }
+ return dst;
+}
+
+/**
+ * ntfs_rl_replace - overwrite a runlist element with another runlist
+ * @dst: original runlist to be worked on
+ * @dsize: number of elements in @dst (including end marker)
+ * @src: new runlist to be inserted
+ * @ssize: number of elements in @src (excluding end marker)
+ * @loc: index in runlist @dst to overwrite with @src
+ *
+ * Replace the runlist element @dst at @loc with @src. Merge the left and
+ * right ends of the inserted runlist, if necessary.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return NULL, with errno set to the error code. Both runlists are
+ * left unmodified.
+ */
+static runlist_element *ntfs_rl_replace(runlist_element *dst,
+ int dsize, runlist_element *src, int ssize, int loc)
+{
+ signed delta;
+ BOOL left = FALSE; /* Left end of @src needs merging */
+ BOOL right = FALSE; /* Right end of @src needs merging */
+ int tail; /* Start of tail of @dst */
+ int marker; /* End of the inserted runs */
+
+ if (!dst || !src) {
+ ntfs_log_debug("Eeek. ntfs_rl_replace() invoked with NULL "
+ "pointer!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* First, see if the left and right ends need merging. */
+ if ((loc + 1) < dsize)
+ right = ntfs_rl_are_mergeable(src + ssize - 1, dst + loc + 1);
+ if (loc > 0)
+ left = ntfs_rl_are_mergeable(dst + loc - 1, src);
+
+ /* Allocate some space. We'll need less if the left, right, or both
+ * ends get merged. The -1 accounts for the run being replaced.
+ */
+ delta = ssize - 1 - left - right;
+ if (delta > 0) {
+ dst = ntfs_rl_realloc(dst, dsize, dsize + delta);
+ if (!dst)
+ return NULL;
+ }
+ /*
+ * We are guaranteed to succeed from here so can start modifying the
+ * original runlists.
+ */
+
+ /* First, merge the left and right ends, if necessary. */
+ if (right)
+ __ntfs_rl_merge(src + ssize - 1, dst + loc + 1);
+ if (left)
+ __ntfs_rl_merge(dst + loc - 1, src);
+
+ /*
+ * tail - Offset of the tail of @dst
+ * Nominally: @tail = @loc + 1 (location, skipping the replaced run)
+ * If "right", then one of @dst's runs is already merged into @src.
+ */
+ tail = loc + right + 1;
+
+ /*
+ * marker - First run after the @src runs that have been inserted
+ * Nominally: @marker = @loc + @ssize (location + number of runs in @src)
+ * If "left", then the first run in @src has been merged with one in @dst.
+ */
+ marker = loc + ssize - left;
+
+ /* Move the tail of @dst out of the way, then copy in @src. */
+ ntfs_rl_mm(dst, marker, tail, dsize - tail);
+ ntfs_rl_mc(dst, loc, src, left, ssize - left);
+
+ /* We may have changed the length of the file, so fix the end marker */
+ if (((dsize - tail) > 0) && (dst[marker].lcn == LCN_ENOENT))
+ dst[marker].vcn = dst[marker - 1].vcn + dst[marker - 1].length;
+
+ return dst;
+}
+
+/**
+ * ntfs_rl_split - insert a runlist into the centre of a hole
+ * @dst: original runlist to be worked on
+ * @dsize: number of elements in @dst (including end marker)
+ * @src: new runlist to be inserted
+ * @ssize: number of elements in @src (excluding end marker)
+ * @loc: index in runlist @dst at which to split and insert @src
+ *
+ * Split the runlist @dst at @loc into two and insert @new in between the two
+ * fragments. No merging of runlists is necessary. Adjust the size of the
+ * holes either side.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @dst and @src are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return NULL, with errno set to the error code. Both runlists are
+ * left unmodified.
+ */
+static runlist_element *ntfs_rl_split(runlist_element *dst,
+ int dsize, runlist_element *src, int ssize, int loc)
+{
+ if (!dst || !src) {
+ ntfs_log_trace("Invoked with NULL pointer!\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Space required: @dst size + @src size + one new hole. */
+ dst = ntfs_rl_realloc(dst, dsize, dsize + ssize + 1);
+ if (!dst)
+ return dst;
+ /*
+ * We are guaranteed to succeed from here so can start modifying the
+ * original runlists.
+ */
+
+ /* Move the tail of @dst out of the way, then copy in @src. */
+ ntfs_rl_mm(dst, loc + 1 + ssize, loc, dsize - loc);
+ ntfs_rl_mc(dst, loc + 1, src, 0, ssize);
+
+ /* Adjust the size of the holes either size of @src. */
+ dst[loc].length = dst[loc+1].vcn - dst[loc].vcn;
+ dst[loc+ssize+1].vcn = dst[loc+ssize].vcn + dst[loc+ssize].length;
+ dst[loc+ssize+1].length = dst[loc+ssize+2].vcn - dst[loc+ssize+1].vcn;
+
+ return dst;
+}
+
+
+/**
+ * ntfs_runlists_merge - merge two runlists into one
+ * @drl: original runlist to be worked on
+ * @srl: new runlist to be merged into @drl
+ *
+ * First we sanity check the two runlists @srl and @drl to make sure that they
+ * are sensible and can be merged. The runlist @srl must be either after the
+ * runlist @drl or completely within a hole (or unmapped region) in @drl.
+ *
+ * Merging of runlists is necessary in two cases:
+ * 1. When attribute lists are used and a further extent is being mapped.
+ * 2. When new clusters are allocated to fill a hole or extend a file.
+ *
+ * There are four possible ways @srl can be merged. It can:
+ * - be inserted at the beginning of a hole,
+ * - split the hole in two and be inserted between the two fragments,
+ * - be appended at the end of a hole, or it can
+ * - replace the whole hole.
+ * It can also be appended to the end of the runlist, which is just a variant
+ * of the insert case.
+ *
+ * On success, return a pointer to the new, combined, runlist. Note, both
+ * runlists @drl and @srl are deallocated before returning so you cannot use
+ * the pointers for anything any more. (Strictly speaking the returned runlist
+ * may be the same as @dst but this is irrelevant.)
+ *
+ * On error, return NULL, with errno set to the error code. Both runlists are
+ * left unmodified. The following error codes are defined:
+ * ENOMEM Not enough memory to allocate runlist array.
+ * EINVAL Invalid parameters were passed in.
+ * ERANGE The runlists overlap and cannot be merged.
+ */
+runlist_element *ntfs_runlists_merge(runlist_element *drl,
+ runlist_element *srl)
+{
+ int di, si; /* Current index into @[ds]rl. */
+ int sstart; /* First index with lcn > LCN_RL_NOT_MAPPED. */
+ int dins; /* Index into @drl at which to insert @srl. */
+ int dend, send; /* Last index into @[ds]rl. */
+ int dfinal, sfinal; /* The last index into @[ds]rl with
+ lcn >= LCN_HOLE. */
+ int marker = 0;
+ VCN marker_vcn = 0;
+
+ ntfs_log_debug("dst:\n");
+ ntfs_debug_runlist_dump(drl);
+ ntfs_log_debug("src:\n");
+ ntfs_debug_runlist_dump(srl);
+
+ /* Check for silly calling... */
+ if (!srl)
+ return drl;
+
+ /* Check for the case where the first mapping is being done now. */
+ if (!drl) {
+ drl = srl;
+ /* Complete the source runlist if necessary. */
+ if (drl[0].vcn) {
+ /* Scan to the end of the source runlist. */
+ for (dend = 0; drl[dend].length; dend++)
+ ;
+ dend++;
+ drl = ntfs_rl_realloc(drl, dend, dend + 1);
+ if (!drl)
+ return drl;
+ /* Insert start element at the front of the runlist. */
+ ntfs_rl_mm(drl, 1, 0, dend);
+ drl[0].vcn = 0;
+ drl[0].lcn = LCN_RL_NOT_MAPPED;
+ drl[0].length = drl[1].vcn;
+ }
+ goto finished;
+ }
+
+ si = di = 0;
+
+ /* Skip any unmapped start element(s) in the source runlist. */
+ while (srl[si].length && srl[si].lcn < (LCN)LCN_HOLE)
+ si++;
+
+ /* Can't have an entirely unmapped source runlist. */
+ if (!srl[si].length) {
+ ntfs_log_debug("Eeek! ntfs_runlists_merge() received entirely "
+ "unmapped source runlist.\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Record the starting points. */
+ sstart = si;
+
+ /*
+ * Skip forward in @drl until we reach the position where @srl needs to
+ * be inserted. If we reach the end of @drl, @srl just needs to be
+ * appended to @drl.
+ */
+ for (; drl[di].length; di++) {
+ if (drl[di].vcn + drl[di].length > srl[sstart].vcn)
+ break;
+ }
+ dins = di;
+
+ /* Sanity check for illegal overlaps. */
+ if ((drl[di].vcn == srl[si].vcn) && (drl[di].lcn >= 0) &&
+ (srl[si].lcn >= 0)) {
+ ntfs_log_debug("Run lists overlap. Cannot merge!\n");
+ errno = ERANGE;
+ return NULL;
+ }
+
+ /* Scan to the end of both runlists in order to know their sizes. */
+ for (send = si; srl[send].length; send++)
+ ;
+ for (dend = di; drl[dend].length; dend++)
+ ;
+
+ if (srl[send].lcn == (LCN)LCN_ENOENT)
+ marker_vcn = srl[marker = send].vcn;
+
+ /* Scan to the last element with lcn >= LCN_HOLE. */
+ for (sfinal = send; sfinal >= 0 && srl[sfinal].lcn < LCN_HOLE; sfinal--)
+ ;
+ for (dfinal = dend; dfinal >= 0 && drl[dfinal].lcn < LCN_HOLE; dfinal--)
+ ;
+
+ {
+ BOOL start;
+ BOOL finish;
+ int ds = dend + 1; /* Number of elements in drl & srl */
+ int ss = sfinal - sstart + 1;
+
+ start = ((drl[dins].lcn < LCN_RL_NOT_MAPPED) || /* End of file */
+ (drl[dins].vcn == srl[sstart].vcn)); /* Start of hole */
+ finish = ((drl[dins].lcn >= LCN_RL_NOT_MAPPED) && /* End of file */
+ ((drl[dins].vcn + drl[dins].length) <= /* End of hole */
+ (srl[send - 1].vcn + srl[send - 1].length)));
+
+ /* Or we'll lose an end marker */
+ if (finish && !drl[dins].length)
+ ss++;
+ if (marker && (drl[dins].vcn + drl[dins].length > srl[send - 1].vcn))
+ finish = FALSE;
+
+ ntfs_log_debug("dfinal = %i, dend = %i\n", dfinal, dend);
+ ntfs_log_debug("sstart = %i, sfinal = %i, send = %i\n", sstart, sfinal, send);
+ ntfs_log_debug("start = %i, finish = %i\n", start, finish);
+ ntfs_log_debug("ds = %i, ss = %i, dins = %i\n", ds, ss, dins);
+
+ if (start) {
+ if (finish)
+ drl = ntfs_rl_replace(drl, ds, srl + sstart, ss, dins);
+ else
+ drl = ntfs_rl_insert(drl, ds, srl + sstart, ss, dins);
+ } else {
+ if (finish)
+ drl = ntfs_rl_append(drl, ds, srl + sstart, ss, dins);
+ else
+ drl = ntfs_rl_split(drl, ds, srl + sstart, ss, dins);
+ }
+ if (!drl) {
+ ntfs_log_perror("Merge failed");
+ return drl;
+ }
+ free(srl);
+ if (marker) {
+ ntfs_log_debug("Triggering marker code.\n");
+ for (ds = dend; drl[ds].length; ds++)
+ ;
+ /* We only need to care if @srl ended after @drl. */
+ if (drl[ds].vcn <= marker_vcn) {
+ int slots = 0;
+
+ if (drl[ds].vcn == marker_vcn) {
+ ntfs_log_debug("Old marker = %lli, replacing with "
+ "LCN_ENOENT.\n",
+ (long long)drl[ds].lcn);
+ drl[ds].lcn = (LCN)LCN_ENOENT;
+ goto finished;
+ }
+ /*
+ * We need to create an unmapped runlist element in
+ * @drl or extend an existing one before adding the
+ * ENOENT terminator.
+ */
+ if (drl[ds].lcn == (LCN)LCN_ENOENT) {
+ ds--;
+ slots = 1;
+ }
+ if (drl[ds].lcn != (LCN)LCN_RL_NOT_MAPPED) {
+ /* Add an unmapped runlist element. */
+ if (!slots) {
+ /* FIXME/TODO: We need to have the
+ * extra memory already! (AIA)
+ */
+ drl = ntfs_rl_realloc(drl, ds, ds + 2);
+ if (!drl)
+ goto critical_error;
+ slots = 2;
+ }
+ ds++;
+ /* Need to set vcn if it isn't set already. */
+ if (slots != 1)
+ drl[ds].vcn = drl[ds - 1].vcn +
+ drl[ds - 1].length;
+ drl[ds].lcn = (LCN)LCN_RL_NOT_MAPPED;
+ /* We now used up a slot. */
+ slots--;
+ }
+ drl[ds].length = marker_vcn - drl[ds].vcn;
+ /* Finally add the ENOENT terminator. */
+ ds++;
+ if (!slots) {
+ /* FIXME/TODO: We need to have the extra
+ * memory already! (AIA)
+ */
+ drl = ntfs_rl_realloc(drl, ds, ds + 1);
+ if (!drl)
+ goto critical_error;
+ }
+ drl[ds].vcn = marker_vcn;
+ drl[ds].lcn = (LCN)LCN_ENOENT;
+ drl[ds].length = (s64)0;
+ }
+ }
+ }
+
+finished:
+ /* The merge was completed successfully. */
+ ntfs_log_debug("Merged runlist:\n");
+ ntfs_debug_runlist_dump(drl);
+ return drl;
+
+critical_error:
+ /* Critical error! We cannot afford to fail here. */
+ ntfs_log_perror("libntfs: Critical error");
+ ntfs_log_debug("Forcing segmentation fault!\n");
+ marker_vcn = ((runlist*)NULL)->lcn;
+ return drl;
+}
+
+/**
+ * ntfs_mapping_pairs_decompress - convert mapping pairs array to runlist
+ * @vol: ntfs volume on which the attribute resides
+ * @attr: attribute record whose mapping pairs array to decompress
+ * @old_rl: optional runlist in which to insert @attr's runlist
+ *
+ * Decompress the attribute @attr's mapping pairs array into a runlist. On
+ * success, return the decompressed runlist.
+ *
+ * If @old_rl is not NULL, decompressed runlist is inserted into the
+ * appropriate place in @old_rl and the resultant, combined runlist is
+ * returned. The original @old_rl is deallocated.
+ *
+ * On error, return NULL with errno set to the error code. @old_rl is left
+ * unmodified in that case.
+ *
+ * The following error codes are defined:
+ * ENOMEM Not enough memory to allocate runlist array.
+ * EIO Corrupt runlist.
+ * EINVAL Invalid parameters were passed in.
+ * ERANGE The two runlists overlap.
+ *
+ * FIXME: For now we take the conceptionally simplest approach of creating the
+ * new runlist disregarding the already existing one and then splicing the
+ * two into one, if that is possible (we check for overlap and discard the new
+ * runlist if overlap present before returning NULL, with errno = ERANGE).
+ */
+runlist_element *ntfs_mapping_pairs_decompress(const ntfs_volume *vol,
+ const ATTR_RECORD *attr, runlist_element *old_rl)
+{
+ VCN vcn; /* Current vcn. */
+ LCN lcn; /* Current lcn. */
+ s64 deltaxcn; /* Change in [vl]cn. */
+ runlist_element *rl; /* The output runlist. */
+ const u8 *buf; /* Current position in mapping pairs array. */
+ const u8 *attr_end; /* End of attribute. */
+ int err, rlsize; /* Size of runlist buffer. */
+ u16 rlpos; /* Current runlist position in units of
+ runlist_elements. */
+ u8 b; /* Current byte offset in buf. */
+
+ ntfs_log_trace("Entering for attr 0x%x.\n",
+ (unsigned)le32_to_cpu(attr->type));
+ /* Make sure attr exists and is non-resident. */
+ if (!attr || !attr->non_resident ||
+ sle64_to_cpu(attr->u.nonres.lowest_vcn) < (VCN)0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* Start at vcn = lowest_vcn and lcn 0. */
+ vcn = sle64_to_cpu(attr->u.nonres.lowest_vcn);
+ lcn = 0;
+ /* Get start of the mapping pairs array. */
+ buf = (const u8*)attr + le16_to_cpu(attr->u.nonres.mapping_pairs_offset);
+ attr_end = (const u8*)attr + le32_to_cpu(attr->length);
+ if (buf < (const u8*)attr || buf > attr_end) {
+ ntfs_log_debug("Corrupt attribute.\n");
+ errno = EIO;
+ return NULL;
+ }
+ /* Current position in runlist array. */
+ rlpos = 0;
+ /* Allocate first 4kiB block and set current runlist size to 4kiB. */
+ rlsize = 0x1000;
+ rl = ntfs_malloc(rlsize);
+ if (!rl)
+ return NULL;
+ /* Insert unmapped starting element if necessary. */
+ if (vcn) {
+ rl->vcn = (VCN)0;
+ rl->lcn = (LCN)LCN_RL_NOT_MAPPED;
+ rl->length = vcn;
+ rlpos++;
+ }
+ while (buf < attr_end && *buf) {
+ /*
+ * Allocate more memory if needed, including space for the
+ * not-mapped and terminator elements.
+ */
+ if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) {
+ runlist_element *rl2;
+
+ rlsize += 0x1000;
+ rl2 = realloc(rl, rlsize);
+ if (!rl2) {
+ int eo = errno;
+ free(rl);
+ errno = eo;
+ return NULL;
+ }
+ rl = rl2;
+ }
+ /* Enter the current vcn into the current runlist element. */
+ rl[rlpos].vcn = vcn;
+ /*
+ * Get the change in vcn, i.e. the run length in clusters.
+ * Doing it this way ensures that we signextend negative values.
+ * A negative run length doesn't make any sense, but hey, I
+ * didn't make up the NTFS specs and Windows NT4 treats the run
+ * length as a signed value so that's how it is...
+ */
+ b = *buf & 0xf;
+ if (b) {
+ if (buf + b > attr_end)
+ goto io_error;
+ for (deltaxcn = (s8)buf[b--]; b; b--)
+ deltaxcn = (deltaxcn << 8) + buf[b];
+ } else { /* The length entry is compulsory. */
+ ntfs_log_debug("Missing length entry in mapping pairs "
+ "array.\n");
+ deltaxcn = (s64)-1;
+ }
+ /*
+ * Assume a negative length to indicate data corruption and
+ * hence clean-up and return NULL.
+ */
+ if (deltaxcn < 0) {
+ ntfs_log_debug("Invalid length in mapping pairs array.\n");
+ goto err_out;
+ }
+ /*
+ * Enter the current run length into the current runlist
+ * element.
+ */
+ rl[rlpos].length = deltaxcn;
+ /* Increment the current vcn by the current run length. */
+ vcn += deltaxcn;
+ /*
+ * There might be no lcn change at all, as is the case for
+ * sparse clusters on NTFS 3.0+, in which case we set the lcn
+ * to LCN_HOLE.
+ */
+ if (!(*buf & 0xf0))
+ rl[rlpos].lcn = (LCN)LCN_HOLE;
+ else {
+ /* Get the lcn change which really can be negative. */
+ u8 b2 = *buf & 0xf;
+ b = b2 + ((*buf >> 4) & 0xf);
+ if (buf + b > attr_end)
+ goto io_error;
+ for (deltaxcn = (s8)buf[b--]; b > b2; b--)
+ deltaxcn = (deltaxcn << 8) + buf[b];
+ /* Change the current lcn to it's new value. */
+ lcn += deltaxcn;
+#ifdef DEBUG
+ /*
+ * On NTFS 1.2-, apparently can have lcn == -1 to
+ * indicate a hole. But we haven't verified ourselves
+ * whether it is really the lcn or the deltaxcn that is
+ * -1. So if either is found give us a message so we
+ * can investigate it further!
+ */
+ if (vol->major_ver < 3) {
+ if (deltaxcn == (LCN)-1)
+ ntfs_log_debug("lcn delta == -1\n");
+ if (lcn == (LCN)-1)
+ ntfs_log_debug("lcn == -1\n");
+ }
+#endif
+ /* Check lcn is not below -1. */
+ if (lcn < (LCN)-1) {
+ ntfs_log_debug("Invalid LCN < -1 in mapping pairs "
+ "array.\n");
+ goto err_out;
+ }
+ /* Enter the current lcn into the runlist element. */
+ rl[rlpos].lcn = lcn;
+ }
+ /* Get to the next runlist element. */
+ rlpos++;
+ /* Increment the buffer position to the next mapping pair. */
+ buf += (*buf & 0xf) + ((*buf >> 4) & 0xf) + 1;
+ }
+ if (buf >= attr_end)
+ goto io_error;
+ /*
+ * If there is a highest_vcn specified, it must be equal to the final
+ * vcn in the runlist - 1, or something has gone badly wrong.
+ */
+ deltaxcn = sle64_to_cpu(attr->u.nonres.highest_vcn);
+ if (deltaxcn && vcn - 1 != deltaxcn) {
+mpa_err:
+ ntfs_log_debug("Corrupt mapping pairs array in non-resident "
+ "attribute.\n");
+ goto err_out;
+ }
+ /* Setup not mapped runlist element if this is the base extent. */
+ if (!attr->u.nonres.lowest_vcn) {
+ VCN max_cluster;
+
+ max_cluster = ((sle64_to_cpu(attr->u.nonres.allocated_size) +
+ vol->cluster_size - 1) >>
+ vol->cluster_size_bits) - 1;
+ /*
+ * A highest_vcn of zero means this is a single extent
+ * attribute so simply terminate the runlist with LCN_ENOENT).
+ */
+ if (deltaxcn) {
+ /*
+ * If there is a difference between the highest_vcn and
+ * the highest cluster, the runlist is either corrupt
+ * or, more likely, there are more extents following
+ * this one.
+ */
+ if (deltaxcn < max_cluster) {
+ ntfs_log_debug("More extents to follow; deltaxcn = "
+ "0x%llx, max_cluster = 0x%llx\n",
+ (long long)deltaxcn,
+ (long long)max_cluster);
+ rl[rlpos].vcn = vcn;
+ vcn += rl[rlpos].length = max_cluster - deltaxcn;
+ rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
+ rlpos++;
+ } else if (deltaxcn > max_cluster) {
+ ntfs_log_debug("Corrupt attribute. deltaxcn = "
+ "0x%llx, max_cluster = 0x%llx\n",
+ (long long)deltaxcn,
+ (long long)max_cluster);
+ goto mpa_err;
+ }
+ }
+ rl[rlpos].lcn = (LCN)LCN_ENOENT;
+ } else /* Not the base extent. There may be more extents to follow. */
+ rl[rlpos].lcn = (LCN)LCN_RL_NOT_MAPPED;
+
+ /* Setup terminating runlist element. */
+ rl[rlpos].vcn = vcn;
+ rl[rlpos].length = (s64)0;
+ /* If no existing runlist was specified, we are done. */
+ if (!old_rl) {
+ ntfs_log_debug("Mapping pairs array successfully decompressed:\n");
+ ntfs_debug_runlist_dump(rl);
+ return rl;
+ }
+ /* Now combine the new and old runlists checking for overlaps. */
+ old_rl = ntfs_runlists_merge(old_rl, rl);
+ if (old_rl)
+ return old_rl;
+ err = errno;
+ free(rl);
+ ntfs_log_debug("Failed to merge runlists.\n");
+ errno = err;
+ return NULL;
+io_error:
+ ntfs_log_debug("Corrupt attribute.\n");
+err_out:
+ free(rl);
+ errno = EIO;
+ return NULL;
+}
+
+/**
+ * ntfs_rl_vcn_to_lcn - convert a vcn into a lcn given a runlist
+ * @rl: runlist to use for conversion
+ * @vcn: vcn to convert
+ *
+ * Convert the virtual cluster number @vcn of an attribute into a logical
+ * cluster number (lcn) of a device using the runlist @rl to map vcns to their
+ * corresponding lcns.
+ *
+ * Since lcns must be >= 0, we use negative return values with special meaning:
+ *
+ * Return value Meaning / Description
+ * ==================================================
+ * -1 = LCN_HOLE Hole / not allocated on disk.
+ * -2 = LCN_RL_NOT_MAPPED This is part of the runlist which has not been
+ * inserted into the runlist yet.
+ * -3 = LCN_ENOENT There is no such vcn in the attribute.
+ * -4 = LCN_EINVAL Input parameter error.
+ */
+LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn)
+{
+ int i;
+
+ if (vcn < (VCN)0)
+ return (LCN)LCN_EINVAL;
+ /*
+ * If rl is NULL, assume that we have found an unmapped runlist. The
+ * caller can then attempt to map it and fail appropriately if
+ * necessary.
+ */
+ if (!rl)
+ return (LCN)LCN_RL_NOT_MAPPED;
+
+ /* Catch out of lower bounds vcn. */
+ if (vcn < rl[0].vcn)
+ return (LCN)LCN_ENOENT;
+
+ for (i = 0; rl[i].length; i++) {
+ if (vcn < rl[i+1].vcn) {
+ if (rl[i].lcn >= (LCN)0)
+ return rl[i].lcn + (vcn - rl[i].vcn);
+ return rl[i].lcn;
+ }
+ }
+ /*
+ * The terminator element is setup to the correct value, i.e. one of
+ * LCN_HOLE, LCN_RL_NOT_MAPPED, or LCN_ENOENT.
+ */
+ if (rl[i].lcn < (LCN)0)
+ return rl[i].lcn;
+ /* Just in case... We could replace this with BUG() some day. */
+ return (LCN)LCN_ENOENT;
+}
+
+/**
+ * ntfs_rl_pread - gather read from disk
+ * @vol: ntfs volume to read from
+ * @rl: runlist specifying where to read the data from
+ * @pos: byte position within runlist @rl at which to begin the read
+ * @count: number of bytes to read
+ * @b: data buffer into which to read from disk
+ *
+ * This function will read @count bytes from the volume @vol to the data buffer
+ * @b gathering the data as specified by the runlist @rl. The read begins at
+ * offset @pos into the runlist @rl.
+ *
+ * On success, return the number of successfully read bytes. If this number is
+ * lower than @count this means that the read reached end of file or that an
+ * error was encountered during the read so that the read is partial. 0 means
+ * nothing was read (also return 0 when @count is 0).
+ *
+ * On error and nothing has been read, return -1 with errno set appropriately
+ * to the return code of ntfs_pread(), or to EINVAL in case of invalid
+ * arguments.
+ *
+ * NOTE: If we encounter EOF while reading we return EIO because we assume that
+ * the run list must point to valid locations within the ntfs volume.
+ */
+s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
+ const s64 pos, s64 count, void *b)
+{
+ s64 bytes_read, to_read, ofs, total;
+ int err = EIO;
+
+ if (!vol || !rl || pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return count;
+ /* Seek in @rl to the run containing @pos. */
+ for (ofs = 0; rl->length && (ofs + (rl->length <<
+ vol->cluster_size_bits) <= pos); rl++)
+ ofs += (rl->length << vol->cluster_size_bits);
+ /* Offset in the run at which to begin reading. */
+ ofs = pos - ofs;
+ for (total = 0LL; count; rl++, ofs = 0) {
+ if (!rl->length)
+ goto rl_err_out;
+ if (rl->lcn < (LCN)0) {
+ if (rl->lcn != (LCN)LCN_HOLE)
+ goto rl_err_out;
+ /* It is a hole. Just fill buffer @b with zeroes. */
+ to_read = min(count, (rl->length <<
+ vol->cluster_size_bits) - ofs);
+ memset(b, 0, to_read);
+ /* Update counters and proceed with next run. */
+ total += to_read;
+ count -= to_read;
+ b = (u8*)b + to_read;
+ continue;
+ }
+ /* It is a real lcn, read it from the volume. */
+ to_read = min(count, (rl->length << vol->cluster_size_bits) -
+ ofs);
+retry:
+ bytes_read = ntfs_pread(vol->u.dev, (rl->lcn <<
+ vol->cluster_size_bits) + ofs, to_read, b);
+ /* If everything ok, update progress counters and continue. */
+ if (bytes_read > 0) {
+ total += bytes_read;
+ count -= bytes_read;
+ b = (u8*)b + bytes_read;
+ continue;
+ }
+ /* If the syscall was interrupted, try again. */
+ if (bytes_read == (s64)-1 && errno == EINTR)
+ goto retry;
+ if (bytes_read == (s64)-1)
+ err = errno;
+ goto rl_err_out;
+ }
+ /* Finally, return the number of bytes read. */
+ return total;
+rl_err_out:
+ if (total)
+ return total;
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_rl_pwrite - scatter write to disk
+ * @vol: ntfs volume to write to
+ * @rl: runlist specifying where to write the data to
+ * @pos: byte position within runlist @rl at which to begin the write
+ * @count: number of bytes to write
+ * @b: data buffer to write to disk
+ *
+ * This function will write @count bytes from data buffer @b to the volume @vol
+ * scattering the data as specified by the runlist @rl. The write begins at
+ * offset @pos into the runlist @rl.
+ *
+ * On success, return the number of successfully written bytes. If this number
+ * is lower than @count this means that the write has been interrupted in
+ * flight or that an error was encountered during the write so that the write
+ * is partial. 0 means nothing was written (also return 0 when @count is 0).
+ *
+ * On error and nothing has been written, return -1 with errno set
+ * appropriately to the return code of ntfs_pwrite(), or to to EINVAL in case
+ * of invalid arguments.
+ */
+s64 ntfs_rl_pwrite(const ntfs_volume *vol, const runlist_element *rl,
+ const s64 pos, s64 count, void *b)
+{
+ s64 written, to_write, ofs, total;
+ int err = EIO;
+
+ if (!vol || !rl || pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!count)
+ return count;
+ /* Seek in @rl to the run containing @pos. */
+ for (ofs = 0; rl->length && (ofs + (rl->length <<
+ vol->cluster_size_bits) <= pos); rl++)
+ ofs += (rl->length << vol->cluster_size_bits);
+ /* Offset in the run at which to begin writing. */
+ ofs = pos - ofs;
+ for (total = 0LL; count; rl++, ofs = 0) {
+ if (!rl->length)
+ goto rl_err_out;
+ if (rl->lcn < (LCN)0) {
+ s64 t;
+ int cnt;
+
+ if (rl->lcn != (LCN)LCN_HOLE)
+ goto rl_err_out;
+ /*
+ * It is a hole. Check if the buffer is zero in this
+ * region and if not abort with error.
+ */
+ to_write = min(count, (rl->length <<
+ vol->cluster_size_bits) - ofs);
+ written = to_write / sizeof(unsigned long);
+ for (t = 0; t < written; t++) {
+ if (((unsigned long*)b)[t])
+ goto rl_err_out;
+ }
+ cnt = to_write & (sizeof(unsigned long) - 1);
+ if (cnt) {
+ int i;
+ u8 *b2;
+
+ b2 = (u8*)b + (to_write &
+ ~(sizeof(unsigned long) - 1));
+ for (i = 0; i < cnt; i++) {
+ if (b2[i])
+ goto rl_err_out;
+ }
+ }
+ /*
+ * The buffer region is zero, update progress counters
+ * and proceed with next run.
+ */
+ total += to_write;
+ count -= to_write;
+ b = (u8*)b + to_write;
+ continue;
+ }
+ /* It is a real lcn, write it to the volume. */
+ to_write = min(count, (rl->length << vol->cluster_size_bits) -
+ ofs);
+retry:
+ if (!NVolReadOnly(vol))
+ written = ntfs_pwrite(vol->u.dev, (rl->lcn <<
+ vol->cluster_size_bits) + ofs,
+ to_write, b);
+ else
+ written = to_write;
+ /* If everything ok, update progress counters and continue. */
+ if (written > 0) {
+ total += written;
+ count -= written;
+ b = (u8*)b + written;
+ continue;
+ }
+ /* If the syscall was interrupted, try again. */
+ if (written == (s64)-1 && errno == EINTR)
+ goto retry;
+ if (written == (s64)-1)
+ err = errno;
+ goto rl_err_out;
+ }
+ /* Finally, return the number of bytes written. */
+ return total;
+rl_err_out:
+ if (total)
+ return total;
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_rl_fill_zero - fill given region with zeroes
+ * @vol: ntfs volume to write to
+ * @rl: runlist specifying where to write zeroes to
+ * @pos: byte position within runlist @rl at which to begin the zeroing
+ * @count: number of bytes to fill with zeros
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+int ntfs_rl_fill_zero(const ntfs_volume *vol, const runlist *rl, s64 pos,
+ const s64 count)
+{
+ char *buf;
+ s64 written, size, end = pos + count;
+ int ret = 0;
+
+ ntfs_log_trace("pos %lld, count %lld\n", (long long)pos,
+ (long long)count);
+
+ if (!vol || !rl || pos < 0 || count < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ buf = ntfs_calloc(NTFS_BUF_SIZE);
+ if (!buf)
+ return -1;
+
+ while (pos < end) {
+ size = min(end - pos, NTFS_BUF_SIZE);
+ written = ntfs_rl_pwrite(vol, rl, pos, size, buf);
+ if (written <= 0) {
+ ntfs_log_perror("Failed to zero space");
+ ret = -1;
+ break;
+ }
+ pos += written;
+ }
+ free(buf);
+ return ret;
+}
+
+/**
+ * ntfs_get_nr_significant_bytes - get number of bytes needed to store a number
+ * @n: number for which to get the number of bytes for
+ *
+ * Return the number of bytes required to store @n unambiguously as
+ * a signed number.
+ *
+ * This is used in the context of the mapping pairs array to determine how
+ * many bytes will be needed in the array to store a given logical cluster
+ * number (lcn) or a specific run length.
+ *
+ * Return the number of bytes written. This function cannot fail.
+ */
+int ntfs_get_nr_significant_bytes(const s64 n)
+{
+ s64 l = n;
+ int i;
+ s8 j;
+
+ i = 0;
+ do {
+ l >>= 8;
+ i++;
+ } while (l != 0LL && l != -1LL);
+ j = (n >> 8 * (i - 1)) & 0xff;
+ /* If the sign bit is wrong, we need an extra byte. */
+ if ((n < 0LL && j >= 0) || (n > 0LL && j < 0))
+ i++;
+ return i;
+}
+
+/**
+ * ntfs_get_size_for_mapping_pairs - get bytes needed for mapping pairs array
+ * @vol: ntfs volume (needed for the ntfs version)
+ * @rl: runlist for which to determine the size of the mapping pairs
+ * @start_vcn: vcn at which to start the mapping pairs array
+ *
+ * Walk the runlist @rl and calculate the size in bytes of the mapping pairs
+ * array corresponding to the runlist @rl, starting at vcn @start_vcn. This
+ * for example allows us to allocate a buffer of the right size when building
+ * the mapping pairs array.
+ *
+ * If @rl is NULL, just return 1 (for the single terminator byte).
+ *
+ * Return the calculated size in bytes on success. On error, return -1 with
+ * errno set to the error code. The following error codes are defined:
+ * EINVAL - Run list contains unmapped elements. Make sure to only pass
+ * fully mapped runlists to this function.
+ * - @start_vcn is invalid.
+ * EIO - The runlist is corrupt.
+ */
+int ntfs_get_size_for_mapping_pairs(const ntfs_volume *vol,
+ const runlist_element *rl, const VCN start_vcn)
+{
+ LCN prev_lcn;
+ int rls;
+
+ if (start_vcn < 0) {
+ ntfs_log_trace("start_vcn %lld (should be >= 0)\n",
+ (long long) start_vcn);
+ errno = EINVAL;
+ return -1;
+ }
+ if (!rl) {
+ if (start_vcn) {
+ ntfs_log_trace("rl NULL, start_vcn %lld (should be > 0)\n",
+ (long long) start_vcn);
+ errno = EINVAL;
+ return -1;
+ }
+ return 1;
+ }
+ /* Skip to runlist element containing @start_vcn. */
+ while (rl->length && start_vcn >= rl[1].vcn)
+ rl++;
+ if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn) {
+ errno = EINVAL;
+ return -1;
+ }
+ prev_lcn = 0;
+ /* Always need the terminating zero byte. */
+ rls = 1;
+ /* Do the first partial run if present. */
+ if (start_vcn > rl->vcn) {
+ s64 delta;
+
+ /* We know rl->length != 0 already. */
+ if (rl->length < 0 || rl->lcn < LCN_HOLE)
+ goto err_out;
+ delta = start_vcn - rl->vcn;
+ /* Header byte + length. */
+ rls += 1 + ntfs_get_nr_significant_bytes(rl->length - delta);
+ /*
+ * If the logical cluster number (lcn) denotes a hole and we
+ * are on NTFS 3.0+, we don't store it at all, i.e. we need
+ * zero space. On earlier NTFS versions we just store the lcn.
+ * Note: this assumes that on NTFS 1.2-, holes are stored with
+ * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+ */
+ if (rl->lcn >= 0 || vol->major_ver < 3) {
+ prev_lcn = rl->lcn;
+ if (rl->lcn >= 0)
+ prev_lcn += delta;
+ /* Change in lcn. */
+ rls += ntfs_get_nr_significant_bytes(prev_lcn);
+ }
+ /* Go to next runlist element. */
+ rl++;
+ }
+ /* Do the full runs. */
+ for (; rl->length; rl++) {
+ if (rl->length < 0 || rl->lcn < LCN_HOLE)
+ goto err_out;
+ /* Header byte + length. */
+ rls += 1 + ntfs_get_nr_significant_bytes(rl->length);
+ /*
+ * If the logical cluster number (lcn) denotes a hole and we
+ * are on NTFS 3.0+, we don't store it at all, i.e. we need
+ * zero space. On earlier NTFS versions we just store the lcn.
+ * Note: this assumes that on NTFS 1.2-, holes are stored with
+ * an lcn of -1 and not a delta_lcn of -1 (unless both are -1).
+ */
+ if (rl->lcn >= 0 || vol->major_ver < 3) {
+ /* Change in lcn. */
+ rls += ntfs_get_nr_significant_bytes(rl->lcn -
+ prev_lcn);
+ prev_lcn = rl->lcn;
+ }
+ }
+ return rls;
+err_out:
+ if (rl->lcn == LCN_RL_NOT_MAPPED)
+ errno = EINVAL;
+ else
+ errno = EIO;
+ return -1;
+}
+
+/**
+ * ntfs_write_significant_bytes - write the significant bytes of a number
+ * @dst: destination buffer to write to
+ * @dst_max: pointer to last byte of destination buffer for bounds checking
+ * @n: number whose significant bytes to write
+ *
+ * Store in @dst, the minimum bytes of the number @n which are required to
+ * identify @n unambiguously as a signed number, taking care not to exceed
+ * @dest_max, the maximum position within @dst to which we are allowed to
+ * write.
+ *
+ * This is used when building the mapping pairs array of a runlist to compress
+ * a given logical cluster number (lcn) or a specific run length to the minimum
+ * size possible.
+ *
+ * Return the number of bytes written on success. On error, i.e. the
+ * destination buffer @dst is too small, return -1 with errno set ENOSPC.
+ */
+int ntfs_write_significant_bytes(u8 *dst, const u8 *dst_max, const s64 n)
+{
+ s64 l = n;
+ int i;
+ s8 j;
+
+ i = 0;
+ do {
+ if (dst > dst_max)
+ goto err_out;
+ *dst++ = l & 0xffLL;
+ l >>= 8;
+ i++;
+ } while (l != 0LL && l != -1LL);
+ j = (n >> 8 * (i - 1)) & 0xff;
+ /* If the sign bit is wrong, we need an extra byte. */
+ if (n < 0LL && j >= 0) {
+ if (dst > dst_max)
+ goto err_out;
+ i++;
+ *dst = (u8)-1;
+ } else if (n > 0LL && j < 0) {
+ if (dst > dst_max)
+ goto err_out;
+ i++;
+ *dst = 0;
+ }
+ return i;
+err_out:
+ errno = ENOSPC;
+ return -1;
+}
+
+/**
+ * ntfs_mapping_pairs_build - build the mapping pairs array from a runlist
+ * @vol: ntfs volume (needed for the ntfs version)
+ * @dst: destination buffer to which to write the mapping pairs array
+ * @dst_len: size of destination buffer @dst in bytes
+ * @rl: runlist for which to build the mapping pairs array
+ * @start_vcn: vcn at which to start the mapping pairs array
+ * @stop_vcn: first vcn outside destination buffer on success or ENOSPC error
+ *
+ * Create the mapping pairs array from the runlist @rl, starting at vcn
+ * @start_vcn and save the array in @dst. @dst_len is the size of @dst in
+ * bytes and it should be at least equal to the value obtained by calling
+ * ntfs_get_size_for_mapping_pairs().
+ *
+ * If @rl is NULL, just write a single terminator byte to @dst.
+ *
+ * On success or ENOSPC error, if @stop_vcn is not NULL, *@stop_vcn is set to
+ * the first vcn outside the destination buffer. Note that on error @dst has
+ * been filled with all the mapping pairs that will fit, thus it can be treated
+ * as partial success, in that a new attribute extent needs to be created or the
+ * next extent has to be used and the mapping pairs build has to be continued
+ * with @start_vcn set to *@stop_vcn.
+ *
+ * Return 0 on success. On error, return -1 with errno set to the error code.
+ * The following error codes are defined:
+ * EINVAL - Run list contains unmapped elements. Make sure to only pass
+ * fully mapped runlists to this function.
+ * - @start_vcn is invalid.
+ * EIO - The runlist is corrupt.
+ * ENOSPC - The destination buffer is too small.
+ */
+int ntfs_mapping_pairs_build(const ntfs_volume *vol, u8 *dst,
+ const int dst_len, const runlist_element *rl,
+ const VCN start_vcn, VCN *const stop_vcn)
+{
+ LCN prev_lcn;
+ u8 *dst_max, *dst_next;
+ s8 len_len, lcn_len;
+
+ if (start_vcn < 0)
+ goto val_err;
+ if (!rl) {
+ if (start_vcn)
+ goto val_err;
+ if (stop_vcn)
+ *stop_vcn = 0;
+ if (dst_len < 1) {
+ errno = ENOSPC;
+ return -1;
+ }
+ /* Terminator byte. */
+ *dst = 0;
+ return 0;
+ }
+ /* Skip to runlist element containing @start_vcn. */
+ while (rl->length && start_vcn >= rl[1].vcn)
+ rl++;
+ if ((!rl->length && start_vcn > rl->vcn) || start_vcn < rl->vcn)
+ goto val_err;
+ /*
+ * @dst_max is used for bounds checking in
+ * ntfs_write_significant_bytes().
+ */
+ dst_max = dst + dst_len - 1;
+ prev_lcn = 0;
+ /* Do the first partial run if present. */
+ if (start_vcn > rl->vcn) {
+ s64 delta;
+
+ /* We know rl->length != 0 already. */
+ if (rl->length < 0 || rl->lcn < LCN_HOLE)
+ goto err_out;
+ delta = start_vcn - rl->vcn;
+ /* Write length. */
+ len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+ rl->length - delta);
+ if (len_len < 0)
+ goto size_err;
+ /*
+ * If the logical cluster number (lcn) denotes a hole and we
+ * are on NTFS 3.0+, we don't store it at all, i.e. we need
+ * zero space. On earlier NTFS versions we just write the lcn
+ * change. FIXME: Do we need to write the lcn change or just
+ * the lcn in that case? Not sure as I have never seen this
+ * case on NT4. - We assume that we just need to write the lcn
+ * change until someone tells us otherwise... (AIA)
+ */
+ if (rl->lcn >= 0 || vol->major_ver < 3) {
+ prev_lcn = rl->lcn;
+ if (rl->lcn >= 0)
+ prev_lcn += delta;
+ /* Write change in lcn. */
+ lcn_len = ntfs_write_significant_bytes(dst + 1 +
+ len_len, dst_max, prev_lcn);
+ if (lcn_len < 0)
+ goto size_err;
+ } else
+ lcn_len = 0;
+ dst_next = dst + len_len + lcn_len + 1;
+ if (dst_next > dst_max)
+ goto size_err;
+ /* Update header byte. */
+ *dst = lcn_len << 4 | len_len;
+ /* Position at next mapping pairs array element. */
+ dst = dst_next;
+ /* Go to next runlist element. */
+ rl++;
+ }
+ /* Do the full runs. */
+ for (; rl->length; rl++) {
+ if (rl->length < 0 || rl->lcn < LCN_HOLE)
+ goto err_out;
+ /* Write length. */
+ len_len = ntfs_write_significant_bytes(dst + 1, dst_max,
+ rl->length);
+ if (len_len < 0)
+ goto size_err;
+ /*
+ * If the logical cluster number (lcn) denotes a hole and we
+ * are on NTFS 3.0+, we don't store it at all, i.e. we need
+ * zero space. On earlier NTFS versions we just write the lcn
+ * change. FIXME: Do we need to write the lcn change or just
+ * the lcn in that case? Not sure as I have never seen this
+ * case on NT4. - We assume that we just need to write the lcn
+ * change until someone tells us otherwise... (AIA)
+ */
+ if (rl->lcn >= 0 || vol->major_ver < 3) {
+ /* Write change in lcn. */
+ lcn_len = ntfs_write_significant_bytes(dst + 1 +
+ len_len, dst_max, rl->lcn - prev_lcn);
+ if (lcn_len < 0)
+ goto size_err;
+ prev_lcn = rl->lcn;
+ } else
+ lcn_len = 0;
+ dst_next = dst + len_len + lcn_len + 1;
+ if (dst_next > dst_max)
+ goto size_err;
+ /* Update header byte. */
+ *dst = lcn_len << 4 | len_len;
+ /* Position at next mapping pairs array element. */
+ dst += 1 + len_len + lcn_len;
+ }
+ /* Set stop vcn. */
+ if (stop_vcn)
+ *stop_vcn = rl->vcn;
+ /* Add terminator byte. */
+ *dst = 0;
+ return 0;
+size_err:
+ /* Set stop vcn. */
+ if (stop_vcn)
+ *stop_vcn = rl->vcn;
+ /* Add terminator byte. */
+ *dst = 0;
+ errno = ENOSPC;
+ return -1;
+val_err:
+ errno = EINVAL;
+ return -1;
+err_out:
+ if (rl->lcn == LCN_RL_NOT_MAPPED)
+ errno = EINVAL;
+ else
+ errno = EIO;
+ return -1;
+}
+
+/**
+ * ntfs_rl_truncate - truncate a runlist starting at a specified vcn
+ * @arl: address of runlist to truncate
+ * @start_vcn: first vcn which should be cut off
+ *
+ * Truncate the runlist *@arl starting at vcn @start_vcn as well as the memory
+ * buffer holding the runlist.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ *
+ * NOTE: @arl is the address of the runlist. We need the address so we can
+ * modify the pointer to the runlist with the new, reallocated memory buffer.
+ */
+int ntfs_rl_truncate(runlist **arl, const VCN start_vcn)
+{
+ runlist *rl;
+
+ if (!arl || !*arl) {
+ errno = EINVAL;
+ ntfs_log_perror("rl_truncate error: arl: %p *arl: %p", arl, *arl);
+ return -1;
+ }
+
+ rl = *arl;
+
+ if (start_vcn < rl->vcn) {
+ errno = EINVAL;
+ ntfs_log_perror("Start_vcn lies outside front of runlist");
+ return -1;
+ }
+
+ /* Find the starting vcn in the run list. */
+ while (rl->length) {
+ if (start_vcn < rl[1].vcn)
+ break;
+ rl++;
+ }
+
+ if (!rl->length) {
+ errno = EIO;
+ ntfs_log_trace("Truncating already truncated runlist?\n");
+ return -1;
+ }
+
+ /* Truncate the run. */
+ rl->length = start_vcn - rl->vcn;
+
+ /*
+ * If a run was partially truncated, make the following runlist
+ * element a terminator instead of the truncated runlist
+ * element itself.
+ */
+ if (rl->length) {
+ ++rl;
+ rl->vcn = start_vcn;
+ rl->length = 0;
+ }
+ rl->lcn = (LCN)LCN_ENOENT;
+ return 0;
+}
+
+/**
+ * ntfs_rl_sparse - check whether runlist have sparse regions or not.
+ * @rl: runlist to check
+ *
+ * This function just skips not mapped regions assuming they are not sparse,
+ * so you need to ensure that runlist is fully mapped if you want perform full
+ * check.
+ *
+ * Return 1 if have, 0 if not, -1 on error with errno set to the error code.
+ */
+int ntfs_rl_sparse(runlist *rl)
+{
+ runlist *rlc;
+
+ if (!rl) {
+ ntfs_log_trace("Invalid argument passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (rlc = rl; rlc->length; rlc++) {
+ if (rlc->lcn < 0) {
+ if (rlc->lcn == LCN_RL_NOT_MAPPED)
+ continue;
+ if (rlc->lcn != LCN_HOLE) {
+ ntfs_log_trace("Bad runlist.\n");
+ errno = EIO;
+ return -1;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ntfs_rl_get_compressed_size - calculate length of non sparse regions
+ * @vol: ntfs volume (need for cluster size)
+ * @rl: runlist to calculate for
+ *
+ * Return compressed size or -1 on error with errno set to the error code.
+ */
+s64 ntfs_rl_get_compressed_size(ntfs_volume *vol, runlist *rl)
+{
+ runlist *rlc;
+ s64 ret = 0;
+
+ if (!rl) {
+ ntfs_log_trace("Invalid argument passed.\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (rlc = rl; rlc->length; rlc++) {
+ if (rlc->lcn < 0) {
+ if (rlc->lcn != LCN_HOLE) {
+ ntfs_log_trace("Received unmapped runlist.\n");
+ errno = EINVAL;
+ return -1;
+ }
+ } else
+ ret += rlc->length;
+ }
+ return ret << vol->cluster_size_bits;
+}
+
+
+#ifdef NTFS_TEST
+/**
+ * test_rl_helper
+ */
+#define MKRL(R,V,L,S) \
+ (R)->vcn = V; \
+ (R)->lcn = L; \
+ (R)->length = S;
+/*
+}
+*/
+/**
+ * test_rl_dump_runlist - Runlist test: Display the contents of a runlist
+ * @rl:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_dump_runlist(const runlist_element *rl)
+{
+ int abbr = 0; /* abbreviate long lists */
+ int len = 0;
+ int i;
+ const char *lcn_str[5] = { "HOLE", "NOTMAP", "ENOENT", "XXXX" };
+
+ if (!rl) {
+ printf(" Run list not present.\n");
+ return;
+ }
+
+ if (abbr)
+ for (len = 0; rl[len].length; len++) ;
+
+ printf(" VCN LCN len\n");
+ for (i = 0; ; i++, rl++) {
+ LCN lcn = rl->lcn;
+
+ if ((abbr) && (len > 20)) {
+ if (i == 4)
+ printf(" ...\n");
+ if ((i > 3) && (i < (len - 3)))
+ continue;
+ }
+
+ if (lcn < (LCN)0) {
+ int ind = -lcn - 1;
+
+ if (ind > -LCN_ENOENT - 1)
+ ind = 3;
+ printf("%8lld %8s %8lld\n",
+ rl->vcn, lcn_str[ind], rl->length);
+ } else
+ printf("%8lld %8lld %8lld\n",
+ rl->vcn, rl->lcn, rl->length);
+ if (!rl->length)
+ break;
+ }
+ if ((abbr) && (len > 20))
+ printf(" (%d entries)\n", len+1);
+ printf("\n");
+}
+
+/**
+ * test_rl_runlists_merge - Runlist test: Merge two runlists
+ * @drl:
+ * @srl:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static runlist_element * test_rl_runlists_merge(runlist_element *drl, runlist_element *srl)
+{
+ runlist_element *res = NULL;
+
+ printf("dst:\n");
+ test_rl_dump_runlist(drl);
+ printf("src:\n");
+ test_rl_dump_runlist(srl);
+
+ res = ntfs_runlists_merge(drl, srl);
+
+ printf("res:\n");
+ test_rl_dump_runlist(res);
+
+ return res;
+}
+
+/**
+ * test_rl_read_buffer - Runlist test: Read a file containing a runlist
+ * @file:
+ * @buf:
+ * @bufsize:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int test_rl_read_buffer(const char *file, u8 *buf, int bufsize)
+{
+ FILE *fptr;
+
+ fptr = fopen(file, "r");
+ if (!fptr) {
+ printf("open %s\n", file);
+ return 0;
+ }
+
+ if (fread(buf, bufsize, 1, fptr) == 99) {
+ printf("read %s\n", file);
+ return 0;
+ }
+
+ fclose(fptr);
+ return 1;
+}
+
+/**
+ * test_rl_pure_src - Runlist test: Complicate the simple tests a little
+ * @contig:
+ * @multi:
+ * @vcn:
+ * @len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static runlist_element * test_rl_pure_src(BOOL contig, BOOL multi, int vcn, int len)
+{
+ runlist_element *result;
+ int fudge;
+
+ if (contig)
+ fudge = 0;
+ else
+ fudge = 999;
+
+ result = ntfs_malloc(4096);
+ if (!result)
+ return NULL;
+
+ if (multi) {
+ MKRL(result+0, vcn + (0*len/4), fudge + vcn + 1000 + (0*len/4), len / 4)
+ MKRL(result+1, vcn + (1*len/4), fudge + vcn + 1000 + (1*len/4), len / 4)
+ MKRL(result+2, vcn + (2*len/4), fudge + vcn + 1000 + (2*len/4), len / 4)
+ MKRL(result+3, vcn + (3*len/4), fudge + vcn + 1000 + (3*len/4), len / 4)
+ MKRL(result+4, vcn + (4*len/4), LCN_RL_NOT_MAPPED, 0)
+ } else {
+ MKRL(result+0, vcn, fudge + vcn + 1000, len)
+ MKRL(result+1, vcn + len, LCN_RL_NOT_MAPPED, 0)
+ }
+ return result;
+}
+
+/**
+ * test_rl_pure_test - Runlist test: Perform tests using simple runlists
+ * @test:
+ * @contig:
+ * @multi:
+ * @vcn:
+ * @len:
+ * @file:
+ * @size:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_pure_test(int test, BOOL contig, BOOL multi, int vcn, int len, runlist_element *file, int size)
+{
+ runlist_element *src;
+ runlist_element *dst;
+ runlist_element *res;
+
+ src = test_rl_pure_src(contig, multi, vcn, len);
+ dst = malloc(4096);
+
+ memcpy(dst, file, size);
+
+ printf("Test %2d ----------\n", test);
+ res = test_rl_runlists_merge(dst, src);
+
+ free(res);
+}
+
+/**
+ * test_rl_pure - Runlist test: Create tests using simple runlists
+ * @contig:
+ * @multi:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_pure(char *contig, char *multi)
+{
+ /* VCN, LCN, len */
+ static runlist_element file1[] = {
+ { 0, -1, 100 }, /* HOLE */
+ { 100, 1100, 100 }, /* DATA */
+ { 200, -1, 100 }, /* HOLE */
+ { 300, 1300, 100 }, /* DATA */
+ { 400, -1, 100 }, /* HOLE */
+ { 500, -3, 0 } /* NOENT */
+ };
+ static runlist_element file2[] = {
+ { 0, 1000, 100 }, /* DATA */
+ { 100, -1, 100 }, /* HOLE */
+ { 200, -3, 0 } /* NOENT */
+ };
+ static runlist_element file3[] = {
+ { 0, 1000, 100 }, /* DATA */
+ { 100, -3, 0 } /* NOENT */
+ };
+ static runlist_element file4[] = {
+ { 0, -3, 0 } /* NOENT */
+ };
+ static runlist_element file5[] = {
+ { 0, -2, 100 }, /* NOTMAP */
+ { 100, 1100, 100 }, /* DATA */
+ { 200, -2, 100 }, /* NOTMAP */
+ { 300, 1300, 100 }, /* DATA */
+ { 400, -2, 100 }, /* NOTMAP */
+ { 500, -3, 0 } /* NOENT */
+ };
+ static runlist_element file6[] = {
+ { 0, 1000, 100 }, /* DATA */
+ { 100, -2, 100 }, /* NOTMAP */
+ { 200, -3, 0 } /* NOENT */
+ };
+ BOOL c, m;
+
+ if (strcmp(contig, "contig") == 0)
+ c = TRUE;
+ else if (strcmp(contig, "noncontig") == 0)
+ c = FALSE;
+ else {
+ printf("rl pure [contig|noncontig] [single|multi]\n");
+ return;
+ }
+ if (strcmp(multi, "multi") == 0)
+ m = TRUE;
+ else if (strcmp(multi, "single") == 0)
+ m = FALSE;
+ else {
+ printf("rl pure [contig|noncontig] [single|multi]\n");
+ return;
+ }
+
+ test_rl_pure_test(1, c, m, 0, 40, file1, sizeof(file1));
+ test_rl_pure_test(2, c, m, 40, 40, file1, sizeof(file1));
+ test_rl_pure_test(3, c, m, 60, 40, file1, sizeof(file1));
+ test_rl_pure_test(4, c, m, 0, 100, file1, sizeof(file1));
+ test_rl_pure_test(5, c, m, 200, 40, file1, sizeof(file1));
+ test_rl_pure_test(6, c, m, 240, 40, file1, sizeof(file1));
+ test_rl_pure_test(7, c, m, 260, 40, file1, sizeof(file1));
+ test_rl_pure_test(8, c, m, 200, 100, file1, sizeof(file1));
+ test_rl_pure_test(9, c, m, 400, 40, file1, sizeof(file1));
+ test_rl_pure_test(10, c, m, 440, 40, file1, sizeof(file1));
+ test_rl_pure_test(11, c, m, 460, 40, file1, sizeof(file1));
+ test_rl_pure_test(12, c, m, 400, 100, file1, sizeof(file1));
+ test_rl_pure_test(13, c, m, 160, 100, file2, sizeof(file2));
+ test_rl_pure_test(14, c, m, 100, 140, file2, sizeof(file2));
+ test_rl_pure_test(15, c, m, 200, 40, file2, sizeof(file2));
+ test_rl_pure_test(16, c, m, 240, 40, file2, sizeof(file2));
+ test_rl_pure_test(17, c, m, 100, 40, file3, sizeof(file3));
+ test_rl_pure_test(18, c, m, 140, 40, file3, sizeof(file3));
+ test_rl_pure_test(19, c, m, 0, 40, file4, sizeof(file4));
+ test_rl_pure_test(20, c, m, 40, 40, file4, sizeof(file4));
+ test_rl_pure_test(21, c, m, 0, 40, file5, sizeof(file5));
+ test_rl_pure_test(22, c, m, 40, 40, file5, sizeof(file5));
+ test_rl_pure_test(23, c, m, 60, 40, file5, sizeof(file5));
+ test_rl_pure_test(24, c, m, 0, 100, file5, sizeof(file5));
+ test_rl_pure_test(25, c, m, 200, 40, file5, sizeof(file5));
+ test_rl_pure_test(26, c, m, 240, 40, file5, sizeof(file5));
+ test_rl_pure_test(27, c, m, 260, 40, file5, sizeof(file5));
+ test_rl_pure_test(28, c, m, 200, 100, file5, sizeof(file5));
+ test_rl_pure_test(29, c, m, 400, 40, file5, sizeof(file5));
+ test_rl_pure_test(30, c, m, 440, 40, file5, sizeof(file5));
+ test_rl_pure_test(31, c, m, 460, 40, file5, sizeof(file5));
+ test_rl_pure_test(32, c, m, 400, 100, file5, sizeof(file5));
+ test_rl_pure_test(33, c, m, 160, 100, file6, sizeof(file6));
+ test_rl_pure_test(34, c, m, 100, 140, file6, sizeof(file6));
+}
+
+/**
+ * test_rl_zero - Runlist test: Merge a zero-length runlist
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_zero(void)
+{
+ runlist_element *jim = NULL;
+ runlist_element *bob = NULL;
+
+ bob = calloc(3, sizeof(runlist_element));
+ if (!bob)
+ return;
+
+ MKRL(bob+0, 10, 99, 5)
+ MKRL(bob+1, 15, LCN_RL_NOT_MAPPED, 0)
+
+ jim = test_rl_runlists_merge(jim, bob);
+ if (!jim)
+ return;
+
+ free(jim);
+}
+
+/**
+ * test_rl_frag_combine - Runlist test: Perform tests using fragmented files
+ * @vol:
+ * @attr1:
+ * @attr2:
+ * @attr3:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_frag_combine(ntfs_volume *vol, ATTR_RECORD *attr1, ATTR_RECORD *attr2, ATTR_RECORD *attr3)
+{
+ runlist_element *run1;
+ runlist_element *run2;
+ runlist_element *run3;
+
+ run1 = ntfs_mapping_pairs_decompress(vol, attr1, NULL);
+ if (!run1)
+ return;
+
+ run2 = ntfs_mapping_pairs_decompress(vol, attr2, NULL);
+ if (!run2)
+ return;
+
+ run1 = test_rl_runlists_merge(run1, run2);
+
+ run3 = ntfs_mapping_pairs_decompress(vol, attr3, NULL);
+ if (!run3)
+ return;
+
+ run1 = test_rl_runlists_merge(run1, run3);
+
+ free(run1);
+}
+
+/**
+ * test_rl_frag - Runlist test: Create tests using very fragmented files
+ * @test:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void test_rl_frag(char *test)
+{
+ ntfs_volume vol;
+ ATTR_RECORD *attr1 = ntfs_malloc(1024);
+ ATTR_RECORD *attr2 = ntfs_malloc(1024);
+ ATTR_RECORD *attr3 = ntfs_malloc(1024);
+
+ if (!attr1 || !attr2 || !attr3)
+ goto out;
+
+ vol.sb = NULL;
+ vol.sector_size_bits = 9;
+ vol.cluster_size = 2048;
+ vol.cluster_size_bits = 11;
+ vol.major_ver = 3;
+
+ if (!test_rl_read_buffer("runlist-data/attr1.bin", (u8*) attr1, 1024))
+ goto out;
+ if (!test_rl_read_buffer("runlist-data/attr2.bin", (u8*) attr2, 1024))
+ goto out;
+ if (!test_rl_read_buffer("runlist-data/attr3.bin", (u8*) attr3, 1024))
+ goto out;
+
+ if (strcmp(test, "123") == 0) test_rl_frag_combine(&vol, attr1, attr2, attr3);
+ else if (strcmp(test, "132") == 0) test_rl_frag_combine(&vol, attr1, attr3, attr2);
+ else if (strcmp(test, "213") == 0) test_rl_frag_combine(&vol, attr2, attr1, attr3);
+ else if (strcmp(test, "231") == 0) test_rl_frag_combine(&vol, attr2, attr3, attr1);
+ else if (strcmp(test, "312") == 0) test_rl_frag_combine(&vol, attr3, attr1, attr2);
+ else if (strcmp(test, "321") == 0) test_rl_frag_combine(&vol, attr3, attr2, attr1);
+ else
+ printf("Frag: No such test '%s'\n", test);
+
+out:
+ free(attr1);
+ free(attr2);
+ free(attr3);
+}
+
+/**
+ * test_rl_main - Runlist test: Program start (main)
+ * @argc:
+ * @argv:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+int test_rl_main(int argc, char *argv[])
+{
+ if ((argc == 2) && (strcmp(argv[1], "zero") == 0)) test_rl_zero();
+ else if ((argc == 3) && (strcmp(argv[1], "frag") == 0)) test_rl_frag(argv[2]);
+ else if ((argc == 4) && (strcmp(argv[1], "pure") == 0)) test_rl_pure(argv[2], argv[3]);
+ else
+ printf("rl [zero|frag|pure] {args}\n");
+
+ return 0;
+}
+
+#endif
+
diff --git a/usr/src/lib/libntfs/common/libntfs/security.c b/usr/src/lib/libntfs/common/libntfs/security.c
new file mode 100644
index 0000000000..9b92f6b19b
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/security.c
@@ -0,0 +1,271 @@
+/**
+ * security.c - Handling security/ACLs in NTFS. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2004 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "types.h"
+#include "layout.h"
+#include "security.h"
+
+/*
+ * The zero GUID.
+ */
+static const GUID __zero_guid = { { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } } };
+const GUID *const zero_guid = &__zero_guid;
+
+/**
+ * ntfs_guid_is_zero - check if a GUID is zero
+ * @guid: [IN] guid to check
+ *
+ * Return TRUE if @guid is a valid pointer to a GUID and it is the zero GUID
+ * and FALSE otherwise.
+ */
+BOOL ntfs_guid_is_zero(const GUID *guid)
+{
+ return (memcmp(guid, zero_guid, sizeof(*zero_guid)));
+}
+
+/**
+ * ntfs_guid_to_mbs - convert a GUID to a multi byte string
+ * @guid: [IN] guid to convert
+ * @guid_str: [OUT] string in which to return the GUID (optional)
+ *
+ * Convert the GUID pointed to by @guid to a multi byte string of the form
+ * "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX". Therefore, @guid_str (if not NULL)
+ * needs to be able to store at least 37 bytes.
+ *
+ * If @guid_str is not NULL it will contain the converted GUID on return. If
+ * it is NULL a string will be allocated and this will be returned. The caller
+ * is responsible for free()ing the string in that case.
+ *
+ * On success return the converted string and on failure return NULL with errno
+ * set to the error code.
+ */
+char *ntfs_guid_to_mbs(const GUID *guid, char *guid_str)
+{
+ char *_guid_str;
+ int res;
+
+ if (!guid) {
+ errno = EINVAL;
+ return NULL;
+ }
+ _guid_str = guid_str;
+ if (!_guid_str) {
+ _guid_str = ntfs_malloc(37);
+ if (!_guid_str)
+ return _guid_str;
+ }
+ res = snprintf(_guid_str, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x-%02x%02x%02x%02x%02x%02x", guid->raw[0],
+ guid->raw[1], guid->raw[2], guid->raw[3], guid->raw[4],
+ guid->raw[5], guid->raw[6], guid->raw[7], guid->raw[8],
+ guid->raw[9], guid->raw[10], guid->raw[11],
+ guid->raw[12], guid->raw[13], guid->raw[14],
+ guid->raw[15]);
+ if (res == 36)
+ return _guid_str;
+ if (!guid_str)
+ free(_guid_str);
+ errno = EINVAL;
+ return NULL;
+}
+
+/**
+ * ntfs_sid_to_mbs_size - determine maximum size for the string of a SID
+ * @sid: [IN] SID for which to determine the maximum string size
+ *
+ * Determine the maximum multi byte string size in bytes which is needed to
+ * store the standard textual representation of the SID pointed to by @sid.
+ * See ntfs_sid_to_mbs(), below.
+ *
+ * On success return the maximum number of bytes needed to store the multi byte
+ * string and on failure return -1 with errno set to the error code.
+ */
+int ntfs_sid_to_mbs_size(const SID *sid)
+{
+ int size, i;
+
+ if (!ntfs_sid_is_valid(sid)) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Start with "S-". */
+ size = 2;
+ /*
+ * Add the SID_REVISION. Hopefully the compiler will optimize this
+ * away as SID_REVISION is a constant.
+ */
+ for (i = SID_REVISION; i > 0; i /= 10)
+ size++;
+ /* Add the "-". */
+ size++;
+ /*
+ * Add the identifier authority. If it needs to be in decimal, the
+ * maximum is 2^32-1 = 4294967295 = 10 characters. If it needs to be
+ * in hexadecimal, then maximum is 0x665544332211 = 14 characters.
+ */
+ if (!sid->identifier_authority.s.high_part)
+ size += 10;
+ else
+ size += 14;
+ /*
+ * Finally, add the sub authorities. For each we have a "-" followed
+ * by a decimal which can be up to 2^32-1 = 4294967295 = 10 characters.
+ */
+ size += (1 + 10) * sid->sub_authority_count;
+ /* We need the zero byte at the end, too. */
+ size++;
+ return size * sizeof(char);
+}
+
+/**
+ * ntfs_sid_to_mbs - convert a SID to a multi byte string
+ * @sid: [IN] SID to convert
+ * @sid_str: [OUT] string in which to return the SID (optional)
+ * @sid_str_size: [IN] size in bytes of @sid_str
+ *
+ * Convert the SID pointed to by @sid to its standard textual representation.
+ * @sid_str (if not NULL) needs to be able to store at least
+ * ntfs_sid_to_mbs_size() bytes. @sid_str_size is the size in bytes of
+ * @sid_str if @sid_str is not NULL.
+ *
+ * The standard textual representation of the SID is of the form:
+ * S-R-I-S-S...
+ * Where:
+ * - The first "S" is the literal character 'S' identifying the following
+ * digits as a SID.
+ * - R is the revision level of the SID expressed as a sequence of digits
+ * in decimal.
+ * - I is the 48-bit identifier_authority, expressed as digits in decimal,
+ * if I < 2^32, or hexadecimal prefixed by "0x", if I >= 2^32.
+ * - S... is one or more sub_authority values, expressed as digits in
+ * decimal.
+ *
+ * If @sid_str is not NULL it will contain the converted SUID on return. If it
+ * is NULL a string will be allocated and this will be returned. The caller is
+ * responsible for free()ing the string in that case.
+ *
+ * On success return the converted string and on failure return NULL with errno
+ * set to the error code.
+ */
+char *ntfs_sid_to_mbs(const SID *sid, char *sid_str, size_t sid_str_size)
+{
+ u64 u;
+ char *s;
+ int i, j, cnt;
+
+ /*
+ * No need to check @sid if !@sid_str since ntfs_sid_to_mbs_size() will
+ * check @sid, too. 8 is the minimum SID string size.
+ */
+ if (sid_str && (sid_str_size < 8 || !ntfs_sid_is_valid(sid))) {
+ errno = EINVAL;
+ return NULL;
+ }
+ /* Allocate string if not provided. */
+ if (!sid_str) {
+ cnt = ntfs_sid_to_mbs_size(sid);
+ if (cnt < 0)
+ return NULL;
+ s = ntfs_malloc(cnt);
+ if (!s)
+ return s;
+ sid_str = s;
+ /* So we know we allocated it. */
+ sid_str_size = 0;
+ } else {
+ s = sid_str;
+ cnt = sid_str_size;
+ }
+ /* Start with "S-R-". */
+ i = snprintf(s, cnt, "S-%hhu-", (unsigned char)sid->revision);
+ if (i < 0 || i >= cnt)
+ goto err_out;
+ s += i;
+ cnt -= i;
+ /* Add the identifier authority. */
+ for (u = i = 0, j = 40; i < 6; i++, j -= 8)
+ u += (u64)sid->identifier_authority.value[i] << j;
+ if (!sid->identifier_authority.s.high_part)
+ i = snprintf(s, cnt, "%lu", (unsigned long)u);
+ else
+ i = snprintf(s, cnt, "0x%llx", (unsigned long long)u);
+ if (i < 0 || i >= cnt)
+ goto err_out;
+ s += i;
+ cnt -= i;
+ /* Finally, add the sub authorities. */
+ for (j = 0; j < sid->sub_authority_count; j++) {
+ i = snprintf(s, cnt, "-%u", (unsigned int)
+ le32_to_cpu(sid->sub_authority[j]));
+ if (i < 0 || i >= cnt)
+ goto err_out;
+ s += i;
+ cnt -= i;
+ }
+ return sid_str;
+err_out:
+ if (i >= cnt)
+ i = EMSGSIZE;
+ else
+ i = errno;
+ if (!sid_str_size)
+ free(sid_str);
+ errno = i;
+ return NULL;
+}
+
+/**
+ * ntfs_generate_guid - generatates a random current guid.
+ * @guid: [OUT] pointer to a GUID struct to hold the generated guid.
+ *
+ * perhaps not a very good random number generator though...
+ */
+void ntfs_generate_guid(GUID *guid)
+{
+ unsigned int i;
+ u8 *p = (u8 *)guid;
+
+ for (i = 0; i < sizeof(GUID); i++) {
+ p[i] = (u8)(random() & 0xFF);
+ if (i == 7)
+ p[7] = (p[7] & 0x0F) | 0x40;
+ if (i == 8)
+ p[8] = (p[8] & 0x3F) | 0x80;
+ }
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/unistr.c b/usr/src/lib/libntfs/common/libntfs/unistr.c
new file mode 100644
index 0000000000..6e6aa9b8df
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/unistr.c
@@ -0,0 +1,775 @@
+/**
+ * unistr.c - Unicode string handling. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 Anton Altaparmakov
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "attrib.h"
+#include "endians.h"
+#include "types.h"
+#include "unistr.h"
+#include "debug.h"
+#include "logging.h"
+
+/*
+ * IMPORTANT
+ * =========
+ *
+ * All these routines assume that the Unicode characters are in little endian
+ * encoding inside the strings!!!
+ */
+
+/*
+ * This is used by the name collation functions to quickly determine what
+ * characters are (in)valid.
+ */
+#if 0
+static 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,
+};
+#endif
+
+/**
+ * ntfs_names_are_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 perform a case insensitive comparison.
+ */
+BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len,
+ const ntfschar *s2, size_t s2_len,
+ const IGNORE_CASE_BOOL ic,
+ const ntfschar *upcase, const u32 upcase_size)
+{
+ if (s1_len != s2_len)
+ return FALSE;
+ if (!s1_len)
+ return TRUE;
+ if (ic == CASE_SENSITIVE)
+ return ntfs_ucsncmp(s1, s2, s1_len) ? FALSE: TRUE;
+ return ntfs_ucsncasecmp(s1, s2, s1_len, upcase, upcase_size) ? FALSE:
+ TRUE;
+}
+
+/**
+ * ntfs_names_collate - collate two Unicode names
+ * @name1: first Unicode name to compare
+ * @name1_len: length of first Unicode name to compare
+ * @name2: second Unicode name to compare
+ * @name2_len: length of second Unicode name to compare
+ * @err_val: if @name1 contains an invalid character return this value
+ * @ic: either CASE_SENSITIVE or IGNORE_CASE
+ * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE)
+ * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE)
+ *
+ * ntfs_names_collate() 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
+ * @err_val if an invalid character is found in @name1 during the comparison.
+ *
+ * The following characters are considered invalid: '"', '*', '<', '>' and '?'.
+ */
+int ntfs_names_collate(const ntfschar *name1, const u32 name1_len,
+ const ntfschar *name2, const u32 name2_len,
+ const int err_val __attribute__((unused)),
+ const IGNORE_CASE_BOOL ic, const ntfschar *upcase,
+ const u32 upcase_len)
+{
+ u32 cnt;
+ u16 c1, c2;
+
+#ifdef DEBUG
+ if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) {
+ ntfs_log_debug("ntfs_names_collate received NULL pointer!\n");
+ exit(1);
+ }
+#endif
+ for (cnt = 0; cnt < min(name1_len, name2_len); ++cnt) {
+ c1 = le16_to_cpu(*name1);
+ name1++;
+ c2 = le16_to_cpu(*name2);
+ name2++;
+ if (ic) {
+ if (c1 < upcase_len)
+ c1 = le16_to_cpu(upcase[c1]);
+ if (c2 < upcase_len)
+ c2 = le16_to_cpu(upcase[c2]);
+ }
+#if 0
+ if (c1 < 64 && legal_ansi_char_array[c1] & 8)
+ return err_val;
+#endif
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ }
+ if (name1_len < name2_len)
+ return -1;
+ if (name1_len == name2_len)
+ return 0;
+ /* name1_len > name2_len */
+#if 0
+ c1 = le16_to_cpu(*name1);
+ if (c1 < 64 && legal_ansi_char_array[c1] & 8)
+ return err_val;
+#endif
+ return 1;
+}
+
+/**
+ * ntfs_ucsncmp - compare two little endian Unicode strings
+ * @s1: first string
+ * @s2: second string
+ * @n: maximum unicode characters to compare
+ *
+ * Compare the first @n characters of the Unicode strings @s1 and @s2,
+ * The strings in little endian format and appropriate le16_to_cpu()
+ * conversion is performed on non-little endian machines.
+ *
+ * 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_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n)
+{
+ u16 c1, c2;
+ size_t i;
+
+#ifdef DEBUG
+ if (!s1 || !s2) {
+ ntfs_log_debug("ntfs_wcsncmp() received NULL pointer!\n");
+ exit(1);
+ }
+#endif
+ for (i = 0; i < n; ++i) {
+ c1 = le16_to_cpu(s1[i]);
+ c2 = le16_to_cpu(s2[i]);
+ if (c1 < c2)
+ return -1;
+ if (c1 > c2)
+ return 1;
+ if (!c1)
+ break;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_ucsncasecmp - 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_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n,
+ const ntfschar *upcase, const u32 upcase_size)
+{
+ u16 c1, c2;
+ size_t i;
+
+#ifdef DEBUG
+ if (!s1 || !s2 || !upcase) {
+ ntfs_log_debug("ntfs_wcsncasecmp() received NULL pointer!\n");
+ exit(1);
+ }
+#endif
+ 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;
+}
+
+/**
+ * ntfs_ucsnlen - determine the length of a little endian Unicode string
+ * @s: pointer to Unicode string
+ * @maxlen: maximum length of string @s
+ *
+ * Return the number of Unicode characters in the little endian Unicode
+ * string @s up to a maximum of maxlen Unicode characters, not including
+ * the terminating (ntfschar)'\0'. If there is no (ntfschar)'\0' between @s
+ * and @s + @maxlen, @maxlen is returned.
+ *
+ * This function never looks beyond @s + @maxlen.
+ */
+u32 ntfs_ucsnlen(const ntfschar *s, u32 maxlen)
+{
+ u32 i;
+
+ for (i = 0; i < maxlen; i++) {
+ if (!le16_to_cpu(s[i]))
+ break;
+ }
+ return i;
+}
+
+/**
+ * ntfs_ucsndup - duplicate little endian Unicode string
+ * @s: pointer to Unicode string
+ * @maxlen: maximum length of string @s
+ *
+ * Return a pointer to a new little endian Unicode string which is a duplicate
+ * of the string s. Memory for the new string is obtained with malloc(3), and
+ * can be freed with free(3).
+ *
+ * A maximum of @maxlen Unicode characters are copied and a terminating
+ * (ntfschar)'\0' little endian Unicode character is added.
+ *
+ * This function never looks beyond @s + @maxlen.
+ *
+ * Return a pointer to the new little endian Unicode string on success and NULL
+ * on failure with errno set to the error code.
+ */
+ntfschar *ntfs_ucsndup(const ntfschar *s, u32 maxlen)
+{
+ ntfschar *dst;
+ u32 len;
+
+ len = ntfs_ucsnlen(s, maxlen);
+ dst = ntfs_malloc((len + 1) * sizeof(ntfschar));
+ if (dst) {
+ memcpy(dst, s, len * sizeof(ntfschar));
+ dst[len] = 0;
+ }
+ return dst;
+}
+
+/**
+ * ntfs_name_upcase - Map an Unicode name to its uppercase equivalent
+ * @name:
+ * @name_len:
+ * @upcase:
+ * @upcase_len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase,
+ const u32 upcase_len)
+{
+ u32 i;
+ u16 u;
+
+ for (i = 0; i < name_len; i++)
+ if ((u = le16_to_cpu(name[i])) < upcase_len)
+ name[i] = upcase[u];
+}
+
+/**
+ * ntfs_file_value_upcase - Convert a filename to upper case
+ * @file_name_attr:
+ * @upcase:
+ * @upcase_len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr,
+ const ntfschar *upcase, const u32 upcase_len)
+{
+ ntfs_name_upcase((ntfschar*)&file_name_attr->file_name,
+ file_name_attr->file_name_length, upcase, upcase_len);
+}
+
+/**
+ * ntfs_file_values_compare - Which of two filenames should be listed first
+ * @file_name_attr1:
+ * @file_name_attr2:
+ * @err_val:
+ * @ic:
+ * @upcase:
+ * @upcase_len:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1,
+ const FILE_NAME_ATTR *file_name_attr2,
+ const int err_val, const IGNORE_CASE_BOOL ic,
+ const ntfschar *upcase, const u32 upcase_len)
+{
+ return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name,
+ file_name_attr1->file_name_length,
+ (ntfschar*)&file_name_attr2->file_name,
+ file_name_attr2->file_name_length,
+ err_val, ic, upcase, upcase_len);
+}
+
+/**
+ * ntfs_ucstombs - convert a little endian Unicode string to a multibyte string
+ * @ins: input Unicode string buffer
+ * @ins_len: length of input string in Unicode characters
+ * @outs: on return contains the (allocated) output multibyte string
+ * @outs_len: length of output buffer in bytes
+ *
+ * Convert the input little endian, 2-byte Unicode string @ins, of length
+ * @ins_len into the multibyte string format dictated by the current locale.
+ *
+ * If *@outs is NULL, the function allocates the string and the caller is
+ * responsible for calling free(*@outs); when finished with it.
+ *
+ * On success the function returns the number of bytes written to the output
+ * string *@outs (>= 0), not counting the terminating NULL byte. If the output
+ * string buffer was allocated, *@outs is set to it.
+ *
+ * On error, -1 is returned, and errno is set to the error code. The following
+ * error codes can be expected:
+ * EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
+ * EILSEQ The input string cannot be represented as a multibyte
+ * sequence according to the current locale.
+ * ENAMETOOLONG Destination buffer is too small for input string.
+ * ENOMEM Not enough memory to allocate destination buffer.
+ */
+int ntfs_ucstombs(const ntfschar *ins, const int ins_len, char **outs,
+ int outs_len)
+{
+ char *mbs;
+ wchar_t wc;
+ int i, o, mbs_len;
+ int cnt = 0;
+#ifdef HAVE_MBSINIT
+ mbstate_t mbstate;
+#endif
+
+ if (!ins || !outs) {
+ errno = EINVAL;
+ return -1;
+ }
+ mbs = *outs;
+ mbs_len = outs_len;
+ if (mbs && !mbs_len) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ if (!mbs) {
+ mbs_len = (ins_len + 1) * MB_CUR_MAX;
+ mbs = (char*)ntfs_malloc(mbs_len);
+ if (!mbs)
+ return -1;
+ }
+#ifdef HAVE_MBSINIT
+ memset(&mbstate, 0, sizeof(mbstate));
+#else
+ wctomb(NULL, 0);
+#endif
+ for (i = o = 0; i < ins_len; i++) {
+ /* Reallocate memory if necessary or abort. */
+ if ((int)(o + MB_CUR_MAX) > mbs_len) {
+ char *tc;
+ if (mbs == *outs) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ tc = (char*)ntfs_malloc((mbs_len + 64) & ~63);
+ if (!tc)
+ goto err_out;
+ memcpy(tc, mbs, mbs_len);
+ mbs_len = (mbs_len + 64) & ~63;
+ free(mbs);
+ mbs = tc;
+ }
+ /* Convert the LE Unicode character to a CPU wide character. */
+ wc = (wchar_t)le16_to_cpu(ins[i]);
+ if (!wc)
+ break;
+ /* Convert the CPU endian wide character to multibyte. */
+#ifdef HAVE_MBSINIT
+ cnt = wcrtomb(mbs + o, wc, &mbstate);
+#else
+ cnt = wctomb(mbs + o, wc);
+#endif
+ if (cnt == -1)
+ goto err_out;
+ if (cnt <= 0) {
+ ntfs_log_debug("Eeek. cnt <= 0, cnt = %i\n", cnt);
+ errno = EINVAL;
+ goto err_out;
+ }
+ o += cnt;
+ }
+#ifdef HAVE_MBSINIT
+ /* Make sure we are back in the initial state. */
+ if (!mbsinit(&mbstate)) {
+ ntfs_log_debug("Eeek. mbstate not in initial state!\n");
+ errno = EILSEQ;
+ goto err_out;
+ }
+#endif
+ /* Now write the NULL character. */
+ mbs[o] = 0;
+ if (*outs != mbs)
+ *outs = mbs;
+ return o;
+err_out:
+ if (mbs != *outs)
+ free(mbs);
+ return -1;
+}
+
+/**
+ * ntfs_mbstoucs - convert a multibyte string to a little endian Unicode string
+ * @ins: input multibyte string buffer
+ * @outs: on return contains the (allocated) output Unicode string
+ * @outs_len: length of output buffer in Unicode characters
+ *
+ * Convert the input multibyte string @ins, from the current locale into the
+ * corresponding little endian, 2-byte Unicode string.
+ *
+ * If *@outs is NULL, the function allocates the string and the caller is
+ * responsible for calling free(*@outs); when finished with it.
+ *
+ * On success the function returns the number of Unicode characters written to
+ * the output string *@outs (>= 0), not counting the terminating Unicode NULL
+ * character. If the output string buffer was allocated, *@outs is set to it.
+ *
+ * On error, -1 is returned, and errno is set to the error code. The following
+ * error codes can be expected:
+ * EINVAL Invalid arguments (e.g. @ins or @outs is NULL).
+ * EILSEQ The input string cannot be represented as a Unicode
+ * string according to the current locale.
+ * ENAMETOOLONG Destination buffer is too small for input string.
+ * ENOMEM Not enough memory to allocate destination buffer.
+ */
+int ntfs_mbstoucs(const char *ins, ntfschar **outs, int outs_len)
+{
+ ntfschar *ucs;
+ const char *s;
+ wchar_t wc;
+ int i, o, cnt, ins_len, ucs_len, ins_size;
+#ifdef HAVE_MBSINIT
+ mbstate_t mbstate;
+#endif
+
+ if (!ins || !outs) {
+ errno = EINVAL;
+ return -1;
+ }
+ ucs = *outs;
+ ucs_len = outs_len;
+ if (ucs && !ucs_len) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ /* Determine the size of the multi-byte string in bytes. */
+ ins_size = strlen(ins);
+ /* Determine the length of the multi-byte string. */
+ s = ins;
+#if defined(HAVE_MBSINIT)
+ memset(&mbstate, 0, sizeof(mbstate));
+ ins_len = mbsrtowcs(NULL, (const char **)&s, 0, &mbstate);
+#ifdef __CYGWIN32__
+ if (!ins_len && *ins) {
+ /* Older Cygwin had broken mbsrtowcs() implementation. */
+ ins_len = strlen(ins);
+ }
+#endif
+#elif !defined(DJGPP)
+ ins_len = mbstowcs(NULL, s, 0);
+#else
+ /* Eeek!!! DJGPP has broken mbstowcs() implementation!!! */
+ ins_len = strlen(ins);
+#endif
+ if (ins_len == -1)
+ return ins_len;
+#ifdef HAVE_MBSINIT
+ if ((s != ins) || !mbsinit(&mbstate)) {
+#else
+ if (s != ins) {
+#endif
+ errno = EILSEQ;
+ return -1;
+ }
+ /* Add the NULL terminator. */
+ ins_len++;
+ if (!ucs) {
+ ucs_len = ins_len;
+ ucs = (ntfschar*)ntfs_malloc(ucs_len * sizeof(ntfschar));
+ if (!ucs)
+ return -1;
+ }
+#ifdef HAVE_MBSINIT
+ memset(&mbstate, 0, sizeof(mbstate));
+#else
+ mbtowc(NULL, NULL, 0);
+#endif
+ for (i = o = cnt = 0; i < ins_size; i += cnt, o++) {
+ /* Reallocate memory if necessary or abort. */
+ if (o >= ucs_len) {
+ ntfschar *tc;
+ if (ucs == *outs) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ /*
+ * We will never get here but hey, it's only a bit of
+ * extra code...
+ */
+ ucs_len = (ucs_len * sizeof(ntfschar) + 64) & ~63;
+ tc = (ntfschar*)realloc(ucs, ucs_len);
+ if (!tc)
+ goto err_out;
+ ucs = tc;
+ ucs_len /= sizeof(ntfschar);
+ }
+ /* Convert the multibyte character to a wide character. */
+#ifdef HAVE_MBSINIT
+ cnt = mbrtowc(&wc, ins + i, ins_size - i, &mbstate);
+#else
+ cnt = mbtowc(&wc, ins + i, ins_size - i);
+#endif
+ if (!cnt)
+ break;
+ if (cnt == -1)
+ goto err_out;
+ if (cnt < -1) {
+ ntfs_log_trace("Eeek. cnt = %i\n", cnt);
+ errno = EINVAL;
+ goto err_out;
+ }
+ /* Make sure we are not overflowing the NTFS Unicode set. */
+ if ((unsigned long)wc >= (unsigned long)(1 <<
+ (8 * sizeof(ntfschar)))) {
+ errno = EILSEQ;
+ goto err_out;
+ }
+ /* Convert the CPU wide character to a LE Unicode character. */
+ ucs[o] = cpu_to_le16(wc);
+ }
+#ifdef HAVE_MBSINIT
+ /* Make sure we are back in the initial state. */
+ if (!mbsinit(&mbstate)) {
+ ntfs_log_trace("Eeek. mbstate not in initial state!\n");
+ errno = EILSEQ;
+ goto err_out;
+ }
+#endif
+ /* Now write the NULL character. */
+ ucs[o] = 0;
+ if (*outs != ucs)
+ *outs = ucs;
+ return o;
+err_out:
+ if (ucs != *outs)
+ free(ucs);
+ return -1;
+}
+
+/**
+ * ntfs_upcase_table_build - build the default upcase table for NTFS
+ * @uc: destination buffer where to store the built table
+ * @uc_len: size of destination buffer in bytes
+ *
+ * ntfs_upcase_table_build() builds the default upcase table for NTFS and
+ * stores it in the caller supplied buffer @uc of size @uc_len.
+ *
+ * The generated $UpCase table is the one used by Windows Vista.
+ *
+ * Note, @uc_len must be at least 128kiB in size or bad things will happen!
+ */
+void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len)
+{
+ /*
+ * "Start" is inclusive and "End" is exclusive, every value has the
+ * value of "Add" added to it.
+ */
+ static int add[][3] = { /* Start, End, Add */
+ {0x0061, 0x007b, -32}, {0x00e0, 0x00f7, -32}, {0x00f8, 0x00ff, -32},
+ {0x0256, 0x0258, -205}, {0x028a, 0x028c, -217}, {0x037b, 0x037e, 130},
+ {0x03ac, 0x03ad, -38}, {0x03ad, 0x03b0, -37}, {0x03b1, 0x03c2, -32},
+ {0x03c2, 0x03c3, -31}, {0x03c3, 0x03cc, -32}, {0x03cc, 0x03cd, -64},
+ {0x03cd, 0x03cf, -63}, {0x0430, 0x0450, -32}, {0x0450, 0x0460, -80},
+ {0x0561, 0x0587, -48}, {0x1f00, 0x1f08, 8}, {0x1f10, 0x1f16, 8},
+ {0x1f20, 0x1f28, 8}, {0x1f30, 0x1f38, 8}, {0x1f40, 0x1f46, 8},
+ {0x1f51, 0x1f52, 8}, {0x1f53, 0x1f54, 8}, {0x1f55, 0x1f56, 8},
+ {0x1f57, 0x1f58, 8}, {0x1f60, 0x1f68, 8}, {0x1f70, 0x1f72, 74},
+ {0x1f72, 0x1f76, 86}, {0x1f76, 0x1f78, 100}, {0x1f78, 0x1f7a, 128},
+ {0x1f7a, 0x1f7c, 112}, {0x1f7c, 0x1f7e, 126}, {0x1f80, 0x1f88, 8},
+ {0x1f90, 0x1f98, 8}, {0x1fa0, 0x1fa8, 8}, {0x1fb0, 0x1fb2, 8},
+ {0x1fb3, 0x1fb4, 9}, {0x1fcc, 0x1fcd, -9}, {0x1fd0, 0x1fd2, 8},
+ {0x1fe0, 0x1fe2, 8}, {0x1fe5, 0x1fe6, 7}, {0x1ffc, 0x1ffd, -9},
+ {0x2170, 0x2180, -16}, {0x24d0, 0x24ea, -26}, {0x2c30, 0x2c5f, -48},
+ {0x2d00, 0x2d26, -7264}, {0xff41, 0xff5b, -32}, {0}
+ };
+ /*
+ * "Start" is exclusive and "End" is inclusive, every second value is
+ * decremented by one.
+ */
+ static int skip_dec[][2] = { /* Start, End */
+ {0x0100, 0x012f}, {0x0132, 0x0137}, {0x0139, 0x0149}, {0x014a, 0x0178},
+ {0x0179, 0x017e}, {0x01a0, 0x01a6}, {0x01b3, 0x01b7}, {0x01cd, 0x01dd},
+ {0x01de, 0x01ef}, {0x01f4, 0x01f5}, {0x01f8, 0x01f9}, {0x01fa, 0x0220},
+ {0x0222, 0x0234}, {0x023b, 0x023c}, {0x0241, 0x0242}, {0x0246, 0x024f},
+ {0x03d8, 0x03ef}, {0x03f7, 0x03f8}, {0x03fa, 0x03fb}, {0x0460, 0x0481},
+ {0x048a, 0x04bf}, {0x04c1, 0x04c4}, {0x04c5, 0x04c8}, {0x04c9, 0x04ce},
+ {0x04ec, 0x04ed}, {0x04d0, 0x04eb}, {0x04ee, 0x04f5}, {0x04f6, 0x0513},
+ {0x1e00, 0x1e95}, {0x1ea0, 0x1ef9}, {0x2183, 0x2184}, {0x2c60, 0x2c61},
+ {0x2c67, 0x2c6c}, {0x2c75, 0x2c76}, {0x2c80, 0x2ce3}, {0}
+ };
+ /*
+ * Set the Unicode character at offset "Offset" to "Value". Note,
+ * "Value" is host endian.
+ */
+ static int set[][2] = { /* Offset, Value */
+ {0x00ff, 0x0178}, {0x0180, 0x0243}, {0x0183, 0x0182}, {0x0185, 0x0184},
+ {0x0188, 0x0187}, {0x018c, 0x018b}, {0x0192, 0x0191}, {0x0195, 0x01f6},
+ {0x0199, 0x0198}, {0x019a, 0x023d}, {0x019e, 0x0220}, {0x01a8, 0x01a7},
+ {0x01ad, 0x01ac}, {0x01b0, 0x01af}, {0x01b9, 0x01b8}, {0x01bd, 0x01bc},
+ {0x01bf, 0x01f7}, {0x01c6, 0x01c4}, {0x01c9, 0x01c7}, {0x01cc, 0x01ca},
+ {0x01dd, 0x018e}, {0x01f3, 0x01f1}, {0x023a, 0x2c65}, {0x023e, 0x2c66},
+ {0x0253, 0x0181}, {0x0254, 0x0186}, {0x0259, 0x018f}, {0x025b, 0x0190},
+ {0x0260, 0x0193}, {0x0263, 0x0194}, {0x0268, 0x0197}, {0x0269, 0x0196},
+ {0x026b, 0x2c62}, {0x026f, 0x019c}, {0x0272, 0x019d}, {0x0275, 0x019f},
+ {0x027d, 0x2c64}, {0x0280, 0x01a6}, {0x0283, 0x01a9}, {0x0288, 0x01ae},
+ {0x0289, 0x0244}, {0x028c, 0x0245}, {0x0292, 0x01b7}, {0x03f2, 0x03f9},
+ {0x04cf, 0x04c0}, {0x1d7d, 0x2c63}, {0x214e, 0x2132}, {0}
+ };
+ unsigned i, r;
+
+ memset(uc, 0, uc_len);
+ uc_len /= 2;
+ /* Start with a one-to-one mapping, i.e. no upcasing happens at all. */
+ for (i = 0; i < uc_len; i++)
+ uc[i] = cpu_to_le16(i);
+ /* Adjust specified runs by the specified amount. */
+ for (r = 0; add[r][0]; r++)
+ for (i = add[r][0]; i < add[r][1]; i++)
+ uc[i] = cpu_to_le16(le16_to_cpu(uc[i]) + add[r][2]);
+ /* Decrement every second value in specified runs. */
+ for (r = 0; skip_dec[r][0]; r++)
+ for (i = skip_dec[r][0]; i < skip_dec[r][1];
+ i += 2)
+ uc[i + 1] = cpu_to_le16(le16_to_cpu(uc[i + 1]) - 1);
+ /* Set specified characters to specified values. */
+ for (r = 0; set[r][0]; r++)
+ uc[set[r][0]] = cpu_to_le16(set[r][1]);
+}
+
+/**
+ * ntfs_str2ucs - convert a string to a valid NTFS file name
+ * @s: input string
+ * @len: length of output buffer in Unicode characters
+ *
+ * Convert the input @s string into the corresponding little endian,
+ * 2-byte Unicode string. The length of the converted string is less
+ * or equal to the maximum length allowed by the NTFS format (255).
+ *
+ * If @s is NULL then return AT_UNNAMED.
+ *
+ * On success the function returns the Unicode string in an allocated
+ * buffer and the caller is responsible to free it when it's not needed
+ * anymore.
+ *
+ * On error NULL is returned and errno is set to the error code.
+ */
+ntfschar *ntfs_str2ucs(const char *s, int *len)
+{
+ ntfschar *ucs = NULL;
+
+ if (s && ((*len = ntfs_mbstoucs(s, &ucs, 0)) == -1)) {
+ ntfs_log_perror("Couldn't convert '%s' to Unicode", s);
+ return NULL;
+ }
+ if (*len > NTFS_MAX_NAME_LEN) {
+ free(ucs);
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ if (!ucs || !*len) {
+ ucs = AT_UNNAMED;
+ *len = 0;
+ }
+ return ucs;
+}
+
+/**
+ * ntfs_ucsfree - free memory allocated by ntfs_str2ucs()
+ * @ucs: input string to be freed
+ *
+ * Free memory at @ucs and which was allocated by ntfs_str2ucs.
+ *
+ * Return value: none.
+ */
+void ntfs_ucsfree(ntfschar *ucs)
+{
+ if (ucs && (ucs != AT_UNNAMED))
+ free(ucs);
+}
+
diff --git a/usr/src/lib/libntfs/common/libntfs/unix_io.c b/usr/src/lib/libntfs/common/libntfs/unix_io.c
new file mode 100644
index 0000000000..cd1b457330
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/unix_io.c
@@ -0,0 +1,320 @@
+/**
+ * unix_io.c - Unix style disk io functions. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+
+#include "types.h"
+#include "mst.h"
+#include "debug.h"
+#include "device.h"
+#include "logging.h"
+
+#define DEV_FD(dev) (*(int *)dev->d_private)
+
+/* Define to nothing if not present on this system. */
+#ifndef O_EXCL
+# define O_EXCL 0
+#endif
+
+/**
+ * ntfs_device_unix_io_open - Open a device and lock it exclusively
+ * @dev:
+ * @flags:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_device_unix_io_open(struct ntfs_device *dev, int flags)
+{
+ struct flock flk;
+ struct stat sbuf;
+ int err;
+
+ if (NDevOpen(dev)) {
+ errno = EBUSY;
+ return -1;
+ }
+ if (!(dev->d_private = ntfs_malloc(sizeof(int))))
+ return -1;
+ *(int*)dev->d_private = open(dev->d_name, flags);
+ if (*(int*)dev->d_private == -1) {
+ err = errno;
+ goto err_out;
+ }
+ /* Setup our read-only flag. */
+ if ((flags & O_RDWR) != O_RDWR)
+ NDevSetReadOnly(dev);
+ /* Acquire exclusive (mandatory) lock on the whole device. */
+ memset(&flk, 0, sizeof(flk));
+ if (NDevReadOnly(dev))
+ flk.l_type = F_RDLCK;
+ else
+ flk.l_type = F_WRLCK;
+ flk.l_whence = SEEK_SET;
+ flk.l_start = flk.l_len = 0LL;
+ if (fcntl(DEV_FD(dev), F_SETLK, &flk)) {
+ err = errno;
+ ntfs_log_debug("ntfs_device_unix_io_open: Could not lock %s "
+ "for %s\n", dev->d_name, NDevReadOnly(dev) ?
+ "reading" : "writing");
+ if (close(DEV_FD(dev)))
+ ntfs_log_perror("ntfs_device_unix_io_open: Warning: "
+ "Could not close %s", dev->d_name);
+ goto err_out;
+ }
+ /* Determine if device is a block device or not, ignoring errors. */
+ if (!fstat(DEV_FD(dev), &sbuf) && S_ISBLK(sbuf.st_mode))
+ NDevSetBlock(dev);
+ /* Set our open flag. */
+ NDevSetOpen(dev);
+ return 0;
+err_out:
+ free(dev->d_private);
+ dev->d_private = NULL;
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_device_unix_io_close - Close the device, releasing the lock
+ * @dev:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_device_unix_io_close(struct ntfs_device *dev)
+{
+ struct flock flk;
+
+ if (!NDevOpen(dev)) {
+ errno = EBADF;
+ return -1;
+ }
+ if (NDevDirty(dev))
+ fsync(DEV_FD(dev));
+ /* Release exclusive (mandatory) lock on the whole device. */
+ memset(&flk, 0, sizeof(flk));
+ flk.l_type = F_UNLCK;
+ flk.l_whence = SEEK_SET;
+ flk.l_start = flk.l_len = 0LL;
+ if (fcntl(DEV_FD(dev), F_SETLK, &flk))
+ ntfs_log_perror("ntfs_device_unix_io_close: Warning: Could not "
+ "unlock %s", dev->d_name);
+ /* Close the file descriptor and clear our open flag. */
+ if (close(DEV_FD(dev)))
+ return -1;
+ NDevClearOpen(dev);
+ free(dev->d_private);
+ dev->d_private = NULL;
+ return 0;
+}
+
+/**
+ * ntfs_device_unix_io_seek - Seek to a place on the device
+ * @dev:
+ * @offset:
+ * @whence:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static s64 ntfs_device_unix_io_seek(struct ntfs_device *dev, s64 offset,
+ int whence)
+{
+ return lseek(DEV_FD(dev), offset, whence);
+}
+
+/**
+ * ntfs_device_unix_io_read - Read from the device, from the current location
+ * @dev:
+ * @buf:
+ * @count:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static s64 ntfs_device_unix_io_read(struct ntfs_device *dev, void *buf,
+ s64 count)
+{
+ return read(DEV_FD(dev), buf, count);
+}
+
+/**
+ * ntfs_device_unix_io_write - Write to the device, at the current location
+ * @dev:
+ * @buf:
+ * @count:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static s64 ntfs_device_unix_io_write(struct ntfs_device *dev, const void *buf,
+ s64 count)
+{
+ if (NDevReadOnly(dev)) {
+ errno = EROFS;
+ return -1;
+ }
+ NDevSetDirty(dev);
+ return write(DEV_FD(dev), buf, count);
+}
+
+/**
+ * ntfs_device_unix_io_pread - Perform a positioned read from the device
+ * @dev:
+ * @buf:
+ * @count:
+ * @offset:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static s64 ntfs_device_unix_io_pread(struct ntfs_device *dev, void *buf,
+ s64 count, s64 offset)
+{
+ return pread(DEV_FD(dev), buf, count, offset);
+}
+
+/**
+ * ntfs_device_unix_io_pwrite - Perform a positioned write to the device
+ * @dev:
+ * @buf:
+ * @count:
+ * @offset:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static s64 ntfs_device_unix_io_pwrite(struct ntfs_device *dev, const void *buf,
+ s64 count, s64 offset)
+{
+ if (NDevReadOnly(dev)) {
+ errno = EROFS;
+ return -1;
+ }
+ NDevSetDirty(dev);
+ return pwrite(DEV_FD(dev), buf, count, offset);
+}
+
+/**
+ * ntfs_device_unix_io_sync - Flush any buffered changes to the device
+ * @dev:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_device_unix_io_sync(struct ntfs_device *dev)
+{
+ if (!NDevReadOnly(dev) && NDevDirty(dev)) {
+ int res = fsync(DEV_FD(dev));
+ if (!res)
+ NDevClearDirty(dev);
+ return res;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_device_unix_io_stat - Get information about the device
+ * @dev:
+ * @buf:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_device_unix_io_stat(struct ntfs_device *dev, struct stat *buf)
+{
+ return fstat(DEV_FD(dev), buf);
+}
+
+/**
+ * ntfs_device_unix_io_ioctl - Perform an ioctl on the device
+ * @dev:
+ * @request:
+ * @argp:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static int ntfs_device_unix_io_ioctl(struct ntfs_device *dev, int request,
+ void *argp)
+{
+ return ioctl(DEV_FD(dev), request, argp);
+}
+
+/**
+ * Device operations for working with unix style devices and files.
+ */
+struct ntfs_device_operations ntfs_device_unix_io_ops = {
+ .open = ntfs_device_unix_io_open,
+ .close = ntfs_device_unix_io_close,
+ .seek = ntfs_device_unix_io_seek,
+ .read = ntfs_device_unix_io_read,
+ .write = ntfs_device_unix_io_write,
+ .pread = ntfs_device_unix_io_pread,
+ .pwrite = ntfs_device_unix_io_pwrite,
+ .sync = ntfs_device_unix_io_sync,
+ .stat = ntfs_device_unix_io_stat,
+ .ioctl = ntfs_device_unix_io_ioctl,
+};
diff --git a/usr/src/lib/libntfs/common/libntfs/version.c b/usr/src/lib/libntfs/common/libntfs/version.c
new file mode 100644
index 0000000000..7882e7177b
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/version.c
@@ -0,0 +1,45 @@
+/**
+ * version.c - Info about the NTFS library. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2005 Anton Altaparmakov
+ * Copyright (c) 2005 Richard Russon
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "version.h"
+
+#ifdef LTVERSION_LIBNTFS
+#define LIBNTFS_VERSION_STRING LTVERSION_LIBNTFS
+#else
+#define LIBNTFS_VERSION_STRING "unknown"
+#endif
+
+static const char *libntfs_version_string = LIBNTFS_VERSION_STRING;
+
+/**
+ * ntfs_libntfs_version - query version number of the ntfs library libntfs
+ *
+ * Returns pointer to a text string representing the version of libntfs.
+ */
+const char *ntfs_libntfs_version(void)
+{
+ return libntfs_version_string;
+}
diff --git a/usr/src/lib/libntfs/common/libntfs/volume.c b/usr/src/lib/libntfs/common/libntfs/volume.c
new file mode 100644
index 0000000000..5788cb90bc
--- /dev/null
+++ b/usr/src/lib/libntfs/common/libntfs/volume.c
@@ -0,0 +1,1688 @@
+/**
+ * volume.c - NTFS volume handling code. Part of the Linux-NTFS project.
+ *
+ * Copyright (c) 2000-2006 Anton Altaparmakov
+ * Copyright (c) 2002-2006 Szabolcs Szakacsits
+ * Copyright (c) 2004-2005 Richard Russon
+ * Copyright (c) 2005-2007 Yura Pakhuchiy
+ *
+ * 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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "volume.h"
+#include "attrib.h"
+#include "mft.h"
+#include "bootsect.h"
+#include "device.h"
+#include "debug.h"
+#include "inode.h"
+#include "runlist.h"
+#include "logfile.h"
+#include "dir.h"
+#include "logging.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+/**
+ * ntfs_volume_alloc - Create an NTFS volume object and initialise it
+ *
+ * Description...
+ *
+ * Returns:
+ */
+ntfs_volume *ntfs_volume_alloc(void)
+{
+ ntfs_volume *vol;
+ int i;
+
+ vol = calloc(1, sizeof(ntfs_volume));
+ if (vol) {
+ for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++)
+ INIT_LIST_HEAD(&vol->inode_cache[i]);
+ }
+ return vol;
+}
+
+/**
+ * __ntfs_volume_release - Destroy an NTFS volume object
+ * @v:
+ *
+ * Description...
+ *
+ * Returns:
+ */
+static void __ntfs_volume_release(ntfs_volume *v)
+{
+ struct list_head *pos, *tmp;
+ int i;
+
+ /* Sync and print error about not detached inodes. */
+ for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++)
+ list_for_each_safe(pos, tmp, &v->inode_cache[i]) {
+ ntfs_inode *ni =
+ list_entry(pos, ntfs_inode, list_entry);
+
+ switch (ni->mft_no) {
+ case FILE_Volume:
+ case FILE_Bitmap:
+ case FILE_MFT:
+ case FILE_MFTMirr:
+ if (ni->nr_references == 1)
+ continue;
+ break;
+ }
+
+ ntfs_log_error("%s(): Inode %llu still have %d "
+ "references.\n", "__ntfs_volume_release",
+ ni->mft_no, ni->nr_references);
+ ntfs_inode_sync(ni);
+ }
+ /*
+ * Clear the dirty bit if it was not set before we mounted and this is
+ * not a forensic mount.
+ */
+ if (!NVolReadOnly(v) && !NVolWasDirty(v) && !NVolForensicMount(v)) {
+ v->flags &= ~VOLUME_IS_DIRTY;
+ (void)ntfs_volume_write_flags(v, v->flags);
+ }
+ if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni))
+ ntfs_inode_sync(v->lcnbmp_ni);
+ if (v->vol_ni)
+ ntfs_inode_close(v->vol_ni);
+ if (v->lcnbmp_na)
+ ntfs_attr_close(v->lcnbmp_na);
+ if (v->lcnbmp_ni)
+ ntfs_inode_close(v->lcnbmp_ni);
+ if (v->mft_ni && NInoDirty(v->mft_ni))
+ ntfs_inode_sync(v->mft_ni);
+ if (v->mftbmp_na)
+ ntfs_attr_close(v->mftbmp_na);
+ if (v->mft_na)
+ ntfs_attr_close(v->mft_na);
+ if (v->mft_ni)
+ ntfs_inode_close(v->mft_ni);
+ if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni))
+ ntfs_inode_sync(v->mftmirr_ni);
+ if (v->mftmirr_na)
+ ntfs_attr_close(v->mftmirr_na);
+ if (v->mftmirr_ni)
+ ntfs_inode_close(v->mftmirr_ni);
+ if (v->u.dev) {
+ struct ntfs_device *dev = v->u.dev;
+
+ if (NDevDirty(dev))
+ dev->d_ops->sync(dev);
+ if (dev->d_ops->close(dev))
+ ntfs_log_perror("Failed to close the device");
+ }
+ free(v->vol_name);
+ free(v->upcase);
+ free(v->attrdef);
+ free(v);
+}
+
+/**
+ * ntfs_mft_load - load the $MFT and setup the ntfs volume with it
+ * @vol: ntfs volume whose $MFT to load
+ *
+ * Load $MFT from @vol and setup @vol with it. After calling this function the
+ * volume @vol is ready for use by all read access functions provided by the
+ * ntfs library.
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+static int ntfs_mft_load(ntfs_volume *vol)
+{
+ VCN next_vcn, last_vcn, highest_vcn;
+ s64 l;
+ MFT_RECORD *mb = NULL;
+ ntfs_attr_search_ctx *ctx = NULL;
+ ATTR_RECORD *a;
+ STANDARD_INFORMATION *std_info;
+ int eo;
+
+ /* Manually setup an ntfs_inode. */
+ vol->mft_ni = ntfs_inode_allocate(vol);
+ mb = (MFT_RECORD*)ntfs_malloc(vol->mft_record_size);
+ if (!vol->mft_ni || !mb) {
+ ntfs_log_perror("Error allocating memory for $MFT");
+ goto error_exit;
+ }
+ vol->mft_ni->mft_no = 0;
+ vol->mft_ni->mrec = mb;
+ __ntfs_inode_add_to_cache(vol->mft_ni);
+ /* Can't use any of the higher level functions yet! */
+ l = ntfs_mst_pread(vol->u.dev, vol->mft_lcn << vol->cluster_size_bits, 1,
+ vol->mft_record_size, mb);
+ if (l != 1) {
+ if (l != -1)
+ errno = EIO;
+ ntfs_log_perror("Error reading $MFT");
+ goto error_exit;
+ }
+ if (ntfs_is_baad_record(mb->magic)) {
+ ntfs_log_error("Incomplete multi sector transfer detected in "
+ "$MFT.\n");
+ goto io_error_exit;
+ }
+ if (!ntfs_is_mft_record(mb->magic)) {
+ ntfs_log_error("$MFT has invalid magic.\n");
+ goto io_error_exit;
+ }
+ ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL);
+ if (!ctx) {
+ ntfs_log_perror("Failed to allocate attribute search context");
+ goto error_exit;
+ }
+ if (p2n(ctx->attr) < p2n(mb) ||
+ (char*)ctx->attr > (char*)mb + vol->mft_record_size) {
+ ntfs_log_error("$MFT is corrupt.\n");
+ goto io_error_exit;
+ }
+ /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */
+ if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0,
+ ctx)) {
+ if (errno != ENOENT) {
+ ntfs_log_error("$MFT has corrupt attribute list.\n");
+ goto io_error_exit;
+ }
+ goto mft_has_no_attr_list;
+ }
+ NInoSetAttrList(vol->mft_ni);
+ l = ntfs_get_attribute_value_length(ctx->attr);
+ if (l <= 0 || l > 0x40000) {
+ ntfs_log_error("$MFT/$ATTRIBUTE_LIST has invalid length.\n");
+ goto io_error_exit;
+ }
+ vol->mft_ni->attr_list_size = l;
+ vol->mft_ni->attr_list = ntfs_malloc(l);
+ if (!vol->mft_ni->attr_list)
+ goto error_exit;
+
+ l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list);
+ if (!l) {
+ ntfs_log_error("Failed to get value of "
+ "$MFT/$ATTRIBUTE_LIST.\n");
+ goto io_error_exit;
+ }
+ if (l != vol->mft_ni->attr_list_size) {
+ ntfs_log_error("Got unexpected amount of data when "
+ "reading $MFT/$ATTRIBUTE_LIST.\n");
+ goto io_error_exit;
+ }
+mft_has_no_attr_list:
+ /* Receive attributes from STANDARD_INFORMATION. */
+ std_info = ntfs_attr_readall(vol->mft_ni, AT_STANDARD_INFORMATION,
+ AT_UNNAMED, 0, NULL);
+ vol->mft_ni->flags = std_info->file_attributes;
+ free(std_info);
+
+ /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */
+
+ /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */
+ vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0);
+ if (!vol->mft_na) {
+ ntfs_log_perror("Failed to open ntfs attribute");
+ goto error_exit;
+ }
+ /* Read all extents from the $DATA attribute in $MFT. */
+ ntfs_attr_reinit_search_ctx(ctx);
+ last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits;
+ highest_vcn = next_vcn = 0;
+ a = NULL;
+ while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0,
+ ctx)) {
+ runlist_element *nrl;
+
+ a = ctx->attr;
+ /* $MFT must be non-resident. */
+ if (!a->non_resident) {
+ ntfs_log_error("$MFT must be non-resident but a "
+ "resident extent was found. $MFT is "
+ "corrupt. Run chkdsk.\n");
+ goto io_error_exit;
+ }
+ /* $MFT must be uncompressed and unencrypted. */
+ if (a->flags & ATTR_COMPRESSION_MASK ||
+ a->flags & ATTR_IS_ENCRYPTED) {
+ ntfs_log_error("$MFT must be uncompressed and "
+ "unencrypted but a compressed/encrypted"
+ " extent was found. $MFT is corrupt. "
+ "Run chkdsk.\n");
+ goto io_error_exit;
+ }
+ /*
+ * Decompress the mapping pairs array of this extent and merge
+ * the result into the existing runlist. No need for locking
+ * as we have exclusive access to the inode at this time and we
+ * are a mount in progress task, too.
+ */
+ nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl);
+ if (!nrl) {
+ ntfs_log_perror("ntfs_mapping_pairs_decompress() "
+ "failed");
+ goto error_exit;
+ }
+ vol->mft_na->rl = nrl;
+
+ /* Get the lowest vcn for the next extent. */
+ highest_vcn = sle64_to_cpu(a->u.nonres.highest_vcn);
+ next_vcn = highest_vcn + 1;
+
+ /* Only one extent or error, which we catch below. */
+ if (next_vcn <= 0)
+ break;
+
+ /* Avoid endless loops due to corruption. */
+ if (next_vcn < sle64_to_cpu(a->u.nonres.lowest_vcn)) {
+ ntfs_log_error("$MFT has corrupt attribute list "
+ "attribute. Run chkdsk.\n");
+ goto io_error_exit;
+ }
+ }
+ if (!a) {
+ ntfs_log_error("$MFT/$DATA attribute not found. "
+ "$MFT is corrupt. Run chkdsk.\n");
+ goto io_error_exit;
+ }
+ if (highest_vcn && highest_vcn != last_vcn - 1) {
+ ntfs_log_error("Failed to load the complete runlist for "
+ "$MFT/$DATA. Bug or corrupt $MFT. "
+ "Run chkdsk.\n highest_vcn = 0x%llx, "
+ "last_vcn - 1 = 0x%llx\n", (long long)
+ highest_vcn, (long long)last_vcn - 1);
+ goto io_error_exit;
+ }
+ /* Done with the $Mft mft record. */
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ /*
+ * The volume is now setup so we can use all read access functions.
+ */
+ vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0);
+ if (!vol->mftbmp_na) {
+ ntfs_log_perror("Failed to open $MFT/$BITMAP");
+ goto error_exit;
+ }
+ return 0;
+io_error_exit:
+ errno = EIO;
+error_exit:
+ eo = errno;
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ if (vol->mft_na) {
+ ntfs_attr_close(vol->mft_na);
+ vol->mft_na = NULL;
+ }
+ if (vol->mft_ni) {
+ ntfs_inode_close(vol->mft_ni);
+ vol->mft_ni = NULL;
+ }
+ ntfs_log_error("%s(): Failed.\n", "ntfs_mft_load");
+ errno = eo;
+ return -1;
+}
+
+/**
+ * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it
+ * @vol: ntfs volume whose $MFTMirr to load
+ *
+ * Load $MFTMirr from @vol and setup @vol with it. After calling this function
+ * the volume @vol is ready for use by all write access functions provided by
+ * the ntfs library (assuming ntfs_mft_load() has been called successfully
+ * beforehand).
+ *
+ * Return 0 on success and -1 on error with errno set to the error code.
+ */
+static int ntfs_mftmirr_load(ntfs_volume *vol)
+{
+ int err;
+
+ vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr);
+ if (!vol->mftmirr_ni) {
+ ntfs_log_perror("Failed to open inode $MFTMirr");
+ return -1;
+ }
+ /* Get an ntfs attribute for $MFTMirr/$DATA, too. */
+ vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA,
+ AT_UNNAMED, 0);
+ if (!vol->mftmirr_na) {
+ ntfs_log_perror("Failed to open $MFTMirr/$DATA");
+ goto error_exit;
+ }
+ if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) {
+ ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA");
+ goto error_exit;
+ }
+ /* Check $MFTMirr runlist. */
+ if (vol->mftmirr_na->rl[0].lcn != vol->mftmirr_lcn ||
+ vol->mftmirr_na->rl[0].length < (vol->mftmirr_size *
+ vol->mft_record_size + vol->cluster_size - 1) /
+ vol->cluster_size) {
+ ntfs_log_error("$MFTMirr location mismatch or first 4 records "
+ "are fragmented. Run chkdsk.\n");
+ errno = EIO;
+ goto error_exit;
+
+ }
+ return 0;
+error_exit:
+ err = errno;
+ if (vol->mftmirr_na) {
+ ntfs_attr_close(vol->mftmirr_na);
+ vol->mftmirr_na = NULL;
+ }
+ ntfs_inode_close(vol->mftmirr_ni);
+ vol->mftmirr_ni = NULL;
+ errno = err;
+ return -1;
+}
+
+/**
+ * ntfs_volume_startup - allocate and setup an ntfs volume
+ * @dev: device to open
+ * @flags: optional mount flags
+ *
+ * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After
+ * calling this function, the volume is setup sufficiently to call all read
+ * and write access functions provided by the library.
+ *
+ * Return the allocated volume structure on success and NULL on error with
+ * errno set to the error code.
+ */
+ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
+ ntfs_mount_flags flags)
+{
+ LCN mft_zone_size, mft_lcn;
+ s64 br;
+ ntfs_volume *vol;
+ NTFS_BOOT_SECTOR *bs;
+ int eo;
+#ifdef DEBUG
+ const char *OK = "OK\n";
+ const char *FAILED = "FAILED\n";
+ BOOL debug = 1;
+#else
+ BOOL debug = 0;
+#endif
+
+ if (!dev || !dev->d_ops || !dev->d_name) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(bs = (NTFS_BOOT_SECTOR *)ntfs_malloc(sizeof(NTFS_BOOT_SECTOR))))
+ return NULL;
+
+ /* Allocate the volume structure. */
+ vol = ntfs_volume_alloc();
+ if (!vol)
+ goto error_exit;
+ /* Create the default upcase table. */
+ vol->upcase_len = 65536;
+ vol->upcase = (ntfschar*)ntfs_malloc(vol->upcase_len *
+ sizeof(ntfschar));
+ if (!vol->upcase)
+ goto error_exit;
+ ntfs_upcase_table_build(vol->upcase,
+ vol->upcase_len * sizeof(ntfschar));
+ if (flags & NTFS_MNT_RDONLY)
+ NVolSetReadOnly(vol);
+ if (flags & NTFS_MNT_CASE_SENSITIVE)
+ NVolSetCaseSensitive(vol);
+ if (flags & NTFS_MNT_INTERIX)
+ NVolSetInterix(vol);
+ ntfs_log_debug("Reading bootsector... ");
+ if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY :
+ ((flags & NTFS_MNT_NOT_EXCLUSIVE) ? O_RDWR :
+ (O_RDWR | O_EXCL)))) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Error opening partition device");
+ goto error_exit;
+ }
+ /* Attach the device to the volume. */
+ vol->u.dev = dev;
+ /* Now read the bootsector. */
+ br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs);
+ if (br != sizeof(NTFS_BOOT_SECTOR)) {
+ ntfs_log_debug(FAILED);
+ if (br != -1)
+ errno = EINVAL;
+ if (!br)
+ ntfs_log_debug("Error: partition is smaller than "
+ "bootsector size. Weird!\n");
+ else
+ ntfs_log_perror("Error reading bootsector");
+ goto error_exit;
+ }
+ ntfs_log_debug(OK);
+ if (!ntfs_boot_sector_is_ntfs(bs, !debug)) {
+ ntfs_log_debug("Error: %s is not a valid NTFS partition!\n",
+ dev->d_name);
+ errno = EINVAL;
+ goto error_exit;
+ }
+ if (ntfs_boot_sector_parse(vol, bs) < 0) {
+ ntfs_log_perror("Failed to parse ntfs bootsector");
+ goto error_exit;
+ }
+ free(bs);
+ bs = NULL;
+ /* Now set the device block size to the sector size. */
+ if (ntfs_device_block_size_set(vol->u.dev, vol->sector_size))
+ ntfs_log_debug("Failed to set the device block size to the "
+ "sector size. This may affect performance "
+ "but should be harmless otherwise. Error: "
+ "%s\n", strerror(errno));
+ /*
+ * We now initialize the cluster allocator.
+ *
+ * FIXME: Move this to its own function? (AIA)
+ */
+
+ // TODO: Make this tunable at mount time. (AIA)
+ vol->mft_zone_multiplier = 1;
+
+ /* Determine the size of the MFT zone. */
+ 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 the mft zone. */
+ vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn;
+ ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)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 the overhead would be
+ * higher than the speed increase we would get by doing it.
+ */
+ mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size;
+ if (mft_lcn * vol->cluster_size < 16 * 1024)
+ mft_lcn = (16 * 1024 + vol->cluster_size - 1) /
+ vol->cluster_size;
+ if (vol->mft_zone_start <= mft_lcn)
+ vol->mft_zone_start = 0;
+ ntfs_log_debug("mft_zone_start = 0x%llx\n",
+ (long long)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_log_debug("mft_zone_end = 0x%llx\n", (long long)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_log_debug("data1_zone_pos = 0x%llx\n", vol->data1_zone_pos);
+ vol->data2_zone_pos = 0;
+ ntfs_log_debug("data2_zone_pos = 0x%llx\n", vol->data2_zone_pos);
+
+ /* Set the mft data allocation position to mft record 24. */
+ vol->mft_data_pos = 24;
+
+ /*
+ * The cluster allocator is now fully operational.
+ */
+
+ /* Need to setup $MFT so we can use the library read functions. */
+ ntfs_log_debug("Loading $MFT... ");
+ if (ntfs_mft_load(vol) < 0) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to load $MFT");
+ goto error_exit;
+ }
+ ntfs_log_debug(OK);
+
+ /* Need to setup $MFTMirr so we can use the write functions, too. */
+ ntfs_log_debug("Loading $MFTMirr... ");
+ if (ntfs_mftmirr_load(vol) < 0) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to load $MFTMirr");
+ goto error_exit;
+ }
+ ntfs_log_debug(OK);
+ return vol;
+error_exit:
+ eo = errno;
+ free(bs);
+ if (vol)
+ __ntfs_volume_release(vol);
+ errno = eo;
+ return NULL;
+}
+
+/**
+ * ntfs_volume_check_logfile - check logfile on target volume
+ * @vol: volume on which to check logfile
+ *
+ * Return 0 on success and -1 on error with errno set error code.
+ */
+static int ntfs_volume_check_logfile(ntfs_volume *vol)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na = NULL;
+ RESTART_PAGE_HEADER *rp = NULL;
+ int err = 0;
+
+ if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) {
+ ntfs_log_debug("Failed to open inode FILE_LogFile.\n");
+ errno = EIO;
+ return -1;
+ }
+ if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
+ ntfs_log_debug("Failed to open $FILE_LogFile/$DATA\n");
+ err = EIO;
+ goto exit;
+ }
+ if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp))
+ err = EOPNOTSUPP;
+ free(rp);
+exit:
+ if (na)
+ ntfs_attr_close(na);
+ ntfs_inode_close(ni);
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * ntfs_hiberfile_open - Find and open '/hiberfil.sys'
+ * @vol: An ntfs volume obtained from ntfs_mount
+ *
+ * Return: inode Success, hiberfil.sys is valid
+ * NULL hiberfil.sys doesn't exist or some other error occurred
+ */
+static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol)
+{
+ u64 inode;
+ ntfs_inode *ni_root;
+ ntfs_inode *ni_hibr = NULL;
+ ntfschar *unicode = NULL;
+ int unicode_len;
+ const char *hiberfile = "hiberfil.sys";
+
+ if (!vol) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ ni_root = ntfs_inode_open(vol, FILE_root);
+ if (!ni_root) {
+ ntfs_log_debug("Couldn't open the root directory.\n");
+ return NULL;
+ }
+
+ unicode_len = ntfs_mbstoucs(hiberfile, &unicode, 0);
+ if (unicode_len < 0) {
+ ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode");
+ goto out;
+ }
+
+ inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len);
+ if (inode == (u64)-1) {
+ ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile);
+ goto out;
+ }
+
+ inode = MREF(inode);
+ ni_hibr = ntfs_inode_open(vol, inode);
+ if (!ni_hibr) {
+ ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode);
+ goto out;
+ }
+out:
+ ntfs_inode_close(ni_root);
+ free(unicode);
+ return ni_hibr;
+}
+
+
+#define NTFS_HIBERFILE_HEADER_SIZE 4096
+
+/**
+ * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is
+ * hibernated on the target volume
+ * @vol: volume on which to check hiberfil.sys
+ *
+ * Return: 0 if Windows isn't hibernated for sure
+ * -1 otherwise and errno is set to the appropriate value
+ */
+static int ntfs_volume_check_hiberfile(ntfs_volume *vol)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na = NULL;
+ int bytes_read, ret = -1;
+ char *buf = NULL;
+
+ ni = ntfs_hiberfile_open(vol);
+ if (!ni) {
+ if (errno == ENOENT)
+ return 0;
+ return -1;
+ }
+
+ buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE);
+ if (!buf)
+ goto out;
+
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ ntfs_log_perror("Failed to open hiberfil.sys data attribute");
+ goto out;
+ }
+
+ bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf);
+ if (bytes_read == -1) {
+ ntfs_log_perror("Failed to read hiberfil.sys");
+ goto out;
+ }
+ if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) {
+ ntfs_log_debug("Hibernated non-system partition, refused to "
+ "mount!\n");
+ errno = EPERM;
+ goto out;
+ }
+ if (memcmp(buf, "hibr", 4) == 0) {
+ ntfs_log_debug("Windows is hibernated, refused to mount!\n");
+ errno = EPERM;
+ goto out;
+ }
+ ret = 0;
+out:
+ if (na)
+ ntfs_attr_close(na);
+ free(buf);
+ ntfs_inode_close(ni);
+ return ret;
+}
+
+/**
+ * ntfs_volume_get_nr_free_mft_records - calculate number of free MFT records
+ * vol: ntfs volume for which perform calculations.
+ *
+ * This function initializes @vol->nr_free_mft_records. @vol->mftbmp_na should
+ * be already opened upon call to this function.
+ *
+ * Return 0 on success. On error return -1 with errno set appropriately and
+ * @vol->nr_free_mft_records is not touched in this case.
+ */
+static int ntfs_volume_get_nr_free_mft_records(ntfs_volume *vol)
+{
+ long nr_free = vol->mft_na->data_size >> vol->mft_record_size_bits;
+ s64 br, total = 0;
+ u8 *buf;
+
+ buf = ntfs_malloc(vol->cluster_size);
+ if (!buf)
+ return -1;
+ while (1) {
+ int i, j;
+
+ br = ntfs_attr_pread(vol->mftbmp_na, total,
+ vol->cluster_size, buf);
+ if (br <= 0)
+ break;
+ total += br;
+ for (i = 0; i < br; i++)
+ for (j = 0; j < 8; j++)
+ if ((buf[i] >> j) & 1)
+ nr_free--;
+ }
+ free(buf);
+ if (!total || br < 0) {
+ ntfs_log_error("pread: %s\n", strerror(errno));
+ return -1;
+ }
+ vol->nr_free_mft_records = nr_free;
+ return 0;
+}
+
+/**
+ * ntfs_volume_get_nr_free_clusters - calculate number of free clusters
+ * vol: ntfs volume for which perform calculations.
+ *
+ * This function initializes @vol->nr_free_clusters. @vol->lcnbmp_na should be
+ * already opened upon call to this function.
+ *
+ * Return 0 on success. On error return -1 with errno set appropriately and
+ * @vol->nr_free_clusters is not touched in this case.
+ */
+static long ntfs_volume_get_nr_free_clusters(ntfs_volume *vol)
+{
+ long nr_free = vol->nr_clusters;
+ s64 br, total = 0;
+ u8 *buf;
+
+ buf = ntfs_malloc(vol->cluster_size);
+ if (!buf)
+ return -1;
+ while (1) {
+ int i, j;
+
+ br = ntfs_attr_pread(vol->lcnbmp_na, total,
+ vol->cluster_size, buf);
+ if (br <= 0)
+ break;
+ total += br;
+ for (i = 0; i < br; i++)
+ for (j = 0; j < 8; j++)
+ if ((buf[i] >> j) & 1)
+ nr_free--;
+ }
+ free(buf);
+ if (!total || br < 0) {
+ ntfs_log_error("pread: %s\n", strerror(errno));
+ return -1;
+ }
+ vol->nr_free_clusters = nr_free;
+ return 0;
+}
+
+/**
+ * ntfs_device_mount - open ntfs volume
+ * @dev: device to open
+ * @flags: optional mount flags
+ *
+ * This function mounts an ntfs volume. @dev should describe the device which
+ * to mount as the ntfs volume.
+ *
+ * @flags is an optional second parameter. Some flags are similar to flags used
+ * as for the mount system call (man 2 mount). Currently the following flags
+ * are implemented:
+ * NTFS_MNT_RDONLY - mount volume read-only
+ * NTFS_MNT_CASE_SENSITIVE - treat filenames as case sensitive even if
+ * they are not in POSIX namespace
+ * NTFS_MNT_NOT_EXCLUSIVE - (unix only) do not open volume exclusively
+ * NTFS_MNT_FORENSIC - mount for forensic purposes, i.e. do not do
+ * any writing at all during the mount, i.e. no
+ * journal emptying, no dirty bit setting, etc.
+ * NTFS_MNT_INTERIX - make libntfs recognize special Interix files
+ *
+ * The function opens the device @dev and verifies that it contains a valid
+ * bootsector. Then, it allocates an ntfs_volume structure and initializes
+ * some of the values inside the structure from the information stored in the
+ * bootsector. It proceeds to load the necessary system files and completes
+ * setting up the structure.
+ *
+ * Return the allocated volume structure on success and NULL on error with
+ * errno set to the error code.
+ */
+ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
+{
+ s64 l;
+#ifdef DEBUG
+ const char *OK = "OK\n";
+ const char *FAILED = "FAILED\n";
+#endif
+ ntfs_volume *vol;
+ u8 *m = NULL, *m2 = NULL;
+ ntfs_attr_search_ctx *ctx = NULL;
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ ATTR_RECORD *a;
+ VOLUME_INFORMATION *vinf;
+ ntfschar *vname;
+ int i, j, eo;
+ u32 u;
+
+ vol = ntfs_volume_startup(dev, flags);
+ if (!vol) {
+ ntfs_log_perror("Failed to startup volume");
+ return NULL;
+ }
+ /* Record whether this is a forensic mount. */
+ if (flags & NTFS_MNT_FORENSIC)
+ NVolSetForensicMount(vol);
+ /* Load data from $MFT and $MFTMirr and compare the contents. */
+ m = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits);
+ m2 = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits);
+ if (!m || !m2)
+ goto error_exit;
+
+ l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size,
+ vol->mft_record_size, m);
+ if (l != vol->mftmirr_size) {
+ if (l == -1)
+ ntfs_log_perror("Failed to read $MFT");
+ else {
+ ntfs_log_debug("Failed to read $MFT, unexpected length "
+ "(%d != %lld).\n", vol->mftmirr_size, l);
+ errno = EIO;
+ }
+ goto error_exit;
+ }
+ l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size,
+ vol->mft_record_size, m2);
+ if (l != vol->mftmirr_size) {
+ if (l == -1)
+ ntfs_log_perror("Failed to read $MFTMirr");
+ else {
+ ntfs_log_debug("Failed to read $MFTMirr, unexpected "
+ "length (%d != %lld).\n",
+ vol->mftmirr_size, l);
+ errno = EIO;
+ }
+ goto error_exit;
+ }
+ ntfs_log_debug("Comparing $MFTMirr to $MFT... ");
+ for (i = 0; i < vol->mftmirr_size; ++i) {
+ MFT_RECORD *mrec, *mrec2;
+ const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile",
+ "$Volume", "$AttrDef", "root directory", "$Bitmap",
+ "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" };
+ const char *s;
+
+ if (i < 12)
+ s = ESTR[i];
+ else if (i < 16)
+ s = "system file";
+ else
+ s = "mft record";
+
+ mrec = (MFT_RECORD*)(m + i * vol->mft_record_size);
+ if (mrec->flags & MFT_RECORD_IN_USE) {
+ if (ntfs_is_baad_record(mrec->magic)) {
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("$MFT error: Incomplete multi "
+ "sector transfer detected in "
+ "%s.\n", s);
+ goto io_error_exit;
+ }
+ if (!ntfs_is_mft_record(mrec->magic)) {
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("$MFT error: Invalid mft "
+ "record for %s.\n", s);
+ goto io_error_exit;
+ }
+ }
+ mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size);
+ if (mrec2->flags & MFT_RECORD_IN_USE) {
+ if (ntfs_is_baad_record(mrec2->magic)) {
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("$MFTMirr error: Incomplete "
+ "multi sector transfer "
+ "detected in %s.\n", s);
+ goto io_error_exit;
+ }
+ if (!ntfs_is_mft_record(mrec2->magic)) {
+ ntfs_log_debug("FAILED\n");
+ ntfs_log_debug("$MFTMirr error: Invalid mft "
+ "record for %s.\n", s);
+ goto io_error_exit;
+ }
+ }
+ if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("$MFTMirr does not match $MFT. Run "
+ "chkdsk.\n");
+ goto io_error_exit;
+ }
+ }
+ ntfs_log_debug(OK);
+
+ free(m2);
+ free(m);
+ m = m2 = NULL;
+
+ /* Now load the bitmap from $Bitmap. */
+ ntfs_log_debug("Loading $Bitmap... ");
+ vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap);
+ if (!vol->lcnbmp_ni) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open inode");
+ goto error_exit;
+ }
+ /* Get an ntfs attribute for $Bitmap/$DATA. */
+ vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0);
+ if (!vol->lcnbmp_na) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open ntfs attribute");
+ goto error_exit;
+ }
+ /* Done with the $Bitmap mft record. */
+ ntfs_log_debug(OK);
+
+ /* Now load the upcase table from $UpCase. */
+ ntfs_log_debug("Loading $UpCase... ");
+ ni = ntfs_inode_open(vol, FILE_UpCase);
+ if (!ni) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open inode");
+ goto error_exit;
+ }
+ /* Get an ntfs attribute for $UpCase/$DATA. */
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open ntfs attribute");
+ goto error_exit;
+ }
+ /*
+ * Note: Normally, the upcase table has a length equal to 65536
+ * 2-byte Unicode characters but allow for different cases, so no
+ * checks done. Just check we don't overflow 32-bits worth of Unicode
+ * characters.
+ */
+ if (na->data_size & ~0x1ffffffffULL) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Upcase table is too big (max 32-bit "
+ "allowed).\n");
+ errno = EINVAL;
+ goto error_exit;
+ }
+ if (vol->upcase_len != na->data_size >> 1) {
+ vol->upcase_len = na->data_size >> 1;
+ /* Throw away default table. */
+ free(vol->upcase);
+ vol->upcase = (ntfschar*)ntfs_malloc(na->data_size);
+ if (!vol->upcase) {
+ ntfs_log_debug(FAILED);
+ goto error_exit;
+ }
+ }
+ /* Read in the $DATA attribute value into the buffer. */
+ l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase);
+ if (l != na->data_size) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Amount of data read does not correspond to "
+ "expected length!\n");
+ errno = EIO;
+ goto error_exit;
+ }
+ /* Done with the $UpCase mft record. */
+ ntfs_log_debug(OK);
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ ntfs_log_perror("Failed to close inode, leaking memory");
+
+ /*
+ * Now load $Volume and set the version information and flags in the
+ * vol structure accordingly.
+ */
+ ntfs_log_debug("Loading $Volume... ");
+ vol->vol_ni = ntfs_inode_open(vol, FILE_Volume);
+ if (!vol->vol_ni) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open inode");
+ goto error_exit;
+ }
+ /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */
+ ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
+ if (!ctx) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to allocate attribute search context");
+ goto error_exit;
+ }
+ /* Find the $VOLUME_INFORMATION attribute. */
+ if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL,
+ 0, ctx)) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("$VOLUME_INFORMATION attribute not found in "
+ "$Volume?!?\n");
+ goto error_exit;
+ }
+ a = ctx->attr;
+ /* Has to be resident. */
+ if (a->non_resident) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION must be "
+ "resident (and it isn't)!\n");
+ errno = EIO;
+ goto error_exit;
+ }
+ /* Get a pointer to the value of the attribute. */
+ vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->u.res.value_offset) + (char*)a);
+ /* Sanity checks. */
+ if ((char*)vinf + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec +
+ le32_to_cpu(ctx->mrec->bytes_in_use) ||
+ le16_to_cpu(a->u.res.value_offset) + le32_to_cpu(
+ a->u.res.value_length) > le32_to_cpu(a->length)) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION in "
+ "$Volume is corrupt!\n");
+ errno = EIO;
+ goto error_exit;
+ }
+ /* Setup vol from the volume information attribute value. */
+ vol->major_ver = vinf->major_ver;
+ vol->minor_ver = vinf->minor_ver;
+ /*
+ * Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are defined
+ * using cpu_to_le16() macro and hence are consistent.
+ */
+ vol->flags = vinf->flags;
+ /* Record whether the volume was dirty or not. */
+ if (vol->flags & VOLUME_IS_DIRTY)
+ NVolSetWasDirty(vol);
+ /*
+ * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup.
+ */
+ ntfs_attr_reinit_search_ctx(ctx);
+ if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0,
+ ctx)) {
+ if (errno != ENOENT) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Lookup of $VOLUME_NAME "
+ "attribute in $Volume failed. "
+ "This probably means something is "
+ "corrupt. Run chkdsk.\n");
+ goto error_exit;
+ }
+ /*
+ * Attribute not present. This has been seen in the field.
+ * Treat this the same way as if the attribute was present but
+ * had zero length.
+ */
+ vol->vol_name = ntfs_malloc(1);
+ if (!vol->vol_name) {
+ ntfs_log_debug(FAILED);
+ goto error_exit;
+ }
+ vol->vol_name[0] = '\0';
+ } else {
+ a = ctx->attr;
+ /* Has to be resident. */
+ if (a->non_resident) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Attribute $VOLUME_NAME must be "
+ "resident!\n");
+ errno = EIO;
+ goto error_exit;
+ }
+ /* Get a pointer to the value of the attribute. */
+ vname = (ntfschar*)(le16_to_cpu(a->u.res.value_offset) + (char*)a);
+ u = le32_to_cpu(a->u.res.value_length) / 2;
+ /*
+ * Convert Unicode volume name to current locale multibyte
+ * format.
+ */
+ vol->vol_name = NULL;
+ if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) {
+ ntfs_log_perror("Error: Volume name could not be "
+ "converted to current locale");
+ ntfs_log_debug("Forcing name into ASCII by replacing "
+ "non-ASCII characters with underscores.\n");
+ vol->vol_name = ntfs_malloc(u + 1);
+ if (!vol->vol_name) {
+ ntfs_log_debug(FAILED);
+ goto error_exit;
+ }
+ for (j = 0; j < (s32)u; j++) {
+ u16 uc = le16_to_cpu(vname[j]);
+ if (uc > 0xff)
+ uc = (u16)'_';
+ vol->vol_name[j] = (char)uc;
+ }
+ vol->vol_name[u] = 0;
+ }
+ }
+ ntfs_log_debug(OK);
+ ntfs_attr_put_search_ctx(ctx);
+ ctx = NULL;
+ /* Now load the attribute definitions from $AttrDef. */
+ ntfs_log_debug("Loading $AttrDef... ");
+ ni = ntfs_inode_open(vol, FILE_AttrDef);
+ if (!ni) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open inode");
+ goto error_exit;
+ }
+ /* Get an ntfs attribute for $AttrDef/$DATA. */
+ na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
+ if (!na) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_perror("Failed to open ntfs attribute");
+ goto error_exit;
+ }
+ /* Check we don't overflow 32-bits. */
+ if (na->data_size > 0xffffffffLL) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Error: Attribute definition table is too big "
+ "(max 32-bit allowed).\n");
+ errno = EINVAL;
+ goto error_exit;
+ }
+ vol->attrdef_len = na->data_size;
+ vol->attrdef = (ATTR_DEF*)ntfs_malloc(na->data_size);
+ if (!vol->attrdef) {
+ ntfs_log_debug(FAILED);
+ goto error_exit;
+ }
+ /* Read in the $DATA attribute value into the buffer. */
+ l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef);
+ if (l != na->data_size) {
+ ntfs_log_debug(FAILED);
+ ntfs_log_debug("Amount of data read does not correspond to "
+ "expected length!\n");
+ errno = EIO;
+ goto error_exit;
+ }
+ /* Done with the $AttrDef mft record. */
+ ntfs_log_debug(OK);
+ ntfs_attr_close(na);
+ if (ntfs_inode_close(ni))
+ ntfs_log_perror("Failed to close inode, leaking memory");
+ /* Initialize number of free clusters and MFT records. */
+ if (ntfs_volume_get_nr_free_mft_records(vol)) {
+ ntfs_log_perror("Failed to calculate number of free MFTs");
+ goto error_exit;
+ }
+ if (ntfs_volume_get_nr_free_clusters(vol)) {
+ ntfs_log_perror("Failed to calculate number of free clusters");
+ goto error_exit;
+ }
+ /*
+ * Check for dirty logfile and hibernated Windows.
+ * We care only about read-write mounts.
+ *
+ * If all is ok, reset the logfile and set the dirty bit on the volume.
+ *
+ * But do not do that if this is a FORENSIC mount.
+ */
+ if (!(flags & NTFS_MNT_RDONLY)) {
+ if (ntfs_volume_check_hiberfile(vol) < 0)
+ goto error_exit;
+ if (ntfs_volume_check_logfile(vol) < 0) {
+ if (errno != EOPNOTSUPP || !(flags & NTFS_MNT_FORCE))
+ goto error_exit;
+ ntfs_log_warning("WARNING: $LogFile is not clean, "
+ "forced to continue.\n");
+ NVolSetWasDirty(vol); /* Leave volume dirty since we
+ empted logfile. */
+ }
+ if (!NVolForensicMount(vol)) {
+ if (ntfs_logfile_reset(vol) < 0)
+ goto error_exit;
+ if (!(vol->flags & VOLUME_IS_DIRTY)) {
+ vol->flags |= VOLUME_IS_DIRTY;
+ if (ntfs_volume_write_flags(vol, vol->flags) <
+ 0)
+ goto error_exit;
+ }
+ }
+ }
+ return vol;
+io_error_exit:
+ errno = EIO;
+error_exit:
+ eo = errno;
+ if (ctx)
+ ntfs_attr_put_search_ctx(ctx);
+ free(m);
+ free(m2);
+ __ntfs_volume_release(vol);
+ errno = eo;
+ return NULL;
+}
+
+/**
+ * ntfs_mount - open ntfs volume
+ * @name: name of device/file to open
+ * @flags: optional mount flags
+ *
+ * This function mounts an ntfs volume. @name should contain the name of the
+ * device/file to mount as the ntfs volume.
+ *
+ * @flags is an optional second parameter. See ntfs_device_mount comment for
+ * description.
+ *
+ * The function opens the device or file @name and verifies that it contains a
+ * valid bootsector. Then, it allocates an ntfs_volume structure and initializes
+ * some of the values inside the structure from the information stored in the
+ * bootsector. It proceeds to load the necessary system files and completes
+ * setting up the structure.
+ *
+ * Return the allocated volume structure on success and NULL on error with
+ * errno set to the error code.
+ *
+ * Note, that a copy is made of @name, and hence it can be discarded as
+ * soon as the function returns.
+ */
+ntfs_volume *ntfs_mount(const char *name __attribute__((unused)),
+ ntfs_mount_flags flags __attribute__((unused)))
+{
+#ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS
+ struct ntfs_device *dev;
+ ntfs_volume *vol;
+
+ /* Allocate an ntfs_device structure. */
+ dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL);
+ if (!dev)
+ return NULL;
+ /* Call ntfs_device_mount() to do the actual mount. */
+ vol = ntfs_device_mount(dev, flags);
+ if (!vol) {
+ int eo = errno;
+ ntfs_device_free(dev);
+ errno = eo;
+ }
+ return vol;
+#else
+ /*
+ * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is
+ * defined as there are no device operations available in libntfs in
+ * this case.
+ */
+ errno = EOPNOTSUPP;
+ return NULL;
+#endif
+}
+
+/**
+ * ntfs_device_umount - close ntfs volume
+ * @vol: address of ntfs_volume structure of volume to close
+ * @force: if true force close the volume even if it is busy
+ *
+ * Deallocate all structures (including @vol itself) associated with the ntfs
+ * volume @vol.
+ *
+ * Note it is up to the caller to destroy the device associated with the volume
+ * being unmounted after this function returns.
+ *
+ * Return 0 on success. On error return -1 with errno set appropriately
+ * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that
+ * an operation is in progress and if you try the close later the operation
+ * might be completed and the close succeed.
+ *
+ * If @force is true (i.e. not zero) this function will close the volume even
+ * if this means that data might be lost.
+ *
+ * @vol must have previously been returned by a call to ntfs_device_mount().
+ *
+ * @vol itself is deallocated and should no longer be dereferenced after this
+ * function returns success. If it returns an error then nothing has been done
+ * so it is safe to continue using @vol.
+ */
+int ntfs_device_umount(ntfs_volume *vol,
+ const BOOL force __attribute__((unused)))
+{
+ if (!vol) {
+ errno = EINVAL;
+ return -1;
+ }
+ __ntfs_volume_release(vol);
+ return 0;
+}
+
+/**
+ * ntfs_umount - close ntfs volume
+ * @vol: address of ntfs_volume structure of volume to close
+ * @force: if true force close the volume even if it is busy
+ *
+ * Deallocate all structures (including @vol itself) associated with the ntfs
+ * volume @vol.
+ *
+ * Return 0 on success. On error return -1 with errno set appropriately
+ * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that
+ * an operation is in progress and if you try the close later the operation
+ * might be completed and the close succeed.
+ *
+ * If @force is true (i.e. not zero) this function will close the volume even
+ * if this means that data might be lost.
+ *
+ * @vol must have previously been returned by a call to ntfs_mount().
+ *
+ * @vol itself is deallocated and should no longer be dereferenced after this
+ * function returns success. If it returns an error then nothing has been done
+ * so it is safe to continue using @vol.
+ */
+int ntfs_umount(ntfs_volume *vol,
+ const BOOL force __attribute__((unused)))
+{
+ struct ntfs_device *dev;
+
+ if (!vol) {
+ errno = EINVAL;
+ return -1;
+ }
+ dev = vol->u.dev;
+ __ntfs_volume_release(vol);
+ ntfs_device_free(dev);
+ return 0;
+}
+
+#ifdef HAVE_MNTENT_H
+
+#ifndef HAVE_REALPATH
+/**
+ * realpath - If there is no realpath on the system
+ */
+static char *realpath(const char *path, char *resolved_path)
+{
+ strncpy(resolved_path, path, PATH_MAX);
+ resolved_path[PATH_MAX] = '\0';
+ return resolved_path;
+}
+#endif
+
+/**
+ * ntfs_mntent_check - desc
+ *
+ * If you are wanting to use this, you actually wanted to use
+ * ntfs_check_if_mounted(), you just didn't realize. (-:
+ *
+ * See description of ntfs_check_if_mounted(), below.
+ */
+static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags)
+{
+ struct mntent *mnt;
+ char *real_file = NULL, *real_fsname = NULL;
+ FILE *f;
+ int err = 0;
+
+ real_file = ntfs_malloc(PATH_MAX + 1);
+ if (!real_file)
+ return -1;
+ real_fsname = ntfs_malloc(PATH_MAX + 1);
+ if (!real_fsname) {
+ err = errno;
+ goto exit;
+ }
+ if (!realpath(file, real_file)) {
+ err = errno;
+ goto exit;
+ }
+ if (!(f = setmntent(MOUNTED, "r"))) {
+ err = errno;
+ goto exit;
+ }
+ while ((mnt = getmntent(f))) {
+ if (!realpath(mnt->mnt_fsname, real_fsname))
+ continue;
+ if (!strcmp(real_file, real_fsname))
+ break;
+ }
+ endmntent(f);
+ if (!mnt)
+ goto exit;
+ *mnt_flags = NTFS_MF_MOUNTED;
+ if (!strcmp(mnt->mnt_dir, "/"))
+ *mnt_flags |= NTFS_MF_ISROOT;
+#ifdef HAVE_HASMNTOPT
+ if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw"))
+ *mnt_flags |= NTFS_MF_READONLY;
+#endif
+exit:
+ free(real_file);
+ free(real_fsname);
+ if (err) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+#endif /* HAVE_MNTENT_H */
+
+/**
+ * ntfs_check_if_mounted - check if an ntfs volume is currently mounted
+ * @file: device file to check
+ * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h)
+ *
+ * If the running system does not support the {set,get,end}mntent() calls,
+ * just return 0 and set *@mnt_flags to zero.
+ *
+ * When the system does support the calls, ntfs_check_if_mounted() first tries
+ * to find the device @file in /etc/mtab (or wherever this is kept on the
+ * running system). If it is not found, assume the device is not mounted and
+ * return 0 and set *@mnt_flags to zero.
+ *
+ * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags.
+ *
+ * Further if @file is mounted as the file system root ("/"), set the flag
+ * NTFS_MF_ISROOT in *@mnt_flags.
+ *
+ * Finally, check if the file system is mounted read-only, and if so set the
+ * NTFS_MF_READONLY flag in *@mnt_flags.
+ *
+ * On success return 0 with *@mnt_flags set to the ntfs mount flags.
+ *
+ * On error return -1 with errno set to the error code.
+ */
+int ntfs_check_if_mounted(const char *file __attribute__((unused)),
+ unsigned long *mnt_flags)
+{
+ *mnt_flags = 0;
+#ifdef HAVE_MNTENT_H
+ return ntfs_mntent_check(file, mnt_flags);
+#else
+ return 0;
+#endif
+}
+
+/**
+ * ntfs_version_is_supported - check if NTFS version is supported.
+ * @vol: ntfs volume whose version we're interested in.
+ *
+ * The function checks if the NTFS volume version is known or not.
+ * Version 1.1 and 1.2 are used by Windows NT3.x and NT4.
+ * Version 2.x is used by Windows 2000 Betas.
+ * Version 3.0 is used by Windows 2000.
+ * Version 3.1 is used by Windows XP, Windows Server 2003 and Vista.
+ *
+ * Return 0 if NTFS version is supported otherwise -1 with errno set.
+ *
+ * The following error codes are defined:
+ * EOPNOTSUPP - Unknown NTFS version
+ * EINVAL - Invalid argument
+ */
+int ntfs_version_is_supported(ntfs_volume *vol)
+{
+ u8 major, minor;
+
+ if (!vol) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ major = vol->major_ver;
+ minor = vol->minor_ver;
+
+ if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor))
+ return 0;
+
+ if (NTFS_V2_X(major, minor))
+ return 0;
+
+ if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor))
+ return 0;
+
+ errno = EOPNOTSUPP;
+ return -1;
+}
+
+/**
+ * ntfs_logfile_reset - "empty" $LogFile data attribute value
+ * @vol: ntfs volume whose $LogFile we intend to reset.
+ *
+ * Fill the value of the $LogFile data attribute, i.e. the contents of
+ * the file, with 0xff's, thus marking the journal as empty.
+ *
+ * FIXME(?): We might need to zero the LSN field of every single mft
+ * record as well. (But, first try without doing that and see what
+ * happens, since chkdsk might pickup the pieces and do it for us...)
+ *
+ * On success return 0.
+ *
+ * On error return -1 with errno set to the error code.
+ */
+int ntfs_logfile_reset(ntfs_volume *vol)
+{
+ ntfs_inode *ni;
+ ntfs_attr *na;
+ int eo;
+
+ if (!vol) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) {
+ ntfs_log_perror("Failed to open inode FILE_LogFile.");
+ return -1;
+ }
+
+ if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) {
+ eo = errno;
+ ntfs_log_perror("Failed to open $FILE_LogFile/$DATA");
+ goto error_exit;
+ }
+
+ if (ntfs_empty_logfile(na)) {
+ eo = errno;
+ ntfs_log_perror("Failed to empty $FILE_LogFile/$DATA");
+ ntfs_attr_close(na);
+ goto error_exit;
+ }
+ ntfs_attr_close(na);
+ return ntfs_inode_close(ni);
+
+error_exit:
+ ntfs_inode_close(ni);
+ errno = eo;
+ return -1;
+}
+
+/**
+ * ntfs_volume_write_flags - set the flags of an ntfs volume
+ * @vol: ntfs volume where we set the volume flags
+ * @flags: new flags
+ *
+ * Set the on-disk volume flags in the mft record of $Volume and
+ * on volume @vol to @flags.
+ *
+ * Return 0 if successful and -1 if not with errno set to the error code.
+ */
+int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags)
+{
+ ATTR_RECORD *a;
+ VOLUME_INFORMATION *c;
+ ntfs_attr_search_ctx *ctx;
+ int ret = -1; /* failure */
+
+ if (!vol || !vol->vol_ni) {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Get a pointer to the volume information attribute. */
+ ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL);
+ if (!ctx) {
+ ntfs_log_perror("Failed to allocate attribute search context");
+ return -1;
+ }
+ if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL,
+ 0, ctx)) {
+ ntfs_log_error("Attribute $VOLUME_INFORMATION was not found "
+ "in $Volume!\n");
+ goto err_out;
+ }
+ a = ctx->attr;
+ /* Sanity check. */
+ if (a->non_resident) {
+ ntfs_log_error("Attribute $VOLUME_INFORMATION must be "
+ "resident (and it isn't)!\n");
+ errno = EIO;
+ goto err_out;
+ }
+ /* Get a pointer to the value of the attribute. */
+ c = (VOLUME_INFORMATION*)(le16_to_cpu(a->u.res.value_offset) + (char*)a);
+ /* Sanity checks. */
+ if ((char*)c + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec +
+ le32_to_cpu(ctx->mrec->bytes_in_use) ||
+ le16_to_cpu(a->u.res.value_offset) +
+ le32_to_cpu(a->u.res.value_length) > le32_to_cpu(a->length)) {
+ ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is "
+ "corrupt!\n");
+ errno = EIO;
+ goto err_out;
+ }
+ /* Set the volume flags. */
+ vol->flags = c->flags = flags & VOLUME_FLAGS_MASK;
+ /* Write them to disk. */
+ ntfs_inode_mark_dirty(vol->vol_ni);
+ if (ntfs_inode_sync(vol->vol_ni)) {
+ ntfs_log_perror("Error writing $Volume");
+ goto err_out;
+ }
+ ret = 0; /* success */
+err_out:
+ ntfs_attr_put_search_ctx(ctx);
+ if (ret)
+ ntfs_log_error("%s(): Failed.\n", "ntfs_volume_write_flags");
+ return ret;
+}
+
diff --git a/usr/src/lib/libntfs/common/mapfile-vers b/usr/src/lib/libntfs/common/mapfile-vers
new file mode 100644
index 0000000000..e8ff945aee
--- /dev/null
+++ b/usr/src/lib/libntfs/common/mapfile-vers
@@ -0,0 +1,138 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNW_1.1 {
+ AT_UNNAMED;
+ NTFS_INDEX_I30;
+ NTFS_INDEX_O;
+ NTFS_INDEX_Q;
+ NTFS_INDEX_R;
+ NTFS_INDEX_SDH;
+ NTFS_INDEX_SII;
+ ntfs_attr_add;
+ ntfs_attr_close;
+ ntfs_attr_find_in_attrdef;
+ ntfs_attr_get_search_ctx;
+ ntfs_attr_lookup;
+ ntfs_attr_mst_pread;
+ ntfs_attr_open;
+ ntfs_attr_pread;
+ ntfs_attr_put_search_ctx;
+ ntfs_attr_pwrite;
+ ntfs_attr_readall;
+ __ntfs_attr_truncate;
+ ntfs_boot_sector_is_ntfs;
+ ntfs_calloc;
+ ntfs_check_if_mounted;
+ ntfs_cluster_read;
+ ntfs_create;
+ ntfs_device_alloc;
+ ntfs_device_block_size_set;
+ ntfs_device_free;
+ ntfs_device_heads_get;
+ ntfs_device_partition_start_sector_get;
+ ntfs_device_sector_size_get;
+ ntfs_device_sectors_per_track_get;
+ ntfs_device_size_get;
+ ntfs_device_unix_io_ops;
+ ntfs_file_record_read;
+ ntfs_file_record_read;
+ ntfs_file_values_compare;
+ ntfs_get_attribute_value;
+ ntfs_get_attribute_value_length;
+ ntfs_get_size_for_mapping_pairs;
+ ntfs_guid_is_zero;
+ ntfs_guid_to_mbs;
+ ntfs_ie_get_vcn;
+ ntfs_index_root_get;
+ ntfs_inode_badclus_bad;
+ ntfs_inode_close;
+ ntfs_inode_open;
+ ntfs_inode_sync;
+ ntfs_libntfs_version;
+ ntfs_log_clear_levels;
+ ntfs_logfile_reset;
+ ntfs_log_get_levels;
+ ntfs_log_handler_outerr;
+ ntfs_log_handler_stderr;
+ ntfs_log_parse_option;
+ ntfs_log_redirect;
+ ntfs_log_set_handler;
+ ntfs_log_set_levels;
+ ntfs_malloc;
+ ntfs_mapping_pairs_build;
+ ntfs_mapping_pairs_decompress;
+ ntfs_mbstoucs;
+ ntfs_mft_record_layout;
+ ntfs_mft_records_write;
+ ntfs_mft_usn_dec;
+ ntfs_mount;
+ ntfs_mst_post_read_fixup;
+ ntfs_mst_post_write_fixup;
+ ntfs_mst_pre_write_fixup;
+ ntfs_mst_pwrite;
+ ntfs_names_are_equal;
+ ntfs_names_collate;
+ ntfs_pathname_to_inode;
+ ntfs_readdir;
+ ntfs_resident_attr_value_resize;
+ ntfs_rl_pread;
+ ntfs_rl_pwrite;
+ ntfs_rl_truncate;
+ ntfs_rl_vcn_to_lcn;
+ ntfs_sid_to_mbs;
+ ntfs_str2ucs;
+ ntfs_ucsfree;
+ ntfs_ucsnlen;
+ ntfs_ucstombs;
+ ntfs_umount;
+ ntfs_upcase_table_build;
+ ntfs_version_is_supported;
+ ntfs_volume_alloc;
+ ntfs_volume_startup;
+ ntfs_volume_write_flags;
+ global:
+};
+
+SUNWprivate_1.1 {
+ global:
+ SUNWprivate_1.1;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libntfs/i386/Makefile b/usr/src/lib/libntfs/i386/Makefile
new file mode 100644
index 0000000000..0925ec1791
--- /dev/null
+++ b/usr/src/lib/libntfs/i386/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+SUBDIRS= pics/$(LIBNTFSDIR)
+
+.KEEP_STATE:
+
+all: $(SUBDIRS) $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(DYNLINKLIB)
+
+$(SUBDIRS):
+ mkdir -p $@
diff --git a/usr/src/lib/libparted/AUTHORS b/usr/src/lib/libparted/AUTHORS
new file mode 100644
index 0000000000..e955e6f58c
--- /dev/null
+++ b/usr/src/lib/libparted/AUTHORS
@@ -0,0 +1,233 @@
+This file is part of GNU Parted
+Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation Inc.
+
+This file may be modified and/or distributed without restriction. This is
+not an invitation to misrepresent who contributed to GNU Parted.
+
+-------------------------------------------------------------------------------
+
+We need to keep track of copyright (see the Maintainer HOWTO on www.gnu.org).
+
+
+Leslie Patrick Polzer <polzer@gnu.org>
+ * parts of 1.6.24 PedUnit API
+ * GPT pth_* functions and block size fixes
+ * PED_SECTOR_SIZE -> PED_SECTOR_SIZE_DEFAULT
+ * debugging framework
+ * ext2 "strange layout" fix (experimental)
+ * support for physical block sizes
+ * SCO BFS support
+
+ http://nic-nac-project.de/~skypher/
+
+ Snail Mail:
+ Am Kirchberg 15
+ 89537 Giengen
+ Germany
+
+Andrew Clausen <clausen@gnu.org>
+ * all FAT code (libparted/fs_fat)
+ * all linux-swap code (libparted/fs_linux_swap)
+ * some reiserfs glue code (libparted/fs_reiserfs) with Umanets
+ * most of the API (with lots of discussion with Lennert Buytenhek)
+ (include/parted/*)
+ * generic filesystem code (filesys.c) and device code (device.c)
+ * exception code (exception.c, debug.c)
+ * partition table code (disk.c)
+ * dos partition support (disk_dos.c)
+ * mac partition support (disk_mac.c)
+ * mips partition support (disk_mips.c)
+ * loopback support (disk_loop.c)
+ * some of the PC98 code (disk_pc98.c), with Masahiro Sakai.
+ * misc. hacking on all disk_*.c code
+ * low-level I/O code (device.c, geom.c, linux.c)
+ - init_scsi() stolen from gnome-gfdisk via Matt Wilson.
+ * constraint solver (natmath.c, constraint.c)
+ * command-line, and fdisk-like frontend (parted/*)
+ * stubs for ntfs, hfs, xfs
+ * hacked on libparted/fs_ext2 a fair bit (bug fixes, error handling,
+ support for > 1024 groups, etc.)
+ * libparted/mbr.s (the MBR boot loader code)
+ * misc hacking on GNU/Hurd port
+ * major surgery on GUID Partition Table (GPT) support (disk_gpt.[ch])
+ * progress meter support (libparted/timer.c include/parted/timer.h)
+
+ Snail mail:
+ 18 Shaw St
+ Ashwood, 3147
+ Australia
+
+Lennert Buytenhek <buytenh@gnu.org>
+ * original ext2 code (libparted/fs_ext2)
+ * discussion/ideas for API
+
+Matthew Wilson <msw@redhat.com>
+ * basis of partition table and device code (disk.c, disk_dos.c,
+ and device.c) Has morphed into something that looks completely
+ different now :-)
+ * bug fixes
+ * BSD disklabel support (disk_bsd.c)
+ * Python bindings to libparted
+ * Don't detect AIX physical volumes as msdos partition tables
+ * Code for manipulating AIX PVs
+
+Martin von Löwis <martin@mira.isdn.cs.tu-berlin.de>
+ * German translations
+
+Baty Jean-Marc <baty@club-internet.fr>
+ * French translations
+
+Hiroshi Takekawa <takekawa@sr3.t.u-tokyo.ac.jp>
+ * Japanese translations
+
+Eliphas Levy Theodoro <eliphas@conectiva.com>
+ * Brazillian Portugese translations
+
+Dmitry S. Sivachenko <dima@Chg.RU>
+ * Russian translations
+
+Timshel Knoll <timshel@pobox.com>
+ * man pages (parted.8 partprobe.8)
+ * bug fixes
+
+Ivo Timmermans <itimmermans@bigfoot.com>
+ * Dutch translations
+
+Ryoji Kawagishi <kawagisi@yk.rim.or.jp>
+ * Japanese translation of doc/USER (user documentation)
+ (replaced by Okuji's version, now)
+
+Okuji Yoshinori <okuji@kuicr.kyoto-a.ac.jp>
+ * doc/USER.jp
+ * contributions to Japanese translations
+
+Masahiro Sakai <ZVM01052@nifty.ne.jp>
+ * most of the PC98 support (disk_pc98.c), with Andrew Clausen
+ * lots of tedious work on parted/strlist.c
+
+Damien Genet <damien.genet@free.fr>
+ * parted.m4
+
+Ben Collins <bcollins@debian.org>
+ * Sun disk label support (libparted/disk_sun.c)
+ * stubs for UFS
+
+Vincent Stelhé <vincent.stelhe@free.fr>
+ * move syntax patch (make END specification optional). Trivial
+ for copyright purposes (no disclaimer needed)
+
+Neal H Walfield <neal@cs.uml.edu>
+ * GNU/Hurd port - libparted/gnu.c
+
+Thomas Roelz <tom@suse.de>
+ * misc bug fixes
+
+Matt Domsch <Matt_Domsch@dell.com>
+ * GUID Partition Table (GPT) support (disk_gpt.[ch],
+ crc32.[ch])
+ * misc bug fixes, including end-of-device workaround
+ in libparted/linux.c
+
+Kjetil Torgrim Homme <kjetilho@linpro.no>
+ * Norweigen translations
+
+Jörgen Tegnér <jorgen.tegner@telia.com>
+ * Swedish translations
+
+Keld Simonsen <keld@dkuug.dk>
+ * Danish translations
+
+Richard M. Kreuter <kreuter@ausar.rutgers.edu>
+ * converted doc/USER to texinfo (doc/parted.texi)
+
+Miquel Matas <miquelmatas@wanadoo.es>
+ * Catalan translations
+
+Andreas Dilger <adilger@clusterfs.com>
+ * lots of mix bug fixes/cleanups
+
+Vicente E. Llorens <vllorens@mundofree.com>
+ * Spanish translations
+
+Yury Umanets <torque@ukrpost.net>
+ * basis of libparted/fs_reiserfs
+
+Bernardo João Torres da Silveira
+ <bernardojts@ig.com.br>
+ * pt_BR translation of FAQ and parted.texi
+
+Wojciech Polak <polak@gnu.org>
+ * Polish translations
+
+Miloslav Trmac <mitr@volny.cz>
+ * Czech translations
+
+Maxim V. Dziumanenko <mvd@mylinux.com.ua>
+ * Ukrainian translations
+
+Giuseppe Sacco <eppesuig@debian.org>
+ * Italian translations
+
+Guillaume Knispel <k_guillaume@libertysurf.fr>
+ * nearly all hfs and hfs+ code (libparted/fs_hfs)
+ * hfs+ support for mac partitions (disk_mac.c)
+ * sync_fast code (linux.c gnu.c geom.[ch] device.[ch] )
+ * various fixes (parted.c ui.c filesys.c disk_dos.c disk.c
+ doc/parted.texi doc/API disk_gpt.c disk_mac.c unit.c fs_fat/traverse.c)
+
+Chris Lumens <clumens@redhat.com>
+ * interactive help fixes for filesystem types
+ * gcc-4 pedanticism cleanups
+
+Wei-Lun Chao <chaoweilun@pcmail.com.tw>
+ * Taiwanese dialect of Chinese (Cantonese?) translations.
+
+Tran Thi Hoang Quyen <banhdauxanhhaiduong@gmail.com>
+ * Vietnamese translations
+
+Eduardo Maestri Righes <eduardo@tteng.com.br>
+ * hidden partitions support
+ * setting MS Reserved partitions through "set" command.
+
+Arif E. Nugroho <arif_endro@yahoo.com>
+ * Indonesian translations
+
+Ithamar R. Adema <ithamar@unet.nl>
+ * BeOS support
+
+David Cantrell <dcantrell@redhat.com>
+ * Added support for Promise SX8 devices (original patch from Jeremy
+ Katz <katzj@redhat.com>)
+ * Brought IBM/Red Hat DASD patches forward to parted-1.7.x
+ Original IBM authors: Volker Sameske <sameske@de.ibm.com>
+ Horst Hummel <Horst.Hummel@de.ibm.com>
+ Original Red Hat authors: Phil Knirsch <phil@redhat.de>
+ Harald Hoyer <harald@redhat.de>
+ * Prevent SIGFPE when FAT sector size is 0
+ * Add virtual DASD (VIODASD) on iSeries support
+ * Use O_DIRECT I/O to prevent first partition corruption on GPT disks
+ * Prevent sector reading exceptions from ped_geometry_check() on Mac
+ disklabels.
+ * Various bug fixes and other things.
+
+Peter Jones <pjones@redhat.com>
+ * Add ped_exception_get_handler()
+ * /dev/mapper read/write support on Linux (via libdevmapper)
+
+Darren Lavender <dl1@hppine99.gbr.hp.com>
+ * Fix SIGSEGV in parted 1.6.19 and assertion error in 1.7.0
+ * Add support for LUN/device resize detection and option GPT
+ header corruption
+ * Fixed off-by-one error in GPT header that allowed for overlap
+ between LDAs of LastUsableLBA and PartitionEntryLBA in backup GPT
+
+Olaf Hering <olh@suse.de>
+ * Fixes for Macintosh disk label code
+
+Debarshi Ray <rishi@gnu.org>
+ * Display disk model and transport type in parted(8).
+ * '--list/-l' command line switch.
+ * Introduce 'print devices' and alias 'print list' to 'print all'.
+ * Alias 'mktable' to 'mklabel'.
+ * Code and API clean-up, bug fixes, and other miscellaneous stuff.
diff --git a/usr/src/lib/libparted/COPYING b/usr/src/lib/libparted/COPYING
new file mode 100644
index 0000000000..4432540474
--- /dev/null
+++ b/usr/src/lib/libparted/COPYING
@@ -0,0 +1,676 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state 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) <year> <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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/usr/src/lib/libparted/Makefile b/usr/src/lib/libparted/Makefile
new file mode 100644
index 0000000000..8786ec1596
--- /dev/null
+++ b/usr/src/lib/libparted/Makefile
@@ -0,0 +1,102 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS = $(MACH)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+delete := TARGET= delete
+install := TARGET= install
+_msg := TARGET= _msg
+package := TARGET= package
+
+LIBRARY= libparted.a
+TEXT_DOMAIN= SUNW_OST_OSLIB
+XGETFLAGS= -a
+POFILE= $(LIBRARY:.a=.po)
+POFILES= generic.po
+
+SED= sed
+GREP= grep
+
+.KEEP_STATE:
+
+all clean clobber delete install package: $(SUBDIRS)
+
+# definitions for install_h target
+HDRS= ../common/include/parted/constraint.h \
+ ../common/include/parted/crc32.h \
+ ../common/include/parted/debug.h \
+ ../common/include/parted/device.h \
+ ../common/include/parted/disk.h \
+ ../common/include/parted/endian.h \
+ ../common/include/parted/exception.h \
+ ../common/include/parted/fdasd.h \
+ ../common/include/parted/filesys.h \
+ ../common/include/parted/geom.h \
+ ../common/include/parted/gnu.h \
+ ../common/include/parted/linux.h \
+ ../common/include/parted/natmath.h \
+ ../common/include/parted/parted.h \
+ ../common/include/parted/solaris.h \
+ ../common/include/parted/timer.h \
+ ../common/include/parted/unit.h \
+ ../common/include/parted/vtoc.h
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+# install rule for install_h target
+$(ROOTHDRDIR)/%: %
+ $(INS.file)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) *.[ch]* */*.[ch]*
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+$(MSGDOMAIN):
+ $(INS.dir)
+
+FRC:
diff --git a/usr/src/lib/libparted/Makefile.com b/usr/src/lib/libparted/Makefile.com
new file mode 100644
index 0000000000..28cb243da9
--- /dev/null
+++ b/usr/src/lib/libparted/Makefile.com
@@ -0,0 +1,128 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= libparted.a
+VERS= .8
+
+#
+# All relative to SRCDIR
+#
+
+PLIBDIR= lib
+LIBPDIR= libparted
+LIBPADIR= libparted/arch
+LIBPCSDIR= libparted/cs
+LIBAMIGAFS= libparted/fs/amiga
+LIBEXT2FS= libparted/fs/ext2
+LIBFATFS= libparted/fs/fat
+LIBHFS= libparted/fs/hfs
+LIBJFS= libparted/fs/jfs
+LIBLINUXSWAP= libparted/fs/linux_swap
+LIBNTFS= libparted/fs/ntfs
+LIBREISERFS= libparted/fs/reiserfs
+LIBSOLARISX86= libparted/fs/solaris_x86
+LIBUFS= libparted/fs/ufs
+LIBXFS= libparted/fs/xfs
+LIBLABELS= libparted/labels
+
+OBJECTS= $(PLIBDIR)/basename.o $(PLIBDIR)/quotearg.o \
+ $(PLIBDIR)/close-stream.o $(PLIBDIR)/regex.o \
+ $(PLIBDIR)/closeout.o $(PLIBDIR)/rpmatch.o \
+ $(PLIBDIR)/dirname.o $(PLIBDIR)/safe-read.o \
+ $(PLIBDIR)/error.o $(PLIBDIR)/safe-write.o \
+ $(PLIBDIR)/exitfail.o $(PLIBDIR)/strcspn.o \
+ $(PLIBDIR)/full-write.o $(PLIBDIR)/stripslash.o \
+ $(PLIBDIR)/getopt.o $(PLIBDIR)/strndup.o \
+ $(PLIBDIR)/version-etc-fsf.o \
+ $(PLIBDIR)/localcharset.o $(PLIBDIR)/version-etc.o \
+ $(PLIBDIR)/long-options.o $(PLIBDIR)/xalloc-die.o \
+ $(PLIBDIR)/memcpy.o $(PLIBDIR)/xmalloc.o \
+ $(PLIBDIR)/memmove.o $(PLIBDIR)/xstrndup.o \
+ $(PLIBDIR)/memset.o \
+ $(LIBPDIR)/debug.o $(LIBPDIR)/exception.o \
+ $(LIBPDIR)/device.o $(LIBPDIR)/filesys.o \
+ $(LIBPDIR)/timer.o $(LIBPDIR)/unit.o \
+ $(LIBPDIR)/disk.o $(LIBPDIR)/libparted.o \
+ $(LIBPADIR)/solaris.o \
+ $(LIBPCSDIR)/constraint.o $(LIBPCSDIR)/geom.o \
+ $(LIBPCSDIR)/natmath.o \
+ $(LIBAMIGAFS)/affs.o $(LIBAMIGAFS)/amiga.o \
+ $(LIBAMIGAFS)/apfs.o $(LIBAMIGAFS)/asfs.o \
+ $(LIBAMIGAFS)/interface.o \
+ $(LIBEXT2FS)/interface.o $(LIBEXT2FS)/ext2.o \
+ $(LIBEXT2FS)/ext2_inode_relocator.o \
+ $(LIBEXT2FS)/parted_io.o $(LIBEXT2FS)/ext2_meta.o \
+ $(LIBEXT2FS)/ext2_block_relocator.o \
+ $(LIBEXT2FS)/ext2_mkfs.o $(LIBEXT2FS)/tune.o \
+ $(LIBEXT2FS)/ext2_buffer.o $(LIBEXT2FS)/ext2_resize.o \
+ $(LIBFATFS)/table.o $(LIBFATFS)/bootsector.o \
+ $(LIBFATFS)/clstdup.o $(LIBFATFS)/count.o \
+ $(LIBFATFS)/fatio.o $(LIBFATFS)/traverse.o \
+ $(LIBFATFS)/calc.o $(LIBFATFS)/context.o \
+ $(LIBFATFS)/fat.o $(LIBFATFS)/resize.o \
+ $(LIBHFS)/cache.o $(LIBHFS)/probe.o \
+ $(LIBHFS)/advfs.o $(LIBHFS)/hfs.o \
+ $(LIBHFS)/file.o $(LIBHFS)/reloc.o \
+ $(LIBHFS)/advfs_plus.o $(LIBHFS)/journal.o \
+ $(LIBHFS)/file_plus.o $(LIBHFS)/reloc_plus.o \
+ $(LIBJFS)/jfs.o \
+ $(LIBLINUXSWAP)/linux_swap.o \
+ $(LIBNTFS)/ntfs.o \
+ $(LIBREISERFS)/geom_dal.o $(LIBREISERFS)/reiserfs.o \
+ $(LIBSOLARISX86)/solaris_x86.o \
+ $(LIBUFS)/ufs.o \
+ $(LIBXFS)/xfs.o \
+ $(LIBLABELS)/dos.o $(LIBLABELS)/efi_crc32.o \
+ $(LIBLABELS)/mac.o $(LIBLABELS)/sun.o \
+ $(LIBLABELS)/aix.o $(LIBLABELS)/dvh.o \
+ $(LIBLABELS)/gpt.o $(LIBLABELS)/pc98.o \
+ $(LIBLABELS)/bsd.o $(LIBLABELS)/loop.o \
+ $(LIBLABELS)/rdb.o \
+
+# include library definitions
+include ../../Makefile.lib
+
+SRCDIR = ../common
+
+C99MODE= $(C99_ENABLE)
+CERRWARN += -erroff=E_EXTERN_INLINE_UNDEFINED
+CERRWARN += -erroff=E_CONST_PROMOTED_UNSIGNED_LONG
+
+LIBS = $(DYNLIB)
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRCDIR)/lib -I$(SRCDIR)/include
+DYNFLAGS += $(ZINTERPOSE)
+LDLIBS += -ldiskmgt -luuid -lc -lnvpair
+
+.KEEP_STATE:
+
+#
+# This open source is exempted from lint
+#
+lint:
+
+# include library targets
+include ../../Makefile.targ
diff --git a/usr/src/lib/libparted/README b/usr/src/lib/libparted/README
new file mode 100644
index 0000000000..a9033959fc
--- /dev/null
+++ b/usr/src/lib/libparted/README
@@ -0,0 +1,22 @@
+This is the Solaris ON port of GNU Parted v1.8.8
+For more information, please see http://www.gnu.org/software/parted/
+
+parted has been broken into two pieces: src/lib/libparted and src/cmd/parted
+
+The Makefiles have all been replaced by ON Makefiles.
+
+The common directory contains these three subdirectories from parted-1.8.8:
+include, lib and libparted
+
+$(SUBDIR)/config.status: $(SUBDIR)/configure
+ cd src; \
+ MAKE=gmake ./configure CFLAGS=-I$(ROOT)/usr/include \
+ LDFLAGS="-L$(ROOT)/lib -L$(ROOT)/usr/lib -Wl,-Bdirect -Wl,-M$(MAPFILE.NE
+S) -Wl,-zignore" \
+ LIBS=-ldiskmgt \
+ ac_cv_sys_file_offset_bits=no \
+ ac_cv_func_canonicalize_file_name=yes \
+ --without-readline --disable-nls \
+ --without-libintl-prefix \
+ --disable-dependency-tracking \
+ --disable-dynamic-loading
diff --git a/usr/src/lib/libparted/THANKS b/usr/src/lib/libparted/THANKS
new file mode 100644
index 0000000000..466ccaca55
--- /dev/null
+++ b/usr/src/lib/libparted/THANKS
@@ -0,0 +1,28 @@
+In no particular order:
+
+ * Jonathan duSaint <jon@rockgeeks.net> for binary unit support and more.
+ * Kamil Ignacak <acerion@wp.pl> for help with the migration of the API documentation.
+ * Conectiva, www.conectiva.com, for sponsoring Parted's development (in the
+past). Eliphas, beber (aka Pato), baretta, fuganti, claudio, olive (anyone
+else?)
+ * Stefan Kanthak <101.33761@germanynet.de> for lots of info on boot-loaders,
+and some corrections to the documentation
+ * Fabian Emmes <fab@orlen.de> for RPM spec file, help with hidden
+partitions and autoconfusion :-) - and lots of other misc. help.
+ * Falk Hueffner <falk.hueffner@student.uni-tuebingen.de> for bug fixes.
+ * Kevin Lindsay <klindsay@stormix.com> for lots of bug reports, etc.
+ * John Weismiller <john@stormix.com> for lots of bug reports, etc.
+ * Andries Brouwer <Andries.Brouwer@cwi.nl> for advice on BIOS geometry
+ * Simon Kirby <sim@stormix.com> for advice on BIOS geometry
+ * Glenn McGrath <Glenn.McGrath@jcu.edu.au>
+ * Timshel Knoll <timshel@pobox.com> for Debian stuff
+ * Tim Waugh <twaugh@redhat.com> for bug reports and fixes
+ * My friends for helping with testing: Tristan Zwalf, Menaka Lashitha Bandara,
+Thomas Hambleton
+ * Ryan Weaver <ryanw@infohwy.com> for parted.spec.in patches
+ * Benjamin Herrenschmidt <bh40@calva.net> for answering all of our questions
+on Mac's
+ * Ethan Benson <erbenson@alaska.net> for lots of advice / testing on ppc
+ * Charles Stevenson <csteven@terraplex.com> - ppc stuff
+ * heaps of people we left out!
+
diff --git a/usr/src/lib/libparted/THIRDPARTYLICENSE b/usr/src/lib/libparted/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..4432540474
--- /dev/null
+++ b/usr/src/lib/libparted/THIRDPARTYLICENSE
@@ -0,0 +1,676 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. 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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. 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.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ 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.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ 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
+state 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) <year> <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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU 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 Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/usr/src/lib/libparted/THIRDPARTYLICENSE.descrip b/usr/src/lib/libparted/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..1cafc40dc0
--- /dev/null
+++ b/usr/src/lib/libparted/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+GNU Parted - Partition Editor
diff --git a/usr/src/lib/libparted/THIRDPARTYLICENSE.readme b/usr/src/lib/libparted/THIRDPARTYLICENSE.readme
new file mode 100644
index 0000000000..a83dd277e7
--- /dev/null
+++ b/usr/src/lib/libparted/THIRDPARTYLICENSE.readme
@@ -0,0 +1,7 @@
+"For the avoidance of doubt, except that if any license choice other
+than GPL or LGPL is available it will apply instead, Sun elects to
+use only the General Public License version 3 (GPLv3) at this time
+for any software where a choice of GPL license versions is made
+available with the language indicating that GPLv3 or any later
+version may be used, or where a choice of which version of the GPL
+is applied is otherwise unspecified."
diff --git a/usr/src/lib/libparted/common/include/parted/constraint.h b/usr/src/lib/libparted/common/include/parted/constraint.h
new file mode 100644
index 0000000000..7a39602f34
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/constraint.h
@@ -0,0 +1,96 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_CONSTRAINT_H_INCLUDED
+#define PED_CONSTRAINT_H_INCLUDED
+
+typedef struct _PedConstraint PedConstraint;
+
+#include <parted/device.h>
+#include <parted/natmath.h>
+
+struct _PedConstraint {
+ PedAlignment* start_align;
+ PedAlignment* end_align;
+ PedGeometry* start_range;
+ PedGeometry* end_range;
+ PedSector min_size;
+ PedSector max_size;
+};
+
+extern int
+ped_constraint_init (
+ PedConstraint* constraint,
+ const PedAlignment* start_align,
+ const PedAlignment* end_align,
+ const PedGeometry* start_range,
+ const PedGeometry* end_range,
+ PedSector min_size,
+ PedSector max_size);
+
+extern PedConstraint*
+ped_constraint_new (
+ const PedAlignment* start_align,
+ const PedAlignment* end_align,
+ const PedGeometry* start_range,
+ const PedGeometry* end_range,
+ PedSector min_size,
+ PedSector max_size);
+
+extern PedConstraint*
+ped_constraint_new_from_min_max (
+ const PedGeometry* min,
+ const PedGeometry* max);
+
+extern PedConstraint*
+ped_constraint_new_from_min (const PedGeometry* min);
+
+extern PedConstraint*
+ped_constraint_new_from_max (const PedGeometry* max);
+
+extern PedConstraint*
+ped_constraint_duplicate (const PedConstraint* constraint);
+
+extern void
+ped_constraint_done (PedConstraint* constraint);
+
+extern void
+ped_constraint_destroy (PedConstraint* constraint);
+
+extern PedConstraint*
+ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b);
+
+extern PedGeometry*
+ped_constraint_solve_max (const PedConstraint* constraint);
+
+extern PedGeometry*
+ped_constraint_solve_nearest (
+ const PedConstraint* constraint, const PedGeometry* geom);
+
+extern int
+ped_constraint_is_solution (const PedConstraint* constraint,
+ const PedGeometry* geom);
+
+extern PedConstraint*
+ped_constraint_any (const PedDevice* dev);
+
+extern PedConstraint*
+ped_constraint_exact (const PedGeometry* geom);
+
+#endif /* PED_CONSTRAINT_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/include/parted/crc32.h b/usr/src/lib/libparted/common/include/parted/crc32.h
new file mode 100644
index 0000000000..73a76ff3ac
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/crc32.h
@@ -0,0 +1,34 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ crc32.h
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CRC32_H
+#define _CRC32_H
+
+#include <stdint.h>
+
+/*
+ * This computes a 32 bit CRC of the data in the buffer, and returns the CRC.
+ * The polynomial used is 0xedb88320.
+ */
+
+extern uint32_t __efi_crc32 (const void *buf, unsigned long len,
+ uint32_t seed);
+
+#endif /* _CRC32_H */
diff --git a/usr/src/lib/libparted/common/include/parted/debug.h b/usr/src/lib/libparted/common/include/parted/debug.h
new file mode 100644
index 0000000000..6dc9e57c5f
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/debug.h
@@ -0,0 +1,89 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998, 1999, 2000, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_DEBUG_H_INCLUDED
+#define PED_DEBUG_H_INCLUDED
+
+#include <stdarg.h>
+
+#ifdef DEBUG
+
+typedef void (PedDebugHandler) ( const int level, const char* file, int line,
+ const char* function, const char* msg );
+
+extern void ped_debug_set_handler (PedDebugHandler* handler);
+extern void ped_debug ( const int level, const char* file, int line,
+ const char* function, const char* msg, ... );
+
+extern int ped_assert ( int cond, const char* cond_text,
+ const char* file, int line, const char* function );
+
+#if defined(__GNUC__) && !defined(__JSFTRACE__)
+
+#define PED_DEBUG(level, ...) \
+ ped_debug ( level, __FILE__, __LINE__, __PRETTY_FUNCTION__, \
+ __VA_ARGS__ );
+
+#define PED_ASSERT(cond, action) \
+ do { \
+ if (!ped_assert ( cond, \
+ #cond, \
+ __FILE__, \
+ __LINE__, \
+ __PRETTY_FUNCTION__ )) \
+ { \
+ action; \
+ } \
+ } while (0)
+
+#else /* !__GNUC__ */
+
+/* function because variadic macros are C99 */
+static void PED_DEBUG (int level, ...)
+{
+ va_list va_args;
+
+ va_start (va_args, level);
+ ped_debug ( level, "unknown file", 0, "unknown function", va_args );
+ va_end (va_args);
+}
+
+#define PED_ASSERT(cond, action) \
+ do { \
+ if (!ped_assert ( cond, \
+ #cond, \
+ "unknown", \
+ 0, \
+ "unknown" )) \
+ { \
+ action; \
+ } \
+ } while (0)
+
+#endif /* __GNUC__ */
+
+#else /* !DEBUG */
+
+#define PED_ASSERT(cond, action) while (0) {}
+#define PED_DEBUG(level, ...) while (0) {}
+
+
+#endif /* DEBUG */
+
+#endif /* PED_DEBUG_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/include/parted/device.h b/usr/src/lib/libparted/common/include/parted/device.h
new file mode 100644
index 0000000000..cf32dba7aa
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/device.h
@@ -0,0 +1,150 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998 - 2001, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedDevice
+ * @{
+ */
+
+/** \file device.h */
+
+#ifndef PED_DEVICE_H_INCLUDED
+#define PED_DEVICE_H_INCLUDED
+
+#include <parted/timer.h>
+
+/** We can address 2^63 sectors */
+typedef long long PedSector;
+
+/** \deprecated Removal from API planned */
+typedef enum {
+ PED_DEVICE_UNKNOWN = 0,
+ PED_DEVICE_SCSI = 1,
+ PED_DEVICE_IDE = 2,
+ PED_DEVICE_DAC960 = 3,
+ PED_DEVICE_CPQARRAY = 4,
+ PED_DEVICE_FILE = 5,
+ PED_DEVICE_ATARAID = 6,
+ PED_DEVICE_I2O = 7,
+ PED_DEVICE_UBD = 8,
+ PED_DEVICE_DASD = 9,
+ PED_DEVICE_VIODASD = 10,
+ PED_DEVICE_SX8 = 11,
+#ifdef ENABLE_DEVICE_MAPPER
+ PED_DEVICE_DM = 12,
+#endif
+ PED_DEVICE_XVD = 13
+} PedDeviceType;
+
+typedef struct _PedDevice PedDevice;
+typedef struct _PedDeviceArchOps PedDeviceArchOps;
+typedef struct _PedCHSGeometry PedCHSGeometry;
+
+/**
+ * A cylinder-head-sector "old-style" geometry.
+ *
+ * A device addressed in this way has C*H*S sectors.
+ */
+struct _PedCHSGeometry {
+ int cylinders;
+ int heads;
+ int sectors;
+};
+
+/** A block device - for example, /dev/hda, not /dev/hda3 */
+struct _PedDevice {
+ PedDevice* next;
+
+ char* model; /**< \brief description of hardware
+ (manufacturer, model) */
+ char* path; /**< device /dev entry */
+
+ PedDeviceType type; /**< SCSI, IDE, etc.
+ \deprecated \sa PedDeviceType */
+ long long sector_size; /**< logical sector size */
+ long long phys_sector_size; /**< physical sector size */
+ PedSector length; /**< device length (LBA) */
+
+ int open_count; /**< the number of times this device has
+ been opened with ped_device_open(). */
+ int read_only;
+ int external_mode;
+ int dirty;
+ int boot_dirty;
+
+ PedCHSGeometry hw_geom;
+ PedCHSGeometry bios_geom;
+ short host, did;
+
+ void* arch_specific;
+};
+
+/**
+ * List of functions implementing architecture-specific operations.
+ */
+struct _PedDeviceArchOps {
+ PedDevice* (*_new) (const char* path);
+ void (*destroy) (PedDevice* dev);
+ int (*is_busy) (PedDevice* dev);
+ int (*open) (PedDevice* dev);
+ int (*refresh_open) (PedDevice* dev);
+ int (*close) (PedDevice* dev);
+ int (*refresh_close) (PedDevice* dev);
+ int (*read) (const PedDevice* dev, void* buffer,
+ PedSector start, PedSector count);
+ int (*write) (PedDevice* dev, const void* buffer,
+ PedSector start, PedSector count);
+ int (*sync) (PedDevice* dev);
+ int (*sync_fast) (PedDevice* dev);
+ PedSector (*check) (PedDevice* dev, void* buffer,
+ PedSector start, PedSector count);
+ void (*probe_all) ();
+};
+
+extern void ped_device_probe_all ();
+extern void ped_device_free_all ();
+
+extern PedDevice* ped_device_get (const char* name);
+extern PedDevice* ped_device_get_next (const PedDevice* dev);
+extern int ped_device_is_busy (PedDevice* dev);
+extern int ped_device_open (PedDevice* dev);
+extern int ped_device_close (PedDevice* dev);
+extern void ped_device_destroy (PedDevice* dev);
+extern void ped_device_cache_remove (PedDevice* dev);
+
+extern int ped_device_begin_external_access (PedDevice* dev);
+extern int ped_device_end_external_access (PedDevice* dev);
+
+extern int ped_device_read (const PedDevice* dev, void* buffer,
+ PedSector start, PedSector count);
+extern int ped_device_write (PedDevice* dev, const void* buffer,
+ PedSector start, PedSector count);
+extern int ped_device_sync (PedDevice* dev);
+extern int ped_device_sync_fast (PedDevice* dev);
+extern PedSector ped_device_check (PedDevice* dev, void* buffer,
+ PedSector start, PedSector count);
+extern PedConstraint* ped_device_get_constraint (PedDevice* dev);
+
+/* private stuff ;-) */
+
+extern void _ped_device_probe (const char* path);
+
+#endif /* PED_DEVICE_H_INCLUDED */
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/include/parted/disk.h b/usr/src/lib/libparted/common/include/parted/disk.h
new file mode 100644
index 0000000000..eb72b739d9
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/disk.h
@@ -0,0 +1,348 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedDisk
+ * @{
+ */
+
+/** \file disk.h */
+
+#ifndef PED_DISK_H_INCLUDED
+#define PED_DISK_H_INCLUDED
+
+/**
+ * Partition types
+ */
+enum _PedPartitionType {
+ PED_PARTITION_NORMAL = 0x00,
+ PED_PARTITION_LOGICAL = 0x01,
+ PED_PARTITION_EXTENDED = 0x02,
+ PED_PARTITION_FREESPACE = 0x04,
+ PED_PARTITION_METADATA = 0x08,
+ PED_PARTITION_PROTECTED = 0x10
+};
+
+/**
+ * Partition flags.
+ */
+enum _PedPartitionFlag {
+ PED_PARTITION_BOOT=1,
+ PED_PARTITION_ROOT=2,
+ PED_PARTITION_SWAP=3,
+ PED_PARTITION_HIDDEN=4,
+ PED_PARTITION_RAID=5,
+ PED_PARTITION_LVM=6,
+ PED_PARTITION_LBA=7,
+ PED_PARTITION_HPSERVICE=8,
+ PED_PARTITION_PALO=9,
+ PED_PARTITION_PREP=10,
+ PED_PARTITION_MSFT_RESERVED=11
+};
+#define PED_PARTITION_FIRST_FLAG PED_PARTITION_BOOT
+#define PED_PARTITION_LAST_FLAG PED_PARTITION_MSFT_RESERVED
+
+enum _PedDiskTypeFeature {
+ PED_DISK_TYPE_EXTENDED=1, /**< supports extended partitions */
+ PED_DISK_TYPE_PARTITION_NAME=2 /**< supports partition names */
+};
+#define PED_DISK_TYPE_FIRST_FEATURE PED_DISK_TYPE_EXTENDED
+#define PED_DISK_TYPE_LAST_FEATURE PED_DISK_TYPE_PARTITION_NAME
+
+struct _PedDisk;
+struct _PedPartition;
+struct _PedDiskOps;
+struct _PedDiskType;
+struct _PedDiskArchOps;
+
+typedef enum _PedPartitionType PedPartitionType;
+typedef enum _PedPartitionFlag PedPartitionFlag;
+typedef enum _PedDiskTypeFeature PedDiskTypeFeature;
+typedef struct _PedDisk PedDisk;
+typedef struct _PedPartition PedPartition;
+typedef const struct _PedDiskOps PedDiskOps;
+typedef struct _PedDiskType PedDiskType;
+typedef const struct _PedDiskArchOps PedDiskArchOps;
+
+#include <parted/device.h>
+#include <parted/filesys.h>
+#include <parted/natmath.h>
+#include <parted/geom.h>
+
+/** @} */
+
+/**
+ * \addtogroup PedPartition
+ *
+ * @{
+ */
+
+/** \file disk.h */
+
+/**
+ * PedPartition structure represents a partition.
+ */
+struct _PedPartition {
+ PedPartition* prev;
+ PedPartition* next;
+
+ /**< the partition table of the partition */
+ PedDisk* disk;
+ PedGeometry geom; /**< geometry of the partition */
+
+ /**< the partition number: In Linux, this is the
+ same as the minor number. No assumption
+ should be made about "num" and "type"
+ - different disk labels have different rules. */
+
+ int num;
+ PedPartitionType type; /**< the type of partition: a bit field of
+ PED_PARTITION_LOGICAL, PED_PARTITION_EXTENDED,
+ PED_PARTITION_METADATA
+ and PED_PARTITION_FREESPACE.
+ Both the first two, and the last two are
+ mutually exclusive.
+ An extended partition is a primary
+ partition that may contain logical partitions.
+ There is at most one extended partition on
+ a disk.
+ A logical partition is like a primary
+ partition, except it's inside an extended
+ partition. Internally, pseudo partitions are
+ allocated to represent free space, or disk
+ label meta-data. These have the
+ PED_PARTITION_FREESPACE or
+ PED_PARTITION_METADATA bit set. */
+
+ /**< The type of file system on the partition. NULL if unknown. */
+ const PedFileSystemType* fs_type;
+
+ /**< Only used for an extended partition. The list of logical
+ partitions (and free space and metadata within the extended
+ partition). */
+ PedPartition* part_list;
+
+ void* disk_specific;
+};
+
+/** @} */
+
+/**
+ * \addtogroup PedDisk
+ * @{
+ */
+
+/**
+ * Represents a disk label (partition table).
+ */
+struct _PedDisk {
+ PedDevice* dev; /**< the device where the
+ partition table lies */
+ const PedDiskType* type; /**< type of disk label */
+ const int* block_sizes; /**< block sizes supported
+ by this label */
+ PedPartition* part_list; /**< list of partitions. Access with
+ ped_disk_next_partition() */
+
+ void* disk_specific;
+
+/* office use only ;-) */
+ int needs_clobber; /**< clobber before write? */
+ int update_mode; /**< mode without free/metadata
+ partitions, for easier
+ update */
+};
+
+struct _PedDiskOps {
+ /* disk label operations */
+ int (*probe) (const PedDevice *dev);
+ int (*clobber) (PedDevice* dev);
+ PedDisk* (*alloc) (const PedDevice* dev);
+ PedDisk* (*duplicate) (const PedDisk* disk);
+ void (*free) (PedDisk* disk);
+ int (*read) (PedDisk* disk);
+ int (*write) (const PedDisk* disk);
+ /** \todo add label guessing op here */
+
+ /* partition operations */
+ PedPartition* (*partition_new) (
+ const PedDisk* disk,
+ PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start,
+ PedSector end);
+ PedPartition* (*partition_duplicate) (const PedPartition* part);
+ void (*partition_destroy) (PedPartition* part);
+ int (*partition_set_system) (PedPartition* part,
+ const PedFileSystemType* fs_type);
+ int (*partition_set_flag) (
+ PedPartition* part,
+ PedPartitionFlag flag,
+ int state);
+ int (*partition_get_flag) (
+ const PedPartition* part,
+ PedPartitionFlag flag);
+ int (*partition_is_flag_available) (
+ const PedPartition* part,
+ PedPartitionFlag flag);
+ void (*partition_set_name) (PedPartition* part, const char* name);
+ const char* (*partition_get_name) (const PedPartition* part);
+ int (*partition_align) (PedPartition* part,
+ const PedConstraint* constraint);
+ int (*partition_enumerate) (PedPartition* part);
+
+ /* other */
+ int (*alloc_metadata) (PedDisk* disk);
+ int (*get_max_primary_partition_count) (const PedDisk* disk);
+};
+
+struct _PedDiskType {
+ PedDiskType* next;
+ const char* name; /**< the name of the partition table type.
+ \todo not very intuitive name */
+ PedDiskOps* const ops;
+
+ PedDiskTypeFeature features; /**< bitmap of supported features */
+};
+
+/**
+ * Architecture-specific operations. i.e. communication with kernel (or
+ * whatever) about changes, etc.
+ */
+struct _PedDiskArchOps {
+ char* (*partition_get_path) (const PedPartition* part);
+ int (*partition_is_busy) (const PedPartition* part);
+ int (*disk_commit) (PedDisk* disk);
+};
+
+extern void ped_disk_type_register (PedDiskType* type);
+extern void ped_disk_type_unregister (PedDiskType* type);
+
+extern PedDiskType* ped_disk_type_get_next (PedDiskType* type);
+extern PedDiskType* ped_disk_type_get (const char* name);
+extern int ped_disk_type_check_feature (const PedDiskType* disk_type,
+ PedDiskTypeFeature feature);
+
+extern PedDiskType* ped_disk_probe (PedDevice* dev);
+extern int ped_disk_clobber (PedDevice* dev);
+extern int ped_disk_clobber_exclude (PedDevice* dev,
+ const PedDiskType* exclude);
+extern PedDisk* ped_disk_new (PedDevice* dev);
+extern PedDisk* ped_disk_new_fresh (PedDevice* dev,
+ const PedDiskType* disk_type);
+extern PedDisk* ped_disk_duplicate (const PedDisk* old_disk);
+extern void ped_disk_destroy (PedDisk* disk);
+extern int ped_disk_commit (PedDisk* disk);
+extern int ped_disk_commit_to_dev (PedDisk* disk);
+extern int ped_disk_commit_to_os (PedDisk* disk);
+extern int ped_disk_check (const PedDisk* disk);
+extern void ped_disk_print (const PedDisk* disk);
+
+extern int ped_disk_get_primary_partition_count (const PedDisk* disk);
+extern int ped_disk_get_last_partition_num (const PedDisk* disk);
+extern int ped_disk_get_max_primary_partition_count (const PedDisk* disk);
+
+/** @} */
+
+/**
+ * \addtogroup PedPartition
+ *
+ * @{
+ */
+
+extern PedPartition* ped_partition_new (const PedDisk* disk,
+ PedPartitionType type,
+ const PedFileSystemType* fs_type,
+ PedSector start,
+ PedSector end);
+extern void ped_partition_destroy (PedPartition* part);
+extern int ped_partition_is_active (const PedPartition* part);
+extern int ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag,
+ int state);
+extern int ped_partition_get_flag (const PedPartition* part,
+ PedPartitionFlag flag);
+extern int ped_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag);
+extern int ped_partition_set_system (PedPartition* part,
+ const PedFileSystemType* fs_type);
+extern int ped_partition_set_name (PedPartition* part, const char* name);
+extern const char* ped_partition_get_name (const PedPartition* part);
+extern int ped_partition_is_busy (const PedPartition* part);
+extern char* ped_partition_get_path (const PedPartition* part);
+
+extern const char* ped_partition_type_get_name (PedPartitionType part_type);
+extern const char* ped_partition_flag_get_name (PedPartitionFlag flag);
+extern PedPartitionFlag ped_partition_flag_get_by_name (const char* name);
+extern PedPartitionFlag ped_partition_flag_next (PedPartitionFlag flag);
+
+/** @} */
+
+/**
+ * \addtogroup PedDisk
+ * @{
+ */
+
+extern int ped_disk_add_partition (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint);
+extern int ped_disk_remove_partition (PedDisk* disk, PedPartition* part);
+extern int ped_disk_delete_partition (PedDisk* disk, PedPartition* part);
+extern int ped_disk_delete_all (PedDisk* disk);
+extern int ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint,
+ PedSector start, PedSector end);
+extern int ped_disk_maximize_partition (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint);
+extern PedGeometry* ped_disk_get_max_partition_geometry (PedDisk* disk,
+ PedPartition* part, const PedConstraint* constraint);
+extern int ped_disk_minimize_extended_partition (PedDisk* disk);
+
+extern PedPartition* ped_disk_next_partition (const PedDisk* disk,
+ const PedPartition* part);
+extern PedPartition* ped_disk_get_partition (const PedDisk* disk, int num);
+extern PedPartition* ped_disk_get_partition_by_sector (const PedDisk* disk,
+ PedSector sect);
+extern PedPartition* ped_disk_extended_partition (const PedDisk* disk);
+
+/* internal functions */
+extern PedDisk* _ped_disk_alloc (const PedDevice* dev, const PedDiskType* type);
+extern void _ped_disk_free (PedDisk* disk);
+
+
+/** @} */
+
+/**
+ * \addtogroup PedPartition
+ *
+ * @{
+ */
+
+extern PedPartition* _ped_partition_alloc (const PedDisk* disk,
+ PedPartitionType type,
+ const PedFileSystemType* fs_type,
+ PedSector start,
+ PedSector end);
+extern void _ped_partition_free (PedPartition* part);
+
+extern int _ped_partition_attempt_align (
+ PedPartition* part, const PedConstraint* external,
+ PedConstraint* internal);
+
+#endif /* PED_DISK_H_INCLUDED */
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/include/parted/endian.h b/usr/src/lib/libparted/common/include/parted/endian.h
new file mode 100644
index 0000000000..f968e2c8b1
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/endian.h
@@ -0,0 +1,85 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998-2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* should only be #included by files in libparted */
+
+#ifndef PED_ENDIAN_H_INCLUDED
+#define PED_ENDIAN_H_INCLUDED
+
+#include <stdint.h>
+
+/* returns the n'th least significant byte */
+#define _GET_BYTE(x, n) ( ((x) >> (8 * (n))) & 0xff )
+
+#define _PED_SWAP16(x) ( (_GET_BYTE(x, 0) << 8) \
+ + (_GET_BYTE(x, 1) << 0) )
+
+#define _PED_SWAP32(x) ( (_GET_BYTE(x, 0) << 24) \
+ + (_GET_BYTE(x, 1) << 16) \
+ + (_GET_BYTE(x, 2) << 8) \
+ + (_GET_BYTE(x, 3) << 0) )
+
+#define _PED_SWAP64(x) ( (_GET_BYTE(x, 0) << 56) \
+ + (_GET_BYTE(x, 1) << 48) \
+ + (_GET_BYTE(x, 2) << 40) \
+ + (_GET_BYTE(x, 3) << 32) \
+ + (_GET_BYTE(x, 4) << 24) \
+ + (_GET_BYTE(x, 5) << 16) \
+ + (_GET_BYTE(x, 6) << 8) \
+ + (_GET_BYTE(x, 7) << 0) )
+
+#define PED_SWAP16(x) ((uint16_t) _PED_SWAP16( (uint16_t) (x) ))
+#define PED_SWAP32(x) ((uint32_t) _PED_SWAP32( (uint32_t) (x) ))
+#define PED_SWAP64(x) ((uint64_t) _PED_SWAP64( (uint64_t) (x) ))
+
+#ifdef WORDS_BIGENDIAN
+
+#define PED_CPU_TO_LE16(x) PED_SWAP16(x)
+#define PED_CPU_TO_BE16(x) (x)
+#define PED_CPU_TO_LE32(x) PED_SWAP32(x)
+#define PED_CPU_TO_BE32(x) (x)
+#define PED_CPU_TO_LE64(x) PED_SWAP64(x)
+#define PED_CPU_TO_BE64(x) (x)
+
+#define PED_LE16_TO_CPU(x) PED_SWAP16(x)
+#define PED_BE16_TO_CPU(x) (x)
+#define PED_LE32_TO_CPU(x) PED_SWAP32(x)
+#define PED_BE32_TO_CPU(x) (x)
+#define PED_LE64_TO_CPU(x) PED_SWAP64(x)
+#define PED_BE64_TO_CPU(x) (x)
+
+#else /* !WORDS_BIGENDIAN */
+
+#define PED_CPU_TO_LE16(x) (x)
+#define PED_CPU_TO_BE16(x) PED_SWAP16(x)
+#define PED_CPU_TO_LE32(x) (x)
+#define PED_CPU_TO_BE32(x) PED_SWAP32(x)
+#define PED_CPU_TO_LE64(x) (x)
+#define PED_CPU_TO_BE64(x) PED_SWAP64(x)
+
+#define PED_LE16_TO_CPU(x) (x)
+#define PED_BE16_TO_CPU(x) PED_SWAP16(x)
+#define PED_LE32_TO_CPU(x) (x)
+#define PED_BE32_TO_CPU(x) PED_SWAP32(x)
+#define PED_LE64_TO_CPU(x) (x)
+#define PED_BE64_TO_CPU(x) PED_SWAP64(x)
+
+#endif /* !WORDS_BIGENDIAN */
+
+#endif /* PED_ENDIAN_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/include/parted/exception.h b/usr/src/lib/libparted/common/include/parted/exception.h
new file mode 100644
index 0000000000..aa63c1a27b
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/exception.h
@@ -0,0 +1,116 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedException
+ * @{
+ */
+
+/** \file exception.h */
+
+#ifndef PED_EXCEPTION_H_INCLUDED
+#define PED_EXCEPTION_H_INCLUDED
+
+typedef struct _PedException PedException;
+
+/**
+ * Exception type
+ */
+enum _PedExceptionType {
+ PED_EXCEPTION_INFORMATION=1,
+ PED_EXCEPTION_WARNING=2,
+ PED_EXCEPTION_ERROR=3,
+ PED_EXCEPTION_FATAL=4,
+ PED_EXCEPTION_BUG=5,
+ PED_EXCEPTION_NO_FEATURE=6,
+};
+typedef enum _PedExceptionType PedExceptionType;
+
+/**
+ * Option for resolving the exception
+ */
+enum _PedExceptionOption {
+ PED_EXCEPTION_UNHANDLED=0,
+ PED_EXCEPTION_FIX=1,
+ PED_EXCEPTION_YES=2,
+ PED_EXCEPTION_NO=4,
+ PED_EXCEPTION_OK=8,
+ PED_EXCEPTION_RETRY=16,
+ PED_EXCEPTION_IGNORE=32,
+ PED_EXCEPTION_CANCEL=64,
+};
+typedef enum _PedExceptionOption PedExceptionOption;
+#define PED_EXCEPTION_OK_CANCEL (PED_EXCEPTION_OK + PED_EXCEPTION_CANCEL)
+#define PED_EXCEPTION_YES_NO (PED_EXCEPTION_YES + PED_EXCEPTION_NO)
+#define PED_EXCEPTION_YES_NO_CANCEL (PED_EXCEPTION_YES_NO \
+ + PED_EXCEPTION_CANCEL)
+#define PED_EXCEPTION_IGNORE_CANCEL (PED_EXCEPTION_IGNORE \
+ + PED_EXCEPTION_CANCEL)
+#define PED_EXCEPTION_RETRY_CANCEL (PED_EXCEPTION_RETRY + PED_EXCEPTION_CANCEL)
+#define PED_EXCEPTION_RETRY_IGNORE_CANCEL (PED_EXCEPTION_RETRY \
+ + PED_EXCEPTION_IGNORE_CANCEL)
+#define PED_EXCEPTION_OPTION_FIRST PED_EXCEPTION_FIX
+#define PED_EXCEPTION_OPTION_LAST PED_EXCEPTION_CANCEL
+
+/**
+ * Structure with information about exception
+ */
+struct _PedException {
+ char* message; /**< text describing what the event was */
+ PedExceptionType type; /**< type of exception */
+ PedExceptionOption options; /**< ORed list of options that
+ the exception handler can
+ return (the ways an exception
+ can be resolved) */
+};
+
+typedef PedExceptionOption (PedExceptionHandler) (PedException* ex);
+
+extern int ped_exception; /* set to true if there's an exception */
+
+extern char* ped_exception_get_type_string (PedExceptionType ex_type);
+extern char* ped_exception_get_option_string (PedExceptionOption ex_opt);
+
+extern void ped_exception_set_handler (PedExceptionHandler* handler);
+extern PedExceptionHandler *ped_exception_get_handler(void);
+
+extern PedExceptionOption ped_exception_default_handler (PedException* ex);
+
+extern PedExceptionOption ped_exception_throw (PedExceptionType ex_type,
+ PedExceptionOption ex_opt,
+ const char* message,
+ ...);
+/* rethrows an exception - i.e. calls the exception handler, (or returns a
+ code to return to pass up higher) */
+extern PedExceptionOption ped_exception_rethrow ();
+
+/* frees an exception, indicating that the exception has been handled.
+ Calling an exception handler counts. */
+extern void ped_exception_catch ();
+
+/* indicate that exceptions should not go to the exception handler, but passed
+ up to the calling function(s) */
+extern void ped_exception_fetch_all ();
+
+/* indicate that exceptions should invoke the exception handler */
+extern void ped_exception_leave_all ();
+
+#endif /* PED_EXCEPTION_H_INCLUDED */
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/include/parted/fdasd.h b/usr/src/lib/libparted/common/include/parted/fdasd.h
new file mode 100644
index 0000000000..283191e342
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/fdasd.h
@@ -0,0 +1,230 @@
+/*
+ * File...........: s390-tools/fdasd/fdasd.h
+ * Author(s)......: Volker Sameske <sameske@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Bugreports.to..: <Linux390@de.ibm.com>
+ * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2001-2002
+ *
+ * History of changes (starts March 2001)
+ * version 1.01 - menu entry 's' to show mapping devnode - DS name
+ * 1.02 - DS names count now from 0001 instead from 0000
+ * 1.03 - volser checks: 'AA AAA' to 'AAAAA '
+ * - removed dependency to kernel headers.
+ * 1.04 - added -p option
+ * 1.05 - new API policy, set it back to 0
+ */
+#ifndef FDASD_H
+#define FDASD_H
+
+#include <parted/vtoc.h>
+
+/*****************************************************************************
+ * SECTION: Definitions needed for DASD-API (see dasd.h) *
+ *****************************************************************************/
+
+#define DASD_IOCTL_LETTER 'D'
+
+#define DASD_PARTN_BITS 2
+
+#define PARTITION_LINUX_SWAP 0x82
+#define PARTITION_LINUX 0x83
+#define PARTITION_LINUX_EXT 0x85
+#define PARTITION_LINUX_LVM 0x8e
+#define PARTITION_LINUX_RAID 0xfd
+#define PARTITION_LINUX_LVM_OLD 0xfe
+
+#define PART_TYPE_NATIVE "NATIVE"
+#define PART_TYPE_SWAP "SWAP "
+#define PART_TYPE_RAID "RAID "
+#define PART_TYPE_LVM "LVM "
+
+#ifdef DEBUG_DASD
+#define PDEBUG fprintf(stderr, "%s:%d:%s\n", \
+ __FILE__, \
+ __LINE__, \
+ __PRETTY_FUNCTION__);
+#else
+#define PDEBUG
+#endif
+
+/*
+ * struct dasd_information_t
+ * represents any data about the device, which is visible to userspace.
+ * including foramt and featueres.
+ */
+typedef struct dasd_information_t {
+ unsigned int devno; /* S/390 devno */
+ unsigned int real_devno; /* for aliases */
+ unsigned int schid; /* S/390 subchannel identifier */
+ unsigned int cu_type : 16; /* from SenseID */
+ unsigned int cu_model : 8; /* from SenseID */
+ unsigned int dev_type : 16; /* from SenseID */
+ unsigned int dev_model : 8; /* from SenseID */
+ unsigned int open_count;
+ unsigned int req_queue_len;
+ unsigned int chanq_len; /* length of chanq */
+ char type[4]; /* from discipline.name, 'none' for */
+ /* unknown */
+ unsigned int status; /* current device level */
+ unsigned int label_block; /* where to find the VOLSER */
+ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
+ unsigned int characteristics_size;
+ unsigned int confdata_size;
+ char characteristics[64]; /* from read_device_characteristics */
+ char configuration_data[256]; /* from read_configuration_data */
+} dasd_information_t;
+
+/*
+ * struct format_data_t
+ * represents all data necessary to format a dasd
+ */
+typedef struct format_data_t {
+ int start_unit; /* from track */
+ int stop_unit; /* to track */
+ int blksize; /* sectorsize */
+ int intensity;
+} format_data_t;
+
+/*
+ * values to be used for format_data_t.intensity
+ * 0/8: normal format
+ * 1/9: also write record zero
+ * 3/11: also write home address
+ * 4/12: invalidate track
+ */
+#define DASD_FMT_INT_FMT_R0 1 /* write record zero */
+#define DASD_FMT_INT_FMT_HA 2 /* write home address, also set FMT_R0 ! */
+#define DASD_FMT_INT_INVAL 4 /* invalidate tracks */
+#define DASD_FMT_INT_COMPAT 8 /* use OS/390 compatible disk layout */
+
+
+/* Disable the volume (for Linux) */
+#define BIODASDDISABLE _IO(DASD_IOCTL_LETTER,0)
+/* Enable the volume (for Linux) */
+#define BIODASDENABLE _IO(DASD_IOCTL_LETTER,1)
+
+/* retrieve API version number */
+#define DASDAPIVER _IOR(DASD_IOCTL_LETTER,0,int)
+/* Get information on a dasd device (enhanced) */
+#define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t)
+
+
+/*****************************************************************************
+ * SECTION: Further IOCTL Definitions (see fs.h) *
+ *****************************************************************************/
+/* re-read partition table */
+#define BLKRRPART _IO(0x12,95)
+/* get block device sector size */
+#define BLKSSZGET _IO(0x12,104)
+
+/*****************************************************************************
+ * SECTION: Definition from hdreq.h *
+ *****************************************************************************/
+
+struct fdasd_hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+/* get device geometry */
+#define HDIO_GETGEO 0x0301
+
+/*****************************************************************************
+ * SECTION: FDASD internal types *
+ *****************************************************************************/
+
+#define DASD_MIN_API_VERSION 0
+
+#define DEFAULT_FDASD_CONF "/etc/fdasd.conf" /* default config file */
+
+#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1)
+#define USABLE_PARTITIONS ((1 << DASD_PARTN_BITS) - 1)
+
+#define FDASD_VERSION "1.05"
+#define DEVICE "device"
+#define DISC "disc"
+#define PART "part"
+
+#define ALTERNATE_CYLINDERS_USED 0x10
+
+typedef struct partition_info {
+ u_int8_t used;
+ unsigned long start_trk;
+ unsigned long end_trk;
+ unsigned long len_trk;
+ unsigned long fspace_trk;
+ format1_label_t *f1;
+ struct partition_info *next;
+ struct partition_info *prev;
+ u_int8_t type;
+} partition_info_t;
+
+typedef struct config_data {
+ unsigned long start;
+ unsigned long stop;
+} config_data_t;
+
+typedef struct fdasd_anchor {
+ int vlabel_changed;
+ int vtoc_changed;
+ int devname_specified;
+ int volid_specified;
+ int config_specified;
+ int auto_partition;
+ int print_table;
+ int big_disk;
+ int silent;
+ int verbose;
+ int devno;
+ int option_reuse;
+ int option_recreate;
+ int partno[USABLE_PARTITIONS];
+ u_int16_t dev_type;
+ unsigned int used_partitions;
+ unsigned long label_pos;
+ unsigned int blksize;
+ unsigned long fspace_trk;
+ format4_label_t *f4;
+ format5_label_t *f5;
+ format7_label_t *f7;
+ partition_info_t *first;
+ partition_info_t *last;
+ volume_label_t *vlabel;
+ config_data_t confdata[USABLE_PARTITIONS];
+ struct fdasd_hd_geometry geo;
+} fdasd_anchor_t;
+
+enum offset {lower, upper};
+
+enum fdasd_failure {
+ unable_to_open_disk,
+ unable_to_seek_disk,
+ unable_to_read_disk,
+ read_only_disk,
+ unable_to_ioctl,
+ api_version_mismatch,
+ wrong_disk_type,
+ wrong_disk_format,
+ disk_in_use,
+ config_syntax_error,
+ vlabel_corrupted,
+ dsname_corrupted,
+ malloc_failed,
+ device_verification_failed
+};
+
+void fdasd_cleanup (fdasd_anchor_t *anchor);
+void fdasd_initialize_anchor (fdasd_anchor_t * anc);
+void fdasd_get_geometry (fdasd_anchor_t *anc, int fd);
+void fdasd_check_api_version (fdasd_anchor_t *anc, int fd);
+int fdasd_check_volume (fdasd_anchor_t *anc, int fd);
+int fdasd_write_labels (fdasd_anchor_t *anc, int fd);
+int fdasd_invalid_vtoc_pointer(fdasd_anchor_t *anc);
+void fdasd_recreate_vtoc(fdasd_anchor_t *anc);
+partition_info_t * fdasd_add_partition (fdasd_anchor_t *anc,
+ unsigned int start, unsigned int stop);
+int fdasd_prepare_labels (fdasd_anchor_t *anc, int fd) ;
+
+#endif /* FDASD_H */
diff --git a/usr/src/lib/libparted/common/include/parted/filesys.h b/usr/src/lib/libparted/common/include/parted/filesys.h
new file mode 100644
index 0000000000..eacfa724f4
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/filesys.h
@@ -0,0 +1,116 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2006, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedFileSystem
+ * @{
+ */
+
+/** \file filesys.h */
+
+#ifndef PED_FILESYS_H_INCLUDED
+#define PED_FILESYS_H_INCLUDED
+
+typedef struct _PedFileSystem PedFileSystem;
+typedef struct _PedFileSystemType PedFileSystemType;
+typedef const struct _PedFileSystemOps PedFileSystemOps;
+
+#include <parted/geom.h>
+#include <parted/disk.h>
+#include <parted/exception.h>
+#include <parted/constraint.h>
+#include <parted/timer.h>
+#include <stdio.h>
+
+struct _PedFileSystemOps {
+ PedGeometry* (*probe) (PedGeometry* geom);
+ int (*clobber) (PedGeometry* geom);
+
+ PedFileSystem* (*open) (PedGeometry* geom);
+ PedFileSystem* (*create) (PedGeometry* geom, PedTimer* timer);
+ int (*close) (PedFileSystem* fs);
+ int (*check) (PedFileSystem* fs, PedTimer* timer);
+ PedFileSystem* (*copy) (const PedFileSystem* fs, PedGeometry* geom,
+ PedTimer* timer);
+ int (*resize) (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer);
+
+ PedConstraint* (*get_create_constraint) (const PedDevice* dev);
+ PedConstraint* (*get_resize_constraint) (const PedFileSystem* fs);
+ PedConstraint* (*get_copy_constraint) (const PedFileSystem* fs,
+ const PedDevice* dev);
+};
+
+/**
+ * Structure describing type of file system
+ */
+struct _PedFileSystemType {
+ PedFileSystemType* next;
+ const char* const name; /**< name of the file system type */
+ const int* block_sizes;
+ PedFileSystemOps* const ops;
+};
+
+
+/**
+ * Structure describing file system
+ */
+struct _PedFileSystem {
+ PedFileSystemType* type; /**< the file system type */
+ PedGeometry* geom; /**< where the file system actually is */
+ int checked; /**< 1 if the file system has been checked.
+ 0 otherwise. */
+
+ void* type_specific;
+
+};
+
+extern void ped_file_system_type_register (PedFileSystemType* type);
+extern void ped_file_system_type_unregister (PedFileSystemType* type);
+
+extern PedFileSystemType* ped_file_system_type_get (const char* name);
+extern PedFileSystemType*
+ped_file_system_type_get_next (const PedFileSystemType* fs_type);
+
+extern PedFileSystemType* ped_file_system_probe (PedGeometry* geom);
+extern PedGeometry* ped_file_system_probe_specific (
+ const PedFileSystemType* fs_type,
+ PedGeometry* geom);
+extern int ped_file_system_clobber (PedGeometry* geom);
+
+extern PedFileSystem* ped_file_system_open (PedGeometry* geom);
+extern PedFileSystem* ped_file_system_create (PedGeometry* geom,
+ const PedFileSystemType* type,
+ PedTimer* timer);
+extern int ped_file_system_close (PedFileSystem* fs);
+extern int ped_file_system_check (PedFileSystem* fs, PedTimer* timer);
+extern PedFileSystem* ped_file_system_copy (PedFileSystem* fs,
+ PedGeometry* geom,
+ PedTimer* timer);
+extern int ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom,
+ PedTimer* timer);
+
+extern PedConstraint* ped_file_system_get_create_constraint (
+ const PedFileSystemType* fs_type, const PedDevice* dev);
+extern PedConstraint* ped_file_system_get_resize_constraint (
+ const PedFileSystem* fs);
+extern PedConstraint* ped_file_system_get_copy_constraint (
+ const PedFileSystem* fs, const PedDevice* dev);
+
+#endif /* PED_FILESYS_H_INCLUDED */
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/include/parted/geom.h b/usr/src/lib/libparted/common/include/parted/geom.h
new file mode 100644
index 0000000000..58fd9005a7
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/geom.h
@@ -0,0 +1,83 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1998-2001, 2005, 2006, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedGeometry
+ * @{
+ */
+
+/** \file geom.h */
+
+#ifndef PED_GEOM_H_INCLUDED
+#define PED_GEOM_H_INCLUDED
+
+#include <parted/device.h>
+#include <parted/timer.h>
+
+typedef struct _PedGeometry PedGeometry;
+
+/**
+ * Geometry of the partition
+ */
+struct _PedGeometry {
+ PedDevice* dev;
+ PedSector start;
+ PedSector length;
+ PedSector end;
+};
+
+extern int ped_geometry_init (PedGeometry* geom, const PedDevice* dev,
+ PedSector start, PedSector length);
+extern PedGeometry* ped_geometry_new (const PedDevice* dev, PedSector start,
+ PedSector length);
+extern PedGeometry* ped_geometry_duplicate (const PedGeometry* geom);
+extern PedGeometry* ped_geometry_intersect (const PedGeometry* a,
+ const PedGeometry* b);
+extern void ped_geometry_destroy (PedGeometry* geom);
+extern int ped_geometry_set (PedGeometry* geom, PedSector start,
+ PedSector length);
+extern int ped_geometry_set_start (PedGeometry* geom, PedSector start);
+extern int ped_geometry_set_end (PedGeometry* geom, PedSector end);
+extern int ped_geometry_test_overlap (const PedGeometry* a,
+ const PedGeometry* b);
+extern int ped_geometry_test_inside (const PedGeometry* a,
+ const PedGeometry* b);
+extern int ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b);
+extern int ped_geometry_test_sector_inside (const PedGeometry* geom,
+ PedSector sect);
+
+extern int ped_geometry_read (const PedGeometry* geom, void* buffer,
+ PedSector offset, PedSector count);
+extern int ped_geometry_write (PedGeometry* geom, const void* buffer,
+ PedSector offset, PedSector count);
+extern PedSector ped_geometry_check (PedGeometry* geom, void* buffer,
+ PedSector buffer_size, PedSector offset,
+ PedSector granularity, PedSector count,
+ PedTimer* timer);
+extern int ped_geometry_sync (PedGeometry* geom);
+extern int ped_geometry_sync_fast (PedGeometry* geom);
+
+/* returns -1 if "sector" is not within dest's space. */
+extern PedSector ped_geometry_map (const PedGeometry* dst,
+ const PedGeometry* src,
+ PedSector sector);
+
+#endif /* PED_GEOM_H_INCLUDED */
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/include/parted/gnu.h b/usr/src/lib/libparted/common/include/parted/gnu.h
new file mode 100644
index 0000000000..e809fb7915
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/gnu.h
@@ -0,0 +1,44 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_GNU_H_INCLUDED
+#define PED_GNU_H_INCLUDED
+
+#include <parted/parted.h>
+
+#include <hurd/store.h>
+
+#define GNU_SPECIFIC(dev) ((GNUSpecific*) (dev)->arch_specific)
+
+typedef struct _GNUSpecific GNUSpecific;
+
+struct _GNUSpecific {
+ struct store* store;
+ int consume;
+};
+
+extern PedArchitecture ped_gnu_arch;
+
+/* Initialize a PedDevice using SOURCE. The SOURCE will NOT be destroyed;
+ the caller created it, it is the caller's responsilbility to free it
+ after it calls ped_device_destory. SOURCE is not registered in Parted's
+ list of devices. */
+PedDevice* ped_device_new_from_store (struct store *source);
+
+#endif /* PED_GNU_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/include/parted/linux.h b/usr/src/lib/libparted/common/include/parted/linux.h
new file mode 100644
index 0000000000..52d28be696
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/linux.h
@@ -0,0 +1,45 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_LINUX_H_INCLUDED
+#define PED_LINUX_H_INCLUDED
+
+#include <parted/parted.h>
+#include <parted/device.h>
+
+#if defined __s390__ || defined __s390x__
+# include <parted/fdasd.h>
+#endif
+
+#define LINUX_SPECIFIC(dev) ((LinuxSpecific*) (dev)->arch_specific)
+
+typedef struct _LinuxSpecific LinuxSpecific;
+
+struct _LinuxSpecific {
+ int fd;
+#if defined(__s390__) || defined(__s390x__)
+ unsigned int real_sector_size;
+ /* IBM internal dasd structure (i guess ;), required. */
+ struct fdasd_anchor *anchor;
+#endif
+};
+
+extern PedArchitecture ped_linux_arch;
+
+#endif /* PED_LINUX_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/include/parted/natmath.h b/usr/src/lib/libparted/common/include/parted/natmath.h
new file mode 100644
index 0000000000..16306655b9
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/natmath.h
@@ -0,0 +1,108 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedAlignment
+ * @{
+ */
+
+/** \file natmath.h */
+
+#ifndef PED_NATMATH_H_INCLUDED
+#define PED_NATMATH_H_INCLUDED
+
+
+typedef struct _PedAlignment PedAlignment;
+
+#include <parted/disk.h>
+
+#define PED_MIN(a, b) ( ((a)<(b)) ? (a) : (b) )
+#define PED_MAX(a, b) ( ((a)>(b)) ? (a) : (b) )
+
+/* this is weird (I'm still not sure I should be doing this!)
+ *
+ * For the functions: new, destroy, duplicate and merge: the following values
+ * for align are valid:
+ * * align == NULL (!) represents no solution
+ * * align->grain_size == 0 represents a single solution
+ * (align->offset)
+ * * align->grain_size > 0 represents a set of solutions
+ *
+ * These are invalid:
+ * * align->offset < 0 Note: this gets "normalized"
+ * * align->grain_size < 0
+ *
+ * For the align_* operations, there must be a solution. i.e. align != NULL
+ * All solutions must be greater than zero.
+ */
+
+struct _PedAlignment {
+ PedSector offset;
+ PedSector grain_size;
+};
+
+extern PedSector ped_round_up_to (PedSector sector, PedSector grain_size);
+extern PedSector ped_round_down_to (PedSector sector, PedSector grain_size);
+extern PedSector ped_round_to_nearest (PedSector sector, PedSector grain_size);
+extern PedSector ped_greatest_common_divisor (PedSector a, PedSector b);
+
+extern int ped_alignment_init (PedAlignment* align, PedSector offset,
+ PedSector grain_size);
+extern PedAlignment* ped_alignment_new (PedSector offset, PedSector grain_size);
+extern void ped_alignment_destroy (PedAlignment* align);
+extern PedAlignment* ped_alignment_duplicate (const PedAlignment* align);
+extern PedAlignment* ped_alignment_intersect (const PedAlignment* a,
+ const PedAlignment* b);
+
+extern PedSector
+ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector);
+extern PedSector
+ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector);
+extern PedSector
+ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector);
+
+extern int
+ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector);
+
+extern const PedAlignment* ped_alignment_any;
+extern const PedAlignment* ped_alignment_none;
+
+#ifdef __sun
+extern PedSector
+#else
+extern inline PedSector
+#endif
+ped_div_round_up (PedSector numerator, PedSector divisor);
+
+#ifdef __sun
+extern PedSector
+#else
+extern inline PedSector
+#endif
+ped_div_round_to_nearest (PedSector numerator, PedSector divisor);
+
+#endif /* PED_NATMATH_H_INCLUDED */
+
+/**
+ * @}
+ */
+
diff --git a/usr/src/lib/libparted/common/include/parted/parted.h b/usr/src/lib/libparted/common/include/parted/parted.h
new file mode 100644
index 0000000000..1302d8f1bc
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/parted.h
@@ -0,0 +1,63 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PARTED_H_INCLUDED
+#define PARTED_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _PedArchitecture PedArchitecture;
+
+#include <parted/constraint.h>
+#include <parted/device.h>
+#include <parted/disk.h>
+#include <parted/exception.h>
+#include <parted/filesys.h>
+#include <parted/natmath.h>
+#include <parted/unit.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct _PedArchitecture {
+ PedDiskArchOps* disk_ops;
+ PedDeviceArchOps* dev_ops;
+};
+
+extern const PedArchitecture* ped_architecture;
+
+/* the architecture can't be changed if there are any PedDevice's.
+ * i.e. you should only be doing this if it's the FIRST thing you do...
+ */
+extern int ped_set_architecture (const PedArchitecture* arch);
+
+extern const char* ped_get_version ();
+
+extern void* ped_malloc (size_t size);
+extern void* ped_calloc (size_t size);
+extern int ped_realloc (void** ptr, size_t size);
+extern void ped_free (void* ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PARTED_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/include/parted/solaris.h b/usr/src/lib/libparted/common/include/parted/solaris.h
new file mode 100644
index 0000000000..39f404f16a
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/solaris.h
@@ -0,0 +1,42 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef PED_SOLARIS_H_INCLUDED
+#define PED_SOLARIS_H_INCLUDED
+
+#include <parted/parted.h>
+
+#define SOLARIS_SPECIFIC(dev) ((SolarisSpecific*) (dev)->arch_specific)
+
+typedef struct _SolarisSpecific SolarisSpecific;
+
+struct _SolarisSpecific {
+ int fd;
+};
+
+extern PedArchitecture ped_solaris_arch;
+
+#endif /* PED_SOLARIS_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/include/parted/timer.h b/usr/src/lib/libparted/common/include/parted/timer.h
new file mode 100644
index 0000000000..702d7036dd
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/timer.h
@@ -0,0 +1,65 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedTimer
+ * @{
+ */
+
+/** \file timer.h */
+
+#ifndef PED_TIMER_H_INCLUDED
+#define PED_TIMER_H_INCLUDED
+
+#include <time.h>
+
+typedef struct _PedTimer PedTimer;
+
+typedef void PedTimerHandler (PedTimer* timer, void* context);
+
+/*
+ * Structure keeping track of progress and time
+ */
+struct _PedTimer {
+ float frac; /**< fraction of operation done */
+ time_t start; /**< time of start of op */
+ time_t now; /**< time of last update (now!) */
+ time_t predicted_end; /**< expected finish time */
+ const char* state_name; /**< eg: "copying data" */
+ PedTimerHandler* handler; /**< who to notify on updates */
+ void* context; /**< context to pass to handler */
+};
+
+extern PedTimer* ped_timer_new (PedTimerHandler* handler, void* context);
+extern void ped_timer_destroy (PedTimer* timer);
+
+/* a nested timer automatically notifies it's parent. You should only
+ * create one when you are going to use it (not before)
+ */
+extern PedTimer* ped_timer_new_nested (PedTimer* parent, float nest_frac);
+extern void ped_timer_destroy_nested (PedTimer* timer);
+
+extern void ped_timer_touch (PedTimer* timer);
+extern void ped_timer_reset (PedTimer* timer);
+extern void ped_timer_update (PedTimer* timer, float new_frac);
+extern void ped_timer_set_state_name (PedTimer* timer, const char* state_name);
+
+#endif /* PED_TIMER_H_INCLUDED */
+
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/include/parted/unit.h b/usr/src/lib/libparted/common/include/parted/unit.h
new file mode 100644
index 0000000000..54a9790b45
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/unit.h
@@ -0,0 +1,93 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedUnit
+ * @{
+ */
+
+/** \file unit.h */
+
+#ifndef PED_UNIT_H_INCLUDED
+#define PED_UNIT_H_INCLUDED
+
+#include <parted/device.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define PED_SECTOR_SIZE_DEFAULT 512LL
+#define PED_KILOBYTE_SIZE 1000LL
+#define PED_MEGABYTE_SIZE 1000000LL
+#define PED_GIGABYTE_SIZE 1000000000LL
+#define PED_TERABYTE_SIZE 1000000000000LL
+#define PED_KIBIBYTE_SIZE 1024LL
+#define PED_MEBIBYTE_SIZE 1048576LL
+#define PED_GIBIBYTE_SIZE 1073741824LL
+#define PED_TEBIBYTE_SIZE 1099511627776LL
+
+/**
+ * Human-friendly unit for representation of a location within device
+ */
+typedef enum {
+ PED_UNIT_SECTOR,
+ PED_UNIT_BYTE,
+ PED_UNIT_KILOBYTE,
+ PED_UNIT_MEGABYTE,
+ PED_UNIT_GIGABYTE,
+ PED_UNIT_TERABYTE,
+ PED_UNIT_COMPACT,
+ PED_UNIT_CYLINDER,
+ PED_UNIT_CHS,
+ PED_UNIT_PERCENT,
+ PED_UNIT_KIBIBYTE,
+ PED_UNIT_MEBIBYTE,
+ PED_UNIT_GIBIBYTE,
+ PED_UNIT_TEBIBYTE
+} PedUnit;
+
+#define PED_UNIT_FIRST PED_UNIT_SECTOR
+#define PED_UNIT_LAST PED_UNIT_TEBIBYTE
+
+extern long long ped_unit_get_size (const PedDevice* dev, PedUnit unit);
+extern const char* ped_unit_get_name (PedUnit unit);
+extern PedUnit ped_unit_get_by_name (const char* unit_name);
+
+extern void ped_unit_set_default (PedUnit unit);
+extern PedUnit ped_unit_get_default ();
+
+extern char* ped_unit_format_byte (const PedDevice* dev, PedSector byte);
+extern char* ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte,
+ PedUnit unit);
+
+extern char* ped_unit_format (const PedDevice* dev, PedSector sector);
+extern char* ped_unit_format_custom (const PedDevice* dev, PedSector sector,
+ PedUnit unit);
+
+extern int ped_unit_parse (const char* str, const PedDevice* dev,
+ PedSector* sector,
+ PedGeometry** range);
+extern int ped_unit_parse_custom (const char* str, const PedDevice* dev,
+ PedUnit unit, PedSector* sector,
+ PedGeometry** range);
+
+#endif /* PED_UNIT_H_INCLUDED */
+
+/** @} */
+
+
diff --git a/usr/src/lib/libparted/common/include/parted/vtoc.h b/usr/src/lib/libparted/common/include/parted/vtoc.h
new file mode 100644
index 0000000000..a475ea404b
--- /dev/null
+++ b/usr/src/lib/libparted/common/include/parted/vtoc.h
@@ -0,0 +1,293 @@
+/*
+ * File...........: s390-tools/dasdview/vtoc.h
+ * Author(s)......: Horst Hummel <horst.hummel@de.ibm.com>
+ * Bugreports.to..: <Linux390@de.ibm.com>
+ *
+ * This is a user-space copy of the kernel vtoc,h.
+ *
+ * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2002
+ *
+ * History of changes (starts March 2002)
+ * 2002-03-12 initial
+ */
+
+#ifndef VTOC_H
+#define VTOC_H
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#define LINE_LENGTH 80
+#define VTOC_START_CC 0x0
+#define VTOC_START_HH 0x1
+#define FIRST_USABLE_CYL 1
+#define FIRST_USABLE_TRK 2
+
+#define DASD_3380_TYPE 13148
+#define DASD_3390_TYPE 13200
+#define DASD_9345_TYPE 37701
+
+#define DASD_3380_VALUE 0xbb60
+#define DASD_3390_VALUE 0xe5a2
+#define DASD_9345_VALUE 0xbc98
+
+#define VOLSER_LENGTH 6
+#define BIG_DISK_SIZE 0x10000
+
+#if defined(__sun)
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+#endif
+
+typedef struct ttr ttr_t;
+typedef struct cchhb cchhb_t;
+typedef struct cchh cchh_t;
+typedef struct labeldate labeldate_t;
+typedef struct volume_label volume_label_t;
+typedef struct extent extent_t;
+typedef struct dev_const dev_const_t;
+typedef struct format1_label format1_label_t;
+typedef struct format4_label format4_label_t;
+typedef struct ds5ext ds5ext_t;
+typedef struct format5_label format5_label_t;
+typedef struct ds7ext ds7ext_t;
+typedef struct format7_label format7_label_t;
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) ttr {
+ u_int16_t tt;
+ u_int8_t r;
+};
+
+struct __attribute__ ((packed)) cchhb {
+ u_int16_t cc;
+ u_int16_t hh;
+ u_int8_t b;
+};
+
+struct __attribute__ ((packed)) cchh {
+ u_int16_t cc;
+ u_int16_t hh;
+};
+
+struct __attribute__ ((packed)) labeldate {
+ u_int8_t year;
+ u_int16_t day;
+};
+
+struct __attribute__ ((packed)) volume_label {
+ char volkey[4]; /* volume key = volume label */
+ char vollbl[4]; /* volume label */
+ char volid[6]; /* volume identifier */
+ u_int8_t security; /* security byte */
+ cchhb_t vtoc; /* VTOC address */
+ char res1[5]; /* reserved */
+ char cisize[4]; /* CI-size for FBA,... */
+ /* ...blanks for CKD */
+ char blkperci[4]; /* no of blocks per CI (FBA), blanks for CKD */
+ char labperci[4]; /* no of labels per CI (FBA), blanks for CKD */
+ char res2[4]; /* reserved */
+ char lvtoc[14]; /* owner code for LVTOC */
+ char res3[29]; /* reserved */
+};
+
+struct __attribute__ ((packed)) extent {
+ u_int8_t typeind; /* extent type indicator */
+ u_int8_t seqno; /* extent sequence number */
+ cchh_t llimit; /* starting point of this extent */
+ cchh_t ulimit; /* ending point of this extent */
+};
+
+struct __attribute__ ((packed)) dev_const {
+ u_int16_t DS4DSCYL; /* number of logical cyls */
+ u_int16_t DS4DSTRK; /* number of tracks in a logical cylinder */
+ u_int16_t DS4DEVTK; /* device track length */
+ u_int8_t DS4DEVI; /* non-last keyed record overhead */
+ u_int8_t DS4DEVL; /* last keyed record overhead */
+ u_int8_t DS4DEVK; /* non-keyed record overhead differential */
+ u_int8_t DS4DEVFG; /* flag byte */
+ u_int16_t DS4DEVTL; /* device tolerance */
+ u_int8_t DS4DEVDT; /* number of DSCB's per track */
+ u_int8_t DS4DEVDB; /* number of directory blocks per track */
+};
+
+struct __attribute__ ((packed)) format1_label {
+ char DS1DSNAM[44]; /* data set name */
+ u_int8_t DS1FMTID; /* format identifier */
+ char DS1DSSN[6]; /* data set serial number */
+ u_int16_t DS1VOLSQ; /* volume sequence number */
+ labeldate_t DS1CREDT; /* creation date: ydd */
+ labeldate_t DS1EXPDT; /* expiration date */
+ u_int8_t DS1NOEPV; /* number of extents on volume */
+ u_int8_t DS1NOBDB; /* no. of bytes used in last direction blk */
+ u_int8_t DS1FLAG1; /* flag 1 */
+ char DS1SYSCD[13]; /* system code */
+ labeldate_t DS1REFD; /* date last referenced */
+ u_int8_t DS1SMSFG; /* system managed storage indicators */
+ u_int8_t DS1SCXTF; /* sec. space extension flag byte */
+ u_int16_t DS1SCXTV; /* secondary space extension value */
+ u_int8_t DS1DSRG1; /* data set organisation byte 1 */
+ u_int8_t DS1DSRG2; /* data set organisation byte 2 */
+ u_int8_t DS1RECFM; /* record format */
+ u_int8_t DS1OPTCD; /* option code */
+ u_int16_t DS1BLKL; /* block length */
+ u_int16_t DS1LRECL; /* record length */
+ u_int8_t DS1KEYL; /* key length */
+ u_int16_t DS1RKP; /* relative key position */
+ u_int8_t DS1DSIND; /* data set indicators */
+ u_int8_t DS1SCAL1; /* secondary allocation flag byte */
+ char DS1SCAL3[3]; /* secondary allocation quantity */
+ ttr_t DS1LSTAR; /* last used track and block on track */
+ u_int16_t DS1TRBAL; /* space remaining on last used track */
+ u_int16_t res1; /* reserved */
+ extent_t DS1EXT1; /* first extent description */
+ extent_t DS1EXT2; /* second extent description */
+ extent_t DS1EXT3; /* third extent description */
+ cchhb_t DS1PTRDS; /* possible pointer to f2 or f3 DSCB */
+};
+
+struct __attribute__ ((packed)) format4_label {
+ char DS4KEYCD[44]; /* key code for VTOC labels: 44 times 0x04 */
+ u_int8_t DS4IDFMT; /* format identifier */
+ cchhb_t DS4HPCHR; /* highest address of a format 1 DSCB */
+ u_int16_t DS4DSREC; /* number of available DSCB's */
+ cchh_t DS4HCCHH; /* CCHH of next available alternate track */
+ u_int16_t DS4NOATK; /* number of remaining alternate tracks */
+ u_int8_t DS4VTOCI; /* VTOC indicators */
+ u_int8_t DS4NOEXT; /* number of extents in VTOC */
+ u_int8_t DS4SMSFG; /* system managed storage indicators */
+ u_int8_t DS4DEVAC; /* number of alternate cylinders.
+ Subtract from first two bytes of
+ DS4DEVSZ to get number of usable
+ cylinders. can be zero. valid
+ only if DS4DEVAV on. */
+ dev_const_t DS4DEVCT; /* device constants */
+ char DS4AMTIM[8]; /* VSAM time stamp */
+ char DS4AMCAT[3]; /* VSAM catalog indicator */
+ char DS4R2TIM[8]; /* VSAM volume/catalog match time stamp */
+ char res1[5]; /* reserved */
+ char DS4F6PTR[5]; /* pointer to first format 6 DSCB */
+ extent_t DS4VTOCE; /* VTOC extent description */
+ char res2[10]; /* reserved */
+ u_int8_t DS4EFLVL; /* extended free-space management level */
+ cchhb_t DS4EFPTR; /* pointer to extended free-space info */
+ char res3[9]; /* reserved */
+};
+
+struct __attribute__ ((packed)) ds5ext {
+ u_int16_t t; /* RTA of the first track of free extent */
+ u_int16_t fc; /* number of whole cylinders in free ext. */
+ u_int8_t ft; /* number of remaining free tracks */
+};
+
+struct __attribute__ ((packed)) format5_label {
+ char DS5KEYID[4]; /* key identifier */
+ ds5ext_t DS5AVEXT; /* first available (free-space) extent. */
+ ds5ext_t DS5EXTAV[7]; /* seven available extents */
+ u_int8_t DS5FMTID; /* format identifier */
+ ds5ext_t DS5MAVET[18]; /* eighteen available extents */
+ cchhb_t DS5PTRDS; /* pointer to next format5 DSCB */
+};
+
+struct __attribute__ ((packed)) ds7ext {
+ u_int32_t a; /* starting RTA value */
+ u_int32_t b; /* ending RTA value + 1 */
+};
+
+struct __attribute__ ((packed)) format7_label {
+ char DS7KEYID[4]; /* key identifier */
+ ds7ext_t DS7EXTNT[5]; /* space for 5 extent descriptions */
+ u_int8_t DS7FMTID; /* format identifier */
+ ds7ext_t DS7ADEXT[11]; /* space for 11 extent descriptions */
+ char res1[2]; /* reserved */
+ cchhb_t DS7PTRDS; /* pointer to next FMT7 DSCB */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+char * vtoc_ebcdic_enc (char source[LINE_LENGTH], char target[LINE_LENGTH],
+ int l);
+char * vtoc_ebcdic_dec (char source[LINE_LENGTH], char target[LINE_LENGTH],
+ int l);
+void vtoc_set_extent (extent_t * ext, u_int8_t typeind, u_int8_t seqno,
+ cchh_t * lower, cchh_t * upper);
+void vtoc_set_cchh (cchh_t * addr, u_int16_t cc, u_int16_t hh);
+void vtoc_set_cchhb (cchhb_t * addr, u_int16_t cc, u_int16_t hh, u_int8_t b);
+void vtoc_set_date (labeldate_t * d, u_int8_t year, u_int16_t day);
+
+void vtoc_volume_label_init (volume_label_t *vlabel);
+
+int vtoc_read_volume_label (int fd, unsigned long vlabel_start,
+ volume_label_t * vlabel);
+
+int vtoc_write_volume_label (int fd, unsigned long vlabel_start,
+ volume_label_t *vlabel);
+
+void vtoc_volume_label_set_volser (volume_label_t *vlabel, char *volser);
+
+char *vtoc_volume_label_get_volser (volume_label_t *vlabel, char *volser);
+
+void vtoc_volume_label_set_key (volume_label_t *vlabel, char *key);
+
+void vtoc_volume_label_set_label (volume_label_t *vlabel, char *lbl);
+
+char *vtoc_volume_label_get_label (volume_label_t *vlabel, char *lbl);
+
+void vtoc_read_label (int fd, unsigned long position, format1_label_t *f1,
+ format4_label_t *f4, format5_label_t *f5,
+ format7_label_t *f7);
+
+void vtoc_write_label (int fd, unsigned long position, format1_label_t *f1,
+ format4_label_t *f4, format5_label_t *f5,
+ format7_label_t *f7);
+
+void vtoc_init_format1_label (char *volid, unsigned int blksize,
+ extent_t *part_extent, format1_label_t *f1);
+
+void vtoc_init_format4_label (format4_label_t *f4lbl,
+ unsigned int usable_partitions,
+ unsigned int cylinders,
+ unsigned int tracks,
+ unsigned int blocks,
+ unsigned int blksize,
+ u_int16_t dev_type);
+
+void vtoc_update_format4_label (format4_label_t *f4, cchhb_t *highest_f1,
+ u_int16_t unused_update);
+
+void vtoc_init_format5_label (format5_label_t *f5);
+
+void vtoc_update_format5_label_add (format5_label_t *f5, int verbose, int cyl,
+ int trk, u_int16_t a, u_int16_t b,
+ u_int8_t c);
+
+void vtoc_update_format5_label_del (format5_label_t *f5, int verbose, int cyl,
+ int trk, u_int16_t a, u_int16_t b,
+ u_int8_t c);
+
+void vtoc_init_format7_label (format7_label_t *f7);
+
+void vtoc_update_format7_label_add (format7_label_t *f7, int verbose,
+ u_int32_t a, u_int32_t b);
+
+void vtoc_update_format7_label_del (format7_label_t *f7, int verbose,
+ u_int32_t a, u_int32_t b);
+
+void vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5,
+ format7_label_t *f7, char ch, int verbose,
+ u_int32_t start, u_int32_t stop, int cyl, int trk);
+
+#endif /* VTOC_H */
diff --git a/usr/src/lib/libparted/common/lib/__fpending.h b/usr/src/lib/libparted/common/lib/__fpending.h
new file mode 100644
index 0000000000..8a8aabcc22
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/__fpending.h
@@ -0,0 +1,34 @@
+/* Declare __fpending.
+
+ Copyright (C) 2000, 2003, 2005, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ Written by Jim Meyering. */
+
+#include <stddef.h>
+#include <stdio.h>
+
+#ifndef HAVE_DECL___FPENDING
+"this configure-time declaration test was not run"
+#endif
+
+#if HAVE_DECL___FPENDING
+# if HAVE_STDIO_EXT_H
+# include <stdio_ext.h>
+# endif
+#else
+size_t __fpending (FILE *);
+#endif
diff --git a/usr/src/lib/libparted/common/lib/basename.c b/usr/src/lib/libparted/common/lib/basename.c
new file mode 100644
index 0000000000..fbe17ff910
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/basename.c
@@ -0,0 +1,129 @@
+/* basename.c -- return the last element in a file name
+
+ Copyright (C) 1990, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "dirname.h"
+
+#include <string.h>
+#include "xalloc.h"
+#include "xstrndup.h"
+
+/* Return the address of the last file name component of NAME. If
+ NAME has no relative file name components because it is a file
+ system root, return the empty string. */
+
+char *
+last_component (char const *name)
+{
+ char const *base = name + FILE_SYSTEM_PREFIX_LEN (name);
+ char const *p;
+ bool saw_slash = false;
+
+ while (ISSLASH (*base))
+ base++;
+
+ for (p = base; *p; p++)
+ {
+ if (ISSLASH (*p))
+ saw_slash = true;
+ else if (saw_slash)
+ {
+ base = p;
+ saw_slash = false;
+ }
+ }
+
+ return (char *) base;
+}
+
+
+/* In general, we can't use the builtin `basename' function if available,
+ since it has different meanings in different environments.
+ In some environments the builtin `basename' modifies its argument.
+
+ Return the last file name component of NAME, allocated with
+ xmalloc. On systems with drive letters, a leading "./"
+ distinguishes relative names that would otherwise look like a drive
+ letter. Unlike POSIX basename(), NAME cannot be NULL,
+ base_name("") returns "", and the first trailing slash is not
+ stripped.
+
+ If lstat (NAME) would succeed, then { chdir (dir_name (NAME));
+ lstat (base_name (NAME)); } will access the same file. Likewise,
+ if the sequence { chdir (dir_name (NAME));
+ rename (base_name (NAME), "foo"); } succeeds, you have renamed NAME
+ to "foo" in the same directory NAME was in. */
+
+char *
+base_name (char const *name)
+{
+ char const *base = last_component (name);
+ size_t length;
+
+ /* If there is no last component, then name is a file system root or the
+ empty string. */
+ if (! *base)
+ return xstrndup (name, base_len (name));
+
+ /* Collapse a sequence of trailing slashes into one. */
+ length = base_len (base);
+ if (ISSLASH (base[length]))
+ length++;
+
+ /* On systems with drive letters, `a/b:c' must return `./b:c' rather
+ than `b:c' to avoid confusion with a drive letter. On systems
+ with pure POSIX semantics, this is not an issue. */
+ if (FILE_SYSTEM_PREFIX_LEN (base))
+ {
+ char *p = xmalloc (length + 3);
+ p[0] = '.';
+ p[1] = '/';
+ memcpy (p + 2, base, length);
+ p[length + 2] = '\0';
+ return p;
+ }
+
+ /* Finally, copy the basename. */
+ return xstrndup (base, length);
+}
+
+/* Return the length of the basename NAME. Typically NAME is the
+ value returned by base_name or last_component. Act like strlen
+ (NAME), except omit all trailing slashes. */
+
+size_t
+base_len (char const *name)
+{
+ size_t len;
+ size_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
+
+ for (len = strlen (name); 1 < len && ISSLASH (name[len - 1]); len--)
+ continue;
+
+ if (DOUBLE_SLASH_IS_DISTINCT_ROOT && len == 1
+ && ISSLASH (name[0]) && ISSLASH (name[1]) && ! name[2])
+ return 2;
+
+ if (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE && prefix_len
+ && len == prefix_len && ISSLASH (name[prefix_len]))
+ return prefix_len + 1;
+
+ return len;
+}
diff --git a/usr/src/lib/libparted/common/lib/close-stream.c b/usr/src/lib/libparted/common/lib/close-stream.c
new file mode 100644
index 0000000000..72d0d6816c
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/close-stream.c
@@ -0,0 +1,76 @@
+/* Close a stream, with nicer error checking than fclose's.
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "close-stream.h"
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include "__fpending.h"
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+/* Close STREAM. Return 0 if successful, EOF (setting errno)
+ otherwise. A failure might set errno to 0 if the error number
+ cannot be determined.
+
+ If a program writes *anything* to STREAM, that program should close
+ STREAM and make sure that it succeeds before exiting. Otherwise,
+ suppose that you go to the extreme of checking the return status
+ of every function that does an explicit write to STREAM. The last
+ printf can succeed in writing to the internal stream buffer, and yet
+ the fclose(STREAM) could still fail (due e.g., to a disk full error)
+ when it tries to write out that buffered data. Thus, you would be
+ left with an incomplete output file and the offending program would
+ exit successfully. Even calling fflush is not always sufficient,
+ since some file systems (NFS and CODA) buffer written/flushed data
+ until an actual close call.
+
+ Besides, it's wasteful to check the return value from every call
+ that writes to STREAM -- just let the internal stream state record
+ the failure. That's what the ferror test is checking below. */
+
+int
+close_stream (FILE *stream)
+{
+ bool some_pending = (__fpending (stream) != 0);
+ bool prev_fail = (ferror (stream) != 0);
+ bool fclose_fail = (fclose (stream) != 0);
+
+ /* Return an error indication if there was a previous failure or if
+ fclose failed, with one exception: ignore an fclose failure if
+ there was no previous error, no data remains to be flushed, and
+ fclose failed with EBADF. That can happen when a program like cp
+ is invoked like this `cp a b >&-' (i.e., with standard output
+ closed) and doesn't generate any output (hence no previous error
+ and nothing to be flushed). */
+
+ if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
+ {
+ if (! fclose_fail)
+ errno = 0;
+ return EOF;
+ }
+
+ return 0;
+}
diff --git a/usr/src/lib/libparted/common/lib/close-stream.h b/usr/src/lib/libparted/common/lib/close-stream.h
new file mode 100644
index 0000000000..be3d4196b0
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/close-stream.h
@@ -0,0 +1,2 @@
+#include <stdio.h>
+int close_stream (FILE *stream);
diff --git a/usr/src/lib/libparted/common/lib/closeout.c b/usr/src/lib/libparted/common/lib/closeout.c
new file mode 100644
index 0000000000..830f16f8ae
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/closeout.c
@@ -0,0 +1,86 @@
+/* Close standard output and standard error, exiting with a diagnostic on error.
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "closeout.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#include "close-stream.h"
+#include "error.h"
+#include "exitfail.h"
+#include "quotearg.h"
+
+static const char *file_name;
+
+/* Set the file name to be reported in the event an error is detected
+ by close_stdout. */
+void
+close_stdout_set_file_name (const char *file)
+{
+ file_name = file;
+}
+
+/* Close standard output. On error, issue a diagnostic and _exit
+ with status 'exit_failure'.
+
+ Also close standard error. On error, _exit with status 'exit_failure'.
+
+ Since close_stdout is commonly registered via 'atexit', POSIX
+ and the C standard both say that it should not call 'exit',
+ because the behavior is undefined if 'exit' is called more than
+ once. So it calls '_exit' instead of 'exit'. If close_stdout
+ is registered via atexit before other functions are registered,
+ the other functions can act before this _exit is invoked.
+
+ Applications that use close_stdout should flush any streams
+ other than stdout and stderr before exiting, since the call to
+ _exit will bypass other buffer flushing. Applications should
+ be flushing and closing other streams anyway, to check for I/O
+ errors. Also, applications should not use tmpfile, since _exit
+ can bypass the removal of these files.
+
+ It's important to detect such failures and exit nonzero because many
+ tools (most notably `make' and other build-management systems) depend
+ on being able to detect failure in other tools via their exit status. */
+
+void
+close_stdout (void)
+{
+ if (close_stream (stdout) != 0)
+ {
+ char const *write_error = _("write error");
+ if (file_name)
+ error (0, errno, "%s: %s", quotearg_colon (file_name),
+ write_error);
+ else
+ error (0, errno, "%s", write_error);
+
+ _exit (exit_failure);
+ }
+
+ if (close_stream (stderr) != 0)
+ _exit (exit_failure);
+}
diff --git a/usr/src/lib/libparted/common/lib/closeout.h b/usr/src/lib/libparted/common/lib/closeout.h
new file mode 100644
index 0000000000..8bed23b5f1
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/closeout.h
@@ -0,0 +1,33 @@
+/* Close standard output and standard error.
+
+ Copyright (C) 1998, 2000, 2003, 2004, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef CLOSEOUT_H
+# define CLOSEOUT_H 1
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+void close_stdout_set_file_name (const char *file);
+void close_stdout (void);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
diff --git a/usr/src/lib/libparted/common/lib/config.h b/usr/src/lib/libparted/common/lib/config.h
new file mode 100644
index 0000000000..41503d0b88
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/config.h
@@ -0,0 +1,597 @@
+/* lib/config.h. Generated from config.h.in by configure. */
+/* lib/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to the number of bits in type 'ptrdiff_t'. */
+#define BITSIZEOF_PTRDIFF_T 32
+
+/* Define to the number of bits in type 'sig_atomic_t'. */
+#define BITSIZEOF_SIG_ATOMIC_T 32
+
+/* Define to the number of bits in type 'size_t'. */
+#define BITSIZEOF_SIZE_T 32
+
+/* Define to the number of bits in type 'wchar_t'. */
+#define BITSIZEOF_WCHAR_T 32
+
+/* Define to the number of bits in type 'wint_t'. */
+#define BITSIZEOF_WINT_T 32
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Enable assertions, etc. */
+#define DEBUG 1
+
+/* Probing functionality only */
+/* #undef DISCOVER_ONLY */
+
+/* Define to 1 if // is a file system root distinct from /. */
+/* #undef DOUBLE_SLASH_IS_DISTINCT_ROOT */
+
+/* Lazy linking to fs libs */
+/* #undef DYNAMIC_LOADING */
+
+/* device mapper (libdevmapper) support */
+/* #undef ENABLE_DEVICE_MAPPER */
+
+/* Include file system support. i.e. libparted/fs_... */
+#define ENABLE_FS 1
+
+/* Mtrace malloc() debugging */
+/* #undef ENABLE_MTRACE */
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+/* #undef ENABLE_NLS */
+
+/* Include PC98 partition tables. (Sometimes excluded to avoid collisions with
+ msdos partition tables */
+#define ENABLE_PC98 1
+
+/* Define on systems for which file names may have a so-called `drive letter'
+ prefix, define this to compute the length of that prefix, including the
+ colon. */
+#define FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX 0
+
+/* Define if the backslash character may also serve as a file name component
+ separator. */
+#define FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR 0
+
+/* Define if a drive letter prefix denotes a relative path if it is not
+ followed by a file name component separator. */
+#define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0
+
+/* Define to make the limit macros in <stdint.h> visible. */
+#define GL_TRIGGER_STDC_LIMIT_MACROS 1
+
+/* Define to 1 when using the gnulib module close-stream. */
+#define GNULIB_CLOSE_STREAM 1
+
+/* Define to 1 if you have 'alloca' after including <alloca.h>, a header that
+ may be supplied by this distribution. */
+#define HAVE_ALLOCA 1
+
+/* Define HAVE_ALLOCA_H for backward compatibility with older code that
+ includes <alloca.h> only if HAVE_ALLOCA_H is defined. */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the `atexit' function. */
+#define HAVE_ATEXIT 1
+
+/* Has backtrace support */
+#define HAVE_BACKTRACE 1
+
+/* Define to 1 if you have the <bp-sym.h> header file. */
+/* #undef HAVE_BP_SYM_H */
+
+/* Define to 1 if your system has a GNU libc compatible `calloc' function, and
+ to 0 otherwise. */
+#define HAVE_CALLOC 1
+
+/* Define to 1 if you have the `canonicalize_file_name' function. */
+#define HAVE_CANONICALIZE_FILE_NAME 1
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+/* #undef HAVE_CFLOCALECOPYCURRENT */
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+/* #undef HAVE_DCGETTEXT */
+
+/* Define to 1 if you have the declaration of `getc_unlocked', and to 0 if you
+ don't. */
+#define HAVE_DECL_GETC_UNLOCKED 1
+
+/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't.
+ */
+#define HAVE_DECL_GETENV 1
+
+/* Define to 1 if you have the declaration of `imaxabs', and to 0 if you
+ don't. */
+#define HAVE_DECL_IMAXABS 1
+
+/* Define to 1 if you have the declaration of `imaxdiv', and to 0 if you
+ don't. */
+#define HAVE_DECL_IMAXDIV 1
+
+/* Define to 1 if you have the declaration of `isblank', and to 0 if you
+ don't. */
+#define HAVE_DECL_ISBLANK 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `strndup', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNDUP 0
+
+/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRNLEN 1
+
+/* Define to 1 if you have the declaration of `strtoimax', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRTOIMAX 1
+
+/* Define to 1 if you have the declaration of `strtoumax', and to 0 if you
+ don't. */
+#define HAVE_DECL_STRTOUMAX 1
+
+/* Define to 1 if you have the declaration of `__fpending', and to 0 if you
+ don't. */
+#define HAVE_DECL___FPENDING 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long_only' function. */
+#define HAVE_GETOPT_LONG_ONLY 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+/* #undef HAVE_GETTEXT */
+
+/* Define to 1 if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if your compiler supports the #include_next directive. */
+#define HAVE_INCLUDE_NEXT 1
+
+/* Define to 1 if the compiler supports one of the keywords 'inline',
+ '__inline__', '__inline' and effectively inlines functions marked as such.
+ */
+/* #undef HAVE_INLINE */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `isblank' function. */
+#define HAVE_ISBLANK 1
+
+/* Define to 1 if you have the `iswcntrl' function. */
+#define HAVE_ISWCNTRL 1
+
+/* Define to 1 if you have the `iswctype' function. */
+#define HAVE_ISWCTYPE 1
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define to 1 if you have the `parted' library (-lparted). */
+/* #undef HAVE_LIBPARTED */
+
+/* have readline */
+/* #undef HAVE_LIBREADLINE */
+
+/* Have libreiserfs */
+/* #undef HAVE_LIBREISERFS */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
+ to 0 otherwise. */
+#define HAVE_MALLOC 1
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the `mbsinit' function. */
+#define HAVE_MBSINIT 1
+
+/* Define to 1 if <wchar.h> declares mbstate_t. */
+#define HAVE_MBSTATE_T 1
+
+/* Define to 1 if you have the `memchr' function. */
+#define HAVE_MEMCHR 1
+
+/* Define to 1 if you have the `memcpy' function. */
+/* #undef HAVE_MEMCPY */
+
+/* Define to 1 if you have the `memmove' function. */
+/* #undef HAVE_MEMMOVE */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+/* #undef HAVE_MEMSET */
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+/* #undef HAVE_READLINE_HISTORY_H */
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+/* #undef HAVE_READLINE_READLINE_H */
+
+/* Define to 1 if your system has a GNU libc compatible `realloc' function,
+ and to 0 otherwise. */
+#define HAVE_REALLOC 1
+
+/* Have reiserfs_fs_check() */
+/* #undef HAVE_REISERFS_FS_CHECK */
+
+/* Define to 1 if you have the `rl_completion_matches' function. */
+/* #undef HAVE_RL_COMPLETION_MATCHES */
+
+/* Define to 1 if you have the `rpmatch' function. */
+/* #undef HAVE_RPMATCH */
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if 'sig_atomic_t' is a signed integer type. */
+#define HAVE_SIGNED_SIG_ATOMIC_T 1
+
+/* Define to 1 if 'wchar_t' is a signed integer type. */
+#define HAVE_SIGNED_WCHAR_T 1
+
+/* Define to 1 if 'wint_t' is a signed integer type. */
+#define HAVE_SIGNED_WINT_T 1
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio_ext.h> header file. */
+#define HAVE_STDIO_EXT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcspn' function. */
+/* #undef HAVE_STRCSPN */
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the strndup() function and it works. */
+/* #undef HAVE_STRNDUP */
+
+/* Define to 1 if you have the `strtol' function. */
+#define HAVE_STRTOL 1
+
+/* Define if struct utimbuf is declared -- usually in <utime.h>. Some systems
+ have utime.h but don't declare the struct anywhere. */
+#define HAVE_STRUCT_UTIMBUF 1
+
+/* Define to 1 if you have the <sys/bitypes.h> header file. */
+/* #undef HAVE_SYS_BITYPES_H */
+
+/* Define to 1 if you have the <sys/inttypes.h> header file. */
+#define HAVE_SYS_INTTYPES_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <termcap.h> header file. */
+/* #undef HAVE_TERMCAP_H */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define if utimes accepts a null argument */
+/* #undef HAVE_UTIMES_NULL */
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */
+#define HAVE_UTIME_NULL 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscoll' function. */
+#define HAVE_WCSCOLL 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define if you have the 'wint_t' type. */
+#define HAVE_WINT_T 1
+
+/* Define to 1 if the system has the type `_Bool'. */
+#define HAVE__BOOL 1
+
+/* Define to 1 if you have the `__fpending' function. */
+#define HAVE___FPENDING 1
+
+/* Extract low level special HFS(+) files for debugging purposes when using
+ the "check" command (NOT FOR PACKAGING) */
+/* #undef HFS_EXTRACT_FS */
+
+#if FILE_SYSTEM_BACKSLASH_IS_FILE_NAME_SEPARATOR
+# define ISSLASH(C) ((C) == '/' || (C) == '\\')
+#else
+# define ISSLASH(C) ((C) == '/')
+#endif
+
+/* Define to 1 if assertions should be disabled. */
+/* #undef NDEBUG */
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "parted"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "bug-parted@gnu.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "GNU parted"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "GNU parted 1.8.8"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "parted"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.8.8"
+
+/* the number of pending output bytes on stream `fp' */
+/* #undef PENDING_OUTPUT_N_BYTES */
+
+/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
+/* #undef PRI_MACROS_BROKEN */
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'ptrdiff_t'. */
+#define PTRDIFF_T_SUFFIX
+
+/* Disable all writing code */
+/* #undef READ_ONLY */
+
+/* Define if rename does not work for source file names with a trailing slash,
+ like the one from SunOS 4.1.1_U1. */
+/* #undef RENAME_TRAILING_SLASH_BUG */
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'sig_atomic_t'. */
+#define SIG_ATOMIC_T_SUFFIX
+
+/* The size of `off_t', as computed by sizeof. */
+#define SIZEOF_OFF_T 4
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'size_t'. */
+#define SIZE_T_SUFFIX u
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if strerror_r returns char *. */
+/* #undef STRERROR_R_CHAR_P */
+
+/* Version number of package */
+#define VERSION "1.8.8"
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'wchar_t'. */
+#define WCHAR_T_SUFFIX l
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'wint_t'. */
+#define WINT_T_SUFFIX l
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define if you want regoff_t to be at least as wide POSIX requires. */
+#define _REGEX_LARGE_OFFSETS 1
+
+/* Enable extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+
+/* Define to rpl_ if the getopt replacement functions and variables should be
+ used. */
+#define __GETOPT_PREFIX rpl_
+
+/* Ensure that <stdint.h> defines the limit macros, since gnulib's
+ <inttypes.h> relies on them. */
+#if defined __cplusplus && !defined __STDC_LIMIT_MACROS && GL_TRIGGER_STDC_LIMIT_MACROS
+# define __STDC_LIMIT_MACROS 1
+#endif
+
+
+/* Define to rpl_calloc if the replacement function should be used. */
+/* #undef calloc */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to rpl_free if the replacement function should be used. */
+/* #undef free */
+
+/* A replacement for va_copy, if needed. */
+#define gl_va_copy(a,b) ((a) = (b))
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to rpl_malloc if the replacement function should be used. */
+/* #undef malloc */
+
+/* Define to a type if <wchar.h> does not define. */
+/* #undef mbstate_t */
+
+/* Define to rpl_memcmp if the replacement function should be used. */
+/* #undef memcmp */
+
+/* Define to rpl_re_comp if the replacement should be used. */
+#define re_comp rpl_re_comp
+
+/* Define to rpl_re_compile_fastmap if the replacement should be used. */
+#define re_compile_fastmap rpl_re_compile_fastmap
+
+/* Define to rpl_re_compile_pattern if the replacement should be used. */
+#define re_compile_pattern rpl_re_compile_pattern
+
+/* Define to rpl_re_exec if the replacement should be used. */
+#define re_exec rpl_re_exec
+
+/* Define to rpl_re_match if the replacement should be used. */
+#define re_match rpl_re_match
+
+/* Define to rpl_re_match_2 if the replacement should be used. */
+#define re_match_2 rpl_re_match_2
+
+/* Define to rpl_re_search if the replacement should be used. */
+#define re_search rpl_re_search
+
+/* Define to rpl_re_search_2 if the replacement should be used. */
+#define re_search_2 rpl_re_search_2
+
+/* Define to rpl_re_set_registers if the replacement should be used. */
+#define re_set_registers rpl_re_set_registers
+
+/* Define to rpl_re_set_syntax if the replacement should be used. */
+#define re_set_syntax rpl_re_set_syntax
+
+/* Define to rpl_re_syntax_options if the replacement should be used. */
+#define re_syntax_options rpl_re_syntax_options
+
+/* Define to rpl_realloc if the replacement function should be used. */
+/* #undef realloc */
+
+/* Define to rpl_regcomp if the replacement should be used. */
+#define regcomp rpl_regcomp
+
+/* Define to rpl_regerror if the replacement should be used. */
+#define regerror rpl_regerror
+
+/* Define to rpl_regexec if the replacement should be used. */
+#define regexec rpl_regexec
+
+/* Define to rpl_regfree if the replacement should be used. */
+#define regfree rpl_regfree
+
+/* Define to rpl_rename if the replacement function should be used. */
+/* #undef rename */
+
+/* Define to equivalent of C99 restrict keyword, or to nothing if this is not
+ supported. Do not define if restrict is supported directly. */
+/* #undef restrict */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define as a signed type of the same size as size_t. */
+/* #undef ssize_t */
+
+/* Define to rpl_strnlen if the replacement function should be used. */
+/* #undef strnlen */
+
+/* Define to rpl_strtod if the replacement function should be used. */
+/* #undef strtod */
+
+/* Define to rpl_utime if the replacement function should be used. */
+/* #undef utime */
+
+/* Define as a macro for copying va_list variables. */
+/* #undef va_copy */
diff --git a/usr/src/lib/libparted/common/lib/configmake.h b/usr/src/lib/libparted/common/lib/configmake.h
new file mode 100644
index 0000000000..cd1b6883f7
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/configmake.h
@@ -0,0 +1,25 @@
+/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
+#define PREFIX "/usr/local"
+#define EXEC_PREFIX "/usr/local"
+#define BINDIR "/usr/local/bin"
+#define SBINDIR "/usr/local/sbin"
+#define LIBEXECDIR "/usr/local/libexec"
+#define DATAROOTDIR "/usr/local/share"
+#define DATADIR "/usr/local/share"
+#define SYSCONFDIR "/usr/local/etc"
+#define SHAREDSTATEDIR "/usr/local/com"
+#define LOCALSTATEDIR "/usr/local/var"
+#define INCLUDEDIR "/usr/local/include"
+#define OLDINCLUDEDIR "/usr/include"
+#define DOCDIR "/usr/local/share/doc/parted"
+#define INFODIR "/usr/local/share/info"
+#define HTMLDIR "/usr/local/share/doc/parted"
+#define DVIDIR "/usr/local/share/doc/parted"
+#define PDFDIR "/usr/local/share/doc/parted"
+#define PSDIR "/usr/local/share/doc/parted"
+#define LIBDIR "/usr/local/lib"
+#define LOCALEDIR "/usr/local/share/locale"
+#define MANDIR "/usr/local/share/man"
+#define PKGDATADIR "/usr/local/share/parted"
+#define PKGINCLUDEDIR "/usr/local/include/parted"
+#define PKGLIBDIR "/usr/local/lib/parted"
diff --git a/usr/src/lib/libparted/common/lib/dirname.c b/usr/src/lib/libparted/common/lib/dirname.c
new file mode 100644
index 0000000000..16552c64d2
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/dirname.c
@@ -0,0 +1,85 @@
+/* dirname.c -- return all but the last element in a file name
+
+ Copyright (C) 1990, 1998, 2000, 2001, 2003, 2004, 2005, 2006 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "dirname.h"
+
+#include <string.h>
+#include "xalloc.h"
+
+/* Return the length of the prefix of FILE that will be used by
+ dir_name. If FILE is in the working directory, this returns zero
+ even though `dir_name (FILE)' will return ".". Works properly even
+ if there are trailing slashes (by effectively ignoring them). */
+
+size_t
+dir_len (char const *file)
+{
+ size_t prefix_length = FILE_SYSTEM_PREFIX_LEN (file);
+ size_t length;
+
+ /* Advance prefix_length beyond important leading slashes. */
+ prefix_length += (prefix_length != 0
+ ? (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+ && ISSLASH (file[prefix_length]))
+ : (ISSLASH (file[0])
+ ? ((DOUBLE_SLASH_IS_DISTINCT_ROOT
+ && ISSLASH (file[1]) && ! ISSLASH (file[2])
+ ? 2 : 1))
+ : 0));
+
+ /* Strip the basename and any redundant slashes before it. */
+ for (length = last_component (file) - file;
+ prefix_length < length; length--)
+ if (! ISSLASH (file[length - 1]))
+ break;
+ return length;
+}
+
+
+/* In general, we can't use the builtin `dirname' function if available,
+ since it has different meanings in different environments.
+ In some environments the builtin `dirname' modifies its argument.
+
+ Return the leading directories part of FILE, allocated with xmalloc.
+ Works properly even if there are trailing slashes (by effectively
+ ignoring them). Unlike POSIX dirname(), FILE cannot be NULL.
+
+ If lstat (FILE) would succeed, then { chdir (dir_name (FILE));
+ lstat (base_name (FILE)); } will access the same file. Likewise,
+ if the sequence { chdir (dir_name (FILE));
+ rename (base_name (FILE), "foo"); } succeeds, you have renamed FILE
+ to "foo" in the same directory FILE was in. */
+
+char *
+dir_name (char const *file)
+{
+ size_t length = dir_len (file);
+ bool append_dot = (length == 0
+ || (FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+ && length == FILE_SYSTEM_PREFIX_LEN (file)
+ && file[2] != '\0' && ! ISSLASH (file[2])));
+ char *dir = xmalloc (length + append_dot + 1);
+ memcpy (dir, file, length);
+ if (append_dot)
+ dir[length++] = '.';
+ dir[length] = '\0';
+ return dir;
+}
diff --git a/usr/src/lib/libparted/common/lib/dirname.h b/usr/src/lib/libparted/common/lib/dirname.h
new file mode 100644
index 0000000000..91e7ed3366
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/dirname.h
@@ -0,0 +1,70 @@
+/* Take file names apart into directory and base names.
+
+ Copyright (C) 1998, 2001, 2003-2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef DIRNAME_H_
+# define DIRNAME_H_ 1
+
+# include <stdbool.h>
+# include <stddef.h>
+
+# ifndef DIRECTORY_SEPARATOR
+# define DIRECTORY_SEPARATOR '/'
+# endif
+
+# ifndef ISSLASH
+# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
+# endif
+
+# ifndef FILE_SYSTEM_PREFIX_LEN
+# if FILE_SYSTEM_ACCEPTS_DRIVE_LETTER_PREFIX
+ /* This internal macro assumes ASCII, but all hosts that support drive
+ letters use ASCII. */
+# define _IS_DRIVE_LETTER(c) (((unsigned int) (c) | ('a' - 'A')) - 'a' \
+ <= 'z' - 'a')
+# define FILE_SYSTEM_PREFIX_LEN(Filename) \
+ (_IS_DRIVE_LETTER ((Filename)[0]) && (Filename)[1] == ':' ? 2 : 0)
+# else
+# define FILE_SYSTEM_PREFIX_LEN(Filename) 0
+# endif
+# endif
+
+# ifndef FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+# define FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE 0
+# endif
+
+# ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
+# define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
+# endif
+
+# if FILE_SYSTEM_DRIVE_PREFIX_CAN_BE_RELATIVE
+# define IS_ABSOLUTE_FILE_NAME(F) ISSLASH ((F)[FILE_SYSTEM_PREFIX_LEN (F)])
+# else
+# define IS_ABSOLUTE_FILE_NAME(F) \
+ (ISSLASH ((F)[0]) || 0 < FILE_SYSTEM_PREFIX_LEN (F))
+# endif
+# define IS_RELATIVE_FILE_NAME(F) (! IS_ABSOLUTE_FILE_NAME (F))
+
+char *base_name (char const *file);
+char *dir_name (char const *file);
+size_t base_len (char const *file);
+size_t dir_len (char const *file);
+char *last_component (char const *file);
+
+bool strip_trailing_slashes (char *file);
+
+#endif /* not DIRNAME_H_ */
diff --git a/usr/src/lib/libparted/common/lib/error.c b/usr/src/lib/libparted/common/lib/error.c
new file mode 100644
index 0000000000..9cf67ff904
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/error.c
@@ -0,0 +1,339 @@
+/* Error handler for noninteractive utilities
+ Copyright (C) 1990-1998, 2000-2007 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#if !_LIBC
+# include <config.h>
+#endif
+
+#include "error.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if !_LIBC && ENABLE_NLS
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#endif
+
+#ifdef _LIBC
+# include <libintl.h>
+# include <stdbool.h>
+# include <stdint.h>
+# include <wchar.h>
+# define mbsrtowcs __mbsrtowcs
+#endif
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifndef _
+# define _(String) String
+#endif
+
+/* If NULL, error will flush stdout, then print on stderr the program
+ name, a colon and a space. Otherwise, error will call this
+ function without parameters instead. */
+void (*error_print_progname) (void);
+
+/* This variable is incremented each time `error' is called. */
+unsigned int error_message_count;
+
+#ifdef _LIBC
+/* In the GNU C library, there is a predefined variable for this. */
+
+# define program_name program_invocation_name
+# include <errno.h>
+# include <limits.h>
+# include <libio/libioP.h>
+
+/* In GNU libc we want do not want to use the common name `error' directly.
+ Instead make it a weak alias. */
+extern void __error (int status, int errnum, const char *message, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+extern void __error_at_line (int status, int errnum, const char *file_name,
+ unsigned int line_number, const char *message,
+ ...)
+ __attribute__ ((__format__ (__printf__, 5, 6)));;
+# define error __error
+# define error_at_line __error_at_line
+
+# include <libio/iolibio.h>
+# define fflush(s) INTUSE(_IO_fflush) (s)
+# undef putc
+# define putc(c, fp) INTUSE(_IO_putc) (c, fp)
+
+# include <bits/libc-lock.h>
+
+#else /* not _LIBC */
+
+# if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P
+# ifndef HAVE_DECL_STRERROR_R
+"this configure-time declaration test was not run"
+# endif
+char *strerror_r ();
+# endif
+
+/* The calling program should define program_name and set it to the
+ name of the executing program. */
+extern char *program_name;
+
+# if HAVE_STRERROR_R || defined strerror_r
+# define __strerror_r strerror_r
+# endif /* HAVE_STRERROR_R || defined strerror_r */
+#endif /* not _LIBC */
+
+static void
+print_errno_message (int errnum)
+{
+ char const *s;
+
+#if defined HAVE_STRERROR_R || _LIBC
+ char errbuf[1024];
+# if STRERROR_R_CHAR_P || _LIBC
+ s = __strerror_r (errnum, errbuf, sizeof errbuf);
+# else
+ if (__strerror_r (errnum, errbuf, sizeof errbuf) == 0)
+ s = errbuf;
+ else
+ s = 0;
+# endif
+#else
+ s = strerror (errnum);
+#endif
+
+#if !_LIBC
+ if (! s)
+ s = _("Unknown system error");
+#endif
+
+#if _LIBC
+ __fxprintf (NULL, ": %s", s);
+#else
+ fprintf (stderr, ": %s", s);
+#endif
+}
+
+static void
+error_tail (int status, int errnum, const char *message, va_list args)
+{
+#if _LIBC
+ if (_IO_fwide (stderr, 0) > 0)
+ {
+# define ALLOCA_LIMIT 2000
+ size_t len = strlen (message) + 1;
+ wchar_t *wmessage = NULL;
+ mbstate_t st;
+ size_t res;
+ const char *tmp;
+ bool use_malloc = false;
+
+ while (1)
+ {
+ if (__libc_use_alloca (len * sizeof (wchar_t)))
+ wmessage = (wchar_t *) alloca (len * sizeof (wchar_t));
+ else
+ {
+ if (!use_malloc)
+ wmessage = NULL;
+
+ wchar_t *p = (wchar_t *) realloc (wmessage,
+ len * sizeof (wchar_t));
+ if (p == NULL)
+ {
+ free (wmessage);
+ fputws_unlocked (L"out of memory\n", stderr);
+ return;
+ }
+ wmessage = p;
+ use_malloc = true;
+ }
+
+ memset (&st, '\0', sizeof (st));
+ tmp = message;
+
+ res = mbsrtowcs (wmessage, &tmp, len, &st);
+ if (res != len)
+ break;
+
+ if (__builtin_expect (len >= SIZE_MAX / 2, 0))
+ {
+ /* This really should not happen if everything is fine. */
+ res = (size_t) -1;
+ break;
+ }
+
+ len *= 2;
+ }
+
+ if (res == (size_t) -1)
+ {
+ /* The string cannot be converted. */
+ if (use_malloc)
+ {
+ free (wmessage);
+ use_malloc = false;
+ }
+ wmessage = (wchar_t *) L"???";
+ }
+
+ __vfwprintf (stderr, wmessage, args);
+
+ if (use_malloc)
+ free (wmessage);
+ }
+ else
+#endif
+ vfprintf (stderr, message, args);
+ va_end (args);
+
+ ++error_message_count;
+ if (errnum)
+ print_errno_message (errnum);
+#if _LIBC
+ __fxprintf (NULL, "\n");
+#else
+ putc ('\n', stderr);
+#endif
+ fflush (stderr);
+ if (status)
+ exit (status);
+}
+
+
+/* Print the program name and error message MESSAGE, which is a printf-style
+ format string with optional args.
+ If ERRNUM is nonzero, print its corresponding system error message.
+ Exit with status STATUS if it is nonzero. */
+void
+error (int status, int errnum, const char *message, ...)
+{
+ va_list args;
+
+#if defined _LIBC && defined __libc_ptf_call
+ /* We do not want this call to be cut short by a thread
+ cancellation. Therefore disable cancellation for now. */
+ int state = PTHREAD_CANCEL_ENABLE;
+ __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state),
+ 0);
+#endif
+
+ fflush (stdout);
+#ifdef _LIBC
+ _IO_flockfile (stderr);
+#endif
+ if (error_print_progname)
+ (*error_print_progname) ();
+ else
+ {
+#if _LIBC
+ __fxprintf (NULL, "%s: ", program_name);
+#else
+ fprintf (stderr, "%s: ", program_name);
+#endif
+ }
+
+ va_start (args, message);
+ error_tail (status, errnum, message, args);
+
+#ifdef _LIBC
+ _IO_funlockfile (stderr);
+# ifdef __libc_ptf_call
+ __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0);
+# endif
+#endif
+}
+
+/* Sometimes we want to have at most one error per line. This
+ variable controls whether this mode is selected or not. */
+int error_one_per_line;
+
+void
+error_at_line (int status, int errnum, const char *file_name,
+ unsigned int line_number, const char *message, ...)
+{
+ va_list args;
+
+ if (error_one_per_line)
+ {
+ static const char *old_file_name;
+ static unsigned int old_line_number;
+
+ if (old_line_number == line_number
+ && (file_name == old_file_name
+ || strcmp (old_file_name, file_name) == 0))
+ /* Simply return and print nothing. */
+ return;
+
+ old_file_name = file_name;
+ old_line_number = line_number;
+ }
+
+#if defined _LIBC && defined __libc_ptf_call
+ /* We do not want this call to be cut short by a thread
+ cancellation. Therefore disable cancellation for now. */
+ int state = PTHREAD_CANCEL_ENABLE;
+ __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state),
+ 0);
+#endif
+
+ fflush (stdout);
+#ifdef _LIBC
+ _IO_flockfile (stderr);
+#endif
+ if (error_print_progname)
+ (*error_print_progname) ();
+ else
+ {
+#if _LIBC
+ __fxprintf (NULL, "%s:", program_name);
+#else
+ fprintf (stderr, "%s:", program_name);
+#endif
+ }
+
+#if _LIBC
+ __fxprintf (NULL, file_name != NULL ? "%s:%d: " : " ",
+ file_name, line_number);
+#else
+ fprintf (stderr, file_name != NULL ? "%s:%d: " : " ",
+ file_name, line_number);
+#endif
+
+ va_start (args, message);
+ error_tail (status, errnum, message, args);
+
+#ifdef _LIBC
+ _IO_funlockfile (stderr);
+# ifdef __libc_ptf_call
+ __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0);
+# endif
+#endif
+}
+
+#ifdef _LIBC
+/* Make the weak alias. */
+# undef error
+# undef error_at_line
+weak_alias (__error, error)
+weak_alias (__error_at_line, error_at_line)
+#endif
diff --git a/usr/src/lib/libparted/common/lib/error.h b/usr/src/lib/libparted/common/lib/error.h
new file mode 100644
index 0000000000..5a5f247658
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/error.h
@@ -0,0 +1,66 @@
+/* Declaration for error-reporting function
+ Copyright (C) 1995, 1996, 1997, 2003, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _ERROR_H
+#define _ERROR_H 1
+
+#ifndef __attribute__
+/* This feature is available in gcc versions 2.5 and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
+# define __attribute__(Spec) /* empty */
+# endif
+/* The __-protected variants of `format' and `printf' attributes
+ are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __format__ format
+# define __printf__ printf
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Print a message with `fprintf (stderr, FORMAT, ...)';
+ if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM).
+ If STATUS is nonzero, terminate the program with `exit (STATUS)'. */
+
+extern void error (int __status, int __errnum, const char *__format, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+extern void error_at_line (int __status, int __errnum, const char *__fname,
+ unsigned int __lineno, const char *__format, ...)
+ __attribute__ ((__format__ (__printf__, 5, 6)));
+
+/* If NULL, error will flush stdout, then print on stderr the program
+ name, a colon and a space. Otherwise, error will call this
+ function without parameters instead. */
+extern void (*error_print_progname) (void);
+
+/* This variable is incremented each time `error' is called. */
+extern unsigned int error_message_count;
+
+/* Sometimes we want to have at most one error per line. This
+ variable controls whether this mode is selected or not. */
+extern int error_one_per_line;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* error.h */
diff --git a/usr/src/lib/libparted/common/lib/exitfail.c b/usr/src/lib/libparted/common/lib/exitfail.c
new file mode 100644
index 0000000000..373d325c5a
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/exitfail.c
@@ -0,0 +1,26 @@
+/* Failure exit status
+
+ Copyright (C) 2002, 2003, 2005, 2006, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.
+ If not, write to the Free Software Foundation,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "exitfail.h"
+
+#include <stdlib.h>
+
+int volatile exit_failure = EXIT_FAILURE;
diff --git a/usr/src/lib/libparted/common/lib/exitfail.h b/usr/src/lib/libparted/common/lib/exitfail.h
new file mode 100644
index 0000000000..e46cf9c166
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/exitfail.h
@@ -0,0 +1,20 @@
+/* Failure exit status
+
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING.
+ If not, write to the Free Software Foundation,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+extern int volatile exit_failure;
diff --git a/usr/src/lib/libparted/common/lib/full-write.c b/usr/src/lib/libparted/common/lib/full-write.c
new file mode 100644
index 0000000000..cc168720ea
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/full-write.c
@@ -0,0 +1,81 @@
+/* An interface to read and write that retries (if necessary) until complete.
+
+ Copyright (C) 1993, 1994, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+/* Specification. */
+#ifdef FULL_READ
+# include "full-read.h"
+#else
+# include "full-write.h"
+#endif
+
+#include <errno.h>
+
+#ifdef FULL_READ
+# include "safe-read.h"
+# define safe_rw safe_read
+# define full_rw full_read
+# undef const
+# define const /* empty */
+#else
+# include "safe-write.h"
+# define safe_rw safe_write
+# define full_rw full_write
+#endif
+
+#ifdef FULL_READ
+/* Set errno to zero upon EOF. */
+# define ZERO_BYTE_TRANSFER_ERRNO 0
+#else
+/* Some buggy drivers return 0 when one tries to write beyond
+ a device's end. (Example: Linux 1.2.13 on /dev/fd0.)
+ Set errno to ENOSPC so they get a sensible diagnostic. */
+# define ZERO_BYTE_TRANSFER_ERRNO ENOSPC
+#endif
+
+/* Write(read) COUNT bytes at BUF to(from) descriptor FD, retrying if
+ interrupted or if a partial write(read) occurs. Return the number
+ of bytes transferred.
+ When writing, set errno if fewer than COUNT bytes are written.
+ When reading, if fewer than COUNT bytes are read, you must examine
+ errno to distinguish failure from EOF (errno == 0). */
+size_t
+full_rw (int fd, const void *buf, size_t count)
+{
+ size_t total = 0;
+ const char *ptr = (const char *) buf;
+
+ while (count > 0)
+ {
+ size_t n_rw = safe_rw (fd, ptr, count);
+ if (n_rw == (size_t) -1)
+ break;
+ if (n_rw == 0)
+ {
+ errno = ZERO_BYTE_TRANSFER_ERRNO;
+ break;
+ }
+ total += n_rw;
+ ptr += n_rw;
+ count -= n_rw;
+ }
+
+ return total;
+}
diff --git a/usr/src/lib/libparted/common/lib/full-write.h b/usr/src/lib/libparted/common/lib/full-write.h
new file mode 100644
index 0000000000..d20d2fe4ab
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/full-write.h
@@ -0,0 +1,35 @@
+/* An interface to write() that writes all it is asked to write.
+
+ Copyright (C) 2002-2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <stddef.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Write COUNT bytes at BUF to descriptor FD, retrying if interrupted
+ or if partial writes occur. Return the number of bytes successfully
+ written, setting errno if that is less than COUNT. */
+extern size_t full_write (int fd, const void *buf, size_t count);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/usr/src/lib/libparted/common/lib/getopt.c b/usr/src/lib/libparted/common/lib/getopt.c
new file mode 100644
index 0000000000..3580ad825c
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/getopt.c
@@ -0,0 +1,1191 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+ Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004,2006
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _LIBC
+# include <config.h>
+#endif
+
+#include "getopt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __VMS
+# include <unixlib.h>
+#endif
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#endif
+
+#if defined _LIBC && defined USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* Unlike standard Unix `getopt', functions like `getopt_long'
+ let the user intersperse the options with the other arguments.
+
+ As `getopt_long' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Using `getopt' or setting the environment variable POSIXLY_CORRECT
+ disables permutation.
+ Then the application's behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt_int.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Keep a global copy of all internal members of getopt_data. */
+
+static struct _getopt_data getopt_data;
+
+
+#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV
+extern char *getenv ();
+#endif
+
+#ifdef _LIBC
+/* Stored original parameters.
+ XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# define SWAP_FLAGS(ch1, ch2) \
+ if (d->__nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+# else
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (char **argv, struct _getopt_data *d)
+{
+ int bottom = d->__first_nonopt;
+ int middle = d->__last_nonopt;
+ int top = d->optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ d->__nonoption_flags_max_len),
+ '\0', top + 1 - d->__nonoption_flags_max_len);
+ d->__nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ d->__first_nonopt += (d->optind - d->__last_nonopt);
+ d->__last_nonopt = d->optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (int argc, char **argv, const char *optstring,
+ int posixly_correct, struct _getopt_data *d)
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ d->__first_nonopt = d->__last_nonopt = d->optind;
+
+ d->__nextchar = NULL;
+
+ d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ d->__ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ d->__ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (d->__posixly_correct)
+ d->__ordering = REQUIRE_ORDER;
+ else
+ d->__ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (!d->__posixly_correct
+ && argc == __libc_argc && argv == __libc_argv)
+ {
+ if (d->__nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ d->__nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = d->__nonoption_flags_max_len = strlen (orig_str);
+ if (d->__nonoption_flags_max_len < argc)
+ d->__nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (d->__nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ d->__nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', d->__nonoption_flags_max_len - len);
+ }
+ }
+ d->__nonoption_flags_len = d->__nonoption_flags_max_len;
+ }
+ else
+ d->__nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options.
+
+ If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT
+ environment variable were set. */
+
+int
+_getopt_internal_r (int argc, char **argv, const char *optstring,
+ const struct option *longopts, int *longind,
+ int long_only, int posixly_correct, struct _getopt_data *d)
+{
+ int print_errors = d->opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ if (argc < 1)
+ return -1;
+
+ d->optarg = NULL;
+
+ if (d->optind == 0 || !d->__initialized)
+ {
+ if (d->optind == 0)
+ d->optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring,
+ posixly_correct, d);
+ d->__initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \
+ || (d->optind < d->__nonoption_flags_len \
+ && __getopt_nonoption_flags[d->optind] == '1'))
+#else
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
+#endif
+
+ if (d->__nextchar == NULL || *d->__nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (d->__last_nonopt > d->optind)
+ d->__last_nonopt = d->optind;
+ if (d->__first_nonopt > d->optind)
+ d->__first_nonopt = d->optind;
+
+ if (d->__ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (d->__first_nonopt != d->__last_nonopt
+ && d->__last_nonopt != d->optind)
+ exchange ((char **) argv, d);
+ else if (d->__last_nonopt != d->optind)
+ d->__first_nonopt = d->optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (d->optind < argc && NONOPTION_P)
+ d->optind++;
+ d->__last_nonopt = d->optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (d->optind != argc && !strcmp (argv[d->optind], "--"))
+ {
+ d->optind++;
+
+ if (d->__first_nonopt != d->__last_nonopt
+ && d->__last_nonopt != d->optind)
+ exchange ((char **) argv, d);
+ else if (d->__first_nonopt == d->__last_nonopt)
+ d->__first_nonopt = d->optind;
+ d->__last_nonopt = argc;
+
+ d->optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (d->optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (d->__first_nonopt != d->__last_nonopt)
+ d->optind = d->__first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (d->__ordering == REQUIRE_ORDER)
+ return -1;
+ d->optarg = argv[d->optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ d->__nextchar = (argv[d->optind] + 1
+ + (longopts != NULL && argv[d->optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[d->optind][1] == '-'
+ || (long_only && (argv[d->optind][2]
+ || !strchr (optstring, argv[d->optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[d->optind]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[d->optind]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ d->optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[d->optind - 1][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[d->optind - 1][0],
+ pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[d->optind - 1][0],
+ pfound->name);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+
+ d->__nextchar += strlen (d->__nextchar);
+
+ d->optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[d->optind][1] == '-'
+ || strchr (optstring, *d->__nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[d->optind][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
+ argv[0], d->__nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], d->__nextchar);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[d->optind][0], d->__nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[d->optind][0], d->__nextchar);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ d->__nextchar = (char *) "";
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *d->__nextchar++;
+ char *temp = strchr (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*d->__nextchar == '\0')
+ ++d->optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (d->__posixly_correct)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+ }
+ else
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ d->optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `d->optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ d->optarg = argv[d->optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '=';
+ nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[d->optind]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[d->optind]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+
+ d->__nextchar += strlen (d->__nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ d->__nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ __fxprintf (NULL, "%s", buf);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ d->optarg = argv[d->optind++];
+ d->__nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+_getopt_internal (int argc, char **argv, const char *optstring,
+ const struct option *longopts, int *longind,
+ int long_only, int posixly_correct)
+{
+ int result;
+
+ getopt_data.optind = optind;
+ getopt_data.opterr = opterr;
+
+ result = _getopt_internal_r (argc, argv, optstring, longopts, longind,
+ long_only, posixly_correct, &getopt_data);
+
+ optind = getopt_data.optind;
+ optarg = getopt_data.optarg;
+ optopt = getopt_data.optopt;
+
+ return result;
+}
+
+/* glibc gets a LSB-compliant getopt.
+ Standalone applications get a POSIX-compliant getopt. */
+#if _LIBC
+enum { POSIXLY_CORRECT = 0 };
+#else
+enum { POSIXLY_CORRECT = 1 };
+#endif
+
+int
+getopt (int argc, char *const *argv, const char *optstring)
+{
+ return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0,
+ POSIXLY_CORRECT);
+}
+
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (int argc, char **argv)
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/usr/src/lib/libparted/common/lib/getopt_int.h b/usr/src/lib/libparted/common/lib/getopt_int.h
new file mode 100644
index 0000000000..401579fd28
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/getopt_int.h
@@ -0,0 +1,131 @@
+/* Internal declarations for getopt.
+ Copyright (C) 1989-1994,1996-1999,2001,2003,2004
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _GETOPT_INT_H
+#define _GETOPT_INT_H 1
+
+extern int _getopt_internal (int ___argc, char **___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only, int __posixly_correct);
+
+
+/* Reentrant versions which can handle parsing multiple argument
+ vectors at the same time. */
+
+/* Data type for reentrant functions. */
+struct _getopt_data
+{
+ /* These have exactly the same meaning as the corresponding global
+ variables, except that they are used for the reentrant
+ versions of getopt. */
+ int optind;
+ int opterr;
+ int optopt;
+ char *optarg;
+
+ /* Internal members. */
+
+ /* True if the internal members have been initialized. */
+ int __initialized;
+
+ /* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+ char *__nextchar;
+
+ /* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters, or by calling getopt.
+
+ PERMUTE is the default. We permute the contents of ARGV as we
+ scan, so that eventually all the non-options are at the end.
+ This allows options to be given in any order, even with programs
+ that were not written to expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were
+ written to expect options and other ARGV-elements in any order
+ and that care about the ordering of the two. We describe each
+ non-option ARGV-element as if it were the argument of an option
+ with character code 1. Using `-' as the first character of the
+ list of option characters selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+ enum
+ {
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+ } __ordering;
+
+ /* If the POSIXLY_CORRECT environment variable is set
+ or getopt was called. */
+ int __posixly_correct;
+
+
+ /* Handle permutation of arguments. */
+
+ /* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first
+ of them; `last_nonopt' is the index after the last of them. */
+
+ int __first_nonopt;
+ int __last_nonopt;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ int __nonoption_flags_max_len;
+ int __nonoption_flags_len;
+# endif
+};
+
+/* The initializer is necessary to set OPTIND and OPTERR to their
+ default values and to clear the initialization flag. */
+#define _GETOPT_DATA_INITIALIZER { 1, 1 }
+
+extern int _getopt_internal_r (int ___argc, char **___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only, int __posixly_correct,
+ struct _getopt_data *__data);
+
+extern int _getopt_long_r (int ___argc, char **___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ struct _getopt_data *__data);
+
+extern int _getopt_long_only_r (int ___argc, char **___argv,
+ const char *__shortopts,
+ const struct option *__longopts,
+ int *__longind,
+ struct _getopt_data *__data);
+
+#endif /* getopt_int.h */
diff --git a/usr/src/lib/libparted/common/lib/gettext.h b/usr/src/lib/libparted/common/lib/gettext.h
new file mode 100644
index 0000000000..9d76ec9afc
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/gettext.h
@@ -0,0 +1,270 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by
+ the gettext() and ngettext() macros. This is an alternative to calling
+ textdomain(), and is useful for libraries. */
+# ifdef DEFAULT_TEXT_DOMAIN
+# undef gettext
+# define gettext(Msgid) \
+ dgettext (DEFAULT_TEXT_DOMAIN, Msgid)
+# undef ngettext
+# define ngettext(Msgid1, Msgid2, N) \
+ dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N)
+# endif
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Many header files from the libstdc++ coming with g++ 3.3 or newer include
+ <libintl.h>, which chokes if dcgettext is defined as a macro. So include
+ it now, to make later inclusions of <libintl.h> a NOP. */
+#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
+# include <cstdlib>
+# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H
+# include <libintl.h>
+# endif
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid))
+# define dcgettext(Domainname, Msgid, Category) \
+ ((void) (Category), dgettext (Domainname, Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 \
+ ? ((void) (Msgid2), (const char *) (Msgid1)) \
+ : ((void) (Msgid1), (const char *) (Msgid2)))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((void) (Domainname), ngettext (Msgid1, Msgid2, N))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) \
+ ((void) (Domainname), (const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) \
+ ((void) (Domainname), (const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+/* The separator between msgctxt and msgid in a .mo file. */
+#define GETTEXT_CONTEXT_GLUE "\004"
+
+/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a
+ MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be
+ short and rarely need to change.
+ The letter 'p' stands for 'particular' or 'special'. */
+#ifdef DEFAULT_TEXT_DOMAIN
+# define pgettext(Msgctxt, Msgid) \
+ pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#else
+# define pgettext(Msgctxt, Msgid) \
+ pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#endif
+#define dpgettext(Domainname, Msgctxt, Msgid) \
+ pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
+#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \
+ pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category)
+#ifdef DEFAULT_TEXT_DOMAIN
+# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
+ npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#else
+# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
+ npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#endif
+#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
+ npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
+#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \
+ npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+pgettext_aux (const char *domain,
+ const char *msg_ctxt_id, const char *msgid,
+ int category)
+{
+ const char *translation = dcgettext (domain, msg_ctxt_id, category);
+ if (translation == msg_ctxt_id)
+ return msgid;
+ else
+ return translation;
+}
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+npgettext_aux (const char *domain,
+ const char *msg_ctxt_id, const char *msgid,
+ const char *msgid_plural, unsigned long int n,
+ int category)
+{
+ const char *translation =
+ dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
+ if (translation == msg_ctxt_id || translation == msgid_plural)
+ return (n == 1 ? msgid : msgid_plural);
+ else
+ return translation;
+}
+
+/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID
+ can be arbitrary expressions. But for string literals these macros are
+ less efficient than those above. */
+
+#include <string.h>
+
+#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \
+ (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \
+ /* || __STDC_VERSION__ >= 199901L */ )
+
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+#include <stdlib.h>
+#endif
+
+#define pgettext_expr(Msgctxt, Msgid) \
+ dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES)
+#define dpgettext_expr(Domainname, Msgctxt, Msgid) \
+ dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+dcpgettext_expr (const char *domain,
+ const char *msgctxt, const char *msgid,
+ int category)
+{
+ size_t msgctxt_len = strlen (msgctxt) + 1;
+ size_t msgid_len = strlen (msgid) + 1;
+ const char *translation;
+#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+ char msg_ctxt_id[msgctxt_len + msgid_len];
+#else
+ char buf[1024];
+ char *msg_ctxt_id =
+ (msgctxt_len + msgid_len <= sizeof (buf)
+ ? buf
+ : (char *) malloc (msgctxt_len + msgid_len));
+ if (msg_ctxt_id != NULL)
+#endif
+ {
+ memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
+ msg_ctxt_id[msgctxt_len - 1] = '\004';
+ memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
+ translation = dcgettext (domain, msg_ctxt_id, category);
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+ if (msg_ctxt_id != buf)
+ free (msg_ctxt_id);
+#endif
+ if (translation != msg_ctxt_id)
+ return translation;
+ }
+ return msgid;
+}
+
+#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \
+ dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
+#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
+ dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static const char *
+dcnpgettext_expr (const char *domain,
+ const char *msgctxt, const char *msgid,
+ const char *msgid_plural, unsigned long int n,
+ int category)
+{
+ size_t msgctxt_len = strlen (msgctxt) + 1;
+ size_t msgid_len = strlen (msgid) + 1;
+ const char *translation;
+#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+ char msg_ctxt_id[msgctxt_len + msgid_len];
+#else
+ char buf[1024];
+ char *msg_ctxt_id =
+ (msgctxt_len + msgid_len <= sizeof (buf)
+ ? buf
+ : (char *) malloc (msgctxt_len + msgid_len));
+ if (msg_ctxt_id != NULL)
+#endif
+ {
+ memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
+ msg_ctxt_id[msgctxt_len - 1] = '\004';
+ memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
+ translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
+#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
+ if (msg_ctxt_id != buf)
+ free (msg_ctxt_id);
+#endif
+ if (!(translation == msg_ctxt_id || translation == msgid_plural))
+ return translation;
+ }
+ return (n == 1 ? msgid : msgid_plural);
+}
+
+#endif /* _LIBGETTEXT_H */
diff --git a/usr/src/lib/libparted/common/lib/localcharset.c b/usr/src/lib/libparted/common/lib/localcharset.c
new file mode 100644
index 0000000000..a0f7cca691
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/localcharset.c
@@ -0,0 +1,460 @@
+/* Determine a canonical name for the current locale's character encoding.
+
+ Copyright (C) 2000-2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Bruno Haible <bruno@clisp.org>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "localcharset.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined _WIN32 || defined __WIN32__
+# define WIN32_NATIVE
+#endif
+
+#if defined __EMX__
+/* Assume EMX program runs on OS/2, even if compiled under DOS. */
+# define OS2
+#endif
+
+#if !defined WIN32_NATIVE
+# if HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+# else
+# if 0 /* see comment below */
+# include <locale.h>
+# endif
+# endif
+# ifdef __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# endif
+#elif defined WIN32_NATIVE
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+#if defined OS2
+# define INCL_DOS
+# include <os2.h>
+#endif
+
+#if ENABLE_RELOCATABLE
+# include "relocatable.h"
+#else
+# define relocate(pathname) (pathname)
+#endif
+
+/* Get LIBDIR. */
+#ifndef LIBDIR
+# include "configmake.h"
+#endif
+
+#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
+ /* Win32, Cygwin, OS/2, DOS */
+# define ISSLASH(C) ((C) == '/' || (C) == '\\')
+#endif
+
+#ifndef DIRECTORY_SEPARATOR
+# define DIRECTORY_SEPARATOR '/'
+#endif
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
+#endif
+
+#if HAVE_DECL_GETC_UNLOCKED
+# undef getc
+# define getc getc_unlocked
+#endif
+
+/* The following static variable is declared 'volatile' to avoid a
+ possible multithread problem in the function get_charset_aliases. If we
+ are running in a threaded environment, and if two threads initialize
+ 'charset_aliases' simultaneously, both will produce the same value,
+ and everything will be ok if the two assignments to 'charset_aliases'
+ are atomic. But I don't know what will happen if the two assignments mix. */
+#if __STDC__ != 1
+# define volatile /* empty */
+#endif
+/* Pointer to the contents of the charset.alias file, if it has already been
+ read, else NULL. Its format is:
+ ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0' */
+static const char * volatile charset_aliases;
+
+/* Return a pointer to the contents of the charset.alias file. */
+static const char *
+get_charset_aliases (void)
+{
+ const char *cp;
+
+ cp = charset_aliases;
+ if (cp == NULL)
+ {
+#if !(defined VMS || defined WIN32_NATIVE || defined __CYGWIN__)
+ FILE *fp;
+ const char *dir;
+ const char *base = "charset.alias";
+ char *file_name;
+
+ /* Make it possible to override the charset.alias location. This is
+ necessary for running the testsuite before "make install". */
+ dir = getenv ("CHARSETALIASDIR");
+ if (dir == NULL || dir[0] == '\0')
+ dir = relocate (LIBDIR);
+
+ /* Concatenate dir and base into freshly allocated file_name. */
+ {
+ size_t dir_len = strlen (dir);
+ size_t base_len = strlen (base);
+ int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
+ file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
+ if (file_name != NULL)
+ {
+ memcpy (file_name, dir, dir_len);
+ if (add_slash)
+ file_name[dir_len] = DIRECTORY_SEPARATOR;
+ memcpy (file_name + dir_len + add_slash, base, base_len + 1);
+ }
+ }
+
+ if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
+ /* Out of memory or file not found, treat it as empty. */
+ cp = "";
+ else
+ {
+ /* Parse the file's contents. */
+ char *res_ptr = NULL;
+ size_t res_size = 0;
+
+ for (;;)
+ {
+ int c;
+ char buf1[50+1];
+ char buf2[50+1];
+ size_t l1, l2;
+ char *old_res_ptr;
+
+ c = getc (fp);
+ if (c == EOF)
+ break;
+ if (c == '\n' || c == ' ' || c == '\t')
+ continue;
+ if (c == '#')
+ {
+ /* Skip comment, to end of line. */
+ do
+ c = getc (fp);
+ while (!(c == EOF || c == '\n'));
+ if (c == EOF)
+ break;
+ continue;
+ }
+ ungetc (c, fp);
+ if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
+ break;
+ l1 = strlen (buf1);
+ l2 = strlen (buf2);
+ old_res_ptr = res_ptr;
+ if (res_size == 0)
+ {
+ res_size = l1 + 1 + l2 + 1;
+ res_ptr = (char *) malloc (res_size + 1);
+ }
+ else
+ {
+ res_size += l1 + 1 + l2 + 1;
+ res_ptr = (char *) realloc (res_ptr, res_size + 1);
+ }
+ if (res_ptr == NULL)
+ {
+ /* Out of memory. */
+ res_size = 0;
+ if (old_res_ptr != NULL)
+ free (old_res_ptr);
+ break;
+ }
+ strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
+ strcpy (res_ptr + res_size - (l2 + 1), buf2);
+ }
+ fclose (fp);
+ if (res_size == 0)
+ cp = "";
+ else
+ {
+ *(res_ptr + res_size) = '\0';
+ cp = res_ptr;
+ }
+ }
+
+ if (file_name != NULL)
+ free (file_name);
+
+#else
+
+# if defined VMS
+ /* To avoid the troubles of an extra file charset.alias_vms in the
+ sources of many GNU packages, simply inline the aliases here. */
+ /* The list of encodings is taken from the OpenVMS 7.3-1 documentation
+ "Compaq C Run-Time Library Reference Manual for OpenVMS systems"
+ section 10.7 "Handling Different Character Sets". */
+ cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
+ "ISO8859-2" "\0" "ISO-8859-2" "\0"
+ "ISO8859-5" "\0" "ISO-8859-5" "\0"
+ "ISO8859-7" "\0" "ISO-8859-7" "\0"
+ "ISO8859-8" "\0" "ISO-8859-8" "\0"
+ "ISO8859-9" "\0" "ISO-8859-9" "\0"
+ /* Japanese */
+ "eucJP" "\0" "EUC-JP" "\0"
+ "SJIS" "\0" "SHIFT_JIS" "\0"
+ "DECKANJI" "\0" "DEC-KANJI" "\0"
+ "SDECKANJI" "\0" "EUC-JP" "\0"
+ /* Chinese */
+ "eucTW" "\0" "EUC-TW" "\0"
+ "DECHANYU" "\0" "DEC-HANYU" "\0"
+ "DECHANZI" "\0" "GB2312" "\0"
+ /* Korean */
+ "DECKOREAN" "\0" "EUC-KR" "\0";
+# endif
+
+# if defined WIN32_NATIVE || defined __CYGWIN__
+ /* To avoid the troubles of installing a separate file in the same
+ directory as the DLL and of retrieving the DLL's directory at
+ runtime, simply inline the aliases here. */
+
+ cp = "CP936" "\0" "GBK" "\0"
+ "CP1361" "\0" "JOHAB" "\0"
+ "CP20127" "\0" "ASCII" "\0"
+ "CP20866" "\0" "KOI8-R" "\0"
+ "CP20936" "\0" "GB2312" "\0"
+ "CP21866" "\0" "KOI8-RU" "\0"
+ "CP28591" "\0" "ISO-8859-1" "\0"
+ "CP28592" "\0" "ISO-8859-2" "\0"
+ "CP28593" "\0" "ISO-8859-3" "\0"
+ "CP28594" "\0" "ISO-8859-4" "\0"
+ "CP28595" "\0" "ISO-8859-5" "\0"
+ "CP28596" "\0" "ISO-8859-6" "\0"
+ "CP28597" "\0" "ISO-8859-7" "\0"
+ "CP28598" "\0" "ISO-8859-8" "\0"
+ "CP28599" "\0" "ISO-8859-9" "\0"
+ "CP28605" "\0" "ISO-8859-15" "\0"
+ "CP38598" "\0" "ISO-8859-8" "\0"
+ "CP51932" "\0" "EUC-JP" "\0"
+ "CP51936" "\0" "GB2312" "\0"
+ "CP51949" "\0" "EUC-KR" "\0"
+ "CP51950" "\0" "EUC-TW" "\0"
+ "CP54936" "\0" "GB18030" "\0"
+ "CP65001" "\0" "UTF-8" "\0";
+# endif
+#endif
+
+ charset_aliases = cp;
+ }
+
+ return cp;
+}
+
+/* Determine the current locale's character encoding, and canonicalize it
+ into one of the canonical names listed in config.charset.
+ The result must not be freed; it is statically allocated.
+ If the canonical name cannot be determined, the result is a non-canonical
+ name. */
+
+#ifdef STATIC
+STATIC
+#endif
+const char *
+locale_charset (void)
+{
+ const char *codeset;
+ const char *aliases;
+
+#if !(defined WIN32_NATIVE || defined OS2)
+
+# if HAVE_LANGINFO_CODESET
+
+ /* Most systems support nl_langinfo (CODESET) nowadays. */
+ codeset = nl_langinfo (CODESET);
+
+# ifdef __CYGWIN__
+ /* Cygwin 2006 does not have locales. nl_langinfo (CODESET) always
+ returns "US-ASCII". As long as this is not fixed, return the suffix
+ of the locale name from the environment variables (if present) or
+ the codepage as a number. */
+ if (codeset != NULL && strcmp (codeset, "US-ASCII") == 0)
+ {
+ const char *locale;
+ static char buf[2 + 10 + 1];
+
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ if (locale != NULL && locale[0] != '\0')
+ {
+ /* If the locale name contains an encoding after the dot, return
+ it. */
+ const char *dot = strchr (locale, '.');
+
+ if (dot != NULL)
+ {
+ const char *modifier;
+
+ dot++;
+ /* Look for the possible @... trailer and remove it, if any. */
+ modifier = strchr (dot, '@');
+ if (modifier == NULL)
+ return dot;
+ if (modifier - dot < sizeof (buf))
+ {
+ memcpy (buf, dot, modifier - dot);
+ buf [modifier - dot] = '\0';
+ return buf;
+ }
+ }
+ }
+
+ /* Woe32 has a function returning the locale's codepage as a number. */
+ sprintf (buf, "CP%u", GetACP ());
+ codeset = buf;
+ }
+# endif
+
+# else
+
+ /* On old systems which lack it, use setlocale or getenv. */
+ const char *locale = NULL;
+
+ /* But most old systems don't have a complete set of locales. Some
+ (like SunOS 4 or DJGPP) have only the C locale. Therefore we don't
+ use setlocale here; it would return "C" when it doesn't support the
+ locale name the user has set. */
+# if 0
+ locale = setlocale (LC_CTYPE, NULL);
+# endif
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ }
+
+ /* On some old systems, one used to set locale = "iso8859_1". On others,
+ you set it to "language_COUNTRY.charset". In any case, we resolve it
+ through the charset.alias file. */
+ codeset = locale;
+
+# endif
+
+#elif defined WIN32_NATIVE
+
+ static char buf[2 + 10 + 1];
+
+ /* Woe32 has a function returning the locale's codepage as a number. */
+ sprintf (buf, "CP%u", GetACP ());
+ codeset = buf;
+
+#elif defined OS2
+
+ const char *locale;
+ static char buf[2 + 10 + 1];
+ ULONG cp[3];
+ ULONG cplen;
+
+ /* Allow user to override the codeset, as set in the operating system,
+ with standard language environment variables. */
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ if (locale != NULL && locale[0] != '\0')
+ {
+ /* If the locale name contains an encoding after the dot, return it. */
+ const char *dot = strchr (locale, '.');
+
+ if (dot != NULL)
+ {
+ const char *modifier;
+
+ dot++;
+ /* Look for the possible @... trailer and remove it, if any. */
+ modifier = strchr (dot, '@');
+ if (modifier == NULL)
+ return dot;
+ if (modifier - dot < sizeof (buf))
+ {
+ memcpy (buf, dot, modifier - dot);
+ buf [modifier - dot] = '\0';
+ return buf;
+ }
+ }
+
+ /* Resolve through the charset.alias file. */
+ codeset = locale;
+ }
+ else
+ {
+ /* OS/2 has a function returning the locale's codepage as a number. */
+ if (DosQueryCp (sizeof (cp), cp, &cplen))
+ codeset = "";
+ else
+ {
+ sprintf (buf, "CP%u", cp[0]);
+ codeset = buf;
+ }
+ }
+
+#endif
+
+ if (codeset == NULL)
+ /* The canonical name cannot be determined. */
+ codeset = "";
+
+ /* Resolve alias. */
+ for (aliases = get_charset_aliases ();
+ *aliases != '\0';
+ aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
+ if (strcmp (codeset, aliases) == 0
+ || (aliases[0] == '*' && aliases[1] == '\0'))
+ {
+ codeset = aliases + strlen (aliases) + 1;
+ break;
+ }
+
+ /* Don't return an empty string. GNU libc and GNU libiconv interpret
+ the empty string as denoting "the locale's character encoding",
+ thus GNU libiconv would call this function a second time. */
+ if (codeset[0] == '\0')
+ codeset = "ASCII";
+
+ return codeset;
+}
diff --git a/usr/src/lib/libparted/common/lib/localcharset.h b/usr/src/lib/libparted/common/lib/localcharset.h
new file mode 100644
index 0000000000..5030210d11
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/localcharset.h
@@ -0,0 +1,41 @@
+/* Determine a canonical name for the current locale's character encoding.
+ Copyright (C) 2000-2003 Free Software Foundation, Inc.
+ This file is part of the GNU CHARSET Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _LOCALCHARSET_H
+#define _LOCALCHARSET_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Determine the current locale's character encoding, and canonicalize it
+ into one of the canonical names listed in config.charset.
+ The result must not be freed; it is statically allocated.
+ If the canonical name cannot be determined, the result is a non-canonical
+ name. */
+extern const char * locale_charset (void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LOCALCHARSET_H */
diff --git a/usr/src/lib/libparted/common/lib/long-options.c b/usr/src/lib/libparted/common/lib/long-options.c
new file mode 100644
index 0000000000..fb61661aac
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/long-options.c
@@ -0,0 +1,89 @@
+/* Utility to accept --help and --version options as unobtrusively as possible.
+
+ Copyright (C) 1993, 1994, 1998, 1999, 2000, 2002, 2003, 2004, 2005,
+ 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering. */
+
+#include <config.h>
+
+/* Specification. */
+#include "long-options.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "version-etc.h"
+
+static struct option const long_options[] =
+{
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'v'},
+ {NULL, 0, NULL, 0}
+};
+
+/* Process long options --help and --version, but only if argc == 2.
+ Be careful not to gobble up `--'. */
+
+void
+parse_long_options (int argc,
+ char **argv,
+ const char *command_name,
+ const char *package,
+ const char *version,
+ void (*usage_func) (int),
+ /* const char *author1, ...*/ ...)
+{
+ int c;
+ int saved_opterr;
+
+ saved_opterr = opterr;
+
+ /* Don't print an error message for unrecognized options. */
+ opterr = 0;
+
+ if (argc == 2
+ && (c = getopt_long (argc, argv, "+", long_options, NULL)) != -1)
+ {
+ switch (c)
+ {
+ case 'h':
+ (*usage_func) (EXIT_SUCCESS);
+
+ case 'v':
+ {
+ va_list authors;
+ va_start (authors, usage_func);
+ version_etc_va (stdout, command_name, package, version, authors);
+ exit (0);
+ }
+
+ default:
+ /* Don't process any other long-named options. */
+ break;
+ }
+ }
+
+ /* Restore previous value. */
+ opterr = saved_opterr;
+
+ /* Reset this to zero so that getopt internals get initialized from
+ the probably-new parameters when/if getopt is called later. */
+ optind = 0;
+}
diff --git a/usr/src/lib/libparted/common/lib/long-options.h b/usr/src/lib/libparted/common/lib/long-options.h
new file mode 100644
index 0000000000..03106a8def
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/long-options.h
@@ -0,0 +1,26 @@
+/* long-options.h -- declaration for --help- and --version-handling function.
+ Copyright (C) 1993, 1994, 1998, 1999, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering. */
+
+void parse_long_options (int _argc,
+ char **_argv,
+ const char *_command_name,
+ const char *_package,
+ const char *_version,
+ void (*_usage) (int),
+ /* const char *author1, ...*/ ...);
diff --git a/usr/src/lib/libparted/common/lib/memcpy.c b/usr/src/lib/libparted/common/lib/memcpy.c
new file mode 100644
index 0000000000..d1e49c6fc7
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/memcpy.c
@@ -0,0 +1,36 @@
+/* Copyright (C) 1995, 1997, 2000, 2003, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering <meyering@na-net.ornl.gov>. */
+
+#include <config.h>
+
+#include <stddef.h>
+
+/* Copy LEN bytes starting at SRCADDR to DESTADDR. Result undefined
+ if the source overlaps with the destination.
+ Return DESTADDR. */
+
+void *
+memcpy (void *destaddr, void const *srcaddr, size_t len)
+{
+ char *dest = destaddr;
+ char const *src = srcaddr;
+
+ while (len-- > 0)
+ *dest++ = *src++;
+ return destaddr;
+}
diff --git a/usr/src/lib/libparted/common/lib/memmove.c b/usr/src/lib/libparted/common/lib/memmove.c
new file mode 100644
index 0000000000..c5ff8b520d
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/memmove.c
@@ -0,0 +1,26 @@
+/* memmove.c -- copy memory.
+ Copy LENGTH bytes from SOURCE to DEST. Does not null-terminate.
+ In the public domain.
+ By David MacKenzie <djm@gnu.ai.mit.edu>. */
+
+#include <config.h>
+
+#include <stddef.h>
+
+void *
+memmove (void *dest0, void const *source0, size_t length)
+{
+ char *dest = dest0;
+ char const *source = source0;
+ if (source < dest)
+ /* Moving from low mem to hi mem; start at end. */
+ for (source += length, dest += length; length; --length)
+ *--dest = *--source;
+ else if (source != dest)
+ {
+ /* Moving from hi mem to low mem; start at beginning. */
+ for (; length; --length)
+ *dest++ = *source++;
+ }
+ return dest0;
+}
diff --git a/usr/src/lib/libparted/common/lib/memset.c b/usr/src/lib/libparted/common/lib/memset.c
new file mode 100644
index 0000000000..890cbf12d0
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/memset.c
@@ -0,0 +1,28 @@
+/* memset.c -- set an area of memory to a given value
+ Copyright (C) 1991, 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <stddef.h>
+
+void *
+memset (void *str, int c, size_t len)
+{
+ register char *st = str;
+
+ while (len-- > 0)
+ *st++ = c;
+ return str;
+}
diff --git a/usr/src/lib/libparted/common/lib/quotearg.c b/usr/src/lib/libparted/common/lib/quotearg.c
new file mode 100644
index 0000000000..f7f326ac50
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/quotearg.c
@@ -0,0 +1,697 @@
+/* quotearg.c - quote arguments for output
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Paul Eggert <eggert@twinsun.com> */
+
+#include <config.h>
+
+#include "quotearg.h"
+
+#include "xalloc.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+#define N_(msgid) msgid
+
+#if !HAVE_MBRTOWC
+/* Disable multibyte processing entirely. Since MB_CUR_MAX is 1, the
+ other macros are defined only for documentation and to satisfy C
+ syntax. */
+# undef MB_CUR_MAX
+# define MB_CUR_MAX 1
+# undef mbstate_t
+# define mbstate_t int
+# define mbrtowc(pwc, s, n, ps) ((*(pwc) = *(s)) != 0)
+# define iswprint(wc) isprint ((unsigned char) (wc))
+# undef HAVE_MBSINIT
+#endif
+
+#if !defined mbsinit && !HAVE_MBSINIT
+# define mbsinit(ps) 1
+#endif
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#define INT_BITS (sizeof (int) * CHAR_BIT)
+
+struct quoting_options
+{
+ /* Basic quoting style. */
+ enum quoting_style style;
+
+ /* Quote the characters indicated by this bit vector even if the
+ quoting style would not normally require them to be quoted. */
+ unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1];
+};
+
+/* Names of quoting styles. */
+char const *const quoting_style_args[] =
+{
+ "literal",
+ "shell",
+ "shell-always",
+ "c",
+ "escape",
+ "locale",
+ "clocale",
+ 0
+};
+
+/* Correspondences to quoting style names. */
+enum quoting_style const quoting_style_vals[] =
+{
+ literal_quoting_style,
+ shell_quoting_style,
+ shell_always_quoting_style,
+ c_quoting_style,
+ escape_quoting_style,
+ locale_quoting_style,
+ clocale_quoting_style
+};
+
+/* The default quoting options. */
+static struct quoting_options default_quoting_options;
+
+/* Allocate a new set of quoting options, with contents initially identical
+ to O if O is not null, or to the default if O is null.
+ It is the caller's responsibility to free the result. */
+struct quoting_options *
+clone_quoting_options (struct quoting_options *o)
+{
+ int e = errno;
+ struct quoting_options *p = xmemdup (o ? o : &default_quoting_options,
+ sizeof *o);
+ errno = e;
+ return p;
+}
+
+/* Get the value of O's quoting style. If O is null, use the default. */
+enum quoting_style
+get_quoting_style (struct quoting_options *o)
+{
+ return (o ? o : &default_quoting_options)->style;
+}
+
+/* In O (or in the default if O is null),
+ set the value of the quoting style to S. */
+void
+set_quoting_style (struct quoting_options *o, enum quoting_style s)
+{
+ (o ? o : &default_quoting_options)->style = s;
+}
+
+/* In O (or in the default if O is null),
+ set the value of the quoting options for character C to I.
+ Return the old value. Currently, the only values defined for I are
+ 0 (the default) and 1 (which means to quote the character even if
+ it would not otherwise be quoted). */
+int
+set_char_quoting (struct quoting_options *o, char c, int i)
+{
+ unsigned char uc = c;
+ unsigned int *p =
+ (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS;
+ int shift = uc % INT_BITS;
+ int r = (*p >> shift) & 1;
+ *p ^= ((i & 1) ^ r) << shift;
+ return r;
+}
+
+/* MSGID approximates a quotation mark. Return its translation if it
+ has one; otherwise, return either it or "\"", depending on S. */
+static char const *
+gettext_quote (char const *msgid, enum quoting_style s)
+{
+ char const *translation = _(msgid);
+ if (translation == msgid && s == clocale_quoting_style)
+ translation = "\"";
+ return translation;
+}
+
+/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
+ argument ARG (of size ARGSIZE), using QUOTING_STYLE and the
+ non-quoting-style part of O to control quoting.
+ Terminate the output with a null character, and return the written
+ size of the output, not counting the terminating null.
+ If BUFFERSIZE is too small to store the output string, return the
+ value that would have been returned had BUFFERSIZE been large enough.
+ If ARGSIZE is SIZE_MAX, use the string length of the argument for ARGSIZE.
+
+ This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
+ ARGSIZE, O), except it uses QUOTING_STYLE instead of the quoting
+ style specified by O, and O may not be null. */
+
+static size_t
+quotearg_buffer_restyled (char *buffer, size_t buffersize,
+ char const *arg, size_t argsize,
+ enum quoting_style quoting_style,
+ struct quoting_options const *o)
+{
+ size_t i;
+ size_t len = 0;
+ char const *quote_string = 0;
+ size_t quote_string_len = 0;
+ bool backslash_escapes = false;
+ bool unibyte_locale = MB_CUR_MAX == 1;
+
+#define STORE(c) \
+ do \
+ { \
+ if (len < buffersize) \
+ buffer[len] = (c); \
+ len++; \
+ } \
+ while (0)
+
+ switch (quoting_style)
+ {
+ case c_quoting_style:
+ STORE ('"');
+ backslash_escapes = true;
+ quote_string = "\"";
+ quote_string_len = 1;
+ break;
+
+ case escape_quoting_style:
+ backslash_escapes = true;
+ break;
+
+ case locale_quoting_style:
+ case clocale_quoting_style:
+ {
+ /* TRANSLATORS:
+ Get translations for open and closing quotation marks.
+
+ The message catalog should translate "`" to a left
+ quotation mark suitable for the locale, and similarly for
+ "'". If the catalog has no translation,
+ locale_quoting_style quotes `like this', and
+ clocale_quoting_style quotes "like this".
+
+ For example, an American English Unicode locale should
+ translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and
+ should translate "'" to U+201D (RIGHT DOUBLE QUOTATION
+ MARK). A British English Unicode locale should instead
+ translate these to U+2018 (LEFT SINGLE QUOTATION MARK) and
+ U+2019 (RIGHT SINGLE QUOTATION MARK), respectively.
+
+ If you don't know what to put here, please see
+ <http://en.wikipedia.org/wiki/Quotation_mark#Glyphs>
+ and use glyphs suitable for your language. */
+
+ char const *left = gettext_quote (N_("`"), quoting_style);
+ char const *right = gettext_quote (N_("'"), quoting_style);
+ for (quote_string = left; *quote_string; quote_string++)
+ STORE (*quote_string);
+ backslash_escapes = true;
+ quote_string = right;
+ quote_string_len = strlen (quote_string);
+ }
+ break;
+
+ case shell_always_quoting_style:
+ STORE ('\'');
+ quote_string = "'";
+ quote_string_len = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ for (i = 0; ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize); i++)
+ {
+ unsigned char c;
+ unsigned char esc;
+
+ if (backslash_escapes
+ && quote_string_len
+ && i + quote_string_len <= argsize
+ && memcmp (arg + i, quote_string, quote_string_len) == 0)
+ STORE ('\\');
+
+ c = arg[i];
+ switch (c)
+ {
+ case '\0':
+ if (backslash_escapes)
+ {
+ STORE ('\\');
+ STORE ('0');
+ STORE ('0');
+ c = '0';
+ }
+ break;
+
+ case '?':
+ switch (quoting_style)
+ {
+ case shell_quoting_style:
+ goto use_shell_always_quoting_style;
+
+ case c_quoting_style:
+ if (i + 2 < argsize && arg[i + 1] == '?')
+ switch (arg[i + 2])
+ {
+ case '!': case '\'':
+ case '(': case ')': case '-': case '/':
+ case '<': case '=': case '>':
+ /* Escape the second '?' in what would otherwise be
+ a trigraph. */
+ c = arg[i + 2];
+ i += 2;
+ STORE ('?');
+ STORE ('\\');
+ STORE ('?');
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case '\a': esc = 'a'; goto c_escape;
+ case '\b': esc = 'b'; goto c_escape;
+ case '\f': esc = 'f'; goto c_escape;
+ case '\n': esc = 'n'; goto c_and_shell_escape;
+ case '\r': esc = 'r'; goto c_and_shell_escape;
+ case '\t': esc = 't'; goto c_and_shell_escape;
+ case '\v': esc = 'v'; goto c_escape;
+ case '\\': esc = c; goto c_and_shell_escape;
+
+ c_and_shell_escape:
+ if (quoting_style == shell_quoting_style)
+ goto use_shell_always_quoting_style;
+ c_escape:
+ if (backslash_escapes)
+ {
+ c = esc;
+ goto store_escape;
+ }
+ break;
+
+ case '{': case '}': /* sometimes special if isolated */
+ if (! (argsize == SIZE_MAX ? arg[1] == '\0' : argsize == 1))
+ break;
+ /* Fall through. */
+ case '#': case '~':
+ if (i != 0)
+ break;
+ /* Fall through. */
+ case ' ':
+ case '!': /* special in bash */
+ case '"': case '$': case '&':
+ case '(': case ')': case '*': case ';':
+ case '<':
+ case '=': /* sometimes special in 0th or (with "set -k") later args */
+ case '>': case '[':
+ case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */
+ case '`': case '|':
+ /* A shell special character. In theory, '$' and '`' could
+ be the first bytes of multibyte characters, which means
+ we should check them with mbrtowc, but in practice this
+ doesn't happen so it's not worth worrying about. */
+ if (quoting_style == shell_quoting_style)
+ goto use_shell_always_quoting_style;
+ break;
+
+ case '\'':
+ switch (quoting_style)
+ {
+ case shell_quoting_style:
+ goto use_shell_always_quoting_style;
+
+ case shell_always_quoting_style:
+ STORE ('\'');
+ STORE ('\\');
+ STORE ('\'');
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case '%': case '+': case ',': case '-': case '.': case '/':
+ case '0': case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9': case ':':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
+ case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z': case ']': case '_': case 'a': case 'b':
+ case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
+ case 'o': case 'p': case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
+ /* These characters don't cause problems, no matter what the
+ quoting style is. They cannot start multibyte sequences. */
+ break;
+
+ default:
+ /* If we have a multibyte sequence, copy it until we reach
+ its end, find an error, or come back to the initial shift
+ state. For C-like styles, if the sequence has
+ unprintable characters, escape the whole sequence, since
+ we can't easily escape single characters within it. */
+ {
+ /* Length of multibyte sequence found so far. */
+ size_t m;
+
+ bool printable;
+
+ if (unibyte_locale)
+ {
+ m = 1;
+ printable = isprint (c) != 0;
+ }
+ else
+ {
+ mbstate_t mbstate;
+ memset (&mbstate, 0, sizeof mbstate);
+
+ m = 0;
+ printable = true;
+ if (argsize == SIZE_MAX)
+ argsize = strlen (arg);
+
+ do
+ {
+ wchar_t w;
+ size_t bytes = mbrtowc (&w, &arg[i + m],
+ argsize - (i + m), &mbstate);
+ if (bytes == 0)
+ break;
+ else if (bytes == (size_t) -1)
+ {
+ printable = false;
+ break;
+ }
+ else if (bytes == (size_t) -2)
+ {
+ printable = false;
+ while (i + m < argsize && arg[i + m])
+ m++;
+ break;
+ }
+ else
+ {
+ /* Work around a bug with older shells that "see" a '\'
+ that is really the 2nd byte of a multibyte character.
+ In practice the problem is limited to ASCII
+ chars >= '@' that are shell special chars. */
+ if ('[' == 0x5b && quoting_style == shell_quoting_style)
+ {
+ size_t j;
+ for (j = 1; j < bytes; j++)
+ switch (arg[i + m + j])
+ {
+ case '[': case '\\': case '^':
+ case '`': case '|':
+ goto use_shell_always_quoting_style;
+
+ default:
+ break;
+ }
+ }
+
+ if (! iswprint (w))
+ printable = false;
+ m += bytes;
+ }
+ }
+ while (! mbsinit (&mbstate));
+ }
+
+ if (1 < m || (backslash_escapes && ! printable))
+ {
+ /* Output a multibyte sequence, or an escaped
+ unprintable unibyte character. */
+ size_t ilim = i + m;
+
+ for (;;)
+ {
+ if (backslash_escapes && ! printable)
+ {
+ STORE ('\\');
+ STORE ('0' + (c >> 6));
+ STORE ('0' + ((c >> 3) & 7));
+ c = '0' + (c & 7);
+ }
+ if (ilim <= i + 1)
+ break;
+ STORE (c);
+ c = arg[++i];
+ }
+
+ goto store_c;
+ }
+ }
+ }
+
+ if (! (backslash_escapes
+ && o->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))))
+ goto store_c;
+
+ store_escape:
+ STORE ('\\');
+
+ store_c:
+ STORE (c);
+ }
+
+ if (i == 0 && quoting_style == shell_quoting_style)
+ goto use_shell_always_quoting_style;
+
+ if (quote_string)
+ for (; *quote_string; quote_string++)
+ STORE (*quote_string);
+
+ if (len < buffersize)
+ buffer[len] = '\0';
+ return len;
+
+ use_shell_always_quoting_style:
+ return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
+ shell_always_quoting_style, o);
+}
+
+/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
+ argument ARG (of size ARGSIZE), using O to control quoting.
+ If O is null, use the default.
+ Terminate the output with a null character, and return the written
+ size of the output, not counting the terminating null.
+ If BUFFERSIZE is too small to store the output string, return the
+ value that would have been returned had BUFFERSIZE been large enough.
+ If ARGSIZE is SIZE_MAX, use the string length of the argument for
+ ARGSIZE. */
+size_t
+quotearg_buffer (char *buffer, size_t buffersize,
+ char const *arg, size_t argsize,
+ struct quoting_options const *o)
+{
+ struct quoting_options const *p = o ? o : &default_quoting_options;
+ int e = errno;
+ size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
+ p->style, p);
+ errno = e;
+ return r;
+}
+
+/* Like quotearg_buffer (..., ARG, ARGSIZE, O), except return newly
+ allocated storage containing the quoted string. */
+char *
+quotearg_alloc (char const *arg, size_t argsize,
+ struct quoting_options const *o)
+{
+ int e = errno;
+ size_t bufsize = quotearg_buffer (0, 0, arg, argsize, o) + 1;
+ char *buf = xcharalloc (bufsize);
+ quotearg_buffer (buf, bufsize, arg, argsize, o);
+ errno = e;
+ return buf;
+}
+
+/* A storage slot with size and pointer to a value. */
+struct slotvec
+{
+ size_t size;
+ char *val;
+};
+
+/* Preallocate a slot 0 buffer, so that the caller can always quote
+ one small component of a "memory exhausted" message in slot 0. */
+static char slot0[256];
+static unsigned int nslots = 1;
+static struct slotvec slotvec0 = {sizeof slot0, slot0};
+static struct slotvec *slotvec = &slotvec0;
+
+void
+quotearg_free (void)
+{
+ struct slotvec *sv = slotvec;
+ unsigned int i;
+ for (i = 1; i < nslots; i++)
+ free (sv[i].val);
+ if (sv[0].val != slot0)
+ {
+ free (sv[0].val);
+ slotvec0.size = sizeof slot0;
+ slotvec0.val = slot0;
+ }
+ if (sv != &slotvec0)
+ {
+ free (sv);
+ slotvec = &slotvec0;
+ }
+ nslots = 1;
+}
+
+/* Use storage slot N to return a quoted version of argument ARG.
+ ARG is of size ARGSIZE, but if that is SIZE_MAX, ARG is a
+ null-terminated string.
+ OPTIONS specifies the quoting options.
+ The returned value points to static storage that can be
+ reused by the next call to this function with the same value of N.
+ N must be nonnegative. N is deliberately declared with type "int"
+ to allow for future extensions (using negative values). */
+static char *
+quotearg_n_options (int n, char const *arg, size_t argsize,
+ struct quoting_options const *options)
+{
+ int e = errno;
+
+ unsigned int n0 = n;
+ struct slotvec *sv = slotvec;
+
+ if (n < 0)
+ abort ();
+
+ if (nslots <= n0)
+ {
+ /* FIXME: technically, the type of n1 should be `unsigned int',
+ but that evokes an unsuppressible warning from gcc-4.0.1 and
+ older. If gcc ever provides an option to suppress that warning,
+ revert to the original type, so that the test in xalloc_oversized
+ is once again performed only at compile time. */
+ size_t n1 = n0 + 1;
+ bool preallocated = (sv == &slotvec0);
+
+ if (xalloc_oversized (n1, sizeof *sv))
+ xalloc_die ();
+
+ slotvec = sv = xrealloc (preallocated ? NULL : sv, n1 * sizeof *sv);
+ if (preallocated)
+ *sv = slotvec0;
+ memset (sv + nslots, 0, (n1 - nslots) * sizeof *sv);
+ nslots = n1;
+ }
+
+ {
+ size_t size = sv[n].size;
+ char *val = sv[n].val;
+ size_t qsize = quotearg_buffer (val, size, arg, argsize, options);
+
+ if (size <= qsize)
+ {
+ sv[n].size = size = qsize + 1;
+ if (val != slot0)
+ free (val);
+ sv[n].val = val = xcharalloc (size);
+ quotearg_buffer (val, size, arg, argsize, options);
+ }
+
+ errno = e;
+ return val;
+ }
+}
+
+char *
+quotearg_n (int n, char const *arg)
+{
+ return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options);
+}
+
+char *
+quotearg (char const *arg)
+{
+ return quotearg_n (0, arg);
+}
+
+/* Return quoting options for STYLE, with no extra quoting. */
+static struct quoting_options
+quoting_options_from_style (enum quoting_style style)
+{
+ struct quoting_options o;
+ o.style = style;
+ memset (o.quote_these_too, 0, sizeof o.quote_these_too);
+ return o;
+}
+
+char *
+quotearg_n_style (int n, enum quoting_style s, char const *arg)
+{
+ struct quoting_options const o = quoting_options_from_style (s);
+ return quotearg_n_options (n, arg, SIZE_MAX, &o);
+}
+
+char *
+quotearg_n_style_mem (int n, enum quoting_style s,
+ char const *arg, size_t argsize)
+{
+ struct quoting_options const o = quoting_options_from_style (s);
+ return quotearg_n_options (n, arg, argsize, &o);
+}
+
+char *
+quotearg_style (enum quoting_style s, char const *arg)
+{
+ return quotearg_n_style (0, s, arg);
+}
+
+char *
+quotearg_char (char const *arg, char ch)
+{
+ struct quoting_options options;
+ options = default_quoting_options;
+ set_char_quoting (&options, ch, 1);
+ return quotearg_n_options (0, arg, SIZE_MAX, &options);
+}
+
+char *
+quotearg_colon (char const *arg)
+{
+ return quotearg_char (arg, ':');
+}
diff --git a/usr/src/lib/libparted/common/lib/quotearg.h b/usr/src/lib/libparted/common/lib/quotearg.h
new file mode 100644
index 0000000000..4887df3b6d
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/quotearg.h
@@ -0,0 +1,140 @@
+/* quotearg.h - quote arguments for output
+
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Paul Eggert <eggert@twinsun.com> */
+
+#ifndef QUOTEARG_H_
+# define QUOTEARG_H_ 1
+
+# include <stddef.h>
+
+/* Basic quoting styles. */
+enum quoting_style
+ {
+ /* Output names as-is (ls --quoting-style=literal). */
+ literal_quoting_style,
+
+ /* Quote names for the shell if they contain shell metacharacters
+ or would cause ambiguous output (ls --quoting-style=shell). */
+ shell_quoting_style,
+
+ /* Quote names for the shell, even if they would normally not
+ require quoting (ls --quoting-style=shell-always). */
+ shell_always_quoting_style,
+
+ /* Quote names as for a C language string (ls --quoting-style=c). */
+ c_quoting_style,
+
+ /* Like c_quoting_style except omit the surrounding double-quote
+ characters (ls --quoting-style=escape). */
+ escape_quoting_style,
+
+ /* Like clocale_quoting_style, but quote `like this' instead of
+ "like this" in the default C locale (ls --quoting-style=locale). */
+ locale_quoting_style,
+
+ /* Like c_quoting_style except use quotation marks appropriate for
+ the locale (ls --quoting-style=clocale). */
+ clocale_quoting_style
+ };
+
+/* For now, --quoting-style=literal is the default, but this may change. */
+# ifndef DEFAULT_QUOTING_STYLE
+# define DEFAULT_QUOTING_STYLE literal_quoting_style
+# endif
+
+/* Names of quoting styles and their corresponding values. */
+extern char const *const quoting_style_args[];
+extern enum quoting_style const quoting_style_vals[];
+
+struct quoting_options;
+
+/* The functions listed below set and use a hidden variable
+ that contains the default quoting style options. */
+
+/* Allocate a new set of quoting options, with contents initially identical
+ to O if O is not null, or to the default if O is null.
+ It is the caller's responsibility to free the result. */
+struct quoting_options *clone_quoting_options (struct quoting_options *o);
+
+/* Get the value of O's quoting style. If O is null, use the default. */
+enum quoting_style get_quoting_style (struct quoting_options *o);
+
+/* In O (or in the default if O is null),
+ set the value of the quoting style to S. */
+void set_quoting_style (struct quoting_options *o, enum quoting_style s);
+
+/* In O (or in the default if O is null),
+ set the value of the quoting options for character C to I.
+ Return the old value. Currently, the only values defined for I are
+ 0 (the default) and 1 (which means to quote the character even if
+ it would not otherwise be quoted). */
+int set_char_quoting (struct quoting_options *o, char c, int i);
+
+/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
+ argument ARG (of size ARGSIZE), using O to control quoting.
+ If O is null, use the default.
+ Terminate the output with a null character, and return the written
+ size of the output, not counting the terminating null.
+ If BUFFERSIZE is too small to store the output string, return the
+ value that would have been returned had BUFFERSIZE been large enough.
+ If ARGSIZE is -1, use the string length of the argument for ARGSIZE. */
+size_t quotearg_buffer (char *buffer, size_t buffersize,
+ char const *arg, size_t argsize,
+ struct quoting_options const *o);
+
+/* Like quotearg_buffer, except return the result in a newly allocated
+ buffer. It is the caller's responsibility to free the result. */
+char *quotearg_alloc (char const *arg, size_t argsize,
+ struct quoting_options const *o);
+
+/* Use storage slot N to return a quoted version of the string ARG.
+ Use the default quoting options.
+ The returned value points to static storage that can be
+ reused by the next call to this function with the same value of N.
+ N must be nonnegative. */
+char *quotearg_n (int n, char const *arg);
+
+/* Equivalent to quotearg_n (0, ARG). */
+char *quotearg (char const *arg);
+
+/* Use style S and storage slot N to return a quoted version of the string ARG.
+ This is like quotearg_n (N, ARG), except that it uses S with no other
+ options to specify the quoting method. */
+char *quotearg_n_style (int n, enum quoting_style s, char const *arg);
+
+/* Use style S and storage slot N to return a quoted version of the
+ argument ARG of size ARGSIZE. This is like quotearg_n_style
+ (N, S, ARG), except it can quote null bytes. */
+char *quotearg_n_style_mem (int n, enum quoting_style s,
+ char const *arg, size_t argsize);
+
+/* Equivalent to quotearg_n_style (0, S, ARG). */
+char *quotearg_style (enum quoting_style s, char const *arg);
+
+/* Like quotearg (ARG), except also quote any instances of CH. */
+char *quotearg_char (char const *arg, char ch);
+
+/* Equivalent to quotearg_char (ARG, ':'). */
+char *quotearg_colon (char const *arg);
+
+/* Free any dynamically allocated memory. */
+void quotearg_free (void);
+
+#endif /* !QUOTEARG_H_ */
diff --git a/usr/src/lib/libparted/common/lib/regcomp.c b/usr/src/lib/libparted/common/lib/regcomp.c
new file mode 100644
index 0000000000..fe4d243d54
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regcomp.c
@@ -0,0 +1,3832 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002,2003,2004,2005,2006,2007 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+ size_t length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+ const re_dfastate_t *init_state,
+ char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+ reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+ reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+ bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint);
+static Idx search_duplicated_node (const re_dfa_t *dfa, Idx org_node,
+ unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+ Idx node, bool root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static Idx fetch_number (re_string_t *input, re_token_t *token,
+ reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax) internal_function;
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ Idx nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ Idx nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ Idx nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ Idx nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+ re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax,
+ reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token, int token_len,
+ re_dfa_t *dfa,
+ reg_syntax_t syntax,
+ bool accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+ re_charset_t *mbcset,
+ Idx *equiv_class_alloc,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+ bitset_t sbcset,
+ re_charset_t *mbcset,
+ Idx *char_class_alloc,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+ bitset_t sbcset,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+ RE_TRANSLATE_TYPE trans,
+ const unsigned char *class_name,
+ const unsigned char *extra,
+ bool non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ const re_token_t *token);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+static const char __re_error_msgid[] =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+static const size_t __re_error_msgid_idx[] =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry. */
+
+#ifdef _LIBC
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+#else /* size_t might promote */
+const char *
+re_compile_pattern (const char *pattern, size_t length,
+ struct re_pattern_buffer *bufp)
+#endif
+{
+ reg_errcode_t ret;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub, unless RE_NO_SUB is set. */
+ bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+ if (!ret)
+ return NULL;
+ return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ char *fastmap = bufp->fastmap;
+
+ memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+ re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+ if (dfa->init_state != dfa->init_state_word)
+ re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+ if (dfa->init_state != dfa->init_state_nl)
+ re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+ if (dfa->init_state != dfa->init_state_begbuf)
+ re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+ bufp->fastmap_accurate = 1;
+ return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, bool icase, int ch)
+{
+ fastmap[ch] = 1;
+ if (icase)
+ fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+ Compile fastmap for the initial_state INIT_STATE. */
+
+static void
+re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+ char *fastmap)
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ Idx node_cnt;
+ bool icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+ for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+ {
+ Idx node = init_state->nodes.elems[node_cnt];
+ re_token_type_t type = dfa->nodes[node].type;
+
+ if (type == CHARACTER)
+ {
+ re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ unsigned char buf[MB_LEN_MAX];
+ unsigned char *p;
+ wchar_t wc;
+ mbstate_t state;
+
+ p = buf;
+ *p++ = dfa->nodes[node].opr.c;
+ while (++node < dfa->nodes_len
+ && dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].mb_partial)
+ *p++ = dfa->nodes[node].opr.c;
+ memset (&state, '\0', sizeof (state));
+ if (mbrtowc (&wc, (const char *) buf, p - buf,
+ &state) == p - buf
+ && (__wcrtomb ((char *) buf, towlower (wc), &state)
+ != (size_t) -1))
+ re_set_fastmap (fastmap, false, buf[0]);
+ }
+#endif
+ }
+ else if (type == SIMPLE_BRACKET)
+ {
+ int i, ch;
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ {
+ int j;
+ bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ if (w & ((bitset_word_t) 1 << j))
+ re_set_fastmap (fastmap, icase, ch);
+ }
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET)
+ {
+ Idx i;
+ re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+ if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+ || cset->nranges || cset->nchar_classes)
+ {
+# ifdef _LIBC
+ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+ {
+ /* In this case we want to catch the bytes which are
+ the first byte of any collation elements.
+ e.g. In da_DK, we want to catch 'a' since "aa"
+ is a valid collation element, and don't catch
+ 'b' since 'b' is the only collation element
+ which starts from 'b'. */
+ const int32_t *table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ for (i = 0; i < SBC_MAX; ++i)
+ if (table[i] < 0)
+ re_set_fastmap (fastmap, icase, i);
+ }
+# else
+ if (dfa->mb_cur_max > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ re_set_fastmap (fastmap, icase, i);
+# endif /* not _LIBC */
+ }
+ for (i = 0; i < cset->nmbchars; ++i)
+ {
+ char buf[256];
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+ if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+ re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+ != (size_t) -1)
+ re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+ || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+ || type == END_OF_RE)
+ {
+ memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+ if (type == END_OF_RE)
+ bufp->can_be_null = 1;
+ return;
+ }
+ }
+}
+
+/* Entry point for POSIX code. */
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *_Restrict_ preg;
+ const char *_Restrict_ pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = re_malloc (char, SBC_MAX);
+ if (BE (preg->fastmap == NULL, 0))
+ return REG_ESPACE;
+
+ syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+ preg->no_sub = !!(cflags & REG_NOSUB);
+ preg->translate = NULL;
+
+ ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ /* We have already checked preg->fastmap != NULL. */
+ if (BE (ret == REG_NOERROR, 1))
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. This function never fails in this implementation. */
+ (void) re_compile_fastmap (preg);
+ else
+ {
+ /* Some error occurred while compiling the expression. */
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+#ifdef _LIBC
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+ int errcode;
+ const regex_t *_Restrict_ preg;
+ char *_Restrict_ errbuf;
+ size_t errbuf_size;
+#else /* size_t might promote */
+size_t
+regerror (int errcode, const regex_t *_Restrict_ preg,
+ char *_Restrict_ errbuf, size_t errbuf_size)
+#endif
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (BE (errcode < 0
+ || errcode >= (int) (sizeof (__re_error_msgid_idx)
+ / sizeof (__re_error_msgid_idx[0])), 0))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (BE (errbuf_size != 0, 1))
+ {
+ size_t cpy_size = msg_size;
+ if (BE (msg_size > errbuf_size, 0))
+ {
+ cpy_size = errbuf_size - 1;
+ errbuf[cpy_size] = '\0';
+ }
+ memcpy (errbuf, msg, cpy_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+ UTF-8 is used. Otherwise we would allocate memory just to initialize
+ it the same all the time. UTF-8 is the preferred encoding so this is
+ a worthwhile optimization. */
+static const bitset_t utf8_sb_map =
+{
+ /* Set the first 128 bits. */
+# if 4 * BITSET_WORD_BITS < ASCII_CHARS
+# error "bitset_word_t is narrower than 32 bits"
+# elif 3 * BITSET_WORD_BITS < ASCII_CHARS
+ BITSET_WORD_MAX, BITSET_WORD_MAX, BITSET_WORD_MAX,
+# elif 2 * BITSET_WORD_BITS < ASCII_CHARS
+ BITSET_WORD_MAX, BITSET_WORD_MAX,
+# elif 1 * BITSET_WORD_BITS < ASCII_CHARS
+ BITSET_WORD_MAX,
+# endif
+ (BITSET_WORD_MAX
+ >> (SBC_MAX % BITSET_WORD_BITS == 0
+ ? 0
+ : BITSET_WORD_BITS - SBC_MAX % BITSET_WORD_BITS))
+};
+#endif
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+ Idx i, j;
+
+ if (dfa->nodes)
+ for (i = 0; i < dfa->nodes_len; ++i)
+ free_token (dfa->nodes + i);
+ re_free (dfa->nexts);
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ if (dfa->eclosures != NULL)
+ re_node_set_free (dfa->eclosures + i);
+ if (dfa->inveclosures != NULL)
+ re_node_set_free (dfa->inveclosures + i);
+ if (dfa->edests != NULL)
+ re_node_set_free (dfa->edests + i);
+ }
+ re_free (dfa->edests);
+ re_free (dfa->eclosures);
+ re_free (dfa->inveclosures);
+ re_free (dfa->nodes);
+
+ if (dfa->state_table)
+ for (i = 0; i <= dfa->state_hash_mask; ++i)
+ {
+ struct re_state_table_entry *entry = dfa->state_table + i;
+ for (j = 0; j < entry->num; ++j)
+ {
+ re_dfastate_t *state = entry->array[j];
+ free_state (state);
+ }
+ re_free (entry->array);
+ }
+ re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+ if (dfa->sb_char != utf8_sb_map)
+ re_free (dfa->sb_char);
+#endif
+ re_free (dfa->subexp_map);
+#ifdef DEBUG
+ re_free (dfa->re_str);
+#endif
+
+ re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ if (BE (dfa != NULL, 1))
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+
+ re_free (preg->translate);
+ preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec above without link errors. */
+weak_function
+# endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+ char *fastmap;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (re_comp_buf.buffer)
+ {
+ fastmap = re_comp_buf.fastmap;
+ re_comp_buf.fastmap = NULL;
+ __regfree (&re_comp_buf);
+ memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+ re_comp_buf.fastmap = fastmap;
+ }
+
+ if (re_comp_buf.fastmap == NULL)
+ {
+ re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (__re_error_msgid
+ + __re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+ __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+ Compile the regular expression PATTERN, whose length is LENGTH.
+ SYNTAX indicate regular expression's syntax. */
+
+static reg_errcode_t
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+ reg_syntax_t syntax)
+{
+ reg_errcode_t err = REG_NOERROR;
+ re_dfa_t *dfa;
+ re_string_t regexp;
+
+ /* Initialize the pattern buffer. */
+ preg->fastmap_accurate = 0;
+ preg->syntax = syntax;
+ preg->not_bol = preg->not_eol = 0;
+ preg->used = 0;
+ preg->re_nsub = 0;
+ preg->can_be_null = 0;
+ preg->regs_allocated = REGS_UNALLOCATED;
+
+ /* Initialize the dfa. */
+ dfa = (re_dfa_t *) preg->buffer;
+ if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+ {
+ /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. If ->buffer is NULL this
+ is a simple allocation. */
+ dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+ if (dfa == NULL)
+ return REG_ESPACE;
+ preg->allocated = sizeof (re_dfa_t);
+ preg->buffer = (unsigned char *) dfa;
+ }
+ preg->used = sizeof (re_dfa_t);
+
+ err = init_dfa (dfa, length);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+#ifdef DEBUG
+ /* Note: length+1 will not overflow since it is checked in init_dfa. */
+ dfa->re_str = re_malloc (char, length + 1);
+ strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+ __libc_lock_init (dfa->lock);
+
+ err = re_string_construct (&regexp, pattern, length, preg->translate,
+ syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_compile_internal_free_return:
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+
+ /* Parse the regular expression, and build a structure tree. */
+ preg->re_nsub = 0;
+ dfa->str_tree = parse (&regexp, preg, syntax, &err);
+ if (BE (dfa->str_tree == NULL, 0))
+ goto re_compile_internal_free_return;
+
+ /* Analyze the tree and create the nfa. */
+ err = analyze (preg);
+ if (BE (err != REG_NOERROR, 0))
+ goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* If possible, do searching in single byte encoding to speed things up. */
+ if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+ optimize_utf8 (dfa);
+#endif
+
+ /* Then create the initial state of the dfa. */
+ err = create_initial_state (dfa);
+
+ /* Release work areas. */
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ }
+
+ return err;
+}
+
+/* Initialize DFA. We use the length of the regular expression PAT_LEN
+ as the initial length of some arrays. */
+
+static reg_errcode_t
+init_dfa (re_dfa_t *dfa, size_t pat_len)
+{
+ __re_size_t table_size;
+#ifdef RE_ENABLE_I18N
+ size_t max_i18n_object_size = MAX (sizeof (wchar_t), sizeof (wctype_t));
+#else
+ size_t max_i18n_object_size = 0;
+#endif
+ size_t max_object_size =
+ MAX (sizeof (struct re_state_table_entry),
+ MAX (sizeof (re_token_t),
+ MAX (sizeof (re_node_set),
+ MAX (sizeof (regmatch_t),
+ max_i18n_object_size))));
+
+ memset (dfa, '\0', sizeof (re_dfa_t));
+
+ /* Force allocation of str_tree_storage the first time. */
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+ /* Avoid overflows. The extra "/ 2" is for the table_size doubling
+ calculation below, and for similar doubling calculations
+ elsewhere. And it's <= rather than <, because some of the
+ doubling calculations add 1 afterwards. */
+ if (BE (SIZE_MAX / max_object_size / 2 <= pat_len, 0))
+ return REG_ESPACE;
+
+ dfa->nodes_alloc = pat_len + 1;
+ dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+ /* table_size = 2 ^ ceil(log pat_len) */
+ for (table_size = 1; ; table_size <<= 1)
+ if (table_size > pat_len)
+ break;
+
+ dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_hash_mask = table_size - 1;
+
+ dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+ if (dfa->mb_cur_max == 6
+ && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+ dfa->is_utf8 = 1;
+ dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+ != 0);
+#else
+ if (strcmp (locale_charset (), "UTF-8") == 0)
+ dfa->is_utf8 = 1;
+
+ /* We check exhaustively in the loop below if this charset is a
+ superset of ASCII. */
+ dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ if (dfa->is_utf8)
+ dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+ else
+ {
+ int i, j, ch;
+
+ dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+ if (BE (dfa->sb_char == NULL, 0))
+ return REG_ESPACE;
+
+ /* Set the bits corresponding to single byte chars. */
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ {
+ wint_t wch = __btowc (ch);
+ if (wch != WEOF)
+ dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+ if (isascii (ch) && wch != ch)
+ dfa->map_notascii = 1;
+# endif
+ }
+ }
+ }
+#endif
+
+ if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+ "word". In this case "word" means that it is the word construction
+ character used by some operators like "\<", "\>", etc. */
+
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
+{
+ int i, j, ch;
+ dfa->word_ops_used = 1;
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ if (isalnum (ch) || ch == '_')
+ dfa->word_char[i] |= (bitset_word_t) 1 << j;
+}
+
+/* Free the work area which are only used while compiling. */
+
+static void
+free_workarea_compile (regex_t *preg)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_storage_t *storage, *next;
+ for (storage = dfa->str_tree_storage; storage; storage = next)
+ {
+ next = storage->next;
+ re_free (storage);
+ }
+ dfa->str_tree_storage = NULL;
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+ dfa->str_tree = NULL;
+ re_free (dfa->org_indices);
+ dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts. */
+
+static reg_errcode_t
+create_initial_state (re_dfa_t *dfa)
+{
+ Idx first, i;
+ reg_errcode_t err;
+ re_node_set init_nodes;
+
+ /* Initial states have the epsilon closure of the node which is
+ the first node of the regular expression. */
+ first = dfa->str_tree->first->node_idx;
+ dfa->init_node = first;
+ err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* The back-references which are in initial states can epsilon transit,
+ since in this case all of the subexpressions can be null.
+ Then we add epsilon closures of the nodes which are the next nodes of
+ the back-references. */
+ if (dfa->nbackref > 0)
+ for (i = 0; i < init_nodes.nelem; ++i)
+ {
+ Idx node_idx = init_nodes.elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+
+ Idx clexp_idx;
+ if (type != OP_BACK_REF)
+ continue;
+ for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+ {
+ re_token_t *clexp_node;
+ clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+ if (clexp_node->type == OP_CLOSE_SUBEXP
+ && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+ break;
+ }
+ if (clexp_idx == init_nodes.nelem)
+ continue;
+
+ if (type == OP_BACK_REF)
+ {
+ Idx dest_idx = dfa->edests[node_idx].elems[0];
+ if (!re_node_set_contains (&init_nodes, dest_idx))
+ {
+ re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+ i = 0;
+ }
+ }
+ }
+
+ /* It must be the first time to invoke acquire_state. */
+ dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+ /* We don't check ERR here, since the initial state must not be NULL. */
+ if (BE (dfa->init_state == NULL, 0))
+ return err;
+ if (dfa->init_state->has_constraint)
+ {
+ dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_WORD);
+ dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_NEWLINE);
+ dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+ &init_nodes,
+ CONTEXT_NEWLINE
+ | CONTEXT_BEGBUF);
+ if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return err;
+ }
+ else
+ dfa->init_state_word = dfa->init_state_nl
+ = dfa->init_state_begbuf = dfa->init_state;
+
+ re_node_set_free (&init_nodes);
+ return REG_NOERROR;
+}
+
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+ to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+ DFA nodes where needed. */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+ Idx node;
+ int i;
+ bool mb_chars = false;
+ bool has_period = false;
+
+ for (node = 0; node < dfa->nodes_len; ++node)
+ switch (dfa->nodes[node].type)
+ {
+ case CHARACTER:
+ if (dfa->nodes[node].opr.c >= ASCII_CHARS)
+ mb_chars = true;
+ break;
+ case ANCHOR:
+ switch (dfa->nodes[node].opr.idx)
+ {
+ case LINE_FIRST:
+ case LINE_LAST:
+ case BUF_FIRST:
+ case BUF_LAST:
+ break;
+ default:
+ /* Word anchors etc. cannot be handled. */
+ return;
+ }
+ break;
+ case OP_PERIOD:
+ has_period = true;
+ break;
+ case OP_BACK_REF:
+ case OP_ALT:
+ case END_OF_RE:
+ case OP_DUP_ASTERISK:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ break;
+ case COMPLEX_BRACKET:
+ return;
+ case SIMPLE_BRACKET:
+ /* Just double check. */
+ {
+ int rshift = (ASCII_CHARS % BITSET_WORD_BITS == 0
+ ? 0
+ : BITSET_WORD_BITS - ASCII_CHARS % BITSET_WORD_BITS);
+ for (i = ASCII_CHARS / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+ {
+ if (dfa->nodes[node].opr.sbcset[i] >> rshift != 0)
+ return;
+ rshift = 0;
+ }
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ if (mb_chars || has_period)
+ for (node = 0; node < dfa->nodes_len; ++node)
+ {
+ if (dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].opr.c >= ASCII_CHARS)
+ dfa->nodes[node].mb_partial = 0;
+ else if (dfa->nodes[node].type == OP_PERIOD)
+ dfa->nodes[node].type = OP_UTF8_PERIOD;
+ }
+
+ /* The search can be in single byte locale. */
+ dfa->mb_cur_max = 1;
+ dfa->is_utf8 = 0;
+ dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+ "eclosure", and "inveclosure". */
+
+static reg_errcode_t
+analyze (regex_t *preg)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ reg_errcode_t ret;
+
+ /* Allocate arrays. */
+ dfa->nexts = re_malloc (Idx, dfa->nodes_alloc);
+ dfa->org_indices = re_malloc (Idx, dfa->nodes_alloc);
+ dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+ || dfa->eclosures == NULL, 0))
+ return REG_ESPACE;
+
+ dfa->subexp_map = re_malloc (Idx, preg->re_nsub);
+ if (dfa->subexp_map != NULL)
+ {
+ Idx i;
+ for (i = 0; i < preg->re_nsub; i++)
+ dfa->subexp_map[i] = i;
+ preorder (dfa->str_tree, optimize_subexps, dfa);
+ for (i = 0; i < preg->re_nsub; i++)
+ if (dfa->subexp_map[i] != i)
+ break;
+ if (i == preg->re_nsub)
+ {
+ free (dfa->subexp_map);
+ dfa->subexp_map = NULL;
+ }
+ }
+
+ ret = postorder (dfa->str_tree, lower_subexps, preg);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ ret = postorder (dfa->str_tree, calc_first, dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ preorder (dfa->str_tree, calc_next, dfa);
+ ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ ret = calc_eclosure (dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ /* We only need this during the prune_impossible_nodes pass in regexec.c;
+ skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */
+ if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+ || dfa->nbackref)
+ {
+ dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+ if (BE (dfa->inveclosures == NULL, 0))
+ return REG_ESPACE;
+ ret = calc_inveclosure (dfa);
+ }
+
+ return ret;
+}
+
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+ implement parse tree visits. Instead, we use parent pointers and
+ some hairy code in these two functions. */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra)
+{
+ bin_tree_t *node, *prev;
+
+ for (node = root; ; )
+ {
+ /* Descend down the tree, preferably to the left (or to the right
+ if that's the only child). */
+ while (node->left || node->right)
+ if (node->left)
+ node = node->left;
+ else
+ node = node->right;
+
+ do
+ {
+ reg_errcode_t err = fn (extra, node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (node->parent == NULL)
+ return REG_NOERROR;
+ prev = node;
+ node = node->parent;
+ }
+ /* Go up while we have a node that is reached from the right. */
+ while (node->right == prev || node->right == NULL);
+ node = node->right;
+ }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra)
+{
+ bin_tree_t *node;
+
+ for (node = root; ; )
+ {
+ reg_errcode_t err = fn (extra, node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Go to the left node, or up and to the right. */
+ if (node->left)
+ node = node->left;
+ else
+ {
+ bin_tree_t *prev = NULL;
+ while (node->right == prev || node->right == NULL)
+ {
+ prev = node;
+ node = node->parent;
+ if (!node)
+ return REG_NOERROR;
+ }
+ node = node->right;
+ }
+ }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+ re_search_internal to map the inner one's opr.idx to this one's. Adjust
+ backreferences as well. Requires a preorder visit. */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+
+ if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+ {
+ int idx = node->token.opr.idx;
+ node->token.opr.idx = dfa->subexp_map[idx];
+ dfa->used_bkref_map |= 1 << node->token.opr.idx;
+ }
+
+ else if (node->token.type == SUBEXP
+ && node->left && node->left->token.type == SUBEXP)
+ {
+ Idx other_idx = node->left->token.opr.idx;
+
+ node->left = node->left->left;
+ if (node->left)
+ node->left->parent = node;
+
+ dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+ if (other_idx < BITSET_WORD_BITS)
+ dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
+ }
+
+ return REG_NOERROR;
+}
+
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+ of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
+{
+ regex_t *preg = (regex_t *) extra;
+ reg_errcode_t err = REG_NOERROR;
+
+ if (node->left && node->left->token.type == SUBEXP)
+ {
+ node->left = lower_subexp (&err, preg, node->left);
+ if (node->left)
+ node->left->parent = node;
+ }
+ if (node->right && node->right->token.type == SUBEXP)
+ {
+ node->right = lower_subexp (&err, preg, node->right);
+ if (node->right)
+ node->right->parent = node;
+ }
+
+ return err;
+}
+
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *body = node->left;
+ bin_tree_t *op, *cls, *tree1, *tree;
+
+ if (preg->no_sub
+ /* We do not optimize empty subexpressions, because otherwise we may
+ have bad CONCAT nodes with NULL children. This is obviously not
+ very common, so we do not lose much. An example that triggers
+ this case is the sed "script" /\(\)/x. */
+ && node->left != NULL
+ && (node->token.opr.idx >= BITSET_WORD_BITS
+ || !(dfa->used_bkref_map
+ & ((bitset_word_t) 1 << node->token.opr.idx))))
+ return node->left;
+
+ /* Convert the SUBEXP node to the concatenation of an
+ OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */
+ op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+ cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+ tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+ tree = create_tree (dfa, op, tree1, CONCAT);
+ if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+ op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+ return tree;
+}
+
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+ nodes. Requires a postorder visit. */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+ if (node->token.type == CONCAT)
+ {
+ node->first = node->left->first;
+ node->node_idx = node->left->node_idx;
+ }
+ else
+ {
+ node->first = node;
+ node->node_idx = re_dfa_add_node (dfa, node->token);
+ if (BE (node->node_idx == REG_MISSING, 0))
+ return REG_ESPACE;
+ }
+ return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree. Preorder visit. */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+ switch (node->token.type)
+ {
+ case OP_DUP_ASTERISK:
+ node->left->next = node;
+ break;
+ case CONCAT:
+ node->left->next = node->right->first;
+ node->right->next = node->next;
+ break;
+ default:
+ if (node->left)
+ node->left->next = node->next;
+ if (node->right)
+ node->right->next = node->next;
+ break;
+ }
+ return REG_NOERROR;
+}
+
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+ Idx idx = node->node_idx;
+ reg_errcode_t err = REG_NOERROR;
+
+ switch (node->token.type)
+ {
+ case CONCAT:
+ break;
+
+ case END_OF_RE:
+ assert (node->next == NULL);
+ break;
+
+ case OP_DUP_ASTERISK:
+ case OP_ALT:
+ {
+ Idx left, right;
+ dfa->has_plural_match = 1;
+ if (node->left != NULL)
+ left = node->left->first->node_idx;
+ else
+ left = node->next->node_idx;
+ if (node->right != NULL)
+ right = node->right->first->node_idx;
+ else
+ right = node->next->node_idx;
+ assert (REG_VALID_INDEX (left));
+ assert (REG_VALID_INDEX (right));
+ err = re_node_set_init_2 (dfa->edests + idx, left, right);
+ }
+ break;
+
+ case ANCHOR:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+ break;
+
+ case OP_BACK_REF:
+ dfa->nexts[idx] = node->next->node_idx;
+ if (node->token.type == OP_BACK_REF)
+ re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+ break;
+
+ default:
+ assert (!IS_EPSILON_NODE (node->token.type));
+ dfa->nexts[idx] = node->next->node_idx;
+ break;
+ }
+
+ return err;
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+ Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+ to their own constraint. */
+
+static reg_errcode_t
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, Idx top_org_node, Idx top_clone_node,
+ Idx root_node, unsigned int init_constraint)
+{
+ Idx org_node, clone_node;
+ bool ok;
+ unsigned int constraint = init_constraint;
+ for (org_node = top_org_node, clone_node = top_clone_node;;)
+ {
+ Idx org_dest, clone_dest;
+ if (dfa->nodes[org_node].type == OP_BACK_REF)
+ {
+ /* If the back reference epsilon-transit, its destination must
+ also have the constraint. Then duplicate the epsilon closure
+ of the destination of the back reference, and store it in
+ edests of the back reference. */
+ org_dest = dfa->nexts[org_node];
+ re_node_set_empty (dfa->edests + clone_node);
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == REG_MISSING, 0))
+ return REG_ESPACE;
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+ else if (dfa->edests[org_node].nelem == 0)
+ {
+ /* In case of the node can't epsilon-transit, don't duplicate the
+ destination and store the original destination as the
+ destination of the node. */
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ break;
+ }
+ else if (dfa->edests[org_node].nelem == 1)
+ {
+ /* In case of the node can epsilon-transit, and it has only one
+ destination. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ if (dfa->nodes[org_node].type == ANCHOR)
+ {
+ /* In case of the node has another constraint, append it. */
+ if (org_node == root_node && clone_node != org_node)
+ {
+ /* ...but if the node is root_node itself, it means the
+ epsilon closure have a loop, then tie it to the
+ destination of the root_node. */
+ ok = re_node_set_insert (dfa->edests + clone_node, org_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ break;
+ }
+ constraint |= dfa->nodes[org_node].opr.ctx_type;
+ }
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == REG_MISSING, 0))
+ return REG_ESPACE;
+ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+ else /* dfa->edests[org_node].nelem == 2 */
+ {
+ /* In case of the node can epsilon-transit, and it has two
+ destinations. In the bin_tree_t and DFA, that's '|' and '*'. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ /* Search for a duplicated node which satisfies the constraint. */
+ clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+ if (clone_dest == REG_MISSING)
+ {
+ /* There are no such a duplicated node, create a new one. */
+ reg_errcode_t err;
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == REG_MISSING, 0))
+ return REG_ESPACE;
+ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ err = duplicate_node_closure (dfa, org_dest, clone_dest,
+ root_node, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ {
+ /* There are a duplicated node which satisfy the constraint,
+ use it to avoid infinite loop. */
+ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+
+ org_dest = dfa->edests[org_node].elems[1];
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == REG_MISSING, 0))
+ return REG_ESPACE;
+ ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+ org_node = org_dest;
+ clone_node = clone_dest;
+ }
+ return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+ satisfies the constraint CONSTRAINT. */
+
+static Idx
+search_duplicated_node (const re_dfa_t *dfa, Idx org_node,
+ unsigned int constraint)
+{
+ Idx idx;
+ for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+ {
+ if (org_node == dfa->org_indices[idx]
+ && constraint == dfa->nodes[idx].constraint)
+ return idx; /* Found. */
+ }
+ return REG_MISSING; /* Not found. */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+ Return the index of the new node, or REG_MISSING if insufficient storage is
+ available. */
+
+static Idx
+duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint)
+{
+ Idx dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+ if (BE (dup_idx != REG_MISSING, 1))
+ {
+ dfa->nodes[dup_idx].constraint = constraint;
+ if (dfa->nodes[org_idx].type == ANCHOR)
+ dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
+ dfa->nodes[dup_idx].duplicated = 1;
+
+ /* Store the index of the original node. */
+ dfa->org_indices[dup_idx] = org_idx;
+ }
+ return dup_idx;
+}
+
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
+{
+ Idx src, idx;
+ bool ok;
+ for (idx = 0; idx < dfa->nodes_len; ++idx)
+ re_node_set_init_empty (dfa->inveclosures + idx);
+
+ for (src = 0; src < dfa->nodes_len; ++src)
+ {
+ Idx *elems = dfa->eclosures[src].elems;
+ for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+ {
+ ok = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Calculate "eclosure" for all the node in DFA. */
+
+static reg_errcode_t
+calc_eclosure (re_dfa_t *dfa)
+{
+ Idx node_idx;
+ bool incomplete;
+#ifdef DEBUG
+ assert (dfa->nodes_len > 0);
+#endif
+ incomplete = false;
+ /* For each nodes, calculate epsilon closure. */
+ for (node_idx = 0; ; ++node_idx)
+ {
+ reg_errcode_t err;
+ re_node_set eclosure_elem;
+ if (node_idx == dfa->nodes_len)
+ {
+ if (!incomplete)
+ break;
+ incomplete = false;
+ node_idx = 0;
+ }
+
+#ifdef DEBUG
+ assert (dfa->eclosures[node_idx].nelem != REG_MISSING);
+#endif
+
+ /* If we have already calculated, skip it. */
+ if (dfa->eclosures[node_idx].nelem != 0)
+ continue;
+ /* Calculate epsilon closure of `node_idx'. */
+ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (dfa->eclosures[node_idx].nelem == 0)
+ {
+ incomplete = true;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE. */
+
+static reg_errcode_t
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root)
+{
+ reg_errcode_t err;
+ unsigned int constraint;
+ Idx i;
+ bool incomplete;
+ bool ok;
+ re_node_set eclosure;
+ incomplete = false;
+ err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* This indicates that we are calculating this node now.
+ We reference this value to avoid infinite loop. */
+ dfa->eclosures[node].nelem = REG_MISSING;
+
+ constraint = ((dfa->nodes[node].type == ANCHOR)
+ ? dfa->nodes[node].opr.ctx_type : 0);
+ /* If the current node has constraints, duplicate all nodes.
+ Since they must inherit the constraints. */
+ if (constraint
+ && dfa->edests[node].nelem
+ && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+ {
+ err = duplicate_node_closure (dfa, node, node, node, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Expand each epsilon destination nodes. */
+ if (IS_EPSILON_NODE(dfa->nodes[node].type))
+ for (i = 0; i < dfa->edests[node].nelem; ++i)
+ {
+ re_node_set eclosure_elem;
+ Idx edest = dfa->edests[node].elems[i];
+ /* If calculating the epsilon closure of `edest' is in progress,
+ return intermediate result. */
+ if (dfa->eclosures[edest].nelem == REG_MISSING)
+ {
+ incomplete = true;
+ continue;
+ }
+ /* If we haven't calculated the epsilon closure of `edest' yet,
+ calculate now. Otherwise use calculated epsilon closure. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ eclosure_elem = dfa->eclosures[edest];
+ /* Merge the epsilon closure of `edest'. */
+ re_node_set_merge (&eclosure, &eclosure_elem);
+ /* If the epsilon closure of `edest' is incomplete,
+ the epsilon closure of this node is also incomplete. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ incomplete = true;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* Epsilon closures include itself. */
+ ok = re_node_set_insert (&eclosure, node);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ if (incomplete && !root)
+ dfa->eclosures[node].nelem = 0;
+ else
+ dfa->eclosures[node] = eclosure;
+ *new_set = eclosure;
+ return REG_NOERROR;
+}
+
+/* Functions for token which are used in the parser. */
+
+/* Fetch a token from INPUT.
+ We must not use this function inside bracket expressions. */
+
+static void
+internal_function
+fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
+{
+ re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function inside bracket expressions. */
+
+static int
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+ unsigned char c;
+
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+ token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+ token->mb_partial = 0;
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ token->mb_partial = 1;
+ return 1;
+ }
+#endif
+ if (c == '\\')
+ {
+ unsigned char c2;
+ if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+ {
+ token->type = BACK_SLASH;
+ return 1;
+ }
+
+ c2 = re_string_peek_byte_case (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input,
+ re_string_cur_idx (input) + 1);
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (c2) != 0;
+
+ switch (c2)
+ {
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (!(syntax & RE_NO_BK_REFS))
+ {
+ token->type = OP_BACK_REF;
+ token->opr.idx = c2 - '1';
+ }
+ break;
+ case '<':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_FIRST;
+ }
+ break;
+ case '>':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_LAST;
+ }
+ break;
+ case 'b':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_DELIM;
+ }
+ break;
+ case 'B':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = NOT_WORD_DELIM;
+ }
+ break;
+ case 'w':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_WORD;
+ break;
+ case 'W':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTWORD;
+ break;
+ case 's':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_SPACE;
+ break;
+ case 'S':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTSPACE;
+ break;
+ case '`':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_FIRST;
+ }
+ break;
+ case '\'':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_LAST;
+ }
+ break;
+ case '(':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ default:
+ break;
+ }
+ return 2;
+ }
+
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (token->opr.c);
+
+ switch (c)
+ {
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ token->type = OP_ALT;
+ break;
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '*':
+ token->type = OP_DUP_ASTERISK;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '[':
+ token->type = OP_OPEN_BRACKET;
+ break;
+ case '.':
+ token->type = OP_PERIOD;
+ break;
+ case '^':
+ if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+ re_string_cur_idx (input) != 0)
+ {
+ char prev = re_string_peek_byte (input, -1);
+ if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_FIRST;
+ break;
+ case '$':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) + 1 != re_string_length (input))
+ {
+ re_token_t next;
+ re_string_skip_bytes (input, 1);
+ peek_token (&next, input, syntax);
+ re_string_skip_bytes (input, -1);
+ if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_LAST;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function out of bracket expressions. */
+
+static int
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+ unsigned char c;
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ return 1;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+ && re_string_cur_idx (input) + 1 < re_string_length (input))
+ {
+ /* In this case, '\' escape a character. */
+ unsigned char c2;
+ re_string_skip_bytes (input, 1);
+ c2 = re_string_peek_byte (input, 0);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ return 1;
+ }
+ if (c == '[') /* '[' is a special char in a bracket exps. */
+ {
+ unsigned char c2;
+ int token_len;
+ if (re_string_cur_idx (input) + 1 < re_string_length (input))
+ c2 = re_string_peek_byte (input, 1);
+ else
+ c2 = 0;
+ token->opr.c = c2;
+ token_len = 2;
+ switch (c2)
+ {
+ case '.':
+ token->type = OP_OPEN_COLL_ELEM;
+ break;
+ case '=':
+ token->type = OP_OPEN_EQUIV_CLASS;
+ break;
+ case ':':
+ if (syntax & RE_CHAR_CLASSES)
+ {
+ token->type = OP_OPEN_CHAR_CLASS;
+ break;
+ }
+ /* else fall through. */
+ default:
+ token->type = CHARACTER;
+ token->opr.c = c;
+ token_len = 1;
+ break;
+ }
+ return token_len;
+ }
+ switch (c)
+ {
+ case '-':
+ token->type = OP_CHARSET_RANGE;
+ break;
+ case ']':
+ token->type = OP_CLOSE_BRACKET;
+ break;
+ case '^':
+ token->type = OP_NON_MATCH_LIST;
+ break;
+ default:
+ token->type = CHARACTER;
+ }
+ return 1;
+}
+
+/* Functions for parser. */
+
+/* Entry point of the parser.
+ Parse the regular expression REGEXP and return the structure tree.
+ If an error is occured, ERR is set by error code, and return NULL.
+ This function build the following tree, from regular expression <reg_exp>:
+ CAT
+ / \
+ / \
+ <reg_exp> EOR
+
+ CAT means concatenation.
+ EOR means end of regular expression. */
+
+static bin_tree_t *
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+ reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *eor, *root;
+ re_token_t current_token;
+ dfa->syntax = syntax;
+ fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ eor = create_tree (dfa, NULL, NULL, END_OF_RE);
+ if (tree != NULL)
+ root = create_tree (dfa, tree, eor, CONCAT);
+ else
+ root = eor;
+ if (BE (eor == NULL || root == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ return root;
+}
+
+/* This function build the following tree, from regular expression
+ <branch1>|<branch2>:
+ ALT
+ / \
+ / \
+ <branch1> <branch2>
+
+ ALT means alternative, which represents the operator `|'. */
+
+static bin_tree_t *
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *branch = NULL;
+ tree = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type == OP_ALT)
+ {
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ if (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ branch = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && branch == NULL, 0))
+ return NULL;
+ }
+ else
+ branch = NULL;
+ tree = create_tree (dfa, tree, branch, OP_ALT);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ <exp1><exp2>:
+ CAT
+ / \
+ / \
+ <exp1> <exp2>
+
+ CAT means concatenation. */
+
+static bin_tree_t *
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
+{
+ bin_tree_t *tree, *expr;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ tree = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ expr = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && expr == NULL, 0))
+ {
+ return NULL;
+ }
+ if (tree != NULL && expr != NULL)
+ {
+ tree = create_tree (dfa, tree, expr, CONCAT);
+ if (tree == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else if (tree == NULL)
+ tree = expr;
+ /* Otherwise expr == NULL, we don't need to create new tree. */
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+ *
+ |
+ a
+*/
+
+static bin_tree_t *
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ switch (token->type)
+ {
+ case CHARACTER:
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (!re_string_eoi (regexp)
+ && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+ {
+ bin_tree_t *mbc_remain;
+ fetch_token (token, regexp, syntax);
+ mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+ tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+ if (BE (mbc_remain == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ }
+#endif
+ break;
+ case OP_OPEN_SUBEXP:
+ tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_OPEN_BRACKET:
+ tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_BACK_REF:
+ if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+ {
+ *err = REG_ESUBREG;
+ return NULL;
+ }
+ dfa->used_bkref_map |= 1 << token->opr.idx;
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ ++dfa->nbackref;
+ dfa->has_mb_node = 1;
+ break;
+ case OP_OPEN_DUP_NUM:
+ if (syntax & RE_CONTEXT_INVALID_DUP)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ /* FALLTHROUGH */
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ case OP_DUP_QUESTION:
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ {
+ fetch_token (token, regexp, syntax);
+ return parse_expression (regexp, preg, token, syntax, nest, err);
+ }
+ /* else fall through */
+ case OP_CLOSE_SUBEXP:
+ if ((token->type == OP_CLOSE_SUBEXP) &&
+ !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+ {
+ *err = REG_ERPAREN;
+ return NULL;
+ }
+ /* else fall through */
+ case OP_CLOSE_DUP_NUM:
+ /* We treat it as a normal character. */
+
+ /* Then we can these characters as normal characters. */
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be initialized already
+ by peek_token. */
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ break;
+ case ANCHOR:
+ if ((token->opr.ctx_type
+ & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+ && dfa->word_ops_used == 0)
+ init_word_char (dfa);
+ if (token->opr.ctx_type == WORD_DELIM
+ || token->opr.ctx_type == NOT_WORD_DELIM)
+ {
+ bin_tree_t *tree_first, *tree_last;
+ if (token->opr.ctx_type == WORD_DELIM)
+ {
+ token->opr.ctx_type = WORD_FIRST;
+ tree_first = create_token_tree (dfa, NULL, NULL, token);
+ token->opr.ctx_type = WORD_LAST;
+ }
+ else
+ {
+ token->opr.ctx_type = INSIDE_WORD;
+ tree_first = create_token_tree (dfa, NULL, NULL, token);
+ token->opr.ctx_type = INSIDE_NOTWORD;
+ }
+ tree_last = create_token_tree (dfa, NULL, NULL, token);
+ tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+ if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else
+ {
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ /* We must return here, since ANCHORs can't be followed
+ by repetition operators.
+ eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+ it must not be "<ANCHOR(^)><REPEAT(*)>". */
+ fetch_token (token, regexp, syntax);
+ return tree;
+ case OP_PERIOD:
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ if (dfa->mb_cur_max > 1)
+ dfa->has_mb_node = 1;
+ break;
+ case OP_WORD:
+ case OP_NOTWORD:
+ tree = build_charclass_op (dfa, regexp->trans,
+ (const unsigned char *) "alnum",
+ (const unsigned char *) "_",
+ token->type == OP_NOTWORD, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_SPACE:
+ case OP_NOTSPACE:
+ tree = build_charclass_op (dfa, regexp->trans,
+ (const unsigned char *) "space",
+ (const unsigned char *) "",
+ token->type == OP_NOTSPACE, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_ALT:
+ case END_OF_RE:
+ return NULL;
+ case BACK_SLASH:
+ *err = REG_EESCAPE;
+ return NULL;
+ default:
+ /* Must not happen? */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return NULL;
+ }
+ fetch_token (token, regexp, syntax);
+
+ while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+ || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+ {
+ tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ /* In BRE consecutive duplications are not allowed. */
+ if ((syntax & RE_CONTEXT_INVALID_DUP)
+ && (token->type == OP_DUP_ASTERISK
+ || token->type == OP_OPEN_DUP_NUM))
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ }
+
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ (<reg_exp>):
+ SUBEXP
+ |
+ <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ size_t cur_nsub;
+ cur_nsub = preg->re_nsub++;
+
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+ /* The subexpression may be a null string. */
+ if (token->type == OP_CLOSE_SUBEXP)
+ tree = NULL;
+ else
+ {
+ tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+ if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+ *err = REG_EPAREN;
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+
+ if (cur_nsub <= '9' - '1')
+ dfa->completed_bkref_map |= 1 << cur_nsub;
+
+ tree = create_tree (dfa, tree, NULL, SUBEXP);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ tree->token.opr.idx = cur_nsub;
+ return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
+
+static bin_tree_t *
+parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
+{
+ bin_tree_t *tree = NULL, *old_tree = NULL;
+ Idx i, start, end, start_idx = re_string_cur_idx (regexp);
+ re_token_t start_token = *token;
+
+ if (token->type == OP_OPEN_DUP_NUM)
+ {
+ end = 0;
+ start = fetch_number (regexp, token, syntax);
+ if (start == REG_MISSING)
+ {
+ if (token->type == CHARACTER && token->opr.c == ',')
+ start = 0; /* We treat "{,m}" as "{0,m}". */
+ else
+ {
+ *err = REG_BADBR; /* <re>{} is invalid. */
+ return NULL;
+ }
+ }
+ if (BE (start != REG_ERROR, 1))
+ {
+ /* We treat "{n}" as "{n,n}". */
+ end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+ : ((token->type == CHARACTER && token->opr.c == ',')
+ ? fetch_number (regexp, token, syntax) : REG_ERROR));
+ }
+ if (BE (start == REG_ERROR || end == REG_ERROR, 0))
+ {
+ /* Invalid sequence. */
+ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+ {
+ if (token->type == END_OF_RE)
+ *err = REG_EBRACE;
+ else
+ *err = REG_BADBR;
+
+ return NULL;
+ }
+
+ /* If the syntax bit is set, rollback. */
+ re_string_set_index (regexp, start_idx);
+ *token = start_token;
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be already initialized by
+ peek_token. */
+ return elem;
+ }
+
+ if (BE (end != REG_MISSING && start > end, 0))
+ {
+ /* First number greater than second. */
+ *err = REG_BADBR;
+ return NULL;
+ }
+ }
+ else
+ {
+ start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+ end = (token->type == OP_DUP_QUESTION) ? 1 : REG_MISSING;
+ }
+
+ fetch_token (token, regexp, syntax);
+
+ if (BE (elem == NULL, 0))
+ return NULL;
+ if (BE (start == 0 && end == 0, 0))
+ {
+ postorder (elem, free_tree, NULL);
+ return NULL;
+ }
+
+ /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
+ if (BE (start > 0, 0))
+ {
+ tree = elem;
+ for (i = 2; i <= start; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (start == end)
+ return tree;
+
+ /* Duplicate ELEM before it is marked optional. */
+ elem = duplicate_tree (elem, dfa);
+ old_tree = tree;
+ }
+ else
+ old_tree = NULL;
+
+ if (elem->token.type == SUBEXP)
+ postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+ tree = create_tree (dfa, elem, NULL,
+ (end == REG_MISSING ? OP_DUP_ASTERISK : OP_ALT));
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ /* This loop is actually executed only when end != REG_MISSING,
+ to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have
+ already created the start+1-th copy. */
+ if ((Idx) -1 < 0 || end != REG_MISSING)
+ for (i = start + 2; i <= end; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ tree = create_tree (dfa, tree, NULL, OP_ALT);
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (old_tree)
+ tree = create_tree (dfa, old_tree, tree, CONCAT);
+
+ return tree;
+
+ parse_dup_op_espace:
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+ I'm not sure, but maybe enough. */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+ /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, Idx *range_alloc,
+ bracket_elem_t *start_elem, bracket_elem_t *end_elem)
+# else /* not RE_ENABLE_I18N */
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem)
+# endif /* not RE_ENABLE_I18N */
+{
+ unsigned int start_ch, end_ch;
+ /* Equivalence Classes and Character Classes can't be a range start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ /* We can handle no multi character collating elements without libc
+ support. */
+ if (BE ((start_elem->type == COLL_SYM
+ && strlen ((char *) start_elem->opr.name) > 1)
+ || (end_elem->type == COLL_SYM
+ && strlen ((char *) end_elem->opr.name) > 1), 0))
+ return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+ {
+ wchar_t wc;
+ wint_t start_wc;
+ wint_t end_wc;
+ wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+ start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+ ? __btowc (start_ch) : start_elem->opr.wch);
+ end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+ ? __btowc (end_ch) : end_elem->opr.wch);
+ if (start_wc == WEOF || end_wc == WEOF)
+ return REG_ECOLLATE;
+ cmp_buf[0] = start_wc;
+ cmp_buf[4] = end_wc;
+ if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, for !_LIBC we have no collation elements: if the
+ character set is single byte, the single byte character set
+ that we build below suffices. parse_bracket_exp passes
+ no MBCSET if dfa->mb_cur_max == 1. */
+ if (mbcset)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ wchar_t *new_array_start, *new_array_end;
+ Idx new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ /* Use realloc since mbcset->range_starts and mbcset->range_ends
+ are NULL if *range_alloc == 0. */
+ new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_wc;
+ mbcset->range_ends[mbcset->nranges++] = end_wc;
+ }
+
+ /* Build the table for single byte characters. */
+ for (wc = 0; wc < SBC_MAX; ++wc)
+ {
+ cmp_buf[2] = wc;
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ bitset_set (sbcset, wc);
+ }
+ }
+# else /* not RE_ENABLE_I18N */
+ {
+ unsigned int ch;
+ start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ if (start_ch > end_ch)
+ return REG_ERANGE;
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ if (start_ch <= ch && ch <= end_ch)
+ bitset_set (sbcset, ch);
+ }
+# endif /* not RE_ENABLE_I18N */
+ return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument since we may update it. */
+
+static reg_errcode_t
+internal_function
+build_collating_symbol (bitset_t sbcset,
+# ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset, Idx *coll_sym_alloc,
+# endif
+ const unsigned char *name)
+{
+ size_t name_len = strlen ((const char *) name);
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+ "[[.a-a.]]" etc. */
+
+static bin_tree_t *
+parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err)
+{
+#ifdef _LIBC
+ const unsigned char *collseqmb;
+ const char *collseqwc;
+ uint32_t nrules;
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Seek the collating symbol entry correspondings to NAME.
+ Return the index of the symbol in the SYMB_TABLE. */
+
+ auto inline int32_t
+ __attribute ((always_inline))
+ seek_collating_symbol_entry (name, name_len)
+ const unsigned char *name;
+ size_t name_len;
+ {
+ int32_t hash = elem_hash ((const char *) name, name_len);
+ int32_t elem = hash % table_size;
+ if (symb_table[2 * elem] != 0)
+ {
+ int32_t second = hash % (table_size - 2) + 1;
+
+ do
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ /* Compare the length of the name. */
+ && name_len == extra[symb_table[2 * elem + 1]]
+ /* Compare the name. */
+ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+ name_len) == 0)
+ {
+ /* Yep, this is the entry. */
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+ while (symb_table[2 * elem] != 0);
+ }
+ return elem;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Look up the collation sequence value of BR_ELEM.
+ Return the value if succeeded, UINT_MAX otherwise. */
+
+ auto inline unsigned int
+ __attribute ((always_inline))
+ lookup_collation_sequence_value (br_elem)
+ bracket_elem_t *br_elem;
+ {
+ if (br_elem->type == SB_CHAR)
+ {
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ return collseqmb[br_elem->opr.ch];
+ else
+ {
+ wint_t wc = __btowc (br_elem->opr.ch);
+ return __collseq_table_lookup (collseqwc, wc);
+ }
+ }
+ else if (br_elem->type == MB_CHAR)
+ {
+ return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+ }
+ else if (br_elem->type == COLL_SYM)
+ {
+ size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+ if (nrules != 0)
+ {
+ int32_t elem, idx;
+ elem = seek_collating_symbol_entry (br_elem->opr.name,
+ sym_name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ /* Skip the byte sequence of the collating element. */
+ idx += 1 + extra[idx];
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the multibyte collation sequence value. */
+ idx += sizeof (unsigned int);
+ /* Skip the wide char sequence of the collating element. */
+ idx += sizeof (unsigned int) *
+ (1 + *(unsigned int *) (extra + idx));
+ /* Return the collation sequence value. */
+ return *(unsigned int *) (extra + idx);
+ }
+ else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+ {
+ /* No valid character. Match it as a single byte
+ character. */
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ }
+ else if (sym_name_len == 1)
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ return UINT_MAX;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ Idx *range_alloc;
+ bitset_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+ {
+ unsigned int ch;
+ uint32_t start_collseq;
+ uint32_t end_collseq;
+
+ /* Equivalence Classes and Character Classes can't be a range
+ start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ start_collseq = lookup_collation_sequence_value (start_elem);
+ end_collseq = lookup_collation_sequence_value (end_elem);
+ /* Check start/end collation sequence values. */
+ if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+ return REG_ECOLLATE;
+ if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, if we have no collation elements, and the character set
+ is single byte, the single byte character set that we
+ build below suffices. */
+ if (nrules > 0 || dfa->mb_cur_max > 1)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ uint32_t *new_array_start;
+ uint32_t *new_array_end;
+ Idx new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_collseq;
+ mbcset->range_ends[mbcset->nranges++] = end_collseq;
+ }
+
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ch++)
+ {
+ uint32_t ch_collseq;
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ ch_collseq = collseqmb[ch];
+ else
+ ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+ if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+ bitset_set (sbcset, ch);
+ }
+ return REG_NOERROR;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument sinse we may update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+ re_charset_t *mbcset;
+ Idx *coll_sym_alloc;
+ bitset_t sbcset;
+ const unsigned char *name;
+ {
+ int32_t elem, idx;
+ size_t name_len = strlen ((const char *) name);
+ if (nrules != 0)
+ {
+ elem = seek_collating_symbol_entry (name, name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ }
+ else if (symb_table[2 * elem] == 0 && name_len == 1)
+ {
+ /* No valid character, treat it as a normal
+ character. */
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ else
+ return REG_ECOLLATE;
+
+ /* Got valid collation sequence, add it as a new entry. */
+ /* Check the space of the arrays. */
+ if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->ncoll_syms is 0. */
+ Idx new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+ /* Use realloc since mbcset->coll_syms is NULL
+ if *alloc == 0. */
+ int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+ new_coll_sym_alloc);
+ if (BE (new_coll_syms == NULL, 0))
+ return REG_ESPACE;
+ mbcset->coll_syms = new_coll_syms;
+ *coll_sym_alloc = new_coll_sym_alloc;
+ }
+ mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+ return REG_NOERROR;
+ }
+ else
+ {
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ }
+ }
+#endif
+
+ re_token_t br_token;
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ Idx coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+ Idx equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ bool non_match = false;
+ bin_tree_t *work_tree;
+ int token_len;
+ bool first_round = true;
+#ifdef _LIBC
+ collseqmb = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules)
+ {
+ /*
+ if (MB_CUR_MAX > 1)
+ */
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+ }
+#endif
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+ if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_NON_MATCH_LIST)
+ {
+#ifdef RE_ENABLE_I18N
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ non_match = true;
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set (sbcset, '\n');
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ }
+
+ /* We treat the first ']' as a normal character. */
+ if (token->type == OP_CLOSE_BRACKET)
+ token->type = CHARACTER;
+
+ while (1)
+ {
+ bracket_elem_t start_elem, end_elem;
+ unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+ unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+ reg_errcode_t ret;
+ int token_len2 = 0;
+ bool is_range_exp = false;
+ re_token_t token2;
+
+ start_elem.opr.name = start_name_buf;
+ ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+ syntax, first_round);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+ first_round = false;
+
+ /* Get information about the next token. We need it in any case. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+ /* Do not check for ranges if we know they are not allowed. */
+ if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+ {
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CHARSET_RANGE)
+ {
+ re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
+ token_len2 = peek_token_bracket (&token2, regexp, syntax);
+ if (BE (token2.type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token2.type == OP_CLOSE_BRACKET)
+ {
+ /* We treat the last '-' as a normal character. */
+ re_string_skip_bytes (regexp, -token_len);
+ token->type = CHARACTER;
+ }
+ else
+ is_range_exp = true;
+ }
+ }
+
+ if (is_range_exp == true)
+ {
+ end_elem.opr.name = end_name_buf;
+ ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+ dfa, syntax, true);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+ *err = build_range_exp (sbcset, mbcset, &range_alloc,
+ &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+ *err = build_range_exp (sbcset,
+ dfa->mb_cur_max > 1 ? mbcset : NULL,
+ &range_alloc, &start_elem, &end_elem);
+# else
+ *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ }
+ else
+ {
+ switch (start_elem.type)
+ {
+ case SB_CHAR:
+ bitset_set (sbcset, start_elem.opr.ch);
+ break;
+#ifdef RE_ENABLE_I18N
+ case MB_CHAR:
+ /* Check whether the array has enough space. */
+ if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+ {
+ wchar_t *new_mbchars;
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nmbchars is 0. */
+ mbchar_alloc = 2 * mbcset->nmbchars + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+ mbchar_alloc);
+ if (BE (new_mbchars == NULL, 0))
+ goto parse_bracket_exp_espace;
+ mbcset->mbchars = new_mbchars;
+ }
+ mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+ break;
+#endif /* RE_ENABLE_I18N */
+ case EQUIV_CLASS:
+ *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case COLL_SYM:
+ *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case CHAR_CLASS:
+ *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name, syntax);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CLOSE_BRACKET)
+ break;
+ }
+
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+
+ if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+ || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+ || mbcset->non_match)))
+ {
+ bin_tree_t *mbc_tree;
+ int sbc_idx;
+ /* Build a tree for complex bracket. */
+ dfa->has_mb_node = 1;
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+ if (sbcset[sbc_idx])
+ break;
+ /* If there are no bits set in sbcset, there is no point
+ of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */
+ if (sbc_idx < BITSET_WORDS)
+ {
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+
+ /* Then join them by ALT node. */
+ work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ }
+ else
+ {
+ re_free (sbcset);
+ work_tree = mbc_tree;
+ }
+ }
+ else
+#endif /* not RE_ENABLE_I18N */
+ {
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ }
+ return work_tree;
+
+ parse_bracket_exp_espace:
+ *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ return NULL;
+}
+
+/* Parse an element in the bracket expression. */
+
+static reg_errcode_t
+parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
+ re_token_t *token, int token_len, re_dfa_t *dfa,
+ reg_syntax_t syntax, bool accept_hyphen)
+{
+#ifdef RE_ENABLE_I18N
+ int cur_char_size;
+ cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+ if (cur_char_size > 1)
+ {
+ elem->type = MB_CHAR;
+ elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+ re_string_skip_bytes (regexp, cur_char_size);
+ return REG_NOERROR;
+ }
+#endif /* RE_ENABLE_I18N */
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+ || token->type == OP_OPEN_EQUIV_CLASS)
+ return parse_bracket_symbol (elem, regexp, token);
+ if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+ {
+ /* A '-' must only appear as anything but a range indicator before
+ the closing bracket. Everything else is an error. */
+ re_token_t token2;
+ (void) peek_token_bracket (&token2, regexp, syntax);
+ if (token2.type != OP_CLOSE_BRACKET)
+ /* The actual error value is not standardized since this whole
+ case is undefined. But ERANGE makes good sense. */
+ return REG_ERANGE;
+ }
+ elem->type = SB_CHAR;
+ elem->opr.ch = token->opr.c;
+ return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression. Bracket symbols are
+ such as [:<character_class>:], [.<collating_element>.], and
+ [=<equivalent_class>=]. */
+
+static reg_errcode_t
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+ re_token_t *token)
+{
+ unsigned char ch, delim = token->opr.c;
+ int i = 0;
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ for (;; ++i)
+ {
+ if (i >= BRACKET_NAME_BUF_SIZE)
+ return REG_EBRACK;
+ if (token->type == OP_OPEN_CHAR_CLASS)
+ ch = re_string_fetch_byte_case (regexp);
+ else
+ ch = re_string_fetch_byte (regexp);
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+ break;
+ elem->opr.name[i] = ch;
+ }
+ re_string_skip_bytes (regexp, 1);
+ elem->opr.name[i] = '\0';
+ switch (token->type)
+ {
+ case OP_OPEN_COLL_ELEM:
+ elem->type = COLL_SYM;
+ break;
+ case OP_OPEN_EQUIV_CLASS:
+ elem->type = EQUIV_CLASS;
+ break;
+ case OP_OPEN_CHAR_CLASS:
+ elem->type = CHAR_CLASS;
+ break;
+ default:
+ break;
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the equivalence class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+ Idx *equiv_class_alloc, const unsigned char *name)
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
+#endif /* not RE_ENABLE_I18N */
+{
+#ifdef _LIBC
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *cp;
+ unsigned char char_buf[2];
+ int32_t idx1, idx2;
+ unsigned int ch;
+ size_t len;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+ /* Calculate the index for equivalence class. */
+ cp = name;
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ idx1 = findidx (&cp);
+ if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+ /* This isn't a valid character. */
+ return REG_ECOLLATE;
+
+ /* Build single byte matcing table for this equivalence class. */
+ char_buf[1] = (unsigned char) '\0';
+ len = weights[idx1];
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ {
+ char_buf[0] = ch;
+ cp = char_buf;
+ idx2 = findidx (&cp);
+/*
+ idx2 = table[ch];
+*/
+ if (idx2 == 0)
+ /* This isn't a valid character. */
+ continue;
+ if (len == weights[idx2])
+ {
+ int cnt = 0;
+ while (cnt <= len &&
+ weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+ ++cnt;
+
+ if (cnt > len)
+ bitset_set (sbcset, ch);
+ }
+ }
+ /* Check whether the array has enough space. */
+ if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nequiv_classes is 0. */
+ Idx new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+ /* Use realloc since the array is NULL if *alloc == 0. */
+ int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+ int32_t,
+ new_equiv_class_alloc);
+ if (BE (new_equiv_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->equiv_classes = new_equiv_classes;
+ *equiv_class_alloc = new_equiv_class_alloc;
+ }
+ mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+ }
+ else
+#endif /* _LIBC */
+ {
+ if (BE (strlen ((const char *) name) != 1, 0))
+ return REG_ECOLLATE;
+ bitset_set (sbcset, *name);
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the character class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+ re_charset_t *mbcset, Idx *char_class_alloc,
+ const unsigned char *class_name, reg_syntax_t syntax)
+#else /* not RE_ENABLE_I18N */
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+ const unsigned char *class_name, reg_syntax_t syntax)
+#endif /* not RE_ENABLE_I18N */
+{
+ int i;
+ const char *name = (const char *) class_name;
+
+ /* In case of REG_ICASE "upper" and "lower" match the both of
+ upper and lower cases. */
+ if ((syntax & RE_ICASE)
+ && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
+ name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+ /* Check the space of the arrays. */
+ if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nchar_classes is 0. */
+ Idx new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+ new_char_class_alloc);
+ if (BE (new_char_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->char_classes = new_char_classes;
+ *char_class_alloc = new_char_class_alloc;
+ }
+ mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func) \
+ do { \
+ if (BE (trans != NULL, 0)) \
+ { \
+ for (i = 0; i < SBC_MAX; ++i) \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, trans[i]); \
+ } \
+ else \
+ { \
+ for (i = 0; i < SBC_MAX; ++i) \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, i); \
+ } \
+ } while (0)
+
+ if (strcmp (name, "alnum") == 0)
+ BUILD_CHARCLASS_LOOP (isalnum);
+ else if (strcmp (name, "cntrl") == 0)
+ BUILD_CHARCLASS_LOOP (iscntrl);
+ else if (strcmp (name, "lower") == 0)
+ BUILD_CHARCLASS_LOOP (islower);
+ else if (strcmp (name, "space") == 0)
+ BUILD_CHARCLASS_LOOP (isspace);
+ else if (strcmp (name, "alpha") == 0)
+ BUILD_CHARCLASS_LOOP (isalpha);
+ else if (strcmp (name, "digit") == 0)
+ BUILD_CHARCLASS_LOOP (isdigit);
+ else if (strcmp (name, "print") == 0)
+ BUILD_CHARCLASS_LOOP (isprint);
+ else if (strcmp (name, "upper") == 0)
+ BUILD_CHARCLASS_LOOP (isupper);
+ else if (strcmp (name, "blank") == 0)
+ BUILD_CHARCLASS_LOOP (isblank);
+ else if (strcmp (name, "graph") == 0)
+ BUILD_CHARCLASS_LOOP (isgraph);
+ else if (strcmp (name, "punct") == 0)
+ BUILD_CHARCLASS_LOOP (ispunct);
+ else if (strcmp (name, "xdigit") == 0)
+ BUILD_CHARCLASS_LOOP (isxdigit);
+ else
+ return REG_ECTYPE;
+
+ return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+ const unsigned char *class_name,
+ const unsigned char *extra, bool non_match,
+ reg_errcode_t *err)
+{
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ Idx alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ reg_errcode_t ret;
+ re_token_t br_token;
+ bin_tree_t *tree;
+
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+ if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ if (non_match)
+ {
+#ifdef RE_ENABLE_I18N
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ }
+
+ /* We don't care the syntax in this case. */
+ ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+ class_name, 0);
+
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = ret;
+ return NULL;
+ }
+ /* \w match '_' also. */
+ for (; *extra; extra++)
+ bitset_set (sbcset, *extra);
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (tree == NULL, 0))
+ goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto build_word_op_espace;
+ /* Then join them by ALT node. */
+ tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+ if (BE (mbc_tree != NULL, 1))
+ return tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+ Fetch a number from `input', and return the number.
+ Return REG_MISSING if the number field is empty like "{,1}".
+ Return REG_ERROR if an error occurred. */
+
+static Idx
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+{
+ Idx num = REG_MISSING;
+ unsigned char c;
+ while (1)
+ {
+ fetch_token (token, input, syntax);
+ c = token->opr.c;
+ if (BE (token->type == END_OF_RE, 0))
+ return REG_ERROR;
+ if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+ break;
+ num = ((token->type != CHARACTER || c < '0' || '9' < c
+ || num == REG_ERROR)
+ ? REG_ERROR
+ : ((num == REG_MISSING) ? c - '0' : num * 10 + c - '0'));
+ num = (num > RE_DUP_MAX) ? REG_ERROR : num;
+ }
+ return num;
+}
+
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+ re_free (cset->mbchars);
+# ifdef _LIBC
+ re_free (cset->coll_syms);
+ re_free (cset->equiv_classes);
+ re_free (cset->range_starts);
+ re_free (cset->range_ends);
+# endif
+ re_free (cset->char_classes);
+ re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Functions for binary tree operation. */
+
+/* Create a tree node. */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type)
+{
+ re_token_t t;
+ t.type = type;
+ return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+ const re_token_t *token)
+{
+ bin_tree_t *tree;
+ if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+ {
+ bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+ if (storage == NULL)
+ return NULL;
+ storage->next = dfa->str_tree_storage;
+ dfa->str_tree_storage = storage;
+ dfa->str_tree_storage_idx = 0;
+ }
+ tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+ tree->parent = NULL;
+ tree->left = left;
+ tree->right = right;
+ tree->token = *token;
+ tree->token.duplicated = 0;
+ tree->token.opt_subexp = 0;
+ tree->first = NULL;
+ tree->next = NULL;
+ tree->node_idx = REG_MISSING;
+
+ if (left != NULL)
+ left->parent = tree;
+ if (right != NULL)
+ right->parent = tree;
+ return tree;
+}
+
+/* Mark the tree SRC as an optional subexpression.
+ To be called from preorder or postorder. */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+ Idx idx = (Idx) (long) extra;
+ if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+ node->token.opt_subexp = 1;
+
+ return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+ if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+ free_charset (node->opr.mbcset);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+ re_free (node->opr.sbcset);
+}
+
+/* Worker function for tree walking. Free the allocated memory inside NODE
+ and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+ free_token (&node->token);
+ return REG_NOERROR;
+}
+
+
+/* Duplicate the node SRC, and return new node. This is a preorder
+ visit similar to the one implemented by the generic visitor, but
+ we need more infrastructure to maintain two parallel trees --- so,
+ it's easier to duplicate. */
+
+static bin_tree_t *
+duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
+{
+ const bin_tree_t *node;
+ bin_tree_t *dup_root;
+ bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
+
+ for (node = root; ; )
+ {
+ /* Create a new tree and link it back to the current parent. */
+ *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+ if (*p_new == NULL)
+ return NULL;
+ (*p_new)->parent = dup_node;
+ (*p_new)->token.duplicated = 1;
+ dup_node = *p_new;
+
+ /* Go to the left node, or up and to the right. */
+ if (node->left)
+ {
+ node = node->left;
+ p_new = &dup_node->left;
+ }
+ else
+ {
+ const bin_tree_t *prev = NULL;
+ while (node->right == prev || node->right == NULL)
+ {
+ prev = node;
+ node = node->parent;
+ dup_node = dup_node->parent;
+ if (!node)
+ return dup_root;
+ }
+ node = node->right;
+ p_new = &dup_node->right;
+ }
+ }
+}
diff --git a/usr/src/lib/libparted/common/lib/regex.c b/usr/src/lib/libparted/common/lib/regex.c
new file mode 100644
index 0000000000..720b2f5b1d
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regex.c
@@ -0,0 +1,71 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+/* Make sure noone compiles this code with a C++ compiler. */
+#if defined __cplusplus && defined _LIBC
+# error "This is C code, use a C compiler"
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean. */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+ __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+ __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+ __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+ __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+ __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
+#endif
+
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+ GNU regex allows. Include it before <regex.h>, which correctly
+ #undefs RE_DUP_MAX and sets it to the right value. */
+#include <limits.h>
+
+#include "regex.h"
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#include "regcomp.c"
+#include "regexec.c"
+
+/* Binary backward compatibility. */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/usr/src/lib/libparted/common/lib/regex.h b/usr/src/lib/libparted/common/lib/regex.h
new file mode 100644
index 0000000000..7a79ca3724
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regex.h
@@ -0,0 +1,675 @@
+/* Definitions for data structures and routines for the regular
+ expression library.
+ Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+#include <sys/types.h>
+
+/* Allow the use in C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Define __USE_GNU_REGEX to declare GNU extensions that violate the
+ POSIX name space rules. */
+#undef __USE_GNU_REGEX
+#if (defined _GNU_SOURCE \
+ || (!defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE \
+ && !defined _XOPEN_SOURCE))
+# define __USE_GNU_REGEX 1
+#endif
+
+#ifdef _REGEX_LARGE_OFFSETS
+
+/* Use types and values that are wide enough to represent signed and
+ unsigned byte offsets in memory. This currently works only when
+ the regex code is used outside of the GNU C library; it is not yet
+ supported within glibc itself, and glibc users should not define
+ _REGEX_LARGE_OFFSETS. */
+
+/* The type of the offset of a byte within a string.
+ For historical reasons POSIX 1003.1-2004 requires that regoff_t be
+ at least as wide as off_t. However, many common POSIX platforms set
+ regoff_t to the more-sensible ssize_t and the Open Group has
+ signalled its intention to change the requirement to be that
+ regoff_t be at least as wide as ptrdiff_t and ssize_t; see XBD ERN
+ 60 (2005-08-25). We don't know of any hosts where ssize_t or
+ ptrdiff_t is wider than ssize_t, so ssize_t is safe. */
+typedef ssize_t regoff_t;
+
+/* The type of nonnegative object indexes. Traditionally, GNU regex
+ uses 'int' for these. Code that uses __re_idx_t should work
+ regardless of whether the type is signed. */
+typedef size_t __re_idx_t;
+
+/* The type of object sizes. */
+typedef size_t __re_size_t;
+
+/* The type of object sizes, in places where the traditional code
+ uses unsigned long int. */
+typedef size_t __re_long_size_t;
+
+#else
+
+/* Use types that are binary-compatible with the traditional GNU regex
+ implementation, which mishandles strings longer than INT_MAX. */
+
+typedef int regoff_t;
+typedef int __re_idx_t;
+typedef unsigned int __re_size_t;
+typedef unsigned long int __re_long_size_t;
+
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+ wide enough to hold a value of a pointer. For most ANSI compilers
+ ptrdiff_t and size_t should be likely OK. Still size of these two
+ types is 2 for Microsoft C. Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned long int reg_syntax_t;
+
+#ifdef __USE_GNU_REGEX
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+# define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+ without further backtracking. */
+# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+ If not set, then the GNU regex operators are recognized. */
+# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+ If not set, and debugging was on, turn it off.
+ This only works if regex.c is compiled -DDEBUG.
+ We define this bit always, so that all that's needed to turn on
+ debugging is to recompile regex.c; the calling code can always have
+ this bit set, and it won't affect anything in the normal case. */
+# define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+ a string of ordinary characters. For example, the ERE 'a{1' is
+ treated as 'a\{1'. */
+# define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+ for ^, because it is difficult to scan the regex backwards to find
+ whether ^ should be special. */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+ immediately after an alternation or begin-group operator. */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+ re_compile_pattern. */
+# define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+
+#endif /* defined __USE_GNU_REGEX */
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+#ifdef __USE_GNU_REGEX
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+# define RE_SYNTAX_EMACS 0
+
+# define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+# define RE_SYNTAX_GNU_AWK \
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \
+ & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \
+ | RE_CONTEXT_INVALID_OPS ))
+
+# define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INTERVALS | RE_NO_GNU_OPS)
+
+# define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+# define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+# define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
+ | RE_INVALID_INTERVAL_ORD)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+# define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+# define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+# define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+# define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+# define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+ removed and RE_NO_BK_REFS is added. */
+# define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+#endif /* defined __USE_GNU_REGEX */
+
+#ifdef __USE_GNU_REGEX
+
+/* Maximum number of duplicates an interval can allow. POSIX-conforming
+ systems might define this in <limits.h>, but we want our
+ value, so remove any previous define. */
+# ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+# endif
+
+/* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored
+ the counter as a 2-byte signed integer. This is no longer true, so
+ RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to
+ ((SIZE_MAX - 2) / 10 - 1) if _REGEX_LARGE_OFFSETS is defined.
+ However, there would be a huge performance problem if someone
+ actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains
+ its historical value. */
+# define RE_DUP_MAX (0x7fff)
+
+#endif /* defined __USE_GNU_REGEX */
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (1 << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (1 << 2)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (1 << 3)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+/* Use PMATCH[0] to delimit the start and end of the search in the
+ buffer. */
+#define REG_STARTEND (1 << 2)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `__re_error_msgid' table in regcomp.c. */
+
+typedef enum
+{
+ _REG_ENOSYS = -1, /* This will never happen for this implementation. */
+ _REG_NOERROR = 0, /* Success. */
+ _REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ _REG_BADPAT, /* Invalid pattern. */
+ _REG_ECOLLATE, /* Invalid collating element. */
+ _REG_ECTYPE, /* Invalid character class name. */
+ _REG_EESCAPE, /* Trailing backslash. */
+ _REG_ESUBREG, /* Invalid back reference. */
+ _REG_EBRACK, /* Unmatched left bracket. */
+ _REG_EPAREN, /* Parenthesis imbalance. */
+ _REG_EBRACE, /* Unmatched \{. */
+ _REG_BADBR, /* Invalid contents of \{\}. */
+ _REG_ERANGE, /* Invalid range end. */
+ _REG_ESPACE, /* Ran out of memory. */
+ _REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ _REG_EEND, /* Premature end. */
+ _REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ _REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+#ifdef _XOPEN_SOURCE
+# define REG_ENOSYS _REG_ENOSYS
+#endif
+#define REG_NOERROR _REG_NOERROR
+#define REG_NOMATCH _REG_NOMATCH
+#define REG_BADPAT _REG_BADPAT
+#define REG_ECOLLATE _REG_ECOLLATE
+#define REG_ECTYPE _REG_ECTYPE
+#define REG_EESCAPE _REG_EESCAPE
+#define REG_ESUBREG _REG_ESUBREG
+#define REG_EBRACK _REG_EBRACK
+#define REG_EPAREN _REG_EPAREN
+#define REG_EBRACE _REG_EBRACE
+#define REG_BADBR _REG_BADBR
+#define REG_ERANGE _REG_ERANGE
+#define REG_ESPACE _REG_ESPACE
+#define REG_BADRPT _REG_BADRPT
+#define REG_EEND _REG_EEND
+#define REG_ESIZE _REG_ESIZE
+#define REG_ERPAREN _REG_ERPAREN
+
+/* struct re_pattern_buffer normally uses member names like `buffer'
+ that POSIX does not allow. In POSIX mode these members have names
+ with leading `re_' (e.g., `re_buffer'). */
+#ifdef __USE_GNU_REGEX
+# define _REG_RE_NAME(id) id
+# define _REG_RM_NAME(id) id
+#else
+# define _REG_RE_NAME(id) re_##id
+# define _REG_RM_NAME(id) rm_##id
+#endif
+
+/* The user can specify the type of the re_translate member by
+ defining the macro RE_TRANSLATE_TYPE, which defaults to unsigned
+ char *. This pollutes the POSIX name space, so in POSIX mode just
+ use unsigned char *. */
+#ifdef __USE_GNU_REGEX
+# ifndef RE_TRANSLATE_TYPE
+# define RE_TRANSLATE_TYPE unsigned char *
+# endif
+# define REG_TRANSLATE_TYPE RE_TRANSLATE_TYPE
+#else
+# define REG_TRANSLATE_TYPE unsigned char *
+#endif
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+struct re_pattern_buffer
+{
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are sometimes used as
+ array indexes. */
+ unsigned char *_REG_RE_NAME (buffer);
+
+ /* Number of bytes to which `buffer' points. */
+ __re_long_size_t _REG_RE_NAME (allocated);
+
+ /* Number of bytes actually used in `buffer'. */
+ __re_long_size_t _REG_RE_NAME (used);
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t _REG_RE_NAME (syntax);
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses the
+ fastmap, if there is one, to skip over impossible starting points
+ for matches. */
+ char *_REG_RE_NAME (fastmap);
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation is
+ applied to a pattern when it is compiled and to a string when it
+ is matched. */
+ REG_TRANSLATE_TYPE _REG_RE_NAME (translate);
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see whether or
+ not we should use the fastmap, so we don't set this absolutely
+ perfectly; see `re_compile_fastmap' (the `duplicate' case). */
+ unsigned int _REG_RE_NAME (can_be_null) : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#ifdef __USE_GNU_REGEX
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+ unsigned int _REG_RE_NAME (regs_allocated) : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned int _REG_RE_NAME (fastmap_accurate) : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned int _REG_RE_NAME (no_sub) : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the beginning
+ of the string. */
+ unsigned int _REG_RE_NAME (not_bol) : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned int _REG_RE_NAME (not_eol) : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned int _REG_RE_NAME (newline_anchor) : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ __re_size_t _REG_RM_NAME (num_regs);
+ regoff_t *_REG_RM_NAME (start);
+ regoff_t *_REG_RM_NAME (end);
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#if !defined RE_NREGS && defined __USE_GNU_REGEX
+# define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern (const char *__pattern, size_t __length,
+ struct re_pattern_buffer *__buffer);
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern regoff_t re_search (struct re_pattern_buffer *__buffer,
+ const char *__string, __re_idx_t __length,
+ __re_idx_t __start, regoff_t __range,
+ struct re_registers *__regs);
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, __re_idx_t __length1,
+ const char *__string2, __re_idx_t __length2,
+ __re_idx_t __start, regoff_t __range,
+ struct re_registers *__regs,
+ __re_idx_t __stop);
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern regoff_t re_match (struct re_pattern_buffer *__buffer,
+ const char *__string, __re_idx_t __length,
+ __re_idx_t __start, struct re_registers *__regs);
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, __re_idx_t __length1,
+ const char *__string2, __re_idx_t __length2,
+ __re_idx_t __start, struct re_registers *__regs,
+ __re_idx_t __stop);
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+ struct re_registers *__regs,
+ __re_size_t __num_regs,
+ regoff_t *__starts, regoff_t *__ends);
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+# ifndef _CRAY
+/* 4.2 bsd compatibility. */
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+ "restrict", and "configure" may have defined "restrict".
+ Other compilers use __restrict, __restrict__, and _Restrict, and
+ 'configure' might #define 'restrict' to those words, so pick a
+ different name. */
+#ifndef _Restrict_
+# if 199901L <= __STDC_VERSION__
+# define _Restrict_ restrict
+# elif 2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)
+# define _Restrict_ __restrict
+# else
+# define _Restrict_
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax. Don't trust
+ sys/cdefs.h's definition of __restrict_arr, though, as it
+ mishandles gcc -ansi -pedantic. */
+#ifndef _Restrict_arr_
+# if ((199901L <= __STDC_VERSION__ \
+ || ((3 < __GNUC__ || (3 == __GNUC__ && 1 <= __GNUC_MINOR__)) \
+ && !__STRICT_ANSI__)) \
+ && !defined __GNUG__)
+# define _Restrict_arr_ _Restrict_
+# else
+# define _Restrict_arr_
+# endif
+#endif
+
+/* POSIX compatibility. */
+extern int regcomp (regex_t *_Restrict_ __preg,
+ const char *_Restrict_ __pattern,
+ int __cflags);
+
+extern int regexec (const regex_t *_Restrict_ __preg,
+ const char *_Restrict_ __string, size_t __nmatch,
+ regmatch_t __pmatch[_Restrict_arr_],
+ int __eflags);
+
+extern size_t regerror (int __errcode, const regex_t *_Restrict_ __preg,
+ char *_Restrict_ __errbuf, size_t __errbuf_size);
+
+extern void regfree (regex_t *__preg);
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
diff --git a/usr/src/lib/libparted/common/lib/regex_internal.c b/usr/src/lib/libparted/common/lib/regex_internal.c
new file mode 100644
index 0000000000..21298889a3
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regex_internal.c
@@ -0,0 +1,1741 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software
+ Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+static void re_string_construct_common (const char *str, Idx len,
+ re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, bool icase,
+ const re_dfa_t *dfa) internal_function;
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
+ const re_node_set *nodes,
+ re_hashval_t hash) internal_function;
+static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context,
+ re_hashval_t hash) internal_function;
+
+/* Functions for string operation. */
+
+/* This function allocate the buffers. It is necessary to call
+ re_string_reconstruct before using the object. */
+
+static reg_errcode_t
+internal_function
+re_string_allocate (re_string_t *pstr, const char *str, Idx len, Idx init_len,
+ RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa)
+{
+ reg_errcode_t ret;
+ Idx init_buf_len;
+
+ /* Ensure at least one character fits into the buffers. */
+ if (init_len < dfa->mb_cur_max)
+ init_len = dfa->mb_cur_max;
+ init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ ret = re_string_realloc_buffers (pstr, init_buf_len);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ pstr->word_char = dfa->word_char;
+ pstr->word_ops_used = dfa->word_ops_used;
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+ pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+ pstr->valid_raw_len = pstr->valid_len;
+ return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them. */
+
+static reg_errcode_t
+internal_function
+re_string_construct (re_string_t *pstr, const char *str, Idx len,
+ RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa)
+{
+ reg_errcode_t ret;
+ memset (pstr, '\0', sizeof (re_string_t));
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ if (len > 0)
+ {
+ ret = re_string_realloc_buffers (pstr, len + 1);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+ if (icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ if (pstr->valid_raw_len >= len)
+ break;
+ if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+ break;
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (trans != NULL)
+ re_string_translate_buffer (pstr);
+ else
+ {
+ pstr->valid_len = pstr->bufs_len;
+ pstr->valid_raw_len = pstr->bufs_len;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct. */
+
+static reg_errcode_t
+internal_function
+re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len)
+{
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ wint_t *new_wcs;
+
+ /* Avoid overflow. */
+ size_t max_object_size = MAX (sizeof (wint_t), sizeof (Idx));
+ if (BE (SIZE_MAX / max_object_size < new_buf_len, 0))
+ return REG_ESPACE;
+
+ new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+ if (BE (new_wcs == NULL, 0))
+ return REG_ESPACE;
+ pstr->wcs = new_wcs;
+ if (pstr->offsets != NULL)
+ {
+ Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len);
+ if (BE (new_offsets == NULL, 0))
+ return REG_ESPACE;
+ pstr->offsets = new_offsets;
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ {
+ unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+ new_buf_len);
+ if (BE (new_mbs == NULL, 0))
+ return REG_ESPACE;
+ pstr->mbs = new_mbs;
+ }
+ pstr->bufs_len = new_buf_len;
+ return REG_NOERROR;
+}
+
+
+static void
+internal_function
+re_string_construct_common (const char *str, Idx len, re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, bool icase,
+ const re_dfa_t *dfa)
+{
+ pstr->raw_mbs = (const unsigned char *) str;
+ pstr->len = len;
+ pstr->raw_len = len;
+ pstr->trans = trans;
+ pstr->icase = icase;
+ pstr->mbs_allocated = (trans != NULL || icase);
+ pstr->mb_cur_max = dfa->mb_cur_max;
+ pstr->is_utf8 = dfa->is_utf8;
+ pstr->map_notascii = dfa->map_notascii;
+ pstr->stop = pstr->len;
+ pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+ If the byte sequence of the string are:
+ <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+ Then wide character buffer will be:
+ <wc1> , WEOF , <wc2> , WEOF , <wc3>
+ We use WEOF for padding, they indicate that the position isn't
+ a first byte of a multibyte character.
+
+ Note that this function assumes PSTR->VALID_LEN elements are already
+ built and starts from PSTR->VALID_LEN. */
+
+static void
+internal_function
+build_wcs_buffer (re_string_t *pstr)
+{
+#ifdef _LIBC
+ unsigned char buf[MB_LEN_MAX];
+ assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+ unsigned char buf[64];
+#endif
+ mbstate_t prev_st;
+ Idx byte_idx, end_idx, remain_len;
+ size_t mbclen;
+
+ /* Build the buffers from pstr->valid_len to either pstr->len or
+ pstr->bufs_len. */
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+ for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ /* Apply the translation if we need. */
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+ buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+ mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2, 0))
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ if (BE (pstr->trans != NULL, 0))
+ wc = pstr->trans[wc];
+ pstr->cur_state = prev_st;
+ }
+
+ /* Write wide character and padding. */
+ pstr->wcs[byte_idx++] = wc;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+ but for REG_ICASE. */
+
+static reg_errcode_t
+internal_function
+build_wcs_upper_buffer (re_string_t *pstr)
+{
+ mbstate_t prev_st;
+ Idx src_idx, byte_idx, end_idx, remain_len;
+ size_t mbclen;
+#ifdef _LIBC
+ char buf[MB_LEN_MAX];
+ assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+ char buf[64];
+#endif
+
+ byte_idx = pstr->valid_len;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ /* The following optimization assumes that ASCII characters can be
+ mapped to wide characters with a simple cast. */
+ if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+ {
+ while (byte_idx < end_idx)
+ {
+ wchar_t wc;
+
+ if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+ && mbsinit (&pstr->cur_state))
+ {
+ /* In case of a singlebyte character. */
+ pstr->mbs[byte_idx]
+ = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+ /* The next step uses the assumption that wchar_t is encoded
+ ASCII-safe: all ASCII values can be converted like this. */
+ pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+ ++byte_idx;
+ continue;
+ }
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc,
+ ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ + byte_idx), remain_len, &pstr->cur_state);
+ if (BE (mbclen < (size_t) -2, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ size_t mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb (buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else
+ {
+ src_idx = byte_idx;
+ goto offsets_needed;
+ }
+ }
+ else
+ memcpy (pstr->mbs + byte_idx,
+ pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ pstr->mbs[byte_idx] = ch;
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+ return REG_NOERROR;
+ }
+ else
+ for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+ offsets_needed:
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+ buf[i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+ mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen < (size_t) -2, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ size_t mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else if (mbcdlen != (size_t) -1)
+ {
+ size_t i;
+
+ if (byte_idx + mbcdlen > pstr->bufs_len)
+ {
+ pstr->cur_state = prev_st;
+ break;
+ }
+
+ if (pstr->offsets == NULL)
+ {
+ pstr->offsets = re_malloc (Idx, pstr->bufs_len);
+
+ if (pstr->offsets == NULL)
+ return REG_ESPACE;
+ }
+ if (!pstr->offsets_needed)
+ {
+ for (i = 0; i < (size_t) byte_idx; ++i)
+ pstr->offsets[i] = i;
+ pstr->offsets_needed = 1;
+ }
+
+ memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+ pstr->wcs[byte_idx] = wcu;
+ pstr->offsets[byte_idx] = src_idx;
+ for (i = 1; i < mbcdlen; ++i)
+ {
+ pstr->offsets[byte_idx + i]
+ = src_idx + (i < mbclen ? i : mbclen - 1);
+ pstr->wcs[byte_idx + i] = WEOF;
+ }
+ pstr->len += mbcdlen - mbclen;
+ if (pstr->raw_stop > src_idx)
+ pstr->stop += mbcdlen - mbclen;
+ end_idx = (pstr->bufs_len > pstr->len)
+ ? pstr->len : pstr->bufs_len;
+ byte_idx += mbcdlen;
+ src_idx += mbclen;
+ continue;
+ }
+ else
+ memcpy (pstr->mbs + byte_idx, p, mbclen);
+ }
+ else
+ memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ {
+ size_t i;
+ for (i = 0; i < mbclen; ++i)
+ pstr->offsets[byte_idx + i] = src_idx + i;
+ }
+ src_idx += mbclen;
+
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans [ch];
+ pstr->mbs[byte_idx] = ch;
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ pstr->offsets[byte_idx] = src_idx;
+ ++src_idx;
+
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = src_idx;
+ return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+ Return the index. */
+
+static Idx
+internal_function
+re_string_skip_chars (re_string_t *pstr, Idx new_raw_idx, wint_t *last_wc)
+{
+ mbstate_t prev_st;
+ Idx rawbuf_idx;
+ size_t mbclen;
+ wint_t wc = WEOF;
+
+ /* Skip the characters which are not necessary to check. */
+ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+ rawbuf_idx < new_raw_idx;)
+ {
+ wchar_t wc2;
+ Idx remain_len;
+ remain_len = pstr->len - rawbuf_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx,
+ remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a single byte character. */
+ if (mbclen == 0 || remain_len == 0)
+ wc = L'\0';
+ else
+ wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
+ mbclen = 1;
+ pstr->cur_state = prev_st;
+ }
+ else
+ wc = wc2;
+ /* Then proceed the next character. */
+ rawbuf_idx += mbclen;
+ }
+ *last_wc = wc;
+ return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+ This function is used in case of REG_ICASE. */
+
+static void
+internal_function
+build_upper_buffer (re_string_t *pstr)
+{
+ Idx char_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans[ch];
+ if (islower (ch))
+ pstr->mbs[char_idx] = toupper (ch);
+ else
+ pstr->mbs[char_idx] = ch;
+ }
+ pstr->valid_len = char_idx;
+ pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR. */
+
+static void
+internal_function
+re_string_translate_buffer (re_string_t *pstr)
+{
+ Idx buf_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+ pstr->mbs[buf_idx] = pstr->trans[ch];
+ }
+
+ pstr->valid_len = buf_idx;
+ pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+ Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+ convert to upper case in case of REG_ICASE, apply translation. */
+
+static reg_errcode_t
+internal_function
+re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags)
+{
+ Idx offset;
+
+ if (BE (pstr->raw_mbs_idx <= idx, 0))
+ offset = idx - pstr->raw_mbs_idx;
+ else
+ {
+ /* Reset buffer. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+ pstr->len = pstr->raw_len;
+ pstr->stop = pstr->raw_stop;
+ pstr->valid_len = 0;
+ pstr->raw_mbs_idx = 0;
+ pstr->valid_raw_len = 0;
+ pstr->offsets_needed = 0;
+ pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+ if (!pstr->mbs_allocated)
+ pstr->mbs = (unsigned char *) pstr->raw_mbs;
+ offset = idx;
+ }
+
+ if (BE (offset != 0, 1))
+ {
+ /* Should the already checked characters be kept? */
+ if (BE (offset < pstr->valid_raw_len, 1))
+ {
+ /* Yes, move them to the front of the buffer. */
+#ifdef RE_ENABLE_I18N
+ if (BE (pstr->offsets_needed, 0))
+ {
+ Idx low = 0, high = pstr->valid_len, mid;
+ do
+ {
+ mid = (high + low) / 2;
+ if (pstr->offsets[mid] > offset)
+ high = mid;
+ else if (pstr->offsets[mid] < offset)
+ low = mid + 1;
+ else
+ break;
+ }
+ while (low < high);
+ if (pstr->offsets[mid] < offset)
+ ++mid;
+ pstr->tip_context = re_string_context_at (pstr, mid - 1,
+ eflags);
+ /* This can be quite complicated, so handle specially
+ only the common and easy case where the character with
+ different length representation of lower and upper
+ case is present at or after offset. */
+ if (pstr->valid_len > offset
+ && mid == offset && pstr->offsets[mid] == offset)
+ {
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+ memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+ pstr->valid_raw_len -= offset;
+ for (low = 0; low < pstr->valid_len; low++)
+ pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+ }
+ else
+ {
+ /* Otherwise, just find out how long the partial multibyte
+ character at offset is and fill it with WEOF/255. */
+ pstr->len = pstr->raw_len - idx + offset;
+ pstr->stop = pstr->raw_stop - idx + offset;
+ pstr->offsets_needed = 0;
+ while (mid > 0 && pstr->offsets[mid - 1] == offset)
+ --mid;
+ while (mid < pstr->valid_len)
+ if (pstr->wcs[mid] != WEOF)
+ break;
+ else
+ ++mid;
+ if (mid == pstr->valid_len)
+ pstr->valid_len = 0;
+ else
+ {
+ pstr->valid_len = pstr->offsets[mid] - offset;
+ if (pstr->valid_len)
+ {
+ for (low = 0; low < pstr->valid_len; ++low)
+ pstr->wcs[low] = WEOF;
+ memset (pstr->mbs, 255, pstr->valid_len);
+ }
+ }
+ pstr->valid_raw_len = pstr->valid_len;
+ }
+ }
+ else
+#endif
+ {
+ pstr->tip_context = re_string_context_at (pstr, offset - 1,
+ eflags);
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ memmove (pstr->mbs, pstr->mbs + offset,
+ pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+ pstr->valid_raw_len -= offset;
+#if DEBUG
+ assert (pstr->valid_len > 0);
+#endif
+ }
+ }
+ else
+ {
+ /* No, skip all characters until IDX. */
+ Idx prev_valid_len = pstr->valid_len;
+
+#ifdef RE_ENABLE_I18N
+ if (BE (pstr->offsets_needed, 0))
+ {
+ pstr->len = pstr->raw_len - idx + offset;
+ pstr->stop = pstr->raw_stop - idx + offset;
+ pstr->offsets_needed = 0;
+ }
+#endif
+ pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ Idx wcs_idx;
+ wint_t wc = WEOF;
+
+ if (pstr->is_utf8)
+ {
+ const unsigned char *raw, *p, *end;
+
+ /* Special case UTF-8. Multi-byte chars start with any
+ byte other than 0x80 - 0xbf. */
+ raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+ end = raw + (offset - pstr->mb_cur_max);
+ if (end < pstr->raw_mbs)
+ end = pstr->raw_mbs;
+ p = raw + offset - 1;
+#ifdef _LIBC
+ /* We know the wchar_t encoding is UCS4, so for the simple
+ case, ASCII characters, skip the conversion step. */
+ if (isascii (*p) && BE (pstr->trans == NULL, 1))
+ {
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+ /* pstr->valid_len = 0; */
+ wc = (wchar_t) *p;
+ }
+ else
+#endif
+ for (; p >= end; --p)
+ if ((*p & 0xc0) != 0x80)
+ {
+ mbstate_t cur_state;
+ wchar_t wc2;
+ Idx mlen = raw + pstr->len - p;
+ unsigned char buf[6];
+ size_t mbclen;
+
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i = mlen < 6 ? mlen : 6;
+ while (--i >= 0)
+ buf[i] = pstr->trans[p[i]];
+ }
+ /* XXX Don't use mbrtowc, we know which conversion
+ to use (UTF-8 -> UCS4). */
+ memset (&cur_state, 0, sizeof (cur_state));
+ mbclen = mbrtowc (&wc2, (const char *) p, mlen,
+ &cur_state);
+ if (raw + offset - p <= mbclen
+ && mbclen < (size_t) -2)
+ {
+ memset (&pstr->cur_state, '\0',
+ sizeof (mbstate_t));
+ pstr->valid_len = mbclen - (raw + offset - p);
+ wc = wc2;
+ }
+ break;
+ }
+ }
+
+ if (wc == WEOF)
+ pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+ if (wc == WEOF)
+ pstr->tip_context
+ = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+ else
+ pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+ && IS_WIDE_WORD_CHAR (wc))
+ ? CONTEXT_WORD
+ : ((IS_WIDE_NEWLINE (wc)
+ && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ if (BE (pstr->valid_len, 0))
+ {
+ for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+ pstr->wcs[wcs_idx] = WEOF;
+ if (pstr->mbs_allocated)
+ memset (pstr->mbs, 255, pstr->valid_len);
+ }
+ pstr->valid_raw_len = pstr->valid_len;
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+ pstr->valid_raw_len = 0;
+ if (pstr->trans)
+ c = pstr->trans[c];
+ pstr->tip_context = (bitset_contain (pstr->word_char, c)
+ ? CONTEXT_WORD
+ : ((IS_NEWLINE (c) && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ }
+ }
+ if (!BE (pstr->mbs_allocated, 0))
+ pstr->mbs += offset;
+ }
+ pstr->raw_mbs_idx = idx;
+ pstr->len -= offset;
+ pstr->stop -= offset;
+
+ /* Then build the buffers. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ if (pstr->icase)
+ {
+ reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+ build_wcs_buffer (pstr);
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ {
+ if (pstr->icase)
+ build_upper_buffer (pstr);
+ else if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ else
+ pstr->valid_len = pstr->len;
+
+ pstr->cur_idx = 0;
+ return REG_NOERROR;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, Idx idx)
+{
+ int ch;
+ Idx off;
+
+ /* Handle the common (easiest) cases first. */
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1
+ && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ off = pstr->offsets[off];
+#endif
+
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+ this function returns CAPITAL LETTER I instead of first byte of
+ DOTLESS SMALL LETTER I. The latter would confuse the parser,
+ since peek_byte_case doesn't advance cur_idx in any way. */
+ if (pstr->offsets_needed && !isascii (ch))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ return ch;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ {
+ Idx off;
+ int ch;
+
+ /* For tr_TR.UTF-8 [[:islower:]] there is
+ [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip
+ in that case the whole multi-byte character and return
+ the original letter. On the other side, with
+ [[: DOTLESS SMALL LETTER I return [[:I, as doing
+ anything else would complicate things too much. */
+
+ if (!re_string_first_byte (pstr, pstr->cur_idx))
+ return re_string_fetch_byte (pstr);
+
+ off = pstr->offsets[pstr->cur_idx];
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+ if (! isascii (ch))
+ return re_string_fetch_byte (pstr);
+
+ re_string_skip_bytes (pstr,
+ re_string_char_size_at (pstr, pstr->cur_idx));
+ return ch;
+ }
+#endif
+
+ return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+internal_function
+re_string_destruct (re_string_t *pstr)
+{
+#ifdef RE_ENABLE_I18N
+ re_free (pstr->wcs);
+ re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT. */
+
+static unsigned int
+internal_function
+re_string_context_at (const re_string_t *input, Idx idx, int eflags)
+{
+ int c;
+ if (BE (! REG_VALID_INDEX (idx), 0))
+ /* In this case, we use the value stored in input->tip_context,
+ since we can't know the character in input->mbs[-1] here. */
+ return input->tip_context;
+ if (BE (idx == input->len, 0))
+ return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+ : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc;
+ Idx wc_idx = idx;
+ while(input->wcs[wc_idx] == WEOF)
+ {
+#ifdef DEBUG
+ /* It must not happen. */
+ assert (REG_VALID_INDEX (wc_idx));
+#endif
+ --wc_idx;
+ if (! REG_VALID_INDEX (wc_idx))
+ return input->tip_context;
+ }
+ wc = input->wcs[wc_idx];
+ if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+ return CONTEXT_WORD;
+ return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+ ? CONTEXT_NEWLINE : 0);
+ }
+ else
+#endif
+ {
+ c = re_string_byte_at (input, idx);
+ if (bitset_contain (input->word_char, c))
+ return CONTEXT_WORD;
+ return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+ }
+}
+
+/* Functions for set operation. */
+
+static reg_errcode_t
+internal_function
+re_node_set_alloc (re_node_set *set, Idx size)
+{
+ set->alloc = size;
+ set->nelem = 0;
+ set->elems = re_malloc (Idx, size);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_1 (re_node_set *set, Idx elem)
+{
+ set->alloc = 1;
+ set->nelem = 1;
+ set->elems = re_malloc (Idx, 1);
+ if (BE (set->elems == NULL, 0))
+ {
+ set->alloc = set->nelem = 0;
+ return REG_ESPACE;
+ }
+ set->elems[0] = elem;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_2 (re_node_set *set, Idx elem1, Idx elem2)
+{
+ set->alloc = 2;
+ set->elems = re_malloc (Idx, 2);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ if (elem1 == elem2)
+ {
+ set->nelem = 1;
+ set->elems[0] = elem1;
+ }
+ else
+ {
+ set->nelem = 2;
+ if (elem1 < elem2)
+ {
+ set->elems[0] = elem1;
+ set->elems[1] = elem2;
+ }
+ else
+ {
+ set->elems[0] = elem2;
+ set->elems[1] = elem1;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
+{
+ dest->nelem = src->nelem;
+ if (src->nelem > 0)
+ {
+ dest->alloc = dest->nelem;
+ dest->elems = re_malloc (Idx, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ {
+ dest->alloc = dest->nelem = 0;
+ return REG_ESPACE;
+ }
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx));
+ }
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+ Note: We assume dest->elems is NULL, when dest->alloc is 0. */
+
+static reg_errcode_t
+internal_function
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+ const re_node_set *src2)
+{
+ Idx i1, i2, is, id, delta, sbase;
+ if (src1->nelem == 0 || src2->nelem == 0)
+ return REG_NOERROR;
+
+ /* We need dest->nelem + 2 * elems_in_intersection; this is a
+ conservative estimate. */
+ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+ {
+ Idx new_alloc = src1->nelem + src2->nelem + dest->alloc;
+ Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc);
+ if (BE (new_elems == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_elems;
+ dest->alloc = new_alloc;
+ }
+
+ /* Find the items in the intersection of SRC1 and SRC2, and copy
+ into the top of DEST those that are not already in DEST itself. */
+ sbase = dest->nelem + src1->nelem + src2->nelem;
+ i1 = src1->nelem - 1;
+ i2 = src2->nelem - 1;
+ id = dest->nelem - 1;
+ for (;;)
+ {
+ if (src1->elems[i1] == src2->elems[i2])
+ {
+ /* Try to find the item in DEST. Maybe we could binary search? */
+ while (REG_VALID_INDEX (id) && dest->elems[id] > src1->elems[i1])
+ --id;
+
+ if (! REG_VALID_INDEX (id) || dest->elems[id] != src1->elems[i1])
+ dest->elems[--sbase] = src1->elems[i1];
+
+ if (! REG_VALID_INDEX (--i1) || ! REG_VALID_INDEX (--i2))
+ break;
+ }
+
+ /* Lower the highest of the two items. */
+ else if (src1->elems[i1] < src2->elems[i2])
+ {
+ if (! REG_VALID_INDEX (--i2))
+ break;
+ }
+ else
+ {
+ if (! REG_VALID_INDEX (--i1))
+ break;
+ }
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + src1->nelem + src2->nelem - 1;
+ delta = is - sbase + 1;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place; this is more or
+ less the same loop that is in re_node_set_merge. */
+ dest->nelem += delta;
+ if (delta > 0 && REG_VALID_INDEX (id))
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (! REG_VALID_INDEX (--id))
+ break;
+ }
+ }
+
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx));
+
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+internal_function
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+ const re_node_set *src2)
+{
+ Idx i1, i2, id;
+ if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+ {
+ dest->alloc = src1->nelem + src2->nelem;
+ dest->elems = re_malloc (Idx, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ {
+ if (src1 != NULL && src1->nelem > 0)
+ return re_node_set_init_copy (dest, src1);
+ else if (src2 != NULL && src2->nelem > 0)
+ return re_node_set_init_copy (dest, src2);
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+ }
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ dest->elems[id++] = src2->elems[i2++];
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ ++i2;
+ dest->elems[id++] = src1->elems[i1++];
+ }
+ if (i1 < src1->nelem)
+ {
+ memcpy (dest->elems + id, src1->elems + i1,
+ (src1->nelem - i1) * sizeof (Idx));
+ id += src1->nelem - i1;
+ }
+ else if (i2 < src2->nelem)
+ {
+ memcpy (dest->elems + id, src2->elems + i2,
+ (src2->nelem - i2) * sizeof (Idx));
+ id += src2->nelem - i2;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+internal_function
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
+{
+ Idx is, id, sbase, delta;
+ if (src == NULL || src->nelem == 0)
+ return REG_NOERROR;
+ if (dest->alloc < 2 * src->nelem + dest->nelem)
+ {
+ Idx new_alloc = 2 * (src->nelem + dest->alloc);
+ Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc);
+ if (BE (new_buffer == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_buffer;
+ dest->alloc = new_alloc;
+ }
+
+ if (BE (dest->nelem == 0, 0))
+ {
+ dest->nelem = src->nelem;
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx));
+ return REG_NOERROR;
+ }
+
+ /* Copy into the top of DEST the items of SRC that are not
+ found in DEST. Maybe we could binary search in DEST? */
+ for (sbase = dest->nelem + 2 * src->nelem,
+ is = src->nelem - 1, id = dest->nelem - 1;
+ REG_VALID_INDEX (is) && REG_VALID_INDEX (id); )
+ {
+ if (dest->elems[id] == src->elems[is])
+ is--, id--;
+ else if (dest->elems[id] < src->elems[is])
+ dest->elems[--sbase] = src->elems[is--];
+ else /* if (dest->elems[id] > src->elems[is]) */
+ --id;
+ }
+
+ if (REG_VALID_INDEX (is))
+ {
+ /* If DEST is exhausted, the remaining items of SRC must be unique. */
+ sbase -= is + 1;
+ memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (Idx));
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + 2 * src->nelem - 1;
+ delta = is - sbase + 1;
+ if (delta == 0)
+ return REG_NOERROR;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place. */
+ dest->nelem += delta;
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (! REG_VALID_INDEX (--id))
+ {
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase,
+ delta * sizeof (Idx));
+ break;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have ELEM.
+ Return true if successful. */
+
+static bool
+internal_function
+re_node_set_insert (re_node_set *set, Idx elem)
+{
+ Idx idx;
+ /* In case the set is empty. */
+ if (set->alloc == 0)
+ return BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1);
+
+ if (BE (set->nelem, 0) == 0)
+ {
+ /* We already guaranteed above that set->alloc != 0. */
+ set->elems[0] = elem;
+ ++set->nelem;
+ return true;
+ }
+
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ Idx *new_elems;
+ set->alloc = set->alloc * 2;
+ new_elems = re_realloc (set->elems, Idx, set->alloc);
+ if (BE (new_elems == NULL, 0))
+ return false;
+ set->elems = new_elems;
+ }
+
+ /* Move the elements which follows the new element. Test the
+ first element separately to skip a check in the inner loop. */
+ if (elem < set->elems[0])
+ {
+ idx = 0;
+ for (idx = set->nelem; idx > 0; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+ else
+ {
+ for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+
+ /* Insert the new element. */
+ set->elems[idx] = elem;
+ ++set->nelem;
+ return true;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have any element greater than or equal to ELEM.
+ Return true if successful. */
+
+static bool
+internal_function
+re_node_set_insert_last (re_node_set *set, Idx elem)
+{
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ Idx *new_elems;
+ set->alloc = (set->alloc + 1) * 2;
+ new_elems = re_realloc (set->elems, Idx, set->alloc);
+ if (BE (new_elems == NULL, 0))
+ return false;
+ set->elems = new_elems;
+ }
+
+ /* Insert the new element. */
+ set->elems[set->nelem++] = elem;
+ return true;
+}
+
+/* Compare two node sets SET1 and SET2.
+ Return true if SET1 and SET2 are equivalent. */
+
+static bool
+internal_function __attribute ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
+{
+ Idx i;
+ if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+ return false;
+ for (i = set1->nelem ; REG_VALID_INDEX (--i) ; )
+ if (set1->elems[i] != set2->elems[i])
+ return false;
+ return true;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
+
+static Idx
+internal_function __attribute ((pure))
+re_node_set_contains (const re_node_set *set, Idx elem)
+{
+ __re_size_t idx, right, mid;
+ if (! REG_VALID_NONZERO_INDEX (set->nelem))
+ return 0;
+
+ /* Binary search the element. */
+ idx = 0;
+ right = set->nelem - 1;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+ return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+internal_function
+re_node_set_remove_at (re_node_set *set, Idx idx)
+{
+ if (idx < 0 || idx >= set->nelem)
+ return;
+ --set->nelem;
+ for (; idx < set->nelem; idx++)
+ set->elems[idx] = set->elems[idx + 1];
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+ Or return REG_MISSING if an error occurred. */
+
+static Idx
+internal_function
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
+{
+ if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+ {
+ size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+ Idx *new_nexts, *new_indices;
+ re_node_set *new_edests, *new_eclosures;
+ re_token_t *new_nodes;
+ size_t max_object_size =
+ MAX (sizeof (re_token_t),
+ MAX (sizeof (re_node_set),
+ sizeof (Idx)));
+
+ /* Avoid overflows. */
+ if (BE (SIZE_MAX / 2 / max_object_size < dfa->nodes_alloc, 0))
+ return REG_MISSING;
+
+ new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+ if (BE (new_nodes == NULL, 0))
+ return REG_MISSING;
+ dfa->nodes = new_nodes;
+ new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc);
+ new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc);
+ new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+ new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+ if (BE (new_nexts == NULL || new_indices == NULL
+ || new_edests == NULL || new_eclosures == NULL, 0))
+ return REG_MISSING;
+ dfa->nexts = new_nexts;
+ dfa->org_indices = new_indices;
+ dfa->edests = new_edests;
+ dfa->eclosures = new_eclosures;
+ dfa->nodes_alloc = new_nodes_alloc;
+ }
+ dfa->nodes[dfa->nodes_len] = token;
+ dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+ {
+ int type = token.type;
+ dfa->nodes[dfa->nodes_len].accept_mb =
+ (type == OP_PERIOD && dfa->mb_cur_max > 1) || type == COMPLEX_BRACKET;
+ }
+#endif
+ dfa->nexts[dfa->nodes_len] = REG_MISSING;
+ re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+ re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
+ return dfa->nodes_len++;
+}
+
+static inline re_hashval_t
+internal_function
+calc_state_hash (const re_node_set *nodes, unsigned int context)
+{
+ re_hashval_t hash = nodes->nelem + context;
+ Idx i;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ hash += nodes->elems[i];
+ return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+ const re_node_set *nodes)
+{
+ re_hashval_t hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ Idx i;
+#ifdef lint
+ /* Suppress bogus uninitialized-variable warnings. */
+ *err = REG_NOERROR;
+#endif
+ if (BE (nodes->nelem == 0, 0))
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, 0);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (&state->nodes, nodes))
+ return state;
+ }
+
+ /* There are no appropriate state in the dfa, create the new one. */
+ new_state = create_ci_newstate (dfa, nodes, hash);
+ if (BE (new_state == NULL, 0))
+ *err = REG_ESPACE;
+
+ return new_state;
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+ whose context is equivalent to CONTEXT.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+ const re_node_set *nodes, unsigned int context)
+{
+ re_hashval_t hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ Idx i;
+#ifdef lint
+ /* Suppress bogus uninitialized-variable warnings. */
+ *err = REG_NOERROR;
+#endif
+ if (nodes->nelem == 0)
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, context);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (state->hash == hash
+ && state->context == context
+ && re_node_set_compare (state->entrance_nodes, nodes))
+ return state;
+ }
+ /* There are no appropriate state in `dfa', create the new one. */
+ new_state = create_cd_newstate (dfa, nodes, context, hash);
+ if (BE (new_state == NULL, 0))
+ *err = REG_ESPACE;
+
+ return new_state;
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+ HASH put in the appropriate bucket of DFA's state table. Return value
+ indicates the error code if failed. */
+
+static reg_errcode_t
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+ re_hashval_t hash)
+{
+ struct re_state_table_entry *spot;
+ reg_errcode_t err;
+ Idx i;
+
+ newstate->hash = hash;
+ err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < newstate->nodes.nelem; i++)
+ {
+ Idx elem = newstate->nodes.elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+ if (BE (! re_node_set_insert_last (&newstate->non_eps_nodes, elem), 0))
+ return REG_ESPACE;
+ }
+
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+ if (BE (spot->alloc <= spot->num, 0))
+ {
+ Idx new_alloc = 2 * spot->num + 2;
+ re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+ new_alloc);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ spot->array = new_array;
+ spot->alloc = new_alloc;
+ }
+ spot->array[spot->num++] = newstate;
+ return REG_NOERROR;
+}
+
+static void
+free_state (re_dfastate_t *state)
+{
+ re_node_set_free (&state->non_eps_nodes);
+ re_node_set_free (&state->inveclosure);
+ if (state->entrance_nodes != &state->nodes)
+ {
+ re_node_set_free (state->entrance_nodes);
+ re_free (state->entrance_nodes);
+ }
+ re_node_set_free (&state->nodes);
+ re_free (state->word_trtable);
+ re_free (state->trtable);
+ re_free (state);
+}
+
+/* Create the new state which is independ of contexts.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+internal_function
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+ re_hashval_t hash)
+{
+ Idx i;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->entrance_nodes = &newstate->nodes;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER && !node->constraint)
+ continue;
+#ifdef RE_ENABLE_I18N
+ newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+ /* If the state has the halt node, the state is a halt state. */
+ if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR || node->constraint)
+ newstate->has_constraint = 1;
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+internal_function
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+ unsigned int context, re_hashval_t hash)
+{
+ Idx i, nctx_nodes = 0;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->context = context;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (node->constraint)
+ constraint = node->constraint;
+
+ if (type == CHARACTER && !constraint)
+ continue;
+#ifdef RE_ENABLE_I18N
+ newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+ /* If the state has the halt node, the state is a halt state. */
+ if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR)
+ constraint = node->opr.ctx_type;
+
+ if (constraint)
+ {
+ if (newstate->entrance_nodes == &newstate->nodes)
+ {
+ newstate->entrance_nodes = re_malloc (re_node_set, 1);
+ if (BE (newstate->entrance_nodes == NULL, 0))
+ {
+ free_state (newstate);
+ return NULL;
+ }
+ re_node_set_init_copy (newstate->entrance_nodes, nodes);
+ nctx_nodes = 0;
+ newstate->has_constraint = 1;
+ }
+
+ if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+ {
+ re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+ ++nctx_nodes;
+ }
+ }
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
diff --git a/usr/src/lib/libparted/common/lib/regex_internal.h b/usr/src/lib/libparted/common/lib/regex_internal.h
new file mode 100644
index 0000000000..5c07f9366f
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regex_internal.h
@@ -0,0 +1,857 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# include <langinfo.h>
+#else
+# include "localcharset.h"
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+
+#include <wchar.h>
+#include <wctype.h>
+#include <stdint.h>
+#if defined _LIBC
+# include <bits/libc-lock.h>
+#else
+# define __libc_lock_init(NAME) do { } while (0)
+# define __libc_lock_lock(NAME) do { } while (0)
+# define __libc_lock_unlock(NAME) do { } while (0)
+#endif
+
+/* In case that the system doesn't have isblank(). */
+#if !defined _LIBC && ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK))
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) \
+ INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+/* For loser systems without the definition. */
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_ISWCTYPE && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# ifdef _LIBC
+# define inline
+# endif
+#endif
+
+/* Number of ASCII characters. */
+#define ASCII_CHARS 0x80
+
+/* Number of single byte characters. */
+#define SBC_MAX (UCHAR_MAX + 1)
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline. */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc. */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+typedef __re_idx_t Idx;
+
+/* Special return value for failure to match. */
+#define REG_MISSING ((Idx) -1)
+
+/* Special return value for internal error. */
+#define REG_ERROR ((Idx) -2)
+
+/* Test whether N is a valid index, and is not one of the above. */
+#ifdef _REGEX_LARGE_OFFSETS
+# define REG_VALID_INDEX(n) ((Idx) (n) < REG_ERROR)
+#else
+# define REG_VALID_INDEX(n) (0 <= (n))
+#endif
+
+/* Test whether N is a valid nonzero index. */
+#ifdef _REGEX_LARGE_OFFSETS
+# define REG_VALID_NONZERO_INDEX(n) ((Idx) ((n) - 1) < (Idx) (REG_ERROR - 1))
+#else
+# define REG_VALID_NONZERO_INDEX(n) (0 < (n))
+#endif
+
+/* A hash value, suitable for computing hash tables. */
+typedef __re_size_t re_hashval_t;
+
+/* An integer used to represent a set of bits. It must be unsigned,
+ and must be at least as wide as unsigned int. */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t. */
+#define BITSET_WORD_MAX ULONG_MAX
+
+/* Number of bits in a bitset_word_t. For portability to hosts with
+ padding bits, do not use '(sizeof (bitset_word_t) * CHAR_BIT)';
+ instead, deduce it directly from BITSET_WORD_MAX. Avoid
+ greater-than-32-bit integers and unconditional shifts by more than
+ 31 bits, as they're not portable. */
+#if BITSET_WORD_MAX == 0xffffffff
+# define BITSET_WORD_BITS 32
+#elif BITSET_WORD_MAX >> 31 >> 5 == 1
+# define BITSET_WORD_BITS 36
+#elif BITSET_WORD_MAX >> 31 >> 16 == 1
+# define BITSET_WORD_BITS 48
+#elif BITSET_WORD_MAX >> 31 >> 28 == 1
+# define BITSET_WORD_BITS 60
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 1 == 1
+# define BITSET_WORD_BITS 64
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 9 == 1
+# define BITSET_WORD_BITS 72
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 3 == 1
+# define BITSET_WORD_BITS 128
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 == 1
+# define BITSET_WORD_BITS 256
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 > 1
+# define BITSET_WORD_BITS 257 /* any value > SBC_MAX will do here */
+# if BITSET_WORD_BITS <= SBC_MAX
+# error "Invalid SBC_MAX"
+# endif
+#elif BITSET_WORD_MAX == (0xffffffff + 2) * 0xffffffff
+/* Work around a bug in 64-bit PGC (before version 6.1-2), where the
+ preprocessor mishandles large unsigned values as if they were signed. */
+# define BITSET_WORD_BITS 64
+#else
+# error "Add case for new bitset_word_t size"
+#endif
+
+/* Number of bitset_word_t values in a bitset_t. */
+#define BITSET_WORDS ((SBC_MAX + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS)
+
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+ INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+ LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+ BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+ BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+ WORD_DELIM = WORD_DELIM_CONSTRAINT,
+ NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+ Idx alloc;
+ Idx nelem;
+ Idx *elems;
+} re_node_set;
+
+typedef enum
+{
+ NON_TYPE = 0,
+
+ /* Node type, These are used by token, node, tree. */
+ CHARACTER = 1,
+ END_OF_RE = 2,
+ SIMPLE_BRACKET = 3,
+ OP_BACK_REF = 4,
+ OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+ COMPLEX_BRACKET = 6,
+ OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+ /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+ when the debugger shows values of this enum type. */
+#define EPSILON_BIT 8
+ OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+ OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+ OP_ALT = EPSILON_BIT | 2,
+ OP_DUP_ASTERISK = EPSILON_BIT | 3,
+ ANCHOR = EPSILON_BIT | 4,
+
+ /* Tree type, these are used only by tree. */
+ CONCAT = 16,
+ SUBEXP = 17,
+
+ /* Token type, these are used only by token. */
+ OP_DUP_PLUS = 18,
+ OP_DUP_QUESTION,
+ OP_OPEN_BRACKET,
+ OP_CLOSE_BRACKET,
+ OP_CHARSET_RANGE,
+ OP_OPEN_DUP_NUM,
+ OP_CLOSE_DUP_NUM,
+ OP_NON_MATCH_LIST,
+ OP_OPEN_COLL_ELEM,
+ OP_CLOSE_COLL_ELEM,
+ OP_OPEN_EQUIV_CLASS,
+ OP_CLOSE_EQUIV_CLASS,
+ OP_OPEN_CHAR_CLASS,
+ OP_CLOSE_CHAR_CLASS,
+ OP_WORD,
+ OP_NOTWORD,
+ OP_SPACE,
+ OP_NOTSPACE,
+ BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+ /* Multibyte characters. */
+ wchar_t *mbchars;
+
+ /* Collating symbols. */
+# ifdef _LIBC
+ int32_t *coll_syms;
+# endif
+
+ /* Equivalence classes. */
+# ifdef _LIBC
+ int32_t *equiv_classes;
+# endif
+
+ /* Range expressions. */
+# ifdef _LIBC
+ uint32_t *range_starts;
+ uint32_t *range_ends;
+# else /* not _LIBC */
+ wchar_t *range_starts;
+ wchar_t *range_ends;
+# endif /* not _LIBC */
+
+ /* Character classes. */
+ wctype_t *char_classes;
+
+ /* If this character set is the non-matching list. */
+ unsigned int non_match : 1;
+
+ /* # of multibyte characters. */
+ Idx nmbchars;
+
+ /* # of collating symbols. */
+ Idx ncoll_syms;
+
+ /* # of equivalence classes. */
+ Idx nequiv_classes;
+
+ /* # of range expressions. */
+ Idx nranges;
+
+ /* # of character classes. */
+ Idx nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+ union
+ {
+ unsigned char c; /* for CHARACTER */
+ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset; /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+ Idx idx; /* for BACK_REF */
+ re_context_type ctx_type; /* for ANCHOR */
+ } opr;
+#if __GNUC__ >= 2 && !__STRICT_ANSI__
+ re_token_type_t type : 8;
+#else
+ re_token_type_t type;
+#endif
+ unsigned int constraint : 10; /* context constraint */
+ unsigned int duplicated : 1;
+ unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+ unsigned int accept_mb : 1;
+ /* These 2 bits can be moved into the union if needed (e.g. if running out
+ of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */
+ unsigned int mb_partial : 1;
+#endif
+ unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+
+struct re_string_t
+{
+ /* Indicate the raw buffer which is the original string passed as an
+ argument of regexec(), re_search(), etc.. */
+ const unsigned char *raw_mbs;
+ /* Store the multibyte string. In case of "case insensitive mode" like
+ REG_ICASE, upper cases of the string are stored, otherwise MBS points
+ the same address that RAW_MBS points. */
+ unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+ /* Store the wide character string which is corresponding to MBS. */
+ wint_t *wcs;
+ Idx *offsets;
+ mbstate_t cur_state;
+#endif
+ /* Index in RAW_MBS. Each character mbs[i] corresponds to
+ raw_mbs[raw_mbs_idx + i]. */
+ Idx raw_mbs_idx;
+ /* The length of the valid characters in the buffers. */
+ Idx valid_len;
+ /* The corresponding number of bytes in raw_mbs array. */
+ Idx valid_raw_len;
+ /* The length of the buffers MBS and WCS. */
+ Idx bufs_len;
+ /* The index in MBS, which is updated by re_string_fetch_byte. */
+ Idx cur_idx;
+ /* length of RAW_MBS array. */
+ Idx raw_len;
+ /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */
+ Idx len;
+ /* End of the buffer may be shorter than its length in the cases such
+ as re_match_2, re_search_2. Then, we use STOP for end of the buffer
+ instead of LEN. */
+ Idx raw_stop;
+ /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */
+ Idx stop;
+
+ /* The context of mbs[0]. We store the context independently, since
+ the context of mbs[0] may be different from raw_mbs[0], which is
+ the beginning of the input string. */
+ unsigned int tip_context;
+ /* The translation passed as a part of an argument of re_compile_pattern. */
+ RE_TRANSLATE_TYPE trans;
+ /* Copy of re_dfa_t's word_char. */
+ re_const_bitset_ptr_t word_char;
+ /* true if REG_ICASE. */
+ unsigned char icase;
+ unsigned char is_utf8;
+ unsigned char map_notascii;
+ unsigned char mbs_allocated;
+ unsigned char offsets_needed;
+ unsigned char newline_anchor;
+ unsigned char word_ops_used;
+ int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+# define internal_function __attribute ((regparm (3), stdcall))
+# else
+# define internal_function
+# endif
+#endif
+
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+ Idx new_buf_len)
+ internal_function;
+#ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
+ internal_function;
+#endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static unsigned int re_string_context_at (const re_string_t *input, Idx idx,
+ int eflags)
+ internal_function __attribute ((pure));
+#define re_string_peek_byte(pstr, offset) \
+ ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+ ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+ ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+ ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+ || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#include <alloca.h>
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ allocate anything larger than 4096 bytes. Also care for the possibility
+ of a few compiler-allocated temporary stack slots. */
+# define __libc_use_alloca(n) ((n) < 4032)
+# else
+/* alloca is implemented with malloc, so just use malloc. */
+# define __libc_use_alloca(n) 0
+# endif
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a) < (b) ? (b) : (a))
+#endif
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+ struct bin_tree_t *parent;
+ struct bin_tree_t *left;
+ struct bin_tree_t *right;
+ struct bin_tree_t *first;
+ struct bin_tree_t *next;
+
+ re_token_t token;
+
+ /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+ Otherwise `type' indicate the type of this node. */
+ Idx node_idx;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+ ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+ struct bin_tree_storage_t *next;
+ bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+ re_hashval_t hash;
+ re_node_set nodes;
+ re_node_set non_eps_nodes;
+ re_node_set inveclosure;
+ re_node_set *entrance_nodes;
+ struct re_dfastate_t **trtable, **word_trtable;
+ unsigned int context : 4;
+ unsigned int halt : 1;
+ /* If this state can accept `multi byte'.
+ Note that we refer to multibyte characters, and multi character
+ collating elements as `multi byte'. */
+ unsigned int accept_mb : 1;
+ /* If this state has backreference node(s). */
+ unsigned int has_backref : 1;
+ unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+ Idx num;
+ Idx alloc;
+ re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
+
+typedef struct
+{
+ Idx next_idx;
+ Idx alloc;
+ re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
+
+typedef struct
+{
+ Idx node;
+ Idx str_idx; /* The position NODE match at. */
+ state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+ And information about the node, whose type is OP_CLOSE_SUBEXP,
+ corresponding to NODE is stored in LASTS. */
+
+typedef struct
+{
+ Idx str_idx;
+ Idx node;
+ state_array_t *path;
+ Idx alasts; /* Allocation size of LASTS. */
+ Idx nlasts; /* The number of LASTS. */
+ re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+ Idx node;
+ Idx str_idx;
+ Idx subexp_from;
+ Idx subexp_to;
+ char more;
+ char unused;
+ unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+ /* The string object corresponding to the input string. */
+ re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ const re_dfa_t *const dfa;
+#else
+ const re_dfa_t *dfa;
+#endif
+ /* EFLAGS of the argument of regexec. */
+ int eflags;
+ /* Where the matching ends. */
+ Idx match_last;
+ Idx last_node;
+ /* The state log used by the matcher. */
+ re_dfastate_t **state_log;
+ Idx state_log_top;
+ /* Back reference cache. */
+ Idx nbkref_ents;
+ Idx abkref_ents;
+ struct re_backref_cache_entry *bkref_ents;
+ int max_mb_elem_len;
+ Idx nsub_tops;
+ Idx asub_tops;
+ re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **limited_states;
+ Idx last_node;
+ Idx last_str_idx;
+ re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+ Idx idx;
+ Idx node;
+ regmatch_t *regs;
+ re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+ Idx num;
+ Idx alloc;
+ struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+ re_token_t *nodes;
+ size_t nodes_alloc;
+ size_t nodes_len;
+ Idx *nexts;
+ Idx *org_indices;
+ re_node_set *edests;
+ re_node_set *eclosures;
+ re_node_set *inveclosures;
+ struct re_state_table_entry *state_table;
+ re_dfastate_t *init_state;
+ re_dfastate_t *init_state_word;
+ re_dfastate_t *init_state_nl;
+ re_dfastate_t *init_state_begbuf;
+ bin_tree_t *str_tree;
+ bin_tree_storage_t *str_tree_storage;
+ re_bitset_ptr_t sb_char;
+ int str_tree_storage_idx;
+
+ /* number of subexpressions `re_nsub' is in regex_t. */
+ re_hashval_t state_hash_mask;
+ Idx init_node;
+ Idx nbackref; /* The number of backreference in this dfa. */
+
+ /* Bitmap expressing which backreference is used. */
+ bitset_word_t used_bkref_map;
+ bitset_word_t completed_bkref_map;
+
+ unsigned int has_plural_match : 1;
+ /* If this dfa has "multibyte node", which is a backreference or
+ a node which can accept multibyte character or multi character
+ collating element. */
+ unsigned int has_mb_node : 1;
+ unsigned int is_utf8 : 1;
+ unsigned int map_notascii : 1;
+ unsigned int word_ops_used : 1;
+ int mb_cur_max;
+ bitset_t word_char;
+ reg_syntax_t syntax;
+ Idx *subexp_map;
+#ifdef DEBUG
+ char* re_str;
+#endif
+#ifdef _LIBC
+ __libc_lock_define (, lock)
+#endif
+};
+
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+#define re_node_set_remove(set,id) \
+ (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+
+
+typedef enum
+{
+ SB_CHAR,
+ MB_CHAR,
+ EQUIV_CLASS,
+ COLL_SYM,
+ CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+ bracket_elem_type type;
+ union
+ {
+ unsigned char ch;
+ unsigned char *name;
+ wchar_t wch;
+ } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset_t operation. */
+
+static inline void
+bitset_set (bitset_t set, Idx i)
+{
+ set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS;
+}
+
+static inline void
+bitset_clear (bitset_t set, Idx i)
+{
+ set[i / BITSET_WORD_BITS] &= ~ ((bitset_word_t) 1 << i % BITSET_WORD_BITS);
+}
+
+static inline bool
+bitset_contain (const bitset_t set, Idx i)
+{
+ return (set[i / BITSET_WORD_BITS] >> i % BITSET_WORD_BITS) & 1;
+}
+
+static inline void
+bitset_empty (bitset_t set)
+{
+ memset (set, '\0', sizeof (bitset_t));
+}
+
+static inline void
+bitset_set_all (bitset_t set)
+{
+ memset (set, -1, sizeof (bitset_word_t) * (SBC_MAX / BITSET_WORD_BITS));
+ if (SBC_MAX % BITSET_WORD_BITS != 0)
+ set[BITSET_WORDS - 1] =
+ ((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1;
+}
+
+static inline void
+bitset_copy (bitset_t dest, const bitset_t src)
+{
+ memcpy (dest, src, sizeof (bitset_t));
+}
+
+static inline void
+bitset_not (bitset_t set)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < SBC_MAX / BITSET_WORD_BITS; ++bitset_i)
+ set[bitset_i] = ~set[bitset_i];
+ if (SBC_MAX % BITSET_WORD_BITS != 0)
+ set[BITSET_WORDS - 1] =
+ ((((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1)
+ & ~set[BITSET_WORDS - 1]);
+}
+
+static inline void
+bitset_merge (bitset_t dest, const bitset_t src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+ dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_mask (bitset_t dest, const bitset_t src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+ dest[bitset_i] &= src[bitset_i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string. */
+static inline int
+internal_function __attribute ((pure))
+re_string_char_size_at (const re_string_t *pstr, Idx idx)
+{
+ int byte_idx;
+ if (pstr->mb_cur_max == 1)
+ return 1;
+ for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+ if (pstr->wcs[idx + byte_idx] != WEOF)
+ break;
+ return byte_idx;
+}
+
+static inline wint_t
+internal_function __attribute ((pure))
+re_string_wchar_at (const re_string_t *pstr, Idx idx)
+{
+ if (pstr->mb_cur_max == 1)
+ return (wint_t) pstr->mbs[idx];
+ return (wint_t) pstr->wcs[idx];
+}
+
+static int
+internal_function __attribute ((pure))
+re_string_elem_size_at (const re_string_t *pstr, Idx idx)
+{
+# ifdef _LIBC
+ const unsigned char *p, *extra;
+ const int32_t *table, *indirect;
+ int32_t tmp;
+# include <locale/weight.h>
+ uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+ if (nrules != 0)
+ {
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ p = pstr->mbs + idx;
+ tmp = findidx (&p);
+ return p - pstr->mbs - idx;
+ }
+ else
+# endif /* _LIBC */
+ return 1;
+}
+#endif /* RE_ENABLE_I18N */
+
+#endif /* _REGEX_INTERNAL_H */
diff --git a/usr/src/lib/libparted/common/lib/regexec.c b/usr/src/lib/libparted/common/lib/regexec.c
new file mode 100644
index 0000000000..b136570820
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/regexec.c
@@ -0,0 +1,4399 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation,
+ Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+ Idx n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, Idx node,
+ Idx str_idx, Idx from, Idx to)
+ internal_function;
+static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx)
+ internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, Idx node,
+ Idx str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+ Idx node, Idx str_idx)
+ internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, Idx last_node,
+ Idx last_str_idx)
+ internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+ const char *string, Idx length,
+ Idx start, Idx last_start, Idx stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags) internal_function;
+static regoff_t re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, Idx length1,
+ const char *string2, Idx length2,
+ Idx start, regoff_t range,
+ struct re_registers *regs,
+ Idx stop, bool ret_len) internal_function;
+static regoff_t re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, Idx length, Idx start,
+ regoff_t range, Idx stop,
+ struct re_registers *regs,
+ bool ret_len) internal_function;
+static unsigned int re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+ Idx nregs, int regs_allocated)
+ internal_function;
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx)
+ internal_function;
+static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match,
+ Idx *p_match_first) internal_function;
+static Idx check_halt_state_context (const re_match_context_t *mctx,
+ const re_dfastate_t *state, Idx idx)
+ internal_function;
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+ regmatch_t *prev_idx_match, Idx cur_node,
+ Idx cur_idx, Idx nmatch) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+ Idx str_idx, Idx dest_node, Idx nregs,
+ regmatch_t *regs,
+ re_node_set *eps_via_nodes)
+ internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+ const re_match_context_t *mctx,
+ size_t nmatch, regmatch_t *pmatch,
+ bool fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
+ internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ Idx node_idx, Idx str_idx, Idx max_str_idx)
+ internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
+ re_sift_context_t *sctx)
+ internal_function;
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+ re_sift_context_t *sctx, Idx str_idx,
+ re_node_set *cur_dest)
+ internal_function;
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ Idx str_idx,
+ re_node_set *dest_nodes)
+ internal_function;
+static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates)
+ internal_function;
+static bool check_dst_limits (const re_match_context_t *mctx,
+ const re_node_set *limits,
+ Idx dst_node, Idx dst_idx, Idx src_node,
+ Idx src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+ int boundaries, Idx subexp_idx,
+ Idx from_node, Idx bkref_idx)
+ internal_function;
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+ Idx limit, Idx subexp_idx,
+ Idx node, Idx str_idx,
+ Idx bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates,
+ re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents,
+ Idx str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ Idx str_idx, const re_node_set *candidates)
+ internal_function;
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+ re_dfastate_t **dst,
+ re_dfastate_t **src, Idx num)
+ internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+ re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *next_state)
+ internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+ re_node_set *cur_nodes,
+ Idx str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *pstate)
+ internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+ re_dfastate_t *pstate)
+ internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+ const re_node_set *nodes)
+ internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+ Idx bkref_node, Idx bkref_str_idx)
+ internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+ const re_sub_match_top_t *sub_top,
+ re_sub_match_last_t *sub_last,
+ Idx bkref_node, Idx bkref_str)
+ internal_function;
+static Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+ Idx subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+ state_array_t *path, Idx top_node,
+ Idx top_str, Idx last_node, Idx last_str,
+ int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+ Idx str_idx,
+ re_node_set *cur_nodes,
+ re_node_set *next_nodes)
+ internal_function;
+static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa,
+ re_node_set *cur_nodes,
+ Idx ex_subexp, int type)
+ internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa,
+ re_node_set *dst_nodes,
+ Idx target, Idx ex_subexp,
+ int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+ re_node_set *cur_nodes, Idx cur_str,
+ Idx subexp_num, int type)
+ internal_function;
+static bool build_trtable (const re_dfa_t *dfa,
+ re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
+ const re_string_t *input, Idx idx)
+ internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+ size_t name_len)
+ internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static Idx group_nodes_into_DFAstates (const re_dfa_t *dfa,
+ const re_dfastate_t *state,
+ re_node_set *states_node,
+ bitset_t *states_ch) internal_function;
+static bool check_node_accept (const re_match_context_t *mctx,
+ const re_token_t *node, Idx idx)
+ internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx)
+ internal_function;
+
+/* Entry point for POSIX code. */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *_Restrict_ preg;
+ const char *_Restrict_ string;
+ size_t nmatch;
+ regmatch_t pmatch[_Restrict_arr_];
+ int eflags;
+{
+ reg_errcode_t err;
+ Idx start, length;
+#ifdef _LIBC
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+#endif
+
+ if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+ return REG_BADPAT;
+
+ if (eflags & REG_STARTEND)
+ {
+ start = pmatch[0].rm_so;
+ length = pmatch[0].rm_eo;
+ }
+ else
+ {
+ start = 0;
+ length = strlen (string);
+ }
+
+ __libc_lock_lock (dfa->lock);
+ if (preg->no_sub)
+ err = re_search_internal (preg, string, length, start, length,
+ length, 0, NULL, eflags);
+ else
+ err = re_search_internal (preg, string, length, start, length,
+ length, nmatch, pmatch, eflags);
+ __libc_lock_unlock (dfa->lock);
+ return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *_Restrict_ preg,
+ const char *_Restrict_ string, size_t nmatch,
+ regmatch_t pmatch[], int eflags)
+{
+ return regexec (preg, string, nmatch, pmatch,
+ eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code. */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+ The former two functions operate on STRING with length LENGTH,
+ while the later two operate on concatenation of STRING1 and STRING2
+ with lengths LENGTH1 and LENGTH2, respectively.
+
+ re_match() matches the compiled pattern in BUFP against the string,
+ starting at index START.
+
+ re_search() first tries matching at index START, then it tries to match
+ starting from index START + 1, and so on. The last start position tried
+ is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same
+ way as re_match().)
+
+ The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+ the first STOP characters of the concatenation of the strings should be
+ concerned.
+
+ If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+ and all groups is stored in REGS. (For the "_2" variants, the offsets are
+ computed relative to the concatenation, not relative to the individual
+ strings.)
+
+ On success, re_match* functions return the length of the match, re_search*
+ return the position of the start of the match. Return value -1 means no
+ match was found and -2 indicates an internal error. */
+
+regoff_t
+re_match (bufp, string, length, start, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ Idx length, start;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, 0, length, regs, true);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+regoff_t
+re_search (bufp, string, length, start, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ Idx length, start;
+ regoff_t range;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, range, length, regs,
+ false);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+regoff_t
+re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ Idx length1, length2, start, stop;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, 0, regs, stop, true);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+regoff_t
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ Idx length1, length2, start, stop;
+ regoff_t range;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, range, regs, stop, false);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static regoff_t
+internal_function
+re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, Idx length1,
+ const char *string2, Idx length2,
+ Idx start, regoff_t range, struct re_registers *regs,
+ Idx stop, bool ret_len)
+{
+ const char *str;
+ regoff_t rval;
+ Idx len = length1 + length2;
+ char *s = NULL;
+
+ if (BE (length1 < 0 || length2 < 0 || stop < 0 || len < length1, 0))
+ return -2;
+
+ /* Concatenate the strings. */
+ if (length2 > 0)
+ if (length1 > 0)
+ {
+ s = re_malloc (char, len);
+
+ if (BE (s == NULL, 0))
+ return -2;
+#ifdef _LIBC
+ memcpy (__mempcpy (s, string1, length1), string2, length2);
+#else
+ memcpy (s, string1, length1);
+ memcpy (s + length1, string2, length2);
+#endif
+ str = s;
+ }
+ else
+ str = string2;
+ else
+ str = string1;
+
+ rval = re_search_stub (bufp, str, len, start, range, stop, regs,
+ ret_len);
+ re_free (s);
+ return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+ Additional parameters:
+ If RET_LEN is true the length of the match is returned (re_match style);
+ otherwise the position of the match is returned. */
+
+static regoff_t
+internal_function
+re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, Idx length,
+ Idx start, regoff_t range, Idx stop, struct re_registers *regs,
+ bool ret_len)
+{
+ reg_errcode_t result;
+ regmatch_t *pmatch;
+ Idx nregs;
+ regoff_t rval;
+ int eflags = 0;
+#ifdef _LIBC
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+#endif
+ Idx last_start = start + range;
+
+ /* Check for out-of-range. */
+ if (BE (start < 0 || start > length, 0))
+ return -1;
+ if (BE (length < last_start || (0 <= range && last_start < start), 0))
+ last_start = length;
+ else if (BE (last_start < 0 || (range < 0 && start <= last_start), 0))
+ last_start = 0;
+
+ __libc_lock_lock (dfa->lock);
+
+ eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+ /* Compile fastmap if we haven't yet. */
+ if (start < last_start && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+ re_compile_fastmap (bufp);
+
+ if (BE (bufp->no_sub, 0))
+ regs = NULL;
+
+ /* We need at least 1 register. */
+ if (regs == NULL)
+ nregs = 1;
+ else if (BE (bufp->regs_allocated == REGS_FIXED
+ && regs->num_regs <= bufp->re_nsub, 0))
+ {
+ nregs = regs->num_regs;
+ if (BE (nregs < 1, 0))
+ {
+ /* Nothing can be copied to regs. */
+ regs = NULL;
+ nregs = 1;
+ }
+ }
+ else
+ nregs = bufp->re_nsub + 1;
+ pmatch = re_malloc (regmatch_t, nregs);
+ if (BE (pmatch == NULL, 0))
+ {
+ rval = -2;
+ goto out;
+ }
+
+ result = re_search_internal (bufp, string, length, start, last_start, stop,
+ nregs, pmatch, eflags);
+
+ rval = 0;
+
+ /* I hope we needn't fill ther regs with -1's when no match was found. */
+ if (result != REG_NOERROR)
+ rval = -1;
+ else if (regs != NULL)
+ {
+ /* If caller wants register contents data back, copy them. */
+ bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+ bufp->regs_allocated);
+ if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+ rval = -2;
+ }
+
+ if (BE (rval == 0, 1))
+ {
+ if (ret_len)
+ {
+ assert (pmatch[0].rm_so == start);
+ rval = pmatch[0].rm_eo - start;
+ }
+ else
+ rval = pmatch[0].rm_so;
+ }
+ re_free (pmatch);
+ out:
+ __libc_lock_unlock (dfa->lock);
+ return rval;
+}
+
+static unsigned int
+internal_function
+re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs,
+ int regs_allocated)
+{
+ int rval = REGS_REALLOCATE;
+ Idx i;
+ Idx need_regs = nregs + 1;
+ /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+ uses. */
+
+ /* Have the register data arrays been allocated? */
+ if (regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. */
+ regs->start = re_malloc (regoff_t, need_regs);
+ if (BE (regs->start == NULL, 0))
+ return REGS_UNALLOCATED;
+ regs->end = re_malloc (regoff_t, need_regs);
+ if (BE (regs->end == NULL, 0))
+ {
+ re_free (regs->start);
+ return REGS_UNALLOCATED;
+ }
+ regs->num_regs = need_regs;
+ }
+ else if (regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (BE (need_regs > regs->num_regs, 0))
+ {
+ regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+ regoff_t *new_end;
+ if (BE (new_start == NULL, 0))
+ return REGS_UNALLOCATED;
+ new_end = re_realloc (regs->end, regoff_t, need_regs);
+ if (BE (new_end == NULL, 0))
+ {
+ re_free (new_start);
+ return REGS_UNALLOCATED;
+ }
+ regs->start = new_start;
+ regs->end = new_end;
+ regs->num_regs = need_regs;
+ }
+ }
+ else
+ {
+ assert (regs_allocated == REGS_FIXED);
+ /* This function may not be called with REGS_FIXED and nregs too big. */
+ assert (regs->num_regs >= nregs);
+ rval = REGS_FIXED;
+ }
+
+ /* Copy the regs. */
+ for (i = 0; i < nregs; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ regs->start[i] = regs->end[i] = -1;
+
+ return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ __re_size_t num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = NULL;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+ const char *s;
+{
+ return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point. */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+ length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
+ meaning as with regexec. LAST_START is START + RANGE, where
+ START and RANGE have the same meaning as with re_search.
+ Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+ otherwise return the error code.
+ Note: We assume front end functions already check ranges.
+ (0 <= LAST_START && LAST_START <= LENGTH) */
+
+static reg_errcode_t
+internal_function
+re_search_internal (const regex_t *preg,
+ const char *string, Idx length,
+ Idx start, Idx last_start, Idx stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags)
+{
+ reg_errcode_t err;
+ const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+ Idx left_lim, right_lim;
+ int incr;
+ bool fl_longest_match;
+ int match_kind;
+ Idx match_first;
+ Idx match_last = REG_MISSING;
+ Idx extra_nmatch;
+ bool sb;
+ int ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ re_match_context_t mctx = { .dfa = dfa };
+#else
+ re_match_context_t mctx;
+#endif
+ char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate
+ && start != last_start && !preg->can_be_null)
+ ? preg->fastmap : NULL);
+ RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+ memset (&mctx, '\0', sizeof (re_match_context_t));
+ mctx.dfa = dfa;
+#endif
+
+ extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+ nmatch -= extra_nmatch;
+
+ /* Check if the DFA haven't been compiled. */
+ if (BE (preg->used == 0 || dfa->init_state == NULL
+ || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return REG_NOMATCH;
+
+#ifdef DEBUG
+ /* We assume front-end functions already check them. */
+ assert (0 <= last_start && last_start <= length);
+#endif
+
+ /* If initial states with non-begbuf contexts have no elements,
+ the regex must be anchored. If preg->newline_anchor is set,
+ we'll never use init_state_nl, so do not check it. */
+ if (dfa->init_state->nodes.nelem == 0
+ && dfa->init_state_word->nodes.nelem == 0
+ && (dfa->init_state_nl->nodes.nelem == 0
+ || !preg->newline_anchor))
+ {
+ if (start != 0 && last_start != 0)
+ return REG_NOMATCH;
+ start = last_start = 0;
+ }
+
+ /* We must check the longest matching, if nmatch > 0. */
+ fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+ err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+ preg->translate, preg->syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ mctx.input.stop = stop;
+ mctx.input.raw_stop = stop;
+ mctx.input.newline_anchor = preg->newline_anchor;
+
+ err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* We will log all the DFA states through which the dfa pass,
+ if nmatch > 1, or this dfa has "multibyte node", which is a
+ back-reference or a node which can accept multibyte character or
+ multi character collating element. */
+ if (nmatch > 1 || dfa->has_mb_node)
+ {
+ /* Avoid overflow. */
+ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+
+ mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+ if (BE (mctx.state_log == NULL, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ }
+ else
+ mctx.state_log = NULL;
+
+ match_first = start;
+ mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+ /* Check incrementally whether of not the input string match. */
+ incr = (last_start < start) ? -1 : 1;
+ left_lim = (last_start < start) ? last_start : start;
+ right_lim = (last_start < start) ? start : last_start;
+ sb = dfa->mb_cur_max == 1;
+ match_kind =
+ (fastmap
+ ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+ | (start <= last_start ? 2 : 0)
+ | (t != NULL ? 1 : 0))
+ : 8);
+
+ for (;; match_first += incr)
+ {
+ err = REG_NOMATCH;
+ if (match_first < left_lim || right_lim < match_first)
+ goto free_return;
+
+ /* Advance as rapidly as possible through the string, until we
+ find a plausible place to start matching. This may be done
+ with varying efficiency, so there are various possibilities:
+ only the most common of them are specialized, in order to
+ save on code size. We use a switch statement for speed. */
+ switch (match_kind)
+ {
+ case 8:
+ /* No fastmap. */
+ break;
+
+ case 7:
+ /* Fastmap with single-byte translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[t[(unsigned char) string[match_first]]])
+ ++match_first;
+ goto forward_match_found_start_or_reached_end;
+
+ case 6:
+ /* Fastmap without translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[(unsigned char) string[match_first]])
+ ++match_first;
+
+ forward_match_found_start_or_reached_end:
+ if (BE (match_first == right_lim, 0))
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (!fastmap[t ? t[ch] : ch])
+ goto free_return;
+ }
+ break;
+
+ case 4:
+ case 5:
+ /* Fastmap without multi-byte translation, match backwards. */
+ while (match_first >= left_lim)
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (fastmap[t ? t[ch] : ch])
+ break;
+ --match_first;
+ }
+ if (match_first < left_lim)
+ goto free_return;
+ break;
+
+ default:
+ /* In this case, we can't determine easily the current byte,
+ since it might be a component byte of a multibyte
+ character. Then we use the constructed buffer instead. */
+ for (;;)
+ {
+ /* If MATCH_FIRST is out of the valid range, reconstruct the
+ buffers. */
+ __re_size_t offset = match_first - mctx.input.raw_mbs_idx;
+ if (BE (offset >= (__re_size_t) mctx.input.valid_raw_len, 0))
+ {
+ err = re_string_reconstruct (&mctx.input, match_first,
+ eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ offset = match_first - mctx.input.raw_mbs_idx;
+ }
+ /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+ Note that MATCH_FIRST must not be smaller than 0. */
+ ch = (match_first >= length
+ ? 0 : re_string_byte_at (&mctx.input, offset));
+ if (fastmap[ch])
+ break;
+ match_first += incr;
+ if (match_first < left_lim || match_first > right_lim)
+ {
+ err = REG_NOMATCH;
+ goto free_return;
+ }
+ }
+ break;
+ }
+
+ /* Reconstruct the buffers so that the matcher can assume that
+ the matching starts from the beginning of the buffer. */
+ err = re_string_reconstruct (&mctx.input, match_first, eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* Don't consider this char as a possible match start if it part,
+ yet isn't the head, of a multibyte character. */
+ if (!sb && !re_string_first_byte (&mctx.input, 0))
+ continue;
+#endif
+
+ /* It seems to be appropriate one, then use the matcher. */
+ /* We assume that the matching starts from 0. */
+ mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+ match_last = check_matching (&mctx, fl_longest_match,
+ start <= last_start ? &match_first : NULL);
+ if (match_last != REG_MISSING)
+ {
+ if (BE (match_last == REG_ERROR, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ else
+ {
+ mctx.match_last = match_last;
+ if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+ {
+ re_dfastate_t *pstate = mctx.state_log[match_last];
+ mctx.last_node = check_halt_state_context (&mctx, pstate,
+ match_last);
+ }
+ if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+ || dfa->nbackref)
+ {
+ err = prune_impossible_nodes (&mctx);
+ if (err == REG_NOERROR)
+ break;
+ if (BE (err != REG_NOMATCH, 0))
+ goto free_return;
+ match_last = REG_MISSING;
+ }
+ else
+ break; /* We found a match. */
+ }
+ }
+
+ match_ctx_clean (&mctx);
+ }
+
+#ifdef DEBUG
+ assert (match_last != REG_MISSING);
+ assert (err == REG_NOERROR);
+#endif
+
+ /* Set pmatch[] if we need. */
+ if (nmatch > 0)
+ {
+ Idx reg_idx;
+
+ /* Initialize registers. */
+ for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+ pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+ /* Set the points where matching start/end. */
+ pmatch[0].rm_so = 0;
+ pmatch[0].rm_eo = mctx.match_last;
+ /* FIXME: This function should fail if mctx.match_last exceeds
+ the maximum possible regoff_t value. We need a new error
+ code REG_OVERFLOW. */
+
+ if (!preg->no_sub && nmatch > 1)
+ {
+ err = set_regs (preg, &mctx, nmatch, pmatch,
+ dfa->has_plural_match && dfa->nbackref > 0);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* At last, add the offset to the each registers, since we slided
+ the buffers so that we could assume that the matching starts
+ from 0. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so != -1)
+ {
+#ifdef RE_ENABLE_I18N
+ if (BE (mctx.input.offsets_needed != 0, 0))
+ {
+ pmatch[reg_idx].rm_so =
+ (pmatch[reg_idx].rm_so == mctx.input.valid_len
+ ? mctx.input.valid_raw_len
+ : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+ pmatch[reg_idx].rm_eo =
+ (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+ ? mctx.input.valid_raw_len
+ : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+ }
+#else
+ assert (mctx.input.offsets_needed == 0);
+#endif
+ pmatch[reg_idx].rm_so += match_first;
+ pmatch[reg_idx].rm_eo += match_first;
+ }
+ for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+ {
+ pmatch[nmatch + reg_idx].rm_so = -1;
+ pmatch[nmatch + reg_idx].rm_eo = -1;
+ }
+
+ if (dfa->subexp_map)
+ for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+ if (dfa->subexp_map[reg_idx] != reg_idx)
+ {
+ pmatch[reg_idx + 1].rm_so
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+ pmatch[reg_idx + 1].rm_eo
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+ }
+ }
+
+ free_return:
+ re_free (mctx.state_log);
+ if (dfa->nbackref)
+ match_ctx_free (&mctx);
+ re_string_destruct (&mctx.input);
+ return err;
+}
+
+static reg_errcode_t
+internal_function
+prune_impossible_nodes (re_match_context_t *mctx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx halt_node, match_last;
+ reg_errcode_t ret;
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **lim_states = NULL;
+ re_sift_context_t sctx;
+#ifdef DEBUG
+ assert (mctx->state_log != NULL);
+#endif
+ match_last = mctx->match_last;
+ halt_node = mctx->last_node;
+
+ /* Avoid overflow. */
+ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0))
+ return REG_ESPACE;
+
+ sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (sifted_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ if (dfa->nbackref)
+ {
+ lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (lim_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ while (1)
+ {
+ memset (lim_states, '\0',
+ sizeof (re_dfastate_t *) * (match_last + 1));
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+ match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ if (sifted_states[0] != NULL || lim_states[0] != NULL)
+ break;
+ do
+ {
+ --match_last;
+ if (! REG_VALID_INDEX (match_last))
+ {
+ ret = REG_NOMATCH;
+ goto free_return;
+ }
+ } while (mctx->state_log[match_last] == NULL
+ || !mctx->state_log[match_last]->halt);
+ halt_node = check_halt_state_context (mctx,
+ mctx->state_log[match_last],
+ match_last);
+ }
+ ret = merge_state_array (dfa, sifted_states, lim_states,
+ match_last + 1);
+ re_free (lim_states);
+ lim_states = NULL;
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ }
+ re_free (mctx->state_log);
+ mctx->state_log = sifted_states;
+ sifted_states = NULL;
+ mctx->last_node = halt_node;
+ mctx->match_last = match_last;
+ ret = REG_NOERROR;
+ free_return:
+ re_free (sifted_states);
+ re_free (lim_states);
+ return ret;
+}
+
+/* Acquire an initial state and return it.
+ We must select appropriate initial state depending on the context,
+ since initial states may have constraints like "\<", "^", etc.. */
+
+static inline re_dfastate_t *
+__attribute ((always_inline)) internal_function
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+ Idx idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ if (dfa->init_state->has_constraint)
+ {
+ unsigned int context;
+ context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return dfa->init_state_word;
+ else if (IS_ORDINARY_CONTEXT (context))
+ return dfa->init_state;
+ else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_begbuf;
+ else if (IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_nl;
+ else if (IS_BEGBUF_CONTEXT (context))
+ {
+ /* It is relatively rare case, then calculate on demand. */
+ return re_acquire_state_context (err, dfa,
+ dfa->init_state->entrance_nodes,
+ context);
+ }
+ else
+ /* Must not happen? */
+ return dfa->init_state;
+ }
+ else
+ return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+ and return the index where the matching end. Return REG_MISSING if
+ there is no match, and return REG_ERROR in case of an error.
+ FL_LONGEST_MATCH means we want the POSIX longest matching.
+ If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+ next place where we may want to try matching.
+ Note that the matcher assume that the maching starts from the current
+ index of the buffer. */
+
+static Idx
+internal_function
+check_matching (re_match_context_t *mctx, bool fl_longest_match,
+ Idx *p_match_first)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ Idx match = 0;
+ Idx match_last = REG_MISSING;
+ Idx cur_str_idx = re_string_cur_idx (&mctx->input);
+ re_dfastate_t *cur_state;
+ bool at_init_state = p_match_first != NULL;
+ Idx next_start_idx = cur_str_idx;
+
+ err = REG_NOERROR;
+ cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+ /* An initial state must not be NULL (invalid). */
+ if (BE (cur_state == NULL, 0))
+ {
+ assert (err == REG_ESPACE);
+ return REG_ERROR;
+ }
+
+ if (mctx->state_log != NULL)
+ {
+ mctx->state_log[cur_str_idx] = cur_state;
+
+ /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+ later. E.g. Processing back references. */
+ if (BE (dfa->nbackref, 0))
+ {
+ at_init_state = false;
+ err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (cur_state->has_backref)
+ {
+ err = transit_state_bkref (mctx, &cur_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+
+ /* If the RE accepts NULL string. */
+ if (BE (cur_state->halt, 0))
+ {
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state, cur_str_idx))
+ {
+ if (!fl_longest_match)
+ return cur_str_idx;
+ else
+ {
+ match_last = cur_str_idx;
+ match = 1;
+ }
+ }
+ }
+
+ while (!re_string_eoi (&mctx->input))
+ {
+ re_dfastate_t *old_state = cur_state;
+ Idx next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+ if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+ || (BE (next_char_idx >= mctx->input.valid_len, 0)
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ assert (err == REG_ESPACE);
+ return REG_ERROR;
+ }
+ }
+
+ cur_state = transit_state (&err, mctx, cur_state);
+ if (mctx->state_log != NULL)
+ cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+ if (cur_state == NULL)
+ {
+ /* Reached the invalid state or an error. Try to recover a valid
+ state using the state log, if available and if we have not
+ already found a valid (even if not the longest) match. */
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ERROR;
+
+ if (mctx->state_log == NULL
+ || (match && !fl_longest_match)
+ || (cur_state = find_recover_state (&err, mctx)) == NULL)
+ break;
+ }
+
+ if (BE (at_init_state, 0))
+ {
+ if (old_state == cur_state)
+ next_start_idx = next_char_idx;
+ else
+ at_init_state = false;
+ }
+
+ if (cur_state->halt)
+ {
+ /* Reached a halt state.
+ Check the halt state can satisfy the current context. */
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state,
+ re_string_cur_idx (&mctx->input)))
+ {
+ /* We found an appropriate halt state. */
+ match_last = re_string_cur_idx (&mctx->input);
+ match = 1;
+
+ /* We found a match, do not modify match_first below. */
+ p_match_first = NULL;
+ if (!fl_longest_match)
+ break;
+ }
+ }
+ }
+
+ if (p_match_first)
+ *p_match_first += next_start_idx;
+
+ return match_last;
+}
+
+/* Check NODE match the current context. */
+
+static bool
+internal_function
+check_halt_node_context (const re_dfa_t *dfa, Idx node, unsigned int context)
+{
+ re_token_type_t type = dfa->nodes[node].type;
+ unsigned int constraint = dfa->nodes[node].constraint;
+ if (type != END_OF_RE)
+ return false;
+ if (!constraint)
+ return true;
+ if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+ return false;
+ return true;
+}
+
+/* Check the halt state STATE match the current context.
+ Return 0 if not match, if the node, STATE has, is a halt node and
+ match the context, return the node. */
+
+static Idx
+internal_function
+check_halt_state_context (const re_match_context_t *mctx,
+ const re_dfastate_t *state, Idx idx)
+{
+ Idx i;
+ unsigned int context;
+#ifdef DEBUG
+ assert (state->halt);
+#endif
+ context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+ for (i = 0; i < state->nodes.nelem; ++i)
+ if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+ return state->nodes.elems[i];
+ return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+ corresponding to the DFA).
+ Return the destination node, and update EPS_VIA_NODES;
+ return REG_MISSING in case of errors. */
+
+static Idx
+internal_function
+proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs,
+ Idx *pidx, Idx node, re_node_set *eps_via_nodes,
+ struct re_fail_stack_t *fs)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx i;
+ bool ok;
+ if (IS_EPSILON_NODE (dfa->nodes[node].type))
+ {
+ re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+ re_node_set *edests = &dfa->edests[node];
+ Idx dest_node;
+ ok = re_node_set_insert (eps_via_nodes, node);
+ if (BE (! ok, 0))
+ return REG_ERROR;
+ /* Pick up a valid destination, or return REG_MISSING if none
+ is found. */
+ for (dest_node = REG_MISSING, i = 0; i < edests->nelem; ++i)
+ {
+ Idx candidate = edests->elems[i];
+ if (!re_node_set_contains (cur_nodes, candidate))
+ continue;
+ if (dest_node == REG_MISSING)
+ dest_node = candidate;
+
+ else
+ {
+ /* In order to avoid infinite loop like "(a*)*", return the second
+ epsilon-transition if the first was already considered. */
+ if (re_node_set_contains (eps_via_nodes, dest_node))
+ return candidate;
+
+ /* Otherwise, push the second epsilon-transition on the fail stack. */
+ else if (fs != NULL
+ && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+ eps_via_nodes))
+ return REG_ERROR;
+
+ /* We know we are going to exit. */
+ break;
+ }
+ }
+ return dest_node;
+ }
+ else
+ {
+ Idx naccepted = 0;
+ re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->nodes[node].accept_mb)
+ naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (type == OP_BACK_REF)
+ {
+ Idx subexp_idx = dfa->nodes[node].opr.idx + 1;
+ naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+ if (fs != NULL)
+ {
+ if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+ return REG_MISSING;
+ else if (naccepted)
+ {
+ char *buf = (char *) re_string_get_buffer (&mctx->input);
+ if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+ naccepted) != 0)
+ return REG_MISSING;
+ }
+ }
+
+ if (naccepted == 0)
+ {
+ Idx dest_node;
+ ok = re_node_set_insert (eps_via_nodes, node);
+ if (BE (! ok, 0))
+ return REG_ERROR;
+ dest_node = dfa->edests[node].elems[0];
+ if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node))
+ return dest_node;
+ }
+ }
+
+ if (naccepted != 0
+ || check_node_accept (mctx, dfa->nodes + node, *pidx))
+ {
+ Idx dest_node = dfa->nexts[node];
+ *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+ if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+ || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node)))
+ return REG_MISSING;
+ re_node_set_empty (eps_via_nodes);
+ return dest_node;
+ }
+ }
+ return REG_MISSING;
+}
+
+static reg_errcode_t
+internal_function
+push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node,
+ Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+ reg_errcode_t err;
+ Idx num = fs->num++;
+ if (fs->num == fs->alloc)
+ {
+ struct re_fail_stack_ent_t *new_array;
+ new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+ * fs->alloc * 2));
+ if (new_array == NULL)
+ return REG_ESPACE;
+ fs->alloc *= 2;
+ fs->stack = new_array;
+ }
+ fs->stack[num].idx = str_idx;
+ fs->stack[num].node = dest_node;
+ fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+ if (fs->stack[num].regs == NULL)
+ return REG_ESPACE;
+ memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+ err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+ return err;
+}
+
+static Idx
+internal_function
+pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs,
+ regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+ Idx num = --fs->num;
+ assert (REG_VALID_INDEX (num));
+ *pidx = fs->stack[num].idx;
+ memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+ re_node_set_free (eps_via_nodes);
+ re_free (fs->stack[num].regs);
+ *eps_via_nodes = fs->stack[num].eps_via_nodes;
+ return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+ PMATCH.
+ Note: We assume that pmatch[0] is already set, and
+ pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */
+
+static reg_errcode_t
+internal_function
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+ regmatch_t *pmatch, bool fl_backtrack)
+{
+ const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+ Idx idx, cur_node;
+ re_node_set eps_via_nodes;
+ struct re_fail_stack_t *fs;
+ struct re_fail_stack_t fs_body = { 0, 2, NULL };
+ regmatch_t *prev_idx_match;
+ bool prev_idx_match_malloced = false;
+
+#ifdef DEBUG
+ assert (nmatch > 1);
+ assert (mctx->state_log != NULL);
+#endif
+ if (fl_backtrack)
+ {
+ fs = &fs_body;
+ fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+ if (fs->stack == NULL)
+ return REG_ESPACE;
+ }
+ else
+ fs = NULL;
+
+ cur_node = dfa->init_node;
+ re_node_set_init_empty (&eps_via_nodes);
+
+ if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+ prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+ else
+ {
+ prev_idx_match = re_malloc (regmatch_t, nmatch);
+ if (prev_idx_match == NULL)
+ {
+ free_fail_stack_return (fs);
+ return REG_ESPACE;
+ }
+ prev_idx_match_malloced = true;
+ }
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
+ for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+ {
+ update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
+ if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+ {
+ Idx reg_idx;
+ if (fs)
+ {
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+ break;
+ if (reg_idx == nmatch)
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return free_fail_stack_return (fs);
+ }
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ }
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return REG_NOERROR;
+ }
+ }
+
+ /* Proceed to next node. */
+ cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+ &eps_via_nodes, fs);
+
+ if (BE (! REG_VALID_INDEX (cur_node), 0))
+ {
+ if (BE (cur_node == REG_ERROR, 0))
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ free_fail_stack_return (fs);
+ return REG_ESPACE;
+ }
+ if (fs)
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return REG_NOMATCH;
+ }
+ }
+ }
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+internal_function
+free_fail_stack_return (struct re_fail_stack_t *fs)
+{
+ if (fs)
+ {
+ Idx fs_idx;
+ for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+ {
+ re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+ re_free (fs->stack[fs_idx].regs);
+ }
+ re_free (fs->stack);
+ }
+ return REG_NOERROR;
+}
+
+static void
+internal_function
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+ regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, Idx nmatch)
+{
+ int type = dfa->nodes[cur_node].type;
+ if (type == OP_OPEN_SUBEXP)
+ {
+ Idx reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+ /* We are at the first node of this sub expression. */
+ if (reg_num < nmatch)
+ {
+ pmatch[reg_num].rm_so = cur_idx;
+ pmatch[reg_num].rm_eo = -1;
+ }
+ }
+ else if (type == OP_CLOSE_SUBEXP)
+ {
+ Idx reg_num = dfa->nodes[cur_node].opr.idx + 1;
+ if (reg_num < nmatch)
+ {
+ /* We are at the last node of this sub expression. */
+ if (pmatch[reg_num].rm_so < cur_idx)
+ {
+ pmatch[reg_num].rm_eo = cur_idx;
+ /* This is a non-empty match or we are not inside an optional
+ subexpression. Accept this right away. */
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+ }
+ else
+ {
+ if (dfa->nodes[cur_node].opt_subexp
+ && prev_idx_match[reg_num].rm_so != -1)
+ /* We transited through an empty match for an optional
+ subexpression, like (a?)*, and this is not the subexp's
+ first match. Copy back the old content of the registers
+ so that matches of an inner subexpression are undone as
+ well, like in ((a?))*. */
+ memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+ else
+ /* We completed a subexpression, but it may be part of
+ an optional one, so do not update PREV_IDX_MATCH. */
+ pmatch[reg_num].rm_eo = cur_idx;
+ }
+ }
+ }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+ and sift the nodes in each states according to the following rules.
+ Updated state_log will be wrote to STATE_LOG.
+
+ Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+ 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+ If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+ the LAST_NODE, we throw away the node `a'.
+ 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+ string `s' and transit to `b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+ away the node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+ thrown away, we throw away the node `a'.
+ 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+ node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+ we throw away the node `a'. */
+
+#define STATE_NODE_CONTAINS(state,node) \
+ ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+internal_function
+sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx)
+{
+ reg_errcode_t err;
+ int null_cnt = 0;
+ Idx str_idx = sctx->last_str_idx;
+ re_node_set cur_dest;
+
+#ifdef DEBUG
+ assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+ /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
+ transit to the last_node and the last_node itself. */
+ err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* Then check each states in the state_log. */
+ while (str_idx > 0)
+ {
+ /* Update counters. */
+ null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+ if (null_cnt > mctx->max_mb_elem_len)
+ {
+ memset (sctx->sifted_states, '\0',
+ sizeof (re_dfastate_t *) * str_idx);
+ re_node_set_free (&cur_dest);
+ return REG_NOERROR;
+ }
+ re_node_set_empty (&cur_dest);
+ --str_idx;
+
+ if (mctx->state_log[str_idx])
+ {
+ err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* Add all the nodes which satisfy the following conditions:
+ - It can epsilon transit to a node in CUR_DEST.
+ - It is in CUR_SRC.
+ And update state_log. */
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ err = REG_NOERROR;
+ free_return:
+ re_node_set_free (&cur_dest);
+ return err;
+}
+
+static reg_errcode_t
+internal_function
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ Idx str_idx, re_node_set *cur_dest)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+ Idx i;
+
+ /* Then build the next sifted state.
+ We build the next sifted state on `cur_dest', and update
+ `sifted_states[str_idx]' with `cur_dest'.
+ Note:
+ `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+ `cur_src' points the node_set of the old `state_log[str_idx]'
+ (with the epsilon nodes pre-filtered out). */
+ for (i = 0; i < cur_src->nelem; i++)
+ {
+ Idx prev_node = cur_src->elems[i];
+ int naccepted = 0;
+ bool ok;
+
+#ifdef DEBUG
+ re_token_type_t type = dfa->nodes[prev_node].type;
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (dfa->nodes[prev_node].accept_mb)
+ naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+ str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+ /* We don't check backreferences here.
+ See update_cur_sifted_state(). */
+ if (!naccepted
+ && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+ && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+ dfa->nexts[prev_node]))
+ naccepted = 1;
+
+ if (naccepted == 0)
+ continue;
+
+ if (sctx->limits.nelem)
+ {
+ Idx to_idx = str_idx + naccepted;
+ if (check_dst_limits (mctx, &sctx->limits,
+ dfa->nexts[prev_node], to_idx,
+ prev_node, str_idx))
+ continue;
+ }
+ ok = re_node_set_insert (cur_dest, prev_node);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions. */
+
+static reg_errcode_t
+internal_function
+clean_state_log_if_needed (re_match_context_t *mctx, Idx next_state_log_idx)
+{
+ Idx top = mctx->state_log_top;
+
+ if (next_state_log_idx >= mctx->input.bufs_len
+ || (next_state_log_idx >= mctx->input.valid_len
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ reg_errcode_t err;
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (top < next_state_log_idx)
+ {
+ memset (mctx->state_log + top + 1, '\0',
+ sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+ mctx->state_log_top = next_state_log_idx;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+ re_dfastate_t **src, Idx num)
+{
+ Idx st_idx;
+ reg_errcode_t err;
+ for (st_idx = 0; st_idx < num; ++st_idx)
+ {
+ if (dst[st_idx] == NULL)
+ dst[st_idx] = src[st_idx];
+ else if (src[st_idx] != NULL)
+ {
+ re_node_set merged_set;
+ err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+ &src[st_idx]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+ re_node_set_free (&merged_set);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+update_cur_sifted_state (const re_match_context_t *mctx,
+ re_sift_context_t *sctx, Idx str_idx,
+ re_node_set *dest_nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err = REG_NOERROR;
+ const re_node_set *candidates;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+ : &mctx->state_log[str_idx]->nodes);
+
+ if (dest_nodes->nelem == 0)
+ sctx->sifted_states[str_idx] = NULL;
+ else
+ {
+ if (candidates)
+ {
+ /* At first, add the nodes which can epsilon transit to a node in
+ DEST_NODE. */
+ err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Then, check the limitations in the current sift_context. */
+ if (sctx->limits.nelem)
+ {
+ err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+ mctx->bkref_ents, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+
+ sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (candidates && mctx->state_log[str_idx]->has_backref)
+ {
+ err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes,
+ const re_node_set *candidates)
+{
+ reg_errcode_t err = REG_NOERROR;
+ Idx i;
+
+ re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (!state->inveclosure.alloc)
+ {
+ err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < dest_nodes->nelem; i++)
+ re_node_set_merge (&state->inveclosure,
+ dfa->inveclosures + dest_nodes->elems[i]);
+ }
+ return re_node_set_add_intersect (dest_nodes, candidates,
+ &state->inveclosure);
+}
+
+static reg_errcode_t
+internal_function
+sub_epsilon_src_nodes (const re_dfa_t *dfa, Idx node, re_node_set *dest_nodes,
+ const re_node_set *candidates)
+{
+ Idx ecl_idx;
+ reg_errcode_t err;
+ re_node_set *inv_eclosure = dfa->inveclosures + node;
+ re_node_set except_nodes;
+ re_node_set_init_empty (&except_nodes);
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ Idx cur_node = inv_eclosure->elems[ecl_idx];
+ if (cur_node == node)
+ continue;
+ if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+ {
+ Idx edst1 = dfa->edests[cur_node].elems[0];
+ Idx edst2 = ((dfa->edests[cur_node].nelem > 1)
+ ? dfa->edests[cur_node].elems[1] : REG_MISSING);
+ if ((!re_node_set_contains (inv_eclosure, edst1)
+ && re_node_set_contains (dest_nodes, edst1))
+ || (REG_VALID_NONZERO_INDEX (edst2)
+ && !re_node_set_contains (inv_eclosure, edst2)
+ && re_node_set_contains (dest_nodes, edst2)))
+ {
+ err = re_node_set_add_intersect (&except_nodes, candidates,
+ dfa->inveclosures + cur_node);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&except_nodes);
+ return err;
+ }
+ }
+ }
+ }
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ Idx cur_node = inv_eclosure->elems[ecl_idx];
+ if (!re_node_set_contains (&except_nodes, cur_node))
+ {
+ Idx idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+ re_node_set_remove_at (dest_nodes, idx);
+ }
+ }
+ re_node_set_free (&except_nodes);
+ return REG_NOERROR;
+}
+
+static bool
+internal_function
+check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits,
+ Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx lim_idx, src_pos, dst_pos;
+
+ Idx dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+ Idx src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ Idx subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = mctx->bkref_ents + limits->elems[lim_idx];
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+ dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, dst_node, dst_idx,
+ dst_bkref_idx);
+ src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, src_node, src_idx,
+ src_bkref_idx);
+
+ /* In case of:
+ <src> <dst> ( <subexp> )
+ ( <subexp> ) <src> <dst>
+ ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
+ if (src_pos == dst_pos)
+ continue; /* This is unrelated limitation. */
+ else
+ return true;
+ }
+ return false;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+ Idx subexp_idx, Idx from_node, Idx bkref_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ const re_node_set *eclosures = dfa->eclosures + from_node;
+ Idx node_idx;
+
+ /* Else, we are on the boundary: examine the nodes on the epsilon
+ closure. */
+ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+ {
+ Idx node = eclosures->elems[node_idx];
+ switch (dfa->nodes[node].type)
+ {
+ case OP_BACK_REF:
+ if (bkref_idx != REG_MISSING)
+ {
+ struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+ do
+ {
+ Idx dst;
+ int cpos;
+
+ if (ent->node != node)
+ continue;
+
+ if (subexp_idx < BITSET_WORD_BITS
+ && !(ent->eps_reachable_subexps_map
+ & ((bitset_word_t) 1 << subexp_idx)))
+ continue;
+
+ /* Recurse trying to reach the OP_OPEN_SUBEXP and
+ OP_CLOSE_SUBEXP cases below. But, if the
+ destination node is the same node as the source
+ node, don't recurse because it would cause an
+ infinite loop: a regex that exhibits this behavior
+ is ()\1*\1* */
+ dst = dfa->edests[node].elems[0];
+ if (dst == from_node)
+ {
+ if (boundaries & 1)
+ return -1;
+ else /* if (boundaries & 2) */
+ return 0;
+ }
+
+ cpos =
+ check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ dst, bkref_idx);
+ if (cpos == -1 /* && (boundaries & 1) */)
+ return -1;
+ if (cpos == 0 && (boundaries & 2))
+ return 0;
+
+ if (subexp_idx < BITSET_WORD_BITS)
+ ent->eps_reachable_subexps_map
+ &= ~((bitset_word_t) 1 << subexp_idx);
+ }
+ while (ent++->more);
+ }
+ break;
+
+ case OP_OPEN_SUBEXP:
+ if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+ return -1;
+ break;
+
+ case OP_CLOSE_SUBEXP:
+ if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+ return 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit,
+ Idx subexp_idx, Idx from_node, Idx str_idx,
+ Idx bkref_idx)
+{
+ struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+ int boundaries;
+
+ /* If we are outside the range of the subexpression, return -1 or 1. */
+ if (str_idx < lim->subexp_from)
+ return -1;
+
+ if (lim->subexp_to < str_idx)
+ return 1;
+
+ /* If we are within the subexpression, return 0. */
+ boundaries = (str_idx == lim->subexp_from);
+ boundaries |= (str_idx == lim->subexp_to) << 1;
+ if (boundaries == 0)
+ return 0;
+
+ /* Else, examine epsilon closure. */
+ return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+ which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes,
+ const re_node_set *candidates, re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents, Idx str_idx)
+{
+ reg_errcode_t err;
+ Idx node_idx, lim_idx;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ Idx subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = bkref_ents + limits->elems[lim_idx];
+
+ if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+ continue; /* This is unrelated limitation. */
+
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+ if (ent->subexp_to == str_idx)
+ {
+ Idx ops_node = REG_MISSING;
+ Idx cls_node = REG_MISSING;
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ Idx node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_OPEN_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ ops_node = node;
+ else if (type == OP_CLOSE_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ cls_node = node;
+ }
+
+ /* Check the limitation of the open subexpression. */
+ /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */
+ if (REG_VALID_INDEX (ops_node))
+ {
+ err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Check the limitation of the close subexpression. */
+ if (REG_VALID_INDEX (cls_node))
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ Idx node = dest_nodes->elems[node_idx];
+ if (!re_node_set_contains (dfa->inveclosures + node,
+ cls_node)
+ && !re_node_set_contains (dfa->eclosures + node,
+ cls_node))
+ {
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ --node_idx;
+ }
+ }
+ }
+ else /* (ent->subexp_to != str_idx) */
+ {
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ Idx node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+ {
+ if (subexp_idx != dfa->nodes[node].opr.idx)
+ continue;
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ Idx str_idx, const re_node_set *candidates)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ Idx node_idx, node;
+ re_sift_context_t local_sctx;
+ Idx first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+ if (first_idx == REG_MISSING)
+ return REG_NOERROR;
+
+ local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
+
+ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+ {
+ Idx enabled_idx;
+ re_token_type_t type;
+ struct re_backref_cache_entry *entry;
+ node = candidates->elems[node_idx];
+ type = dfa->nodes[node].type;
+ /* Avoid infinite loop for the REs like "()\1+". */
+ if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+ continue;
+ if (type != OP_BACK_REF)
+ continue;
+
+ entry = mctx->bkref_ents + first_idx;
+ enabled_idx = first_idx;
+ do
+ {
+ Idx subexp_len;
+ Idx to_idx;
+ Idx dst_node;
+ bool ok;
+ re_dfastate_t *cur_state;
+
+ if (entry->node != node)
+ continue;
+ subexp_len = entry->subexp_to - entry->subexp_from;
+ to_idx = str_idx + subexp_len;
+ dst_node = (subexp_len ? dfa->nexts[node]
+ : dfa->edests[node].elems[0]);
+
+ if (to_idx > sctx->last_str_idx
+ || sctx->sifted_states[to_idx] == NULL
+ || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+ || check_dst_limits (mctx, &sctx->limits, node,
+ str_idx, dst_node, to_idx))
+ continue;
+
+ if (local_sctx.sifted_states == NULL)
+ {
+ local_sctx = *sctx;
+ err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ ok = re_node_set_insert (&local_sctx.limits, enabled_idx);
+ if (BE (! ok, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ cur_state = local_sctx.sifted_states[str_idx];
+ err = sift_states_backward (mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ if (sctx->limited_states != NULL)
+ {
+ err = merge_state_array (dfa, sctx->limited_states,
+ local_sctx.sifted_states,
+ str_idx + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.sifted_states[str_idx] = cur_state;
+ re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+ /* mctx->bkref_ents may have changed, reload the pointer. */
+ entry = mctx->bkref_ents + enabled_idx;
+ }
+ while (enabled_idx++, entry++->more);
+ }
+ err = REG_NOERROR;
+ free_return:
+ if (local_sctx.sifted_states != NULL)
+ {
+ re_node_set_free (&local_sctx.limits);
+ }
+
+ return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+internal_function
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ Idx node_idx, Idx str_idx, Idx max_str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int naccepted;
+ /* Check the node can accept `multi byte'. */
+ naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+ if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+ !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+ dfa->nexts[node_idx]))
+ /* The node can't accept the `multi byte', or the
+ destination was already thrown away, then the node
+ could't accept the current input `multi byte'. */
+ naccepted = 0;
+ /* Otherwise, it is sure that the node could accept
+ `naccepted' bytes input. */
+ return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+
+/* Functions for state transition. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte, and update STATE_LOG if necessary.
+ If STATE can accept a multibyte char/collating element/back reference
+ update the destination of STATE_LOG. */
+
+static re_dfastate_t *
+internal_function
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *state)
+{
+ re_dfastate_t **trtable;
+ unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+ /* If the current state can accept multibyte. */
+ if (BE (state->accept_mb, 0))
+ {
+ *err = transit_state_mb (mctx, state);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ /* Then decide the next state with the single byte. */
+#if 0
+ if (0)
+ /* don't use transition table */
+ return transit_state_sb (err, mctx, state);
+#endif
+
+ /* Use transition table */
+ ch = re_string_fetch_byte (&mctx->input);
+ for (;;)
+ {
+ trtable = state->trtable;
+ if (BE (trtable != NULL, 1))
+ return trtable[ch];
+
+ trtable = state->word_trtable;
+ if (BE (trtable != NULL, 1))
+ {
+ unsigned int context;
+ context
+ = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return trtable[ch + SBC_MAX];
+ else
+ return trtable[ch];
+ }
+
+ if (!build_trtable (mctx->dfa, state))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ /* Retry, we now have a transition table. */
+ }
+}
+
+/* Update the state_log if we need */
+static re_dfastate_t *
+internal_function
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *next_state)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx cur_idx = re_string_cur_idx (&mctx->input);
+
+ if (cur_idx > mctx->state_log_top)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ mctx->state_log_top = cur_idx;
+ }
+ else if (mctx->state_log[cur_idx] == 0)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ }
+ else
+ {
+ re_dfastate_t *pstate;
+ unsigned int context;
+ re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+ /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+ the destination of a multibyte char/collating element/
+ back reference. Then the next state is the union set of
+ these destinations and the results of the transition table. */
+ pstate = mctx->state_log[cur_idx];
+ log_nodes = pstate->entrance_nodes;
+ if (next_state != NULL)
+ {
+ table_nodes = next_state->entrance_nodes;
+ *err = re_node_set_init_union (&next_nodes, table_nodes,
+ log_nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ else
+ next_nodes = *log_nodes;
+ /* Note: We already add the nodes of the initial state,
+ then we don't need to add them here. */
+
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ next_state = mctx->state_log[cur_idx]
+ = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ if (table_nodes != NULL)
+ re_node_set_free (&next_nodes);
+ }
+
+ if (BE (dfa->nbackref, 0) && next_state != NULL)
+ {
+ /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+ later. We must check them here, since the back references in the
+ next state might use them. */
+ *err = check_subexp_matching_top (mctx, &next_state->nodes,
+ cur_idx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+
+ /* If the next state has back references. */
+ if (next_state->has_backref)
+ {
+ *err = transit_state_bkref (mctx, &next_state->nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ next_state = mctx->state_log[cur_idx];
+ }
+ }
+
+ return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+ multi-byte match, then look in the log for a state
+ from which to restart matching. */
+static re_dfastate_t *
+internal_function
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+ re_dfastate_t *cur_state;
+ do
+ {
+ Idx max = mctx->state_log_top;
+ Idx cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ do
+ {
+ if (++cur_str_idx > max)
+ return NULL;
+ re_string_skip_bytes (&mctx->input, 1);
+ }
+ while (mctx->state_log[cur_str_idx] == NULL);
+
+ cur_state = merge_state_with_log (err, mctx, NULL);
+ }
+ while (*err == REG_NOERROR && cur_state == NULL);
+ return cur_state;
+}
+
+/* Helper functions for transit_state. */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+ OP_OPEN_SUBEXP and which have corresponding back references in the regular
+ expression. And register them to use them later for evaluating the
+ correspoding back references. */
+
+static reg_errcode_t
+internal_function
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+ Idx str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx node_idx;
+ reg_errcode_t err;
+
+ /* TODO: This isn't efficient.
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+ for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+ {
+ Idx node = cur_nodes->elems[node_idx];
+ if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+ && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+ && (dfa->used_bkref_map
+ & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
+ {
+ err = match_ctx_add_subtop (mctx, node, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte. */
+
+static re_dfastate_t *
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *state)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ re_node_set next_nodes;
+ re_dfastate_t *next_state;
+ Idx node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+ unsigned int context;
+
+ *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+ {
+ Idx cur_node = state->nodes.elems[node_cnt];
+ if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+ {
+ *err = re_node_set_merge (&next_nodes,
+ dfa->eclosures + dfa->nexts[cur_node]);
+ if (BE (*err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return NULL;
+ }
+ }
+ }
+ context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+ next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ re_node_set_free (&next_nodes);
+ re_string_skip_bytes (&mctx->input, 1);
+ return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+internal_function
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ Idx i;
+
+ for (i = 0; i < pstate->nodes.nelem; ++i)
+ {
+ re_node_set dest_nodes, *new_nodes;
+ Idx cur_node_idx = pstate->nodes.elems[i];
+ int naccepted;
+ Idx dest_idx;
+ unsigned int context;
+ re_dfastate_t *dest_state;
+
+ if (!dfa->nodes[cur_node_idx].accept_mb)
+ continue;
+
+ if (dfa->nodes[cur_node_idx].constraint)
+ {
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input),
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+ context))
+ continue;
+ }
+
+ /* How many bytes the node can accept? */
+ naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+ re_string_cur_idx (&mctx->input));
+ if (naccepted == 0)
+ continue;
+
+ /* The node can accepts `naccepted' bytes. */
+ dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+ mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+ : mctx->max_mb_elem_len);
+ err = clean_state_log_if_needed (mctx, dest_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+#ifdef DEBUG
+ assert (dfa->nexts[cur_node_idx] != REG_MISSING);
+#endif
+ new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
+
+ dest_state = mctx->state_log[dest_idx];
+ if (dest_state == NULL)
+ dest_nodes = *new_nodes;
+ else
+ {
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes, new_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ context = re_string_context_at (&mctx->input, dest_idx - 1,
+ mctx->eflags);
+ mctx->state_log[dest_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ if (dest_state != NULL)
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+internal_function
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ Idx i;
+ Idx cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ for (i = 0; i < nodes->nelem; ++i)
+ {
+ Idx dest_str_idx, prev_nelem, bkc_idx;
+ Idx node_idx = nodes->elems[i];
+ unsigned int context;
+ const re_token_t *node = dfa->nodes + node_idx;
+ re_node_set *new_dest_nodes;
+
+ /* Check whether `node' is a backreference or not. */
+ if (node->type != OP_BACK_REF)
+ continue;
+
+ if (node->constraint)
+ {
+ context = re_string_context_at (&mctx->input, cur_str_idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ continue;
+ }
+
+ /* `node' is a backreference.
+ Check the substring which the substring matched. */
+ bkc_idx = mctx->nbkref_ents;
+ err = get_subexp (mctx, node_idx, cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* And add the epsilon closures (which is `new_dest_nodes') of
+ the backreference to appropriate state_log. */
+#ifdef DEBUG
+ assert (dfa->nexts[node_idx] != REG_MISSING);
+#endif
+ for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+ {
+ Idx subexp_len;
+ re_dfastate_t *dest_state;
+ struct re_backref_cache_entry *bkref_ent;
+ bkref_ent = mctx->bkref_ents + bkc_idx;
+ if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+ continue;
+ subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+ new_dest_nodes = (subexp_len == 0
+ ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+ : dfa->eclosures + dfa->nexts[node_idx]);
+ dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+ - bkref_ent->subexp_from);
+ context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+ mctx->eflags);
+ dest_state = mctx->state_log[dest_str_idx];
+ prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+ : mctx->state_log[cur_str_idx]->nodes.nelem);
+ /* Add `new_dest_node' to state_log. */
+ if (dest_state == NULL)
+ {
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, new_dest_nodes,
+ context);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ re_node_set dest_nodes;
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes,
+ new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&dest_nodes);
+ goto free_return;
+ }
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ /* We need to check recursively if the backreference can epsilon
+ transit. */
+ if (subexp_len == 0
+ && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+ {
+ err = check_subexp_matching_top (mctx, new_dest_nodes,
+ cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ err = transit_state_bkref (mctx, new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ }
+ }
+ err = REG_NOERROR;
+ free_return:
+ return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+ at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+ Note that we might collect inappropriate candidates here.
+ However, the cost of checking them strictly here is too high, then we
+ delay these checking for prune_impossible_nodes(). */
+
+static reg_errcode_t
+internal_function
+get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ Idx subexp_num, sub_top_idx;
+ const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+ /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */
+ Idx cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+ if (cache_idx != REG_MISSING)
+ {
+ const struct re_backref_cache_entry *entry
+ = mctx->bkref_ents + cache_idx;
+ do
+ if (entry->node == bkref_node)
+ return REG_NOERROR; /* We already checked it. */
+ while (entry++->more);
+ }
+
+ subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+ /* For each sub expression */
+ for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+ {
+ reg_errcode_t err;
+ re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+ re_sub_match_last_t *sub_last;
+ Idx sub_last_idx, sl_str, bkref_str_off;
+
+ if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+ continue; /* It isn't related. */
+
+ sl_str = sub_top->str_idx;
+ bkref_str_off = bkref_str_idx;
+ /* At first, check the last node of sub expressions we already
+ evaluated. */
+ for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+ {
+ regoff_t sl_str_diff;
+ sub_last = sub_top->lasts[sub_last_idx];
+ sl_str_diff = sub_last->str_idx - sl_str;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_diff > 0)
+ {
+ if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+ {
+ /* Not enough chars for a successful match. */
+ if (bkref_str_off + sl_str_diff > mctx->input.len)
+ break;
+
+ err = clean_state_log_if_needed (mctx,
+ bkref_str_off
+ + sl_str_diff);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+ /* We don't need to search this sub expression any more. */
+ break;
+ }
+ bkref_str_off += sl_str_diff;
+ sl_str += sl_str_diff;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+
+ /* Reload buf, since the preceding call might have reallocated
+ the buffer. */
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (sub_last_idx < sub_top->nlasts)
+ continue;
+ if (sub_last_idx > 0)
+ ++sl_str;
+ /* Then, search for the other last nodes of the sub expression. */
+ for (; sl_str <= bkref_str_idx; ++sl_str)
+ {
+ Idx cls_node;
+ regoff_t sl_str_off;
+ const re_node_set *nodes;
+ sl_str_off = sl_str - sub_top->str_idx;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_off > 0)
+ {
+ if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+ {
+ /* If we are at the end of the input, we cannot match. */
+ if (bkref_str_off >= mctx->input.len)
+ break;
+
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (buf [bkref_str_off++] != buf[sl_str - 1])
+ break; /* We don't need to search this sub expression
+ any more. */
+ }
+ if (mctx->state_log[sl_str] == NULL)
+ continue;
+ /* Does this state have a ')' of the sub expression? */
+ nodes = &mctx->state_log[sl_str]->nodes;
+ cls_node = find_subexp_node (dfa, nodes, subexp_num,
+ OP_CLOSE_SUBEXP);
+ if (cls_node == REG_MISSING)
+ continue; /* No. */
+ if (sub_top->path == NULL)
+ {
+ sub_top->path = calloc (sizeof (state_array_t),
+ sl_str - sub_top->str_idx + 1);
+ if (sub_top->path == NULL)
+ return REG_ESPACE;
+ }
+ /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+ in the current context? */
+ err = check_arrival (mctx, sub_top->path, sub_top->node,
+ sub_top->str_idx, cls_node, sl_str,
+ OP_CLOSE_SUBEXP);
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+ if (BE (sub_last == NULL, 0))
+ return REG_ESPACE;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+ if (err == REG_NOMATCH)
+ continue;
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp(). */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+ If it can arrive, register the sub expression expressed with SUB_TOP
+ and SUB_LAST. */
+
+static reg_errcode_t
+internal_function
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+ re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str)
+{
+ reg_errcode_t err;
+ Idx to_idx;
+ /* Can the subexpression arrive the back reference? */
+ err = check_arrival (mctx, &sub_last->path, sub_last->node,
+ sub_last->str_idx, bkref_node, bkref_str,
+ OP_OPEN_SUBEXP);
+ if (err != REG_NOERROR)
+ return err;
+ err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+ sub_last->str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+ return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+ Search '(' if FL_OPEN, or search ')' otherwise.
+ TODO: This function isn't efficient...
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+
+static Idx
+internal_function
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+ Idx subexp_idx, int type)
+{
+ Idx cls_idx;
+ for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+ {
+ Idx cls_node = nodes->elems[cls_idx];
+ const re_token_t *node = dfa->nodes + cls_node;
+ if (node->type == type
+ && node->opr.idx == subexp_idx)
+ return cls_node;
+ }
+ return REG_MISSING;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+ LAST_NODE at LAST_STR. We record the path onto PATH since it will be
+ heavily reused.
+ Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */
+
+static reg_errcode_t
+internal_function
+check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node,
+ Idx top_str, Idx last_node, Idx last_str, int type)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err = REG_NOERROR;
+ Idx subexp_num, backup_cur_idx, str_idx, null_cnt;
+ re_dfastate_t *cur_state = NULL;
+ re_node_set *cur_nodes, next_nodes;
+ re_dfastate_t **backup_state_log;
+ unsigned int context;
+
+ subexp_num = dfa->nodes[top_node].opr.idx;
+ /* Extend the buffer if we need. */
+ if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+ {
+ re_dfastate_t **new_array;
+ Idx old_alloc = path->alloc;
+ Idx new_alloc = old_alloc + last_str + mctx->max_mb_elem_len + 1;
+ if (BE (new_alloc < old_alloc, 0)
+ || BE (SIZE_MAX / sizeof (re_dfastate_t *) < new_alloc, 0))
+ return REG_ESPACE;
+ new_array = re_realloc (path->array, re_dfastate_t *, new_alloc);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ path->array = new_array;
+ path->alloc = new_alloc;
+ memset (new_array + old_alloc, '\0',
+ sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+ }
+
+ str_idx = path->next_idx ? path->next_idx : top_str;
+
+ /* Temporary modify MCTX. */
+ backup_state_log = mctx->state_log;
+ backup_cur_idx = mctx->input.cur_idx;
+ mctx->state_log = path->array;
+ mctx->input.cur_idx = str_idx;
+
+ /* Setup initial node set. */
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ if (str_idx == top_str)
+ {
+ err = re_node_set_init_1 (&next_nodes, top_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ cur_state = mctx->state_log[str_idx];
+ if (cur_state && cur_state->has_backref)
+ {
+ err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ re_node_set_init_empty (&next_nodes);
+ }
+ if (str_idx == top_str || (cur_state && cur_state->has_backref))
+ {
+ if (next_nodes.nelem)
+ {
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ }
+
+ for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+ {
+ re_node_set_empty (&next_nodes);
+ if (mctx->state_log[str_idx + 1])
+ {
+ err = re_node_set_merge (&next_nodes,
+ &mctx->state_log[str_idx + 1]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ if (cur_state)
+ {
+ err = check_arrival_add_next_nodes (mctx, str_idx,
+ &cur_state->non_eps_nodes,
+ &next_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ ++str_idx;
+ if (next_nodes.nelem)
+ {
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+ }
+ re_node_set_free (&next_nodes);
+ cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+ : &mctx->state_log[last_str]->nodes);
+ path->next_idx = str_idx;
+
+ /* Fix MCTX. */
+ mctx->state_log = backup_state_log;
+ mctx->input.cur_idx = backup_cur_idx;
+
+ /* Then check the current node set has the node LAST_NODE. */
+ if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+ return REG_NOERROR;
+
+ return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival. */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+ to NEXT_NODES.
+ TODO: This function is similar to the functions transit_state*(),
+ however this function has many additional works.
+ Can't we unify them? */
+
+static reg_errcode_t
+internal_function
+check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx,
+ re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ bool ok;
+ Idx cur_idx;
+ reg_errcode_t err = REG_NOERROR;
+ re_node_set union_set;
+ re_node_set_init_empty (&union_set);
+ for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+ {
+ int naccepted = 0;
+ Idx cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
+ re_token_type_t type = dfa->nodes[cur_node].type;
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (dfa->nodes[cur_node].accept_mb)
+ {
+ naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+ str_idx);
+ if (naccepted > 1)
+ {
+ re_dfastate_t *dest_state;
+ Idx next_node = dfa->nexts[cur_node];
+ Idx next_idx = str_idx + naccepted;
+ dest_state = mctx->state_log[next_idx];
+ re_node_set_empty (&union_set);
+ if (dest_state)
+ {
+ err = re_node_set_merge (&union_set, &dest_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ ok = re_node_set_insert (&union_set, next_node);
+ if (BE (! ok, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+ &union_set);
+ if (BE (mctx->state_log[next_idx] == NULL
+ && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (naccepted
+ || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+ {
+ ok = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+ if (BE (! ok, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ }
+ }
+ re_node_set_free (&union_set);
+ return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+ CUR_NODES, however exclude the nodes which are:
+ - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+ - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+ Idx ex_subexp, int type)
+{
+ reg_errcode_t err;
+ Idx idx, outside_node;
+ re_node_set new_nodes;
+#ifdef DEBUG
+ assert (cur_nodes->nelem);
+#endif
+ err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Create a new node set NEW_NODES with the nodes which are epsilon
+ closures of the node in CUR_NODES. */
+
+ for (idx = 0; idx < cur_nodes->nelem; ++idx)
+ {
+ Idx cur_node = cur_nodes->elems[idx];
+ const re_node_set *eclosure = dfa->eclosures + cur_node;
+ outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+ if (outside_node == REG_MISSING)
+ {
+ /* There are no problematic nodes, just merge them. */
+ err = re_node_set_merge (&new_nodes, eclosure);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ /* There are problematic nodes, re-calculate incrementally. */
+ err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ }
+ re_node_set_free (cur_nodes);
+ *cur_nodes = new_nodes;
+ return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+ Check incrementally the epsilon closure of TARGET, and if it isn't
+ problematic append it to DST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+ Idx target, Idx ex_subexp, int type)
+{
+ Idx cur_node;
+ for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+ {
+ bool ok;
+
+ if (dfa->nodes[cur_node].type == type
+ && dfa->nodes[cur_node].opr.idx == ex_subexp)
+ {
+ if (type == OP_CLOSE_SUBEXP)
+ {
+ ok = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ }
+ break;
+ }
+ ok = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (! ok, 0))
+ return REG_ESPACE;
+ if (dfa->edests[cur_node].nelem == 0)
+ break;
+ if (dfa->edests[cur_node].nelem == 2)
+ {
+ reg_errcode_t err;
+ err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+ dfa->edests[cur_node].elems[1],
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ cur_node = dfa->edests[cur_node].elems[0];
+ }
+ return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+ destination of the back references by the appropriate entry
+ in MCTX->BKREF_ENTS. */
+
+static reg_errcode_t
+internal_function
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+ Idx cur_str, Idx subexp_num, int type)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ Idx cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+ struct re_backref_cache_entry *ent;
+
+ if (cache_idx_start == REG_MISSING)
+ return REG_NOERROR;
+
+ restart:
+ ent = mctx->bkref_ents + cache_idx_start;
+ do
+ {
+ Idx to_idx, next_node;
+
+ /* Is this entry ENT is appropriate? */
+ if (!re_node_set_contains (cur_nodes, ent->node))
+ continue; /* No. */
+
+ to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+ /* Calculate the destination of the back reference, and append it
+ to MCTX->STATE_LOG. */
+ if (to_idx == cur_str)
+ {
+ /* The backreference did epsilon transit, we must re-check all the
+ node in the current state. */
+ re_node_set new_dests;
+ reg_errcode_t err2, err3;
+ next_node = dfa->edests[ent->node].elems[0];
+ if (re_node_set_contains (cur_nodes, next_node))
+ continue;
+ err = re_node_set_init_1 (&new_dests, next_node);
+ err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+ err3 = re_node_set_merge (cur_nodes, &new_dests);
+ re_node_set_free (&new_dests);
+ if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+ || err3 != REG_NOERROR, 0))
+ {
+ err = (err != REG_NOERROR ? err
+ : (err2 != REG_NOERROR ? err2 : err3));
+ return err;
+ }
+ /* TODO: It is still inefficient... */
+ goto restart;
+ }
+ else
+ {
+ re_node_set union_set;
+ next_node = dfa->nexts[ent->node];
+ if (mctx->state_log[to_idx])
+ {
+ bool ok;
+ if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+ next_node))
+ continue;
+ err = re_node_set_init_copy (&union_set,
+ &mctx->state_log[to_idx]->nodes);
+ ok = re_node_set_insert (&union_set, next_node);
+ if (BE (err != REG_NOERROR || ! ok, 0))
+ {
+ re_node_set_free (&union_set);
+ err = err != REG_NOERROR ? err : REG_ESPACE;
+ return err;
+ }
+ }
+ else
+ {
+ err = re_node_set_init_1 (&union_set, next_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+ re_node_set_free (&union_set);
+ if (BE (mctx->state_log[to_idx] == NULL
+ && err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ while (ent++->more);
+ return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+ Return true if successful. */
+
+static bool
+internal_function
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
+{
+ reg_errcode_t err;
+ Idx i, j;
+ int ch;
+ bool need_word_trtable = false;
+ bitset_word_t elem, mask;
+ bool dests_node_malloced = false;
+ bool dest_states_malloced = false;
+ Idx ndests; /* Number of the destination states from `state'. */
+ re_dfastate_t **trtable;
+ re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+ re_node_set follows, *dests_node;
+ bitset_t *dests_ch;
+ bitset_t acceptable;
+
+ struct dests_alloc
+ {
+ re_node_set dests_node[SBC_MAX];
+ bitset_t dests_ch[SBC_MAX];
+ } *dests_alloc;
+
+ /* We build DFA states which corresponds to the destination nodes
+ from `state'. `dests_node[i]' represents the nodes which i-th
+ destination state contains, and `dests_ch[i]' represents the
+ characters which i-th destination state accepts. */
+ if (__libc_use_alloca (sizeof (struct dests_alloc)))
+ dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
+ else
+ {
+ dests_alloc = re_malloc (struct dests_alloc, 1);
+ if (BE (dests_alloc == NULL, 0))
+ return false;
+ dests_node_malloced = true;
+ }
+ dests_node = dests_alloc->dests_node;
+ dests_ch = dests_alloc->dests_ch;
+
+ /* Initialize transiton table. */
+ state->word_trtable = state->trtable = NULL;
+
+ /* At first, group all nodes belonging to `state' into several
+ destinations. */
+ ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+ if (BE (! REG_VALID_NONZERO_INDEX (ndests), 0))
+ {
+ if (dests_node_malloced)
+ free (dests_alloc);
+ if (ndests == 0)
+ {
+ state->trtable = (re_dfastate_t **)
+ calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ return true;
+ }
+ return false;
+ }
+
+ err = re_node_set_alloc (&follows, ndests + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+
+ /* Avoid arithmetic overflow in size calculation. */
+ if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX)
+ / (3 * sizeof (re_dfastate_t *)))
+ < ndests),
+ 0))
+ goto out_free;
+
+ if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
+ + ndests * 3 * sizeof (re_dfastate_t *)))
+ dest_states = (re_dfastate_t **)
+ alloca (ndests * 3 * sizeof (re_dfastate_t *));
+ else
+ {
+ dest_states = (re_dfastate_t **)
+ malloc (ndests * 3 * sizeof (re_dfastate_t *));
+ if (BE (dest_states == NULL, 0))
+ {
+out_free:
+ if (dest_states_malloced)
+ free (dest_states);
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+ if (dests_node_malloced)
+ free (dests_alloc);
+ return false;
+ }
+ dest_states_malloced = true;
+ }
+ dest_states_word = dest_states + ndests;
+ dest_states_nl = dest_states_word + ndests;
+ bitset_empty (acceptable);
+
+ /* Then build the states for all destinations. */
+ for (i = 0; i < ndests; ++i)
+ {
+ Idx next_node;
+ re_node_set_empty (&follows);
+ /* Merge the follows of this destination states. */
+ for (j = 0; j < dests_node[i].nelem; ++j)
+ {
+ next_node = dfa->nexts[dests_node[i].elems[j]];
+ if (next_node != REG_MISSING)
+ {
+ err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ }
+ dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+ if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ /* If the new state has context constraint,
+ build appropriate states for these contexts. */
+ if (dest_states[i]->has_constraint)
+ {
+ dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_WORD);
+ if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+
+ if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+ need_word_trtable = true;
+
+ dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_NEWLINE);
+ if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ else
+ {
+ dest_states_word[i] = dest_states[i];
+ dest_states_nl[i] = dest_states[i];
+ }
+ bitset_merge (acceptable, dests_ch[i]);
+ }
+
+ if (!BE (need_word_trtable, 0))
+ {
+ /* We don't care about whether the following character is a word
+ character, or we are in a single-byte character set so we can
+ discern by looking at the character code: allocate a
+ 256-entry transition table. */
+ trtable = state->trtable =
+ (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_WORDS; ++i)
+ for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ if (dfa->word_char[i] & mask)
+ trtable[ch] = dest_states_word[j];
+ else
+ trtable[ch] = dest_states[j];
+ }
+ }
+ else
+ {
+ /* We care about whether the following character is a word
+ character, and we are in a multi-byte character set: discern
+ by looking at the character code: build two 256-entry
+ transition tables, one starting at trtable[0] and one
+ starting at trtable[SBC_MAX]. */
+ trtable = state->word_trtable =
+ (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_WORDS; ++i)
+ for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ trtable[ch] = dest_states[j];
+ trtable[ch + SBC_MAX] = dest_states_word[j];
+ }
+ }
+
+ /* new line */
+ if (bitset_contain (acceptable, NEWLINE_CHAR))
+ {
+ /* The current state accepts newline character. */
+ for (j = 0; j < ndests; ++j)
+ if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+ {
+ /* k-th destination accepts newline character. */
+ trtable[NEWLINE_CHAR] = dest_states_nl[j];
+ if (need_word_trtable)
+ trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+ /* There must be only one destination which accepts
+ newline. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+
+ if (dest_states_malloced)
+ free (dest_states);
+
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+
+ if (dests_node_malloced)
+ free (dests_alloc);
+
+ return true;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+ Then for all destinations, set the nodes belonging to the destination
+ to DESTS_NODE[i] and set the characters accepted by the destination
+ to DEST_CH[i]. This function return the number of destinations. */
+
+static Idx
+internal_function
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+ re_node_set *dests_node, bitset_t *dests_ch)
+{
+ reg_errcode_t err;
+ bool ok;
+ Idx i, j, k;
+ Idx ndests; /* Number of the destinations from `state'. */
+ bitset_t accepts; /* Characters a node can accept. */
+ const re_node_set *cur_nodes = &state->nodes;
+ bitset_empty (accepts);
+ ndests = 0;
+
+ /* For all the nodes belonging to `state', */
+ for (i = 0; i < cur_nodes->nelem; ++i)
+ {
+ re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+ re_token_type_t type = node->type;
+ unsigned int constraint = node->constraint;
+
+ /* Enumerate all single byte character this node can accept. */
+ if (type == CHARACTER)
+ bitset_set (accepts, node->opr.c);
+ else if (type == SIMPLE_BRACKET)
+ {
+ bitset_merge (accepts, node->opr.sbcset);
+ }
+ else if (type == OP_PERIOD)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ bitset_merge (accepts, dfa->sb_char);
+ else
+#endif
+ bitset_set_all (accepts);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == OP_UTF8_PERIOD)
+ {
+ if (ASCII_CHARS % BITSET_WORD_BITS == 0)
+ memset (accepts, -1, ASCII_CHARS / CHAR_BIT);
+ else
+ bitset_merge (accepts, utf8_sb_map);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#endif
+ else
+ continue;
+
+ /* Check the `accepts' and sift the characters which are not
+ match it the context. */
+ if (constraint)
+ {
+ if (constraint & NEXT_NEWLINE_CONSTRAINT)
+ {
+ bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+ bitset_empty (accepts);
+ if (accepts_newline)
+ bitset_set (accepts, NEWLINE_CHAR);
+ else
+ continue;
+ }
+ if (constraint & NEXT_ENDBUF_CONSTRAINT)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+
+ if (constraint & NEXT_WORD_CONSTRAINT)
+ {
+ bitset_word_t any_set = 0;
+ if (type == CHARACTER && !node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ if (constraint & NEXT_NOTWORD_CONSTRAINT)
+ {
+ bitset_word_t any_set = 0;
+ if (type == CHARACTER && node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= ~dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ }
+
+ /* Then divide `accepts' into DFA states, or create a new
+ state. Above, we make sure that accepts is not empty. */
+ for (j = 0; j < ndests; ++j)
+ {
+ bitset_t intersec; /* Intersection sets, see below. */
+ bitset_t remains;
+ /* Flags, see below. */
+ bitset_word_t has_intersec, not_subset, not_consumed;
+
+ /* Optimization, skip if this state doesn't accept the character. */
+ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+ continue;
+
+ /* Enumerate the intersection set of this state and `accepts'. */
+ has_intersec = 0;
+ for (k = 0; k < BITSET_WORDS; ++k)
+ has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+ /* And skip if the intersection set is empty. */
+ if (!has_intersec)
+ continue;
+
+ /* Then check if this state is a subset of `accepts'. */
+ not_subset = not_consumed = 0;
+ for (k = 0; k < BITSET_WORDS; ++k)
+ {
+ not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+ not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+ }
+
+ /* If this state isn't a subset of `accepts', create a
+ new group state, which has the `remains'. */
+ if (not_subset)
+ {
+ bitset_copy (dests_ch[ndests], remains);
+ bitset_copy (dests_ch[j], intersec);
+ err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ }
+
+ /* Put the position in the current group. */
+ ok = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+ if (BE (! ok, 0))
+ goto error_return;
+
+ /* If all characters are consumed, go to next node. */
+ if (!not_consumed)
+ break;
+ }
+ /* Some characters remain, create a new group. */
+ if (j == ndests)
+ {
+ bitset_copy (dests_ch[ndests], accepts);
+ err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ bitset_empty (accepts);
+ }
+ }
+ return ndests;
+ error_return:
+ for (j = 0; j < ndests; ++j)
+ re_node_set_free (dests_node + j);
+ return REG_MISSING;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+ Return the number of the bytes the node accepts.
+ STR_IDX is the current index of the input string.
+
+ This function handles the nodes which can accept one character, or
+ one collating element like '.', '[a-z]', opposite to the other nodes
+ can only accept one byte. */
+
+static int
+internal_function
+check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
+ const re_string_t *input, Idx str_idx)
+{
+ const re_token_t *node = dfa->nodes + node_idx;
+ int char_len, elem_len;
+ Idx i;
+
+ if (BE (node->type == OP_UTF8_PERIOD, 0))
+ {
+ unsigned char c = re_string_byte_at (input, str_idx), d;
+ if (BE (c < 0xc2, 1))
+ return 0;
+
+ if (str_idx + 2 > input->len)
+ return 0;
+
+ d = re_string_byte_at (input, str_idx + 1);
+ if (c < 0xe0)
+ return (d < 0x80 || d > 0xbf) ? 0 : 2;
+ else if (c < 0xf0)
+ {
+ char_len = 3;
+ if (c == 0xe0 && d < 0xa0)
+ return 0;
+ }
+ else if (c < 0xf8)
+ {
+ char_len = 4;
+ if (c == 0xf0 && d < 0x90)
+ return 0;
+ }
+ else if (c < 0xfc)
+ {
+ char_len = 5;
+ if (c == 0xf8 && d < 0x88)
+ return 0;
+ }
+ else if (c < 0xfe)
+ {
+ char_len = 6;
+ if (c == 0xfc && d < 0x84)
+ return 0;
+ }
+ else
+ return 0;
+
+ if (str_idx + char_len > input->len)
+ return 0;
+
+ for (i = 1; i < char_len; ++i)
+ {
+ d = re_string_byte_at (input, str_idx + i);
+ if (d < 0x80 || d > 0xbf)
+ return 0;
+ }
+ return char_len;
+ }
+
+ char_len = re_string_char_size_at (input, str_idx);
+ if (node->type == OP_PERIOD)
+ {
+ if (char_len <= 1)
+ return 0;
+ /* FIXME: I don't think this if is needed, as both '\n'
+ and '\0' are char_len == 1. */
+ /* '.' accepts any one character except the following two cases. */
+ if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+ re_string_byte_at (input, str_idx) == '\n') ||
+ ((dfa->syntax & RE_DOT_NOT_NULL) &&
+ re_string_byte_at (input, str_idx) == '\0'))
+ return 0;
+ return char_len;
+ }
+
+ elem_len = re_string_elem_size_at (input, str_idx);
+ if ((elem_len <= 1 && char_len <= 1) || char_len == 0)
+ return 0;
+
+ if (node->type == COMPLEX_BRACKET)
+ {
+ const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+ const unsigned char *pin
+ = ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+ Idx j;
+ uint32_t nrules;
+# endif /* _LIBC */
+ int match_len = 0;
+ wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+ ? re_string_wchar_at (input, str_idx) : 0);
+
+ /* match with multibyte character? */
+ for (i = 0; i < cset->nmbchars; ++i)
+ if (wc == cset->mbchars[i])
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ /* match with character_class? */
+ for (i = 0; i < cset->nchar_classes; ++i)
+ {
+ wctype_t wt = cset->char_classes[i];
+ if (__iswctype (wc, wt))
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+# ifdef _LIBC
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ unsigned int in_collseq = 0;
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra;
+ const char *collseqwc;
+ int32_t idx;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ /* match with collating_symbol? */
+ if (cset->ncoll_syms)
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ for (i = 0; i < cset->ncoll_syms; ++i)
+ {
+ const unsigned char *coll_sym = extra + cset->coll_syms[i];
+ /* Compare the length of input collating element and
+ the length of current collating element. */
+ if (*coll_sym != elem_len)
+ continue;
+ /* Compare each bytes. */
+ for (j = 0; j < *coll_sym; j++)
+ if (pin[j] != coll_sym[1 + j])
+ break;
+ if (j == *coll_sym)
+ {
+ /* Match if every bytes is equal. */
+ match_len = j;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ if (cset->nranges)
+ {
+ if (elem_len <= char_len)
+ {
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ in_collseq = __collseq_table_lookup (collseqwc, wc);
+ }
+ else
+ in_collseq = find_collation_sequence_value (pin, elem_len);
+ }
+ /* match with range expression? */
+ for (i = 0; i < cset->nranges; ++i)
+ if (cset->range_starts[i] <= in_collseq
+ && in_collseq <= cset->range_ends[i])
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with equivalence_class? */
+ if (cset->nequiv_classes)
+ {
+ const unsigned char *cp = pin;
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+ idx = findidx (&cp);
+ if (idx > 0)
+ for (i = 0; i < cset->nequiv_classes; ++i)
+ {
+ int32_t equiv_class_idx = cset->equiv_classes[i];
+ size_t weight_len = weights[idx];
+ if (weight_len == weights[equiv_class_idx])
+ {
+ Idx cnt = 0;
+ while (cnt <= weight_len
+ && (weights[equiv_class_idx + 1 + cnt]
+ == weights[idx + 1 + cnt]))
+ ++cnt;
+ if (cnt > weight_len)
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ }
+ }
+ else
+# endif /* _LIBC */
+ {
+ /* match with range expression? */
+#if __GNUC__ >= 2 && ! (__STDC_VERSION__ < 199901L && __STRICT_ANSI__)
+ wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+ wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+ cmp_buf[2] = wc;
+#endif
+ for (i = 0; i < cset->nranges; ++i)
+ {
+ cmp_buf[0] = cset->range_starts[i];
+ cmp_buf[4] = cset->range_ends[i];
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ check_node_accept_bytes_match:
+ if (!cset->non_match)
+ return match_len;
+ else
+ {
+ if (match_len > 0)
+ return 0;
+ else
+ return (elem_len > char_len) ? elem_len : char_len;
+ }
+ }
+ return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+internal_function
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
+{
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules == 0)
+ {
+ if (mbs_len == 1)
+ {
+ /* No valid character. Match it as a single byte character. */
+ const unsigned char *collseq = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ return collseq[mbs[0]];
+ }
+ return UINT_MAX;
+ }
+ else
+ {
+ int32_t idx;
+ const unsigned char *extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ int32_t extrasize = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+ for (idx = 0; idx < extrasize;)
+ {
+ int mbs_cnt;
+ bool found = false;
+ int32_t elem_mbs_len;
+ /* Skip the name of collating element name. */
+ idx = idx + extra[idx] + 1;
+ elem_mbs_len = extra[idx++];
+ if (mbs_len == elem_mbs_len)
+ {
+ for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+ if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+ break;
+ if (mbs_cnt == elem_mbs_len)
+ /* Found the entry. */
+ found = true;
+ }
+ /* Skip the byte sequence of the collating element. */
+ idx += elem_mbs_len;
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ /* Skip the wide char sequence of the collating element. */
+ idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+ /* If we found the entry, return the sequence value. */
+ if (found)
+ return *(uint32_t *) (extra + idx);
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ }
+ return UINT_MAX;
+ }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+ byte of the INPUT. */
+
+static bool
+internal_function
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+ Idx idx)
+{
+ unsigned char ch;
+ ch = re_string_byte_at (&mctx->input, idx);
+ switch (node->type)
+ {
+ case CHARACTER:
+ if (node->opr.c != ch)
+ return false;
+ break;
+
+ case SIMPLE_BRACKET:
+ if (!bitset_contain (node->opr.sbcset, ch))
+ return false;
+ break;
+
+#ifdef RE_ENABLE_I18N
+ case OP_UTF8_PERIOD:
+ if (ch >= ASCII_CHARS)
+ return false;
+ /* FALLTHROUGH */
+#endif
+ case OP_PERIOD:
+ if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+ || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+
+ if (node->constraint)
+ {
+ /* The node has constraints. Check whether the current context
+ satisfies the constraints. */
+ unsigned int context = re_string_context_at (&mctx->input, idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ return false;
+ }
+
+ return true;
+}
+
+/* Extend the buffers, if the buffers have run out. */
+
+static reg_errcode_t
+internal_function
+extend_buffers (re_match_context_t *mctx)
+{
+ reg_errcode_t ret;
+ re_string_t *pstr = &mctx->input;
+
+ /* Avoid overflow. */
+ if (BE (SIZE_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0))
+ return REG_ESPACE;
+
+ /* Double the lengthes of the buffers. */
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ if (mctx->state_log != NULL)
+ {
+ /* And double the length of state_log. */
+ /* XXX We have no indication of the size of this buffer. If this
+ allocation fail we have no indication that the state_log array
+ does not have the right size. */
+ re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+ pstr->bufs_len + 1);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->state_log = new_array;
+ }
+
+ /* Then reconstruct the buffers. */
+ if (pstr->icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ }
+ return REG_NOERROR;
+}
+
+
+/* Functions for matching context. */
+
+/* Initialize MCTX. */
+
+static reg_errcode_t
+internal_function
+match_ctx_init (re_match_context_t *mctx, int eflags, Idx n)
+{
+ mctx->eflags = eflags;
+ mctx->match_last = REG_MISSING;
+ if (n > 0)
+ {
+ /* Avoid overflow. */
+ size_t max_object_size =
+ MAX (sizeof (struct re_backref_cache_entry),
+ sizeof (re_sub_match_top_t *));
+ if (BE (SIZE_MAX / max_object_size < n, 0))
+ return REG_ESPACE;
+
+ mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+ mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+ if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+ return REG_ESPACE;
+ }
+ /* Already zero-ed by the caller.
+ else
+ mctx->bkref_ents = NULL;
+ mctx->nbkref_ents = 0;
+ mctx->nsub_tops = 0; */
+ mctx->abkref_ents = n;
+ mctx->max_mb_elem_len = 1;
+ mctx->asub_tops = n;
+ return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+ This function must be invoked when the matcher changes the start index
+ of the input, or changes the input string. */
+
+static void
+internal_function
+match_ctx_clean (re_match_context_t *mctx)
+{
+ Idx st_idx;
+ for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+ {
+ Idx sl_idx;
+ re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+ for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+ {
+ re_sub_match_last_t *last = top->lasts[sl_idx];
+ re_free (last->path.array);
+ re_free (last);
+ }
+ re_free (top->lasts);
+ if (top->path)
+ {
+ re_free (top->path->array);
+ re_free (top->path);
+ }
+ free (top);
+ }
+
+ mctx->nsub_tops = 0;
+ mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX. */
+
+static void
+internal_function
+match_ctx_free (re_match_context_t *mctx)
+{
+ /* First, free all the memory associated with MCTX->SUB_TOPS. */
+ match_ctx_clean (mctx);
+ re_free (mctx->sub_tops);
+ re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+ Note that we assume that caller never call this function with duplicate
+ entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+internal_function
+match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from,
+ Idx to)
+{
+ if (mctx->nbkref_ents >= mctx->abkref_ents)
+ {
+ struct re_backref_cache_entry* new_entry;
+ new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+ mctx->abkref_ents * 2);
+ if (BE (new_entry == NULL, 0))
+ {
+ re_free (mctx->bkref_ents);
+ return REG_ESPACE;
+ }
+ mctx->bkref_ents = new_entry;
+ memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+ sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+ mctx->abkref_ents *= 2;
+ }
+ if (mctx->nbkref_ents > 0
+ && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+ mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+ mctx->bkref_ents[mctx->nbkref_ents].node = node;
+ mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+ /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+ If bit N is clear, means that this entry won't epsilon-transition to
+ an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If
+ it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+ such node.
+
+ A backreference does not epsilon-transition unless it is empty, so set
+ to all zeros if FROM != TO. */
+ mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+ = (from == to ? -1 : 0);
+
+ mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+ if (mctx->max_mb_elem_len < to - from)
+ mctx->max_mb_elem_len = to - from;
+ return REG_NOERROR;
+}
+
+/* Return the first entry with the same str_idx, or REG_MISSING if none is
+ found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */
+
+static Idx
+internal_function
+search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx)
+{
+ Idx left, right, mid, last;
+ last = right = mctx->nbkref_ents;
+ for (left = 0; left < right;)
+ {
+ mid = (left + right) / 2;
+ if (mctx->bkref_ents[mid].str_idx < str_idx)
+ left = mid + 1;
+ else
+ right = mid;
+ }
+ if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+ return left;
+ else
+ return REG_MISSING;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+ at STR_IDX. */
+
+static reg_errcode_t
+internal_function
+match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx)
+{
+#ifdef DEBUG
+ assert (mctx->sub_tops != NULL);
+ assert (mctx->asub_tops > 0);
+#endif
+ if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+ {
+ Idx new_asub_tops = mctx->asub_tops * 2;
+ re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+ re_sub_match_top_t *,
+ new_asub_tops);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops = new_array;
+ mctx->asub_tops = new_asub_tops;
+ }
+ mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+ if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops[mctx->nsub_tops]->node = node;
+ mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+ return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+ at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */
+
+static re_sub_match_last_t *
+internal_function
+match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx)
+{
+ re_sub_match_last_t *new_entry;
+ if (BE (subtop->nlasts == subtop->alasts, 0))
+ {
+ Idx new_alasts = 2 * subtop->alasts + 1;
+ re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+ re_sub_match_last_t *,
+ new_alasts);
+ if (BE (new_array == NULL, 0))
+ return NULL;
+ subtop->lasts = new_array;
+ subtop->alasts = new_alasts;
+ }
+ new_entry = calloc (1, sizeof (re_sub_match_last_t));
+ if (BE (new_entry != NULL, 1))
+ {
+ subtop->lasts[subtop->nlasts] = new_entry;
+ new_entry->node = node;
+ new_entry->str_idx = str_idx;
+ ++subtop->nlasts;
+ }
+ return new_entry;
+}
+
+static void
+internal_function
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx)
+{
+ sctx->sifted_states = sifted_sts;
+ sctx->limited_states = limited_sts;
+ sctx->last_node = last_node;
+ sctx->last_str_idx = last_str_idx;
+ re_node_set_init_empty (&sctx->limits);
+}
diff --git a/usr/src/lib/libparted/common/lib/rpmatch.c b/usr/src/lib/libparted/common/lib/rpmatch.c
new file mode 100644
index 0000000000..e5f79f8228
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/rpmatch.c
@@ -0,0 +1,79 @@
+/* Determine whether string value is affirmation or negative response
+ according to current locale's data.
+
+ Copyright (C) 1996, 1998, 2000, 2002, 2003, 2006 Free Software
+ Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#if ENABLE_NLS
+# include <sys/types.h>
+# include <limits.h>
+# include <regex.h>
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+
+static int
+try (const char *response, const char *pattern, const int match,
+ const int nomatch, const char **lastp, regex_t *re)
+{
+ if (pattern != *lastp)
+ {
+ /* The pattern has changed. */
+ if (*lastp)
+ {
+ /* Free the old compiled pattern. */
+ regfree (re);
+ *lastp = NULL;
+ }
+ /* Compile the pattern and cache it for future runs. */
+ if (regcomp (re, pattern, REG_EXTENDED) != 0)
+ return -1;
+ *lastp = pattern;
+ }
+
+ /* See if the regular expression matches RESPONSE. */
+ return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
+}
+#endif
+
+
+int
+rpmatch (const char *response)
+{
+#if ENABLE_NLS
+ /* Match against one of the response patterns, compiling the pattern
+ first if necessary. */
+
+ /* We cache the response patterns and compiled regexps here. */
+ static const char *yesexpr, *noexpr;
+ static regex_t yesre, nore;
+ int result;
+
+ return ((result = try (response, _("^[yY]"), 1, 0,
+ &yesexpr, &yesre))
+ ? result
+ : try (response, _("^[nN]"), 0, -1, &noexpr, &nore));
+#else
+ /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
+ return (*response == 'y' || *response == 'Y' ? 1
+ : *response == 'n' || *response == 'N' ? 0 : -1);
+#endif
+}
diff --git a/usr/src/lib/libparted/common/lib/safe-read.c b/usr/src/lib/libparted/common/lib/safe-read.c
new file mode 100644
index 0000000000..b7bf1d5cd6
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/safe-read.c
@@ -0,0 +1,78 @@
+/* An interface to read and write that retries after interrupts.
+
+ Copyright (C) 1993, 1994, 1998, 2002, 2003, 2004, 2005, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+/* Specification. */
+#ifdef SAFE_WRITE
+# include "safe-write.h"
+#else
+# include "safe-read.h"
+#endif
+
+/* Get ssize_t. */
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+#ifdef EINTR
+# define IS_EINTR(x) ((x) == EINTR)
+#else
+# define IS_EINTR(x) 0
+#endif
+
+#include <limits.h>
+
+#ifdef SAFE_WRITE
+# define safe_rw safe_write
+# define rw write
+#else
+# define safe_rw safe_read
+# define rw read
+# undef const
+# define const /* empty */
+#endif
+
+/* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
+ interrupted. Return the actual number of bytes read(written), zero for EOF,
+ or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error. */
+size_t
+safe_rw (int fd, void const *buf, size_t count)
+{
+ /* Work around a bug in Tru64 5.1. Attempting to read more than
+ INT_MAX bytes fails with errno == EINVAL. See
+ <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
+ When decreasing COUNT, keep it block-aligned. */
+ enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
+
+ for (;;)
+ {
+ ssize_t result = rw (fd, buf, count);
+
+ if (0 <= result)
+ return result;
+ else if (IS_EINTR (errno))
+ continue;
+ else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
+ count = BUGGY_READ_MAXIMUM;
+ else
+ return result;
+ }
+}
diff --git a/usr/src/lib/libparted/common/lib/safe-read.h b/usr/src/lib/libparted/common/lib/safe-read.h
new file mode 100644
index 0000000000..3451955ad4
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/safe-read.h
@@ -0,0 +1,35 @@
+/* An interface to read() that retries after interrupts.
+ Copyright (C) 2002, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define SAFE_READ_ERROR ((size_t) -1)
+
+/* Read up to COUNT bytes at BUF from descriptor FD, retrying if interrupted.
+ Return the actual number of bytes read, zero for EOF, or SAFE_READ_ERROR
+ upon error. */
+extern size_t safe_read (int fd, void *buf, size_t count);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/usr/src/lib/libparted/common/lib/safe-write.c b/usr/src/lib/libparted/common/lib/safe-write.c
new file mode 100644
index 0000000000..4c375a6ca1
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/safe-write.c
@@ -0,0 +1,19 @@
+/* An interface to write that retries after interrupts.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#define SAFE_WRITE
+#include "safe-read.c"
diff --git a/usr/src/lib/libparted/common/lib/safe-write.h b/usr/src/lib/libparted/common/lib/safe-write.h
new file mode 100644
index 0000000000..c194636209
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/safe-write.h
@@ -0,0 +1,25 @@
+/* An interface to write() that retries after interrupts.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <stddef.h>
+
+#define SAFE_WRITE_ERROR ((size_t) -1)
+
+/* Write up to COUNT bytes at BUF to descriptor FD, retrying if interrupted.
+ Return the actual number of bytes written, zero for EOF, or SAFE_WRITE_ERROR
+ upon error. */
+extern size_t safe_write (int fd, const void *buf, size_t count);
diff --git a/usr/src/lib/libparted/common/lib/strcspn.c b/usr/src/lib/libparted/common/lib/strcspn.c
new file mode 100644
index 0000000000..5a8d6f9c17
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/strcspn.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 1991, 1994, 1996-1997, 2002-2003, 2005-2006 Free Software Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@gnu.org.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <string.h>
+
+#undef strcspn
+
+/* Return the length of the maximum initial segment of S
+ which contains no characters from REJECT. */
+size_t
+strcspn (const char *s, const char *reject)
+{
+ size_t count = 0;
+
+ while (*s != '\0')
+ if (strchr (reject, *s++) == NULL)
+ ++count;
+ else
+ return count;
+
+ return count;
+}
diff --git a/usr/src/lib/libparted/common/lib/stripslash.c b/usr/src/lib/libparted/common/lib/stripslash.c
new file mode 100644
index 0000000000..342d497c89
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/stripslash.c
@@ -0,0 +1,45 @@
+/* stripslash.c -- remove redundant trailing slashes from a file name
+
+ Copyright (C) 1990, 2001, 2003-2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "dirname.h"
+
+/* Remove trailing slashes from FILE. Return true if a trailing slash
+ was removed. This is useful when using file name completion from a
+ shell that adds a "/" after directory names (such as tcsh and
+ bash), because on symlinks to directories, several system calls
+ have different semantics according to whether a trailing slash is
+ present. */
+
+bool
+strip_trailing_slashes (char *file)
+{
+ char *base = last_component (file);
+ char *base_lim;
+ bool had_slash;
+
+ /* last_component returns "" for file system roots, but we need to turn
+ `///' into `/'. */
+ if (! *base)
+ base = file;
+ base_lim = base + base_len (base);
+ had_slash = (*base_lim != '\0');
+ *base_lim = '\0';
+ return had_slash;
+}
diff --git a/usr/src/lib/libparted/common/lib/strndup.c b/usr/src/lib/libparted/common/lib/strndup.c
new file mode 100644
index 0000000000..3a1b0eae2e
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/strndup.c
@@ -0,0 +1,37 @@
+/* A replacement function, for systems that lack strndup.
+
+ Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2005, 2006, 2007
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <stdlib.h>
+
+char *
+strndup (char const *s, size_t n)
+{
+ size_t len = strnlen (s, n);
+ char *new = malloc (len + 1);
+
+ if (new == NULL)
+ return NULL;
+
+ new[len] = '\0';
+ return memcpy (new, s, len);
+}
diff --git a/usr/src/lib/libparted/common/lib/version-etc-fsf.c b/usr/src/lib/libparted/common/lib/version-etc-fsf.c
new file mode 100644
index 0000000000..f25eb653ea
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/version-etc-fsf.c
@@ -0,0 +1,31 @@
+/* Variable with FSF copyright information, for version-etc.
+ Copyright (C) 1999-2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering. */
+
+#include <config.h>
+
+/* Specification. */
+#include "version-etc.h"
+
+/* Default copyright goes to the FSF. */
+
+const char version_etc_copyright[] =
+ /* Do *not* mark this string for translation. %s is a copyright
+ symbol suitable for this locale, and %d is the copyright
+ year. */
+ "Copyright %s %d Free Software Foundation, Inc.";
diff --git a/usr/src/lib/libparted/common/lib/version-etc.c b/usr/src/lib/libparted/common/lib/version-etc.c
new file mode 100644
index 0000000000..14f101e553
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/version-etc.c
@@ -0,0 +1,173 @@
+/* Utility to help print --version output in a consistent format.
+ Copyright (C) 1999-2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering. */
+
+#include <config.h>
+
+/* Specification. */
+#include "version-etc.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+enum { COPYRIGHT_YEAR = 2007 };
+
+/* Like version_etc, below, but with the NULL-terminated author list
+ provided via a variable of type va_list. */
+void
+version_etc_va (FILE *stream,
+ const char *command_name, const char *package,
+ const char *version, va_list authors)
+{
+ size_t n_authors;
+
+ /* Count the number of authors. */
+ {
+ va_list tmp_authors;
+
+ va_copy (tmp_authors, authors);
+
+ n_authors = 0;
+ while (va_arg (tmp_authors, const char *) != NULL)
+ ++n_authors;
+ }
+
+ if (command_name)
+ fprintf (stream, "%s (%s) %s\n", command_name, package, version);
+ else
+ fprintf (stream, "%s %s\n", package, version);
+
+ /* TRANSLATORS: Translate "(C)" to the copyright symbol
+ (C-in-a-circle), if this symbol is available in the user's
+ locale. Otherwise, do not translate "(C)"; leave it as-is. */
+ fprintf (stream, version_etc_copyright, _("(C)"), COPYRIGHT_YEAR);
+
+ fputs (_("\
+\n\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
+This is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+\n\
+"),
+ stream);
+
+ switch (n_authors)
+ {
+ case 0:
+ /* The caller must provide at least one author name. */
+ abort ();
+ case 1:
+ /* TRANSLATORS: %s denotes an author name. */
+ vfprintf (stream, _("Written by %s.\n"), authors);
+ break;
+ case 2:
+ /* TRANSLATORS: Each %s denotes an author name. */
+ vfprintf (stream, _("Written by %s and %s.\n"), authors);
+ break;
+ case 3:
+ /* TRANSLATORS: Each %s denotes an author name. */
+ vfprintf (stream, _("Written by %s, %s, and %s.\n"), authors);
+ break;
+ case 4:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("Written by %s, %s, %s,\nand %s.\n"), authors);
+ break;
+ case 5:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("Written by %s, %s, %s,\n%s, and %s.\n"), authors);
+ break;
+ case 6:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("Written by %s, %s, %s,\n%s, %s, and %s.\n"),
+ authors);
+ break;
+ case 7:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("Written by %s, %s, %s,\n%s, %s, %s, and %s.\n"),
+ authors);
+ break;
+ case 8:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("\
+Written by %s, %s, %s,\n%s, %s, %s, %s,\nand %s.\n"),
+ authors);
+ break;
+ case 9:
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("\
+Written by %s, %s, %s,\n%s, %s, %s, %s,\n%s, and %s.\n"),
+ authors);
+ break;
+ default:
+ /* 10 or more authors. Use an abbreviation, since the human reader
+ will probably not want to read the entire list anyway. */
+ /* TRANSLATORS: Each %s denotes an author name.
+ You can use line breaks, estimating that each author name occupies
+ ca. 16 screen columns and that a screen line has ca. 80 columns. */
+ vfprintf (stream, _("\
+Written by %s, %s, %s,\n%s, %s, %s, %s,\n%s, %s, and others.\n"),
+ authors);
+ break;
+ }
+ va_end (authors);
+}
+
+
+/* Display the --version information the standard way.
+
+ If COMMAND_NAME is NULL, the PACKAGE is asumed to be the name of
+ the program. The formats are therefore:
+
+ PACKAGE VERSION
+
+ or
+
+ COMMAND_NAME (PACKAGE) VERSION.
+
+ The author names are passed as separate arguments, with an additional
+ NULL argument at the end. */
+void
+version_etc (FILE *stream,
+ const char *command_name, const char *package,
+ const char *version, /* const char *author1, ...*/ ...)
+{
+ va_list authors;
+
+ va_start (authors, version);
+ version_etc_va (stream, command_name, package, version, authors);
+}
diff --git a/usr/src/lib/libparted/common/lib/version-etc.h b/usr/src/lib/libparted/common/lib/version-etc.h
new file mode 100644
index 0000000000..84da535001
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/version-etc.h
@@ -0,0 +1,37 @@
+/* Utility to help print --version output in a consistent format.
+ Copyright (C) 1999, 2003, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Jim Meyering. */
+
+#ifndef VERSION_ETC_H
+# define VERSION_ETC_H 1
+
+# include <stdarg.h>
+# include <stdio.h>
+
+extern const char version_etc_copyright[];
+
+extern void version_etc_va (FILE *stream,
+ const char *command_name, const char *package,
+ const char *version, va_list authors);
+
+extern void version_etc (FILE *stream,
+ const char *command_name, const char *package,
+ const char *version,
+ /* const char *author1, ...*/ ...);
+
+#endif /* VERSION_ETC_H */
diff --git a/usr/src/lib/libparted/common/lib/xalloc-die.c b/usr/src/lib/libparted/common/lib/xalloc-die.c
new file mode 100644
index 0000000000..090f060df7
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/xalloc-die.c
@@ -0,0 +1,42 @@
+/* Report a memory allocation failure and exit.
+
+ Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2006 Free
+ Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#include "xalloc.h"
+
+#include <stdlib.h>
+
+#include "error.h"
+#include "exitfail.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+void
+xalloc_die (void)
+{
+ error (exit_failure, 0, "%s", _("memory exhausted"));
+
+ /* The `noreturn' cannot be given to error, since it may return if
+ its first argument is 0. To help compilers understand the
+ xalloc_die does not return, call abort. Also, the abort is a
+ safety feature if exit_failure is 0 (which shouldn't happen). */
+ abort ();
+}
diff --git a/usr/src/lib/libparted/common/lib/xalloc.h b/usr/src/lib/libparted/common/lib/xalloc.h
new file mode 100644
index 0000000000..0c6d8dcf50
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/xalloc.h
@@ -0,0 +1,271 @@
+/* xalloc.h -- malloc with out-of-memory checking
+
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef XALLOC_H_
+# define XALLOC_H_
+
+# include <stddef.h>
+
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+
+# ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
+# define __attribute__(x)
+# endif
+# endif
+
+# ifndef ATTRIBUTE_NORETURN
+# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
+# endif
+
+/* This function is always triggered when memory is exhausted.
+ It must be defined by the application, either explicitly
+ or by using gnulib's xalloc-die module. This is the
+ function to call when one wants the program to die because of a
+ memory allocation failure. */
+extern void xalloc_die (void) ATTRIBUTE_NORETURN;
+
+void *xmalloc (size_t s);
+void *xzalloc (size_t s);
+void *xcalloc (size_t n, size_t s);
+void *xrealloc (void *p, size_t s);
+void *x2realloc (void *p, size_t *pn);
+void *xmemdup (void const *p, size_t s);
+char *xstrdup (char const *str);
+
+/* Return 1 if an array of N objects, each of size S, cannot exist due
+ to size arithmetic overflow. S must be positive and N must be
+ nonnegative. This is a macro, not an inline function, so that it
+ works correctly even when SIZE_MAX < N.
+
+ By gnulib convention, SIZE_MAX represents overflow in size
+ calculations, so the conservative dividend to use here is
+ SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value.
+ However, malloc (SIZE_MAX) fails on all known hosts where
+ sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for
+ exactly-SIZE_MAX allocations on such hosts; this avoids a test and
+ branch when S is known to be 1. */
+# define xalloc_oversized(n, s) \
+ ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n))
+
+
+/* In the following macros, T must be an elementary or structure/union or
+ typedef'ed type, or a pointer to such a type. To apply one of the
+ following macros to a function pointer or array type, you need to typedef
+ it first and use the typedef name. */
+
+/* Allocate an object of type T dynamically, with error checking. */
+/* extern t *XMALLOC (typename t); */
+# define XMALLOC(t) ((t *) xmalloc (sizeof (t)))
+
+/* Allocate memory for N elements of type T, with error checking. */
+/* extern t *XNMALLOC (size_t n, typename t); */
+# define XNMALLOC(n, t) \
+ ((t *) (sizeof (t) == 1 ? xmalloc (n) : xnmalloc (n, sizeof (t))))
+
+/* Allocate an object of type T dynamically, with error checking,
+ and zero it. */
+/* extern t *XZALLOC (typename t); */
+# define XZALLOC(t) ((t *) xzalloc (sizeof (t)))
+
+/* Allocate memory for N elements of type T, with error checking,
+ and zero it. */
+/* extern t *XCALLOC (size_t n, typename t); */
+# define XCALLOC(n, t) \
+ ((t *) (sizeof (t) == 1 ? xzalloc (n) : xcalloc (n, sizeof (t))))
+
+
+# if HAVE_INLINE
+# define static_inline static inline
+# else
+ void *xnmalloc (size_t n, size_t s);
+ void *xnrealloc (void *p, size_t n, size_t s);
+ void *x2nrealloc (void *p, size_t *pn, size_t s);
+ char *xcharalloc (size_t n);
+# endif
+
+# ifdef static_inline
+
+/* Allocate an array of N objects, each with S bytes of memory,
+ dynamically, with error checking. S must be nonzero. */
+
+static_inline void *
+xnmalloc (size_t n, size_t s)
+{
+ if (xalloc_oversized (n, s))
+ xalloc_die ();
+ return xmalloc (n * s);
+}
+
+/* Change the size of an allocated block of memory P to an array of N
+ objects each of S bytes, with error checking. S must be nonzero. */
+
+static_inline void *
+xnrealloc (void *p, size_t n, size_t s)
+{
+ if (xalloc_oversized (n, s))
+ xalloc_die ();
+ return xrealloc (p, n * s);
+}
+
+/* If P is null, allocate a block of at least *PN such objects;
+ otherwise, reallocate P so that it contains more than *PN objects
+ each of S bytes. *PN must be nonzero unless P is null, and S must
+ be nonzero. Set *PN to the new number of objects, and return the
+ pointer to the new block. *PN is never set to zero, and the
+ returned pointer is never null.
+
+ Repeated reallocations are guaranteed to make progress, either by
+ allocating an initial block with a nonzero size, or by allocating a
+ larger block.
+
+ In the following implementation, nonzero sizes are increased by a
+ factor of approximately 1.5 so that repeated reallocations have
+ O(N) overall cost rather than O(N**2) cost, but the
+ specification for this function does not guarantee that rate.
+
+ Here is an example of use:
+
+ int *p = NULL;
+ size_t used = 0;
+ size_t allocated = 0;
+
+ void
+ append_int (int value)
+ {
+ if (used == allocated)
+ p = x2nrealloc (p, &allocated, sizeof *p);
+ p[used++] = value;
+ }
+
+ This causes x2nrealloc to allocate a block of some nonzero size the
+ first time it is called.
+
+ To have finer-grained control over the initial size, set *PN to a
+ nonzero value before calling this function with P == NULL. For
+ example:
+
+ int *p = NULL;
+ size_t used = 0;
+ size_t allocated = 0;
+ size_t allocated1 = 1000;
+
+ void
+ append_int (int value)
+ {
+ if (used == allocated)
+ {
+ p = x2nrealloc (p, &allocated1, sizeof *p);
+ allocated = allocated1;
+ }
+ p[used++] = value;
+ }
+
+ */
+
+static_inline void *
+x2nrealloc (void *p, size_t *pn, size_t s)
+{
+ size_t n = *pn;
+
+ if (! p)
+ {
+ if (! n)
+ {
+ /* The approximate size to use for initial small allocation
+ requests, when the invoking code specifies an old size of
+ zero. 64 bytes is the largest "small" request for the
+ GNU C library malloc. */
+ enum { DEFAULT_MXFAST = 64 };
+
+ n = DEFAULT_MXFAST / s;
+ n += !n;
+ }
+ }
+ else
+ {
+ /* Set N = ceil (1.5 * N) so that progress is made if N == 1.
+ Check for overflow, so that N * S stays in size_t range.
+ The check is slightly conservative, but an exact check isn't
+ worth the trouble. */
+ if ((size_t) -1 / 3 * 2 / s <= n)
+ xalloc_die ();
+ n += (n + 1) / 2;
+ }
+
+ *pn = n;
+ return xrealloc (p, n * s);
+}
+
+/* Return a pointer to a new buffer of N bytes. This is like xmalloc,
+ except it returns char *. */
+
+static_inline char *
+xcharalloc (size_t n)
+{
+ return XNMALLOC (n, char);
+}
+
+# endif
+
+# ifdef __cplusplus
+}
+
+/* C++ does not allow conversions from void * to other pointer types
+ without a cast. Use templates to work around the problem when
+ possible. */
+
+template <typename T> inline T *
+xrealloc (T *p, size_t s)
+{
+ return (T *) xrealloc ((void *) p, s);
+}
+
+template <typename T> inline T *
+xnrealloc (T *p, size_t n, size_t s)
+{
+ return (T *) xnrealloc ((void *) p, n, s);
+}
+
+template <typename T> inline T *
+x2realloc (T *p, size_t *pn)
+{
+ return (T *) x2realloc ((void *) p, pn);
+}
+
+template <typename T> inline T *
+x2nrealloc (T *p, size_t *pn, size_t s)
+{
+ return (T *) x2nrealloc ((void *) p, pn, s);
+}
+
+template <typename T> inline T *
+xmemdup (T const *p, size_t s)
+{
+ return (T *) xmemdup ((void const *) p, s);
+}
+
+# endif
+
+
+#endif /* !XALLOC_H_ */
diff --git a/usr/src/lib/libparted/common/lib/xmalloc.c b/usr/src/lib/libparted/common/lib/xmalloc.c
new file mode 100644
index 0000000000..318e0ddb5a
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/xmalloc.c
@@ -0,0 +1,123 @@
+/* xmalloc.c -- malloc with out of memory checking
+
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+ Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+#if ! HAVE_INLINE
+# define static_inline
+#endif
+#include "xalloc.h"
+#undef static_inline
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+/* 1 if calloc is known to be compatible with GNU calloc. This
+ matters if we are not also using the calloc module, which defines
+ HAVE_CALLOC and supports the GNU API even on non-GNU platforms. */
+#if defined HAVE_CALLOC || defined __GLIBC__
+enum { HAVE_GNU_CALLOC = 1 };
+#else
+enum { HAVE_GNU_CALLOC = 0 };
+#endif
+
+/* Allocate N bytes of memory dynamically, with error checking. */
+
+void *
+xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p && n != 0)
+ xalloc_die ();
+ return p;
+}
+
+/* Change the size of an allocated block of memory P to N bytes,
+ with error checking. */
+
+void *
+xrealloc (void *p, size_t n)
+{
+ p = realloc (p, n);
+ if (!p && n != 0)
+ xalloc_die ();
+ return p;
+}
+
+/* If P is null, allocate a block of at least *PN bytes; otherwise,
+ reallocate P so that it contains more than *PN bytes. *PN must be
+ nonzero unless P is null. Set *PN to the new block's size, and
+ return the pointer to the new block. *PN is never set to zero, and
+ the returned pointer is never null. */
+
+void *
+x2realloc (void *p, size_t *pn)
+{
+ return x2nrealloc (p, pn, 1);
+}
+
+/* Allocate S bytes of zeroed memory dynamically, with error checking.
+ There's no need for xnzalloc (N, S), since it would be equivalent
+ to xcalloc (N, S). */
+
+void *
+xzalloc (size_t s)
+{
+ return memset (xmalloc (s), 0, s);
+}
+
+/* Allocate zeroed memory for N elements of S bytes, with error
+ checking. S must be nonzero. */
+
+void *
+xcalloc (size_t n, size_t s)
+{
+ void *p;
+ /* Test for overflow, since some calloc implementations don't have
+ proper overflow checks. But omit overflow and size-zero tests if
+ HAVE_GNU_CALLOC, since GNU calloc catches overflow and never
+ returns NULL if successful. */
+ if ((! HAVE_GNU_CALLOC && xalloc_oversized (n, s))
+ || (! (p = calloc (n, s)) && (HAVE_GNU_CALLOC || n != 0)))
+ xalloc_die ();
+ return p;
+}
+
+/* Clone an object P of size S, with error checking. There's no need
+ for xnmemdup (P, N, S), since xmemdup (P, N * S) works without any
+ need for an arithmetic overflow check. */
+
+void *
+xmemdup (void const *p, size_t s)
+{
+ return memcpy (xmalloc (s), p, s);
+}
+
+/* Clone STRING. */
+
+char *
+xstrdup (char const *string)
+{
+ return xmemdup (string, strlen (string) + 1);
+}
diff --git a/usr/src/lib/libparted/common/lib/xstrndup.c b/usr/src/lib/libparted/common/lib/xstrndup.c
new file mode 100644
index 0000000000..7ccefd798a
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/xstrndup.c
@@ -0,0 +1,37 @@
+/* Duplicate a bounded initial segment of a string, with out-of-memory
+ checking.
+ Copyright (C) 2003, 2006, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <config.h>
+
+/* Specification. */
+#include "xstrndup.h"
+
+#include <string.h>
+#include "xalloc.h"
+
+/* Return a newly allocated copy of at most N bytes of STRING.
+ In other words, return a copy of the initial segment of length N of
+ STRING. */
+char *
+xstrndup (const char *string, size_t n)
+{
+ char *s = strndup (string, n);
+ if (! s)
+ xalloc_die ();
+ return s;
+}
diff --git a/usr/src/lib/libparted/common/lib/xstrndup.h b/usr/src/lib/libparted/common/lib/xstrndup.h
new file mode 100644
index 0000000000..e65acc13e2
--- /dev/null
+++ b/usr/src/lib/libparted/common/lib/xstrndup.h
@@ -0,0 +1,25 @@
+/* Duplicate a bounded initial segment of a string, with out-of-memory
+ checking.
+ Copyright (C) 2003 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include <stddef.h>
+
+/* Return a newly allocated copy of at most N bytes of STRING.
+ In other words, return a copy of the initial segment of length N of
+ STRING. */
+extern char *strndup (const char *string, size_t n);
+extern char *xstrndup (const char *string, size_t n);
diff --git a/usr/src/lib/libparted/common/libparted/arch/solaris.c b/usr/src/lib/libparted/common/libparted/arch/solaris.c
new file mode 100644
index 0000000000..5e025d509f
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/arch/solaris.c
@@ -0,0 +1,1397 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999 - 2005 Free Software Foundation, Inc.
+ Copyright (C) 2007 Nikhil,Sujay,Nithin,Srivatsa.
+
+ Bug fixes and completion of the module in 2009 by Mark.Logan@sun.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.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+*/
+
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include "config.h"
+#include "xalloc.h"
+#include <sys/dkio.h>
+
+/*
+ * __attribute doesn't exist on solaris
+ */
+#define __attribute__(X) /* nothing */
+
+#include <sys/vtoc.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/solaris.h>
+#include <malloc.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <dirent.h>
+#include <libdiskmgt.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/swap.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(String) dgettext(PACKAGE, String)
+#else
+#define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#ifndef UINT_MAX64
+#define UINT_MAX64 0xffffffffffffffffULL
+#endif
+
+/*
+ * Macro to convert a device number into a partition number
+ */
+#define PARTITION(dev) (minor(dev) & 0x07)
+
+
+char *
+canonicalize_file_name(const char *name)
+{
+ char *buf;
+
+ buf = malloc(MAXPATHLEN);
+ if (!buf) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ return (strcpy(buf, name));
+}
+
+static int
+_device_stat(PedDevice* dev, struct stat *dev_stat)
+{
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(!dev->external_mode, return (0));
+
+ while (1) {
+ if (!stat(dev->path, dev_stat)) {
+ return (1);
+ } else {
+ if (ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_CANCEL,
+ _("Could not stat device %s - %s."),
+ dev->path, strerror(errno)) != PED_EXCEPTION_RETRY)
+ return (0);
+ }
+ }
+}
+
+static void
+_device_set_length_and_sector_size(PedDevice* dev)
+{
+ SolarisSpecific* arch_specific;
+ PedSector size;
+ struct dk_minfo dk_minfo;
+ struct dk_geom dk_geom;
+
+ PED_ASSERT(dev != NULL, return);
+ PED_ASSERT(dev->open_count > 0, return);
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ dev->sector_size = PED_SECTOR_SIZE_DEFAULT;
+ dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT;
+
+ /* this ioctl requires the raw device */
+ if (ioctl(arch_specific->fd, DKIOCGMEDIAINFO, &dk_minfo) < 0) {
+ printf("_device_get_length: ioctl DKIOCGMEDIAINFO failed\n");
+ ped_exception_throw(
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to determine the size of %s (%s)."),
+ dev->path,
+ strerror(errno));
+ } else {
+ size = dk_minfo.dki_capacity;
+ dev->length = size;
+ dev->sector_size = dk_minfo.dki_lbsize;
+ if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw(
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK,
+ _("Device %s has a logical sector size of "
+ "%lld. Not all parts of GNU Parted support "
+ "this at the moment, and the working code "
+ "is HIGHLY EXPERIMENTAL.\n"),
+ dev->path, dev->sector_size);
+ }
+ if (size > 0) {
+ return;
+ }
+ }
+
+ /*
+ * On some disks DKIOCGMEDIAINFO doesn't work, it returns 0,
+ * so try DKIOCG_PHYGEOM next.
+ */
+ /* this ioctl requires the raw device */
+ if (ioctl(arch_specific->fd, DKIOCG_PHYGEOM, &dk_geom) < 0) {
+ printf("_device_get_length: ioctl DKIOCG_PHYGEOM failed\n");
+ ped_exception_throw(
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to determine the size of %s (%s)."),
+ dev->path, strerror(errno));
+
+ return;
+ }
+
+ /*
+ * XXX For large disks, I am adding 16064 to the size of the disk.
+ * Solaris underreports the size of the disk, because it rounds down to
+ * a multiple of 16065. This causes a problem with Vista because Vista
+ * creates a partition that occupies the whole disk, including the
+ * blocks at the end of the disk that Solaris loses.
+ */
+ if (dk_geom.dkg_nhead == 255 && dk_geom.dkg_nsect == 63) {
+ size = ((PedSector) dk_geom.dkg_pcyl *
+ (255 * 63)) + ((255*63)-1);
+ } else {
+ size = (PedSector) dk_geom.dkg_pcyl *
+ dk_geom.dkg_nhead * dk_geom.dkg_nsect;
+ }
+
+ dev->length = size;
+}
+
+static int
+_device_probe_geometry(PedDevice* dev)
+{
+ SolarisSpecific* arch_specific;
+ struct stat dev_stat;
+ struct dk_geom dk_geom;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ _device_set_length_and_sector_size(dev);
+ if (dev->length == 0) {
+ printf("_device_probe_geometry: _device_get_length = 0\n");
+ return (0);
+ }
+
+ dev->bios_geom.sectors = 63;
+ dev->bios_geom.heads = 255;
+ dev->bios_geom.cylinders = dev->length / (63 * 255);
+ if ((ioctl(arch_specific->fd, DKIOCG_PHYGEOM, &dk_geom) >= 0) &&
+ dk_geom.dkg_nsect && dk_geom.dkg_nhead) {
+ dev->hw_geom.sectors = dk_geom.dkg_nsect;
+ dev->hw_geom.heads = dk_geom.dkg_nhead;
+ dev->hw_geom.cylinders = dk_geom.dkg_pcyl;
+ } else {
+ perror("_device_probe_geometry: DKIOCG_PHYGEOM");
+ dev->hw_geom = dev->bios_geom;
+ }
+
+ return (1);
+}
+
+static int
+init_ide(PedDevice *dev)
+{
+ struct stat dev_stat;
+
+ PED_ASSERT(dev != NULL, return (0));
+
+ if (!_device_stat(dev, &dev_stat)) {
+ printf("init_ide: _device_stat failed\n");
+ goto error;
+ }
+ if (!ped_device_open(dev)) {
+ printf("init_ide: ped_device_open failed\n");
+ goto error;
+ }
+ if (!_device_probe_geometry(dev)) {
+ printf("init_ide: _device_probe_geometry failed\n");
+ goto error_close_dev;
+ }
+
+ ped_device_close(dev);
+ return (1);
+
+error_close_dev:
+ ped_device_close(dev);
+error:
+ return (0);
+}
+
+static PedDevice*
+solaris_new(const char *path)
+{
+ PedDevice* dev;
+
+ PED_ASSERT(path != NULL, return (NULL));
+
+ dev = (PedDevice*) ped_malloc(sizeof (PedDevice));
+ if (!dev)
+ goto error;
+
+ dev->path = strdup(path);
+ if (!dev->path)
+ goto error_free_dev;
+
+ dev->arch_specific
+ = (SolarisSpecific*) ped_malloc(sizeof (SolarisSpecific));
+ if (!dev->arch_specific)
+ goto error_free_path;
+
+ dev->open_count = 0;
+ dev->read_only = 0;
+ dev->external_mode = 0;
+ dev->dirty = 0;
+ dev->boot_dirty = 0;
+ dev->model = strdup("Generic Ide");
+ dev->type = PED_DEVICE_IDE;
+ if (!init_ide(dev)) {
+ goto error_free_arch_specific;
+ }
+
+ return (dev);
+
+error_free_arch_specific:
+ ped_free(dev->arch_specific);
+ ped_free(dev->model);
+error_free_path:
+ ped_free(dev->path);
+error_free_dev:
+ ped_free(dev);
+error:
+ return (NULL);
+}
+
+static void
+solaris_destroy(PedDevice* dev)
+{
+ PED_ASSERT(dev != NULL, return);
+
+ ped_free(dev->arch_specific);
+ ped_free(dev->model);
+ ped_free(dev->path);
+ ped_free(dev);
+}
+
+static char *
+_device_get_part_path(PedDevice* dev, int num)
+{
+ int path_len = strlen(dev->path);
+ int result_len = path_len + 16;
+ char *result;
+
+ PED_ASSERT(dev != NULL, return (NULL));
+ PED_ASSERT(num >= 1 && num <= 4, return (NULL));
+
+ result = (char *)ped_malloc(result_len);
+ if (!result)
+ return (NULL);
+
+ /*
+ * Create the path name to the *pn device, where n is the partition #
+ * geom->dev->path looks like this: "/devices/.../cmdk@0,0:q"
+ * or like this: "/dev/dsk/...p0"
+ * ":q" is the "/dev/dsk/...p0" device
+ * :r is p1, :s is p2, :t is p3, :u is p4
+ * 'q' + 1 == 'r'
+ * '0' + 1 == '1'
+ */
+ strncpy(result, dev->path, result_len);
+ result[strlen(result) -1] += num;
+
+ return (result);
+}
+
+static struct swaptable *
+getswapentries(void)
+{
+ register struct swaptable *st;
+ register struct swapent *swapent;
+ int i, num;
+ char fullpathname[MAXPATHLEN];
+
+ /*
+ * get the number of swap entries
+ */
+ if ((num = swapctl(SC_GETNSWP, (void *)NULL)) == -1) {
+ perror("getswapentries: swapctl SC_GETNSWP");
+ return (NULL);
+ }
+ if (num == 0)
+ return (NULL);
+ if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int)))
+ == NULL) {
+ printf("getswapentries: malloc 1 failed.\n");
+ return (NULL);
+ }
+ swapent = st->swt_ent;
+ for (i = 0; i < num; i++, swapent++) {
+ if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) {
+ printf("getswapentries: malloc 2 failed.\n");
+ goto error;
+ }
+ }
+ st->swt_n = num;
+ if ((num = swapctl(SC_LIST, (void *)st)) == -1) {
+ perror("getswapentries: swapctl SC_LIST");
+ goto error;
+ }
+ swapent = st->swt_ent;
+ for (i = 0; i < num; i++, swapent++) {
+ if (*swapent->ste_path != '/') {
+ printf("getswapentries: %s\n", swapent->ste_path);
+ (void) snprintf(fullpathname, sizeof (fullpathname),
+ "/dev/%s", swapent->ste_path);
+ (void) strcpy(swapent->ste_path, fullpathname);
+ }
+ }
+
+ return (st);
+
+error:
+ free(st);
+ return (NULL);
+}
+
+static void
+freeswapentries(st)
+struct swaptable *st;
+{
+ register struct swapent *swapent;
+ int i;
+
+ swapent = st->swt_ent;
+ for (i = 0; i < st->swt_n; i++, swapent++)
+ free(swapent->ste_path);
+ free(st);
+}
+
+/*
+ * function getpartition:
+ */
+static int
+getpartition(PedDevice* dev, char *pathname)
+{
+ SolarisSpecific* arch_specific;
+ int mfd;
+ struct dk_cinfo dkinfo;
+ struct dk_cinfo cur_disk_dkinfo;
+ struct stat stbuf;
+ char raw_device[MAXPATHLEN];
+ int found = -1;
+
+ PED_ASSERT(dev != NULL, return (found));
+ PED_ASSERT(dev->open_count > 0, return (found));
+ PED_ASSERT(pathname != NULL, return (found));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ /*
+ * Map the block device name to the raw device name.
+ * If it doesn't appear to be a device name, skip it.
+ */
+ if (strncmp(pathname, "/dev/", 5))
+ return (found);
+ (void) strcpy(raw_device, "/dev/r");
+ (void) strcat(raw_device, pathname + strlen("/dev/"));
+ /*
+ * Determine if this appears to be a disk device.
+ * First attempt to open the device. If if fails, skip it.
+ */
+ if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
+ return (found);
+ }
+ if (fstat(mfd, &stbuf) == -1) {
+ perror("getpartition: fstat raw_device");
+ (void) close(mfd);
+ return (found);
+ }
+ /*
+ * Must be a character device
+ */
+ if (!S_ISCHR(stbuf.st_mode)) {
+ printf("getpartition: not character device\n");
+ (void) close(mfd);
+ return (found);
+ }
+ /*
+ * Attempt to read the configuration info on the disk.
+ */
+ if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) {
+ perror("getpartition: ioctl DKIOCINFO raw_device");
+ (void) close(mfd);
+ return (found);
+ }
+ /*
+ * Finished with the opened device
+ */
+ (void) close(mfd);
+
+ /*
+ * Now get the info about the current disk
+ */
+ if (ioctl(arch_specific->fd, DKIOCINFO, &cur_disk_dkinfo) < 0) {
+ perror("getpartition: ioctl DKIOCINFO current disk");
+ (void) close(mfd);
+ return (found);
+ }
+
+ /*
+ * If it's not the disk we're interested in, it doesn't apply.
+ */
+ if (cur_disk_dkinfo.dki_ctype != dkinfo.dki_ctype ||
+ cur_disk_dkinfo.dki_cnum != dkinfo.dki_cnum ||
+ cur_disk_dkinfo.dki_unit != dkinfo.dki_unit ||
+ strcmp(cur_disk_dkinfo.dki_dname, dkinfo.dki_dname) != 0) {
+ return (found);
+ }
+
+ /*
+ * Extract the partition that is mounted.
+ */
+ return (PARTITION(stbuf.st_rdev));
+}
+
+/*
+ * This Routine checks to see if there are partitions used for swapping overlaps
+ * a given portion of a disk. If the start parameter is < 0, it means
+ * that the entire disk should be checked
+ */
+static int
+checkswap(PedDevice* dev, diskaddr_t start, diskaddr_t end)
+{
+ SolarisSpecific* arch_specific;
+ struct extvtoc extvtoc;
+ struct swaptable *st;
+ struct swapent *swapent;
+ int i;
+ int found = 0;
+ int part;
+ diskaddr_t p_start;
+ diskaddr_t p_size;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ if (ioctl(arch_specific->fd, DKIOCGEXTVTOC, &extvtoc) == -1) {
+ return (0);
+ }
+
+ /*
+ * check for swap entries
+ */
+ st = getswapentries();
+ /*
+ * if there are no swap entries return.
+ */
+ if (st == (struct swaptable *)NULL)
+ return (0);
+ swapent = st->swt_ent;
+ for (i = 0; i < st->swt_n; i++, swapent++) {
+ if ((part = getpartition(dev, swapent->ste_path)) != -1) {
+ if (start == UINT_MAX64) {
+ found = -1;
+ break;
+ }
+ p_start = extvtoc.v_part[part].p_start;
+ p_size = extvtoc.v_part[part].p_size;
+ if (start >= p_start + p_size || end < p_start) {
+ continue;
+ }
+ found = -1;
+ break;
+ }
+ }
+ freeswapentries(st);
+
+ return (found);
+}
+
+/*
+ * Determines if there are partitions that are a part of an SVM, VxVM, zpool
+ * volume or a live upgrade device, overlapping a given portion of a disk.
+ * Mounts and swap devices are checked in legacy format code.
+ */
+static int
+checkdevinuse(PedDevice *dev, diskaddr_t start, diskaddr_t end, int print)
+{
+ int error;
+ int found = 0;
+ int check = 0;
+ int i;
+ int part = 0;
+ uint64_t slice_start, slice_size;
+ dm_descriptor_t *slices = NULL;
+ nvlist_t *attrs = NULL;
+ char *usage;
+ char *name;
+ char cur_disk_path[MAXPATHLEN];
+ char *pcur_disk_path;
+
+ PED_ASSERT(dev != NULL, return (found));
+ PED_ASSERT(dev->open_count > 0, return (found));
+
+ /*
+ * Truncate the characters following "d*", such as "s*" or "p*"
+ */
+ strcpy(cur_disk_path, dev->path);
+ pcur_disk_path = basename(cur_disk_path);
+ name = strrchr(pcur_disk_path, 'd');
+ if (name) {
+ name++;
+ for (; (*name <= '9') && (*name >= '0'); name++)
+ ;
+ *name = (char)0;
+ }
+
+ /*
+ * For format, we get basic 'in use' details from libdiskmgt. After
+ * that we must do the appropriate checking to see if the 'in use'
+ * details require a bit of additional work.
+ */
+
+ dm_get_slices(pcur_disk_path, &slices, &error);
+ if (error) {
+ /*
+ * If ENODEV, it actually means the device is not in use.
+ * We will return (0) without displaying error.
+ */
+ if (error != ENODEV) {
+ printf("checkdevinuse: Error1 occurred with device in "
+ "use checking: %s\n", strerror(error));
+ return (found);
+ }
+ }
+ if (slices == NULL)
+ return (found);
+
+ for (i = 0; slices[i] != NULL; i++) {
+ /*
+ * If we are checking the whole disk
+ * then any and all in use data is
+ * relevant.
+ */
+ if (start == UINT_MAX64) {
+ name = dm_get_name(slices[i], &error);
+ if (error != 0 || !name) {
+ printf("checkdevinuse: Error2 occurred with "
+ "device in use checking: %s\n",
+ strerror(error));
+ continue;
+ }
+ printf("checkdevinuse: name1 %s\n", name);
+ if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
+ error) {
+ if (error != 0) {
+ dm_free_name(name);
+ name = NULL;
+ printf("checkdevinuse: Error3 "
+ "occurred with device "
+ "in use checking: %s\n",
+ strerror(error));
+ continue;
+ }
+ dm_free_name(name);
+ name = NULL;
+ /*
+ * If this is a dump device, then it is
+ * a failure. You cannot format a slice
+ * that is a dedicated dump device.
+ */
+
+ if (strstr(usage, DM_USE_DUMP)) {
+ if (print) {
+ printf(usage);
+ free(usage);
+ }
+ dm_free_descriptors(slices);
+ return (1);
+ }
+ /*
+ * We really found a device that is in use.
+ * Set 'found' for the return value.
+ */
+ found ++;
+ check = 1;
+ if (print) {
+ printf(usage);
+ free(usage);
+ }
+ }
+ } else {
+ /*
+ * Before getting the in use data, verify that the
+ * current slice is within the range we are checking.
+ */
+ attrs = dm_get_attributes(slices[i], &error);
+ if (error) {
+ printf("checkdevinuse: Error4 occurred with "
+ "device in use checking: %s\n",
+ strerror(error));
+ continue;
+ }
+ if (attrs == NULL) {
+ continue;
+ }
+
+ (void) nvlist_lookup_uint64(attrs, DM_START,
+ &slice_start);
+ (void) nvlist_lookup_uint64(attrs, DM_SIZE,
+ &slice_size);
+ if (start >= (slice_start + slice_size) ||
+ (end < slice_start)) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ continue;
+ }
+ name = dm_get_name(slices[i], &error);
+ if (error != 0 || !name) {
+ printf("checkdevinuse: Error5 occurred with "
+ "device in use checking: %s\n",
+ strerror(error));
+ nvlist_free(attrs);
+ attrs = NULL;
+ continue;
+ }
+ if (dm_inuse(name, &usage,
+ DM_WHO_FORMAT, &error) || error) {
+ if (error != 0) {
+ dm_free_name(name);
+ name = NULL;
+ printf("checkdevinuse: Error6 "
+ "occurred with device "
+ "in use checking: %s\n",
+ strerror(error));
+ nvlist_free(attrs);
+ attrs = NULL;
+ continue;
+ }
+ dm_free_name(name);
+ name = NULL;
+ /*
+ * If this is a dump device, then it is
+ * a failure. You cannot format a slice
+ * that is a dedicated dump device.
+ */
+ if (strstr(usage, DM_USE_DUMP)) {
+ if (print) {
+ printf(usage);
+ free(usage);
+ }
+ dm_free_descriptors(slices);
+ nvlist_free(attrs);
+ return (1);
+ }
+ /*
+ * We really found a device that is in use.
+ * Set 'found' for the return value.
+ */
+ found ++;
+ check = 1;
+ if (print) {
+ printf(usage);
+ free(usage);
+ }
+ }
+ }
+ /*
+ * If check is set it means we found a slice(the current slice)
+ * on this device in use in some way. We potentially want
+ * to check this slice when labeling is requested.
+ */
+ if (check) {
+ name = dm_get_name(slices[i], &error);
+ if (error != 0 || !name) {
+ printf("checkdevinuse: Error7 occurred with "
+ "device in use checking: %s\n",
+ strerror(error));
+ nvlist_free(attrs);
+ attrs = NULL;
+ continue;
+ }
+ part = getpartition(dev, name);
+ dm_free_name(name);
+ name = NULL;
+ check = 0;
+ }
+ /*
+ * If we have attributes then we have successfully
+ * found the slice we were looking for and we also
+ * know this means we are not searching the whole
+ * disk so break out of the loop
+ * now.
+ */
+ if (attrs) {
+ nvlist_free(attrs);
+ break;
+ }
+ }
+
+ if (slices) {
+ dm_free_descriptors(slices);
+ }
+
+ return (found);
+}
+
+/*
+ * This routine checks to see if there are mounted partitions overlapping
+ * a given portion of a disk. If the start parameter is < 0, it means
+ * that the entire disk should be checked.
+ */
+static int
+checkmount(PedDevice* dev, diskaddr_t start, diskaddr_t end)
+{
+ SolarisSpecific* arch_specific;
+ struct extvtoc extvtoc;
+ diskaddr_t p_start;
+ diskaddr_t p_size;
+ FILE *fp;
+ int found = 0;
+ int part;
+ struct mnttab mnt_record;
+ struct mnttab *mp = &mnt_record;
+
+ PED_ASSERT(dev != NULL, return (found));
+ PED_ASSERT(dev->open_count > 0, return (found));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ if (ioctl(arch_specific->fd, DKIOCGEXTVTOC, &extvtoc) == -1) {
+ return (0);
+ }
+
+ /*
+ * Open the mount table.
+ */
+ fp = fopen(MNTTAB, "r");
+ if (fp == NULL) {
+ printf("checkmount: Unable to open mount table.\n");
+ return (0);
+ }
+ /*
+ * Loop through the mount table until we run out of entries.
+ */
+ while ((getmntent(fp, mp)) != -1) {
+
+ if ((part = getpartition(dev, mp->mnt_special)) == -1)
+ continue;
+
+ /*
+ * It's a mount on the disk we're checking. If we are
+ * checking whole disk, then we found trouble. We can
+ * quit searching.
+ */
+ if (start == UINT_MAX64) {
+ found = -1;
+ break;
+ }
+
+ /*
+ * If the partition overlaps the zone we're checking,
+ * then we found trouble. We can quit searching.
+ */
+ p_start = extvtoc.v_part[part].p_start;
+ p_size = extvtoc.v_part[part].p_size;
+ if (start >= p_start + p_size || end < p_start) {
+ continue;
+ }
+ found = -1;
+ break;
+ }
+ /*
+ * Close down the mount table.
+ */
+ (void) fclose(fp);
+
+ return (found);
+}
+
+/*
+ * Return 1 if the device is busy, 0 otherwise.
+ */
+static int
+solaris_is_busy(PedDevice* dev)
+{
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+
+ if (checkmount(dev, (diskaddr_t)-1, (diskaddr_t)-1))
+ return (1);
+
+ if (checkswap(dev, (diskaddr_t)-1, (diskaddr_t)-1))
+ return (1);
+
+ if (checkdevinuse(dev, (diskaddr_t)-1, (diskaddr_t)-1, 1))
+ return (1);
+
+ return (0);
+}
+
+/*
+ * This will accept a dev->path that looks like this:
+ * /devices/pci@0,0/pci-ide@1f,2/ide@0/cmdk@0,0:q
+ * /devices/pci@0,0/pci-ide@1f,2/ide@0/cmdk@0,0:q,raw
+ * or this:
+ * /dev/dsk/c0d0p0
+ * /dev/rdsk/c0d0p0
+ * It has to open the raw device, so it converts to it locally, if necessary.
+ */
+static int
+solaris_open(PedDevice* dev)
+{
+ SolarisSpecific* arch_specific;
+ char rawname[MAXPATHLEN];
+
+ PED_ASSERT(dev != NULL, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ /*
+ * Convert to the raw device, unless it already is.
+ */
+ if (strncmp(dev->path, "/devices", 8) == 0) {
+ if (strncmp(&dev->path[strlen(dev->path)-4], ",raw", 4)) {
+ snprintf(rawname, sizeof (rawname), "%s,raw",
+ dev->path);
+ } else {
+ strcpy(rawname, dev->path);
+ }
+ } else {
+ /*
+ * Assumes it is of the form: /dev/dsk/ or /dev/rdsk/
+ */
+ if (strncmp(dev->path, "/dev/dsk/", 9) == 0) {
+ snprintf(rawname, sizeof (rawname), "/dev/rdsk/%s",
+ &dev->path[9]);
+ } else {
+ strcpy(rawname, dev->path);
+ }
+ }
+
+retry:
+ arch_specific->fd = open(rawname, O_RDWR);
+
+ if (arch_specific->fd == -1) {
+ char *rw_error_msg = strerror(errno);
+
+ arch_specific->fd = open(rawname, O_RDONLY);
+
+ if (arch_specific->fd == -1) {
+ printf("solaris_open: open(\"%s\") failed\n", rawname);
+ if (ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_CANCEL,
+ _("Error opening %s: %s"),
+ rawname, strerror(errno)) != PED_EXCEPTION_RETRY) {
+ return (0);
+ } else {
+ goto retry;
+ }
+ } else {
+ ped_exception_throw(
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK,
+ _("Unable to open %s read-write (%s). %s has "
+ "been opened read-only."),
+ rawname, rw_error_msg, rawname);
+ dev->read_only = 1;
+ }
+ } else {
+ dev->read_only = 0;
+ }
+
+ return (1);
+}
+
+static int
+solaris_refresh_open(PedDevice* dev)
+{
+ return (1);
+}
+
+static int
+solaris_close(PedDevice* dev)
+{
+ SolarisSpecific* arch_specific;
+
+ PED_ASSERT(dev != NULL, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ close(arch_specific->fd);
+ return (1);
+}
+
+static int
+_do_fsync(PedDevice* dev)
+{
+ SolarisSpecific* arch_specific;
+ int status;
+ PedExceptionOption ex_status;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ while (1) {
+ status = fsync(arch_specific->fd);
+ if (status >= 0)
+ break;
+
+ ex_status = ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_IGNORE_CANCEL,
+ _("%s during fsync on %s"),
+ strerror(errno), dev->path);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_IGNORE:
+ return (1);
+
+ case PED_EXCEPTION_RETRY:
+ break;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch();
+ case PED_EXCEPTION_CANCEL:
+ return (0);
+ }
+ }
+ return (1);
+}
+
+static int
+solaris_refresh_close(PedDevice* dev)
+{
+ if (dev->dirty)
+ _do_fsync(dev);
+ return (1);
+}
+
+static int
+_device_seek(const PedDevice* dev, PedSector sector)
+{
+ SolarisSpecific* arch_specific;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+ PED_ASSERT(!dev->external_mode, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ if (sizeof (off_t) < 8) {
+ off64_t pos = (off64_t)(sector * dev->sector_size);
+ return (lseek64(arch_specific->fd, pos, SEEK_SET) == pos);
+ } else {
+ off_t pos = sector * dev->sector_size;
+ return (lseek(arch_specific->fd, pos, SEEK_SET) == pos);
+ }
+}
+
+static int
+solaris_read(const PedDevice* dev, void* vbuffer, PedSector start,
+ PedSector count)
+{
+ SolarisSpecific* arch_specific;
+ int status;
+ PedExceptionOption ex_status;
+ size_t read_length = count * dev->sector_size;
+ void *diobuf;
+ char *buffer = vbuffer;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+ PED_ASSERT(!dev->external_mode, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ while (1) {
+ if (_device_seek(dev, start))
+ break;
+
+ ex_status = ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_IGNORE_CANCEL,
+ _("%s during seek for read on %s"),
+ strerror(errno), dev->path);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_IGNORE:
+ return (1);
+
+ case PED_EXCEPTION_RETRY:
+ break;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch();
+ case PED_EXCEPTION_CANCEL:
+ return (0);
+ }
+ }
+
+ diobuf = memalign(dev->sector_size, read_length);
+ if (diobuf == NULL) {
+ printf("solaris_read: cannot memalign %u\n", read_length);
+ return (0);
+ }
+
+ while (1) {
+ status = read(arch_specific->fd, diobuf, read_length);
+ PED_ASSERT(status <= read_length, break);
+
+ if (status > 0)
+ memcpy(buffer, diobuf, status);
+
+ if (status == read_length)
+ break;
+
+ if (status > 0) {
+ printf("solaris_read: partial read %d of %d\n",
+ status, read_length);
+ read_length -= status;
+ buffer += status;
+ continue;
+ }
+
+ ex_status = ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_IGNORE_CANCEL,
+ _("%s during read on %s"),
+ strerror(errno),
+ dev->path);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_IGNORE:
+ free(diobuf);
+ return (1);
+
+ case PED_EXCEPTION_RETRY:
+ break;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch();
+ case PED_EXCEPTION_CANCEL:
+ free(diobuf);
+ return (0);
+ }
+ }
+
+ free(diobuf);
+
+ return (1);
+}
+
+static int
+solaris_write(PedDevice* dev, const void* buffer, PedSector start,
+ PedSector count)
+{
+ SolarisSpecific* arch_specific;
+ int status;
+ PedExceptionOption ex_status;
+ size_t write_length = count * dev->sector_size;
+ char *diobuf;
+ char *diobuf_start;
+
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
+ PED_ASSERT(dev->open_count > 0, return (0));
+ PED_ASSERT(!dev->external_mode, return (0));
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ if (dev->read_only) {
+ if (ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Can't write to %s, because it is opened read-only."),
+ dev->path) != PED_EXCEPTION_IGNORE)
+ return (0);
+ else
+ return (1);
+ }
+
+ while (1) {
+ if (_device_seek(dev, start))
+ break;
+
+ ex_status = ped_exception_throw(
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL,
+ _("%s during seek for write on %s"),
+ strerror(errno), dev->path);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_IGNORE:
+ return (1);
+
+ case PED_EXCEPTION_RETRY:
+ break;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch();
+ case PED_EXCEPTION_CANCEL:
+ return (0);
+ }
+ }
+
+#ifdef READ_ONLY
+ printf("solaris_write(\"%s\", %p, %d, %d)\n",
+ dev->path, buffer, (int)start, (int)count);
+#else
+ dev->dirty = 1;
+
+ diobuf = memalign((size_t)PED_SECTOR_SIZE_DEFAULT, write_length);
+ if (diobuf == NULL) {
+ printf("solaris_write: cannot memalign %u\n", write_length);
+ return (0);
+ }
+
+ memcpy(diobuf, buffer, write_length);
+ diobuf_start = diobuf;
+ while (1) {
+ status = write(arch_specific->fd, diobuf, write_length);
+ if (status == write_length)
+ break;
+ if (status > 0) {
+ printf("solaris_write: partial write %d of %d\n",
+ status, write_length);
+ write_length -= status;
+ diobuf += status;
+ continue;
+ }
+
+ ex_status = ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_RETRY_IGNORE_CANCEL,
+ _("%s during write on %s"),
+ strerror(errno), dev->path);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_IGNORE:
+ free(diobuf_start);
+ return (1);
+
+ case PED_EXCEPTION_RETRY:
+ break;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch();
+ case PED_EXCEPTION_CANCEL:
+ free(diobuf_start);
+ return (0);
+ }
+ }
+ free(diobuf_start);
+#endif /* !READ_ONLY */
+
+ return (1);
+}
+
+
+/*
+ * returns the number of sectors that are ok.
+ * This is never called. It would get called through ped_device_check().
+ */
+static PedSector
+solaris_check(PedDevice* dev, void* buffer, PedSector start, PedSector count)
+{
+ SolarisSpecific* arch_specific;
+ PedSector done;
+ int status;
+ void* diobuf;
+
+ PED_ASSERT(dev != NULL, return (0LL));
+ PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
+ return (0LL));
+ PED_ASSERT(dev->open_count > 0, return (0LL));
+ PED_ASSERT(!dev->external_mode, return (0LL));
+
+ printf("solaris_check: start %lld count %lld\n", start, count);
+
+ arch_specific = SOLARIS_SPECIFIC(dev);
+
+ if (!_device_seek(dev, start))
+ return (0LL);
+
+ diobuf = memalign(PED_SECTOR_SIZE_DEFAULT, count * dev->sector_size);
+ if (diobuf == NULL) {
+ printf("solaris_check: cannot memalign %u\n",
+ count * dev->sector_size);
+ return (0LL);
+ }
+
+ for (done = 0; done < count; done += status / dev->sector_size) {
+ status = read(arch_specific->fd, diobuf,
+ (size_t)((count - done) * dev->sector_size));
+ if (status < 0)
+ break;
+ }
+ free(diobuf);
+
+ return (done);
+}
+
+static int
+solaris_sync(PedDevice* dev)
+{
+ PED_ASSERT(dev != NULL, return (0));
+ PED_ASSERT(!dev->external_mode, return (0));
+
+ if (dev->read_only)
+ return (1);
+ if (!_do_fsync(dev))
+ return (0);
+ return (1);
+}
+
+/*
+ * Returns all *p0 block devices.
+ * open the raw device so ioctl works.
+ */
+static void
+solaris_probe_all()
+{
+ DIR *dir;
+ struct dirent *dp;
+ char *pname;
+ char block_path[256];
+ char raw_path[256];
+ struct stat buffer;
+ int fd;
+
+ dir = opendir("/dev/dsk");
+ while ((dp = readdir(dir)) != NULL) {
+
+ pname = dp->d_name + strlen(dp->d_name) - 2;
+ if (strcmp(pname, "p0") == 0) {
+
+ strncpy(block_path, "/dev/dsk/", sizeof (block_path));
+ strncat(block_path, dp->d_name, sizeof (block_path));
+
+ strncpy(raw_path, "/dev/rdsk/", sizeof (raw_path));
+ strncat(raw_path, dp->d_name, sizeof (raw_path));
+
+ if (stat(block_path, &buffer) == 0) {
+
+ if ((fd = open(raw_path, O_RDONLY)) < 0) {
+ continue;
+ }
+
+#ifdef DONT_ALLOW_REMOVEABLE_DEVICES
+ int n = 0;
+ if (ioctl(fd, DKIOCREMOVABLE, &n) < 0) {
+ char msg[MAXPATHLEN];
+ snprintf(msg, sizeof (msg),
+ "ioctl(\"%s\", DKIOCREMOVABLE)",
+ raw_path);
+ perror(msg);
+ } else if (!n) {
+ /*
+ * Not a removable device
+ * printf("solaris_probe_all: %s\n",
+ * block_path);
+ */
+ }
+#endif /* DONT_ALLOW_REMOVEABLE_DEVICES */
+
+ _ped_device_probe(block_path);
+ close(fd);
+ }
+ }
+ }
+}
+
+static char *
+solaris_partition_get_path(const PedPartition* part)
+{
+ return (_device_get_part_path(part->disk->dev, part->num));
+}
+
+/*
+ * Returns 1 if the partition is busy in some way, 0 otherwise.
+ */
+static int
+solaris_partition_is_busy(const PedPartition* part)
+{
+ int r1, r2, r3;
+
+ PED_ASSERT(part != NULL, return (0));
+
+ r1 = checkmount(part->geom.dev, part->geom.start, part->geom.end);
+ r2 = checkswap(part->geom.dev, part->geom.start, part->geom.end);
+ r3 = checkdevinuse(part->geom.dev, part->geom.start, part->geom.end, 1);
+
+ if (r1 || r2 || r3)
+ return (1);
+
+ return (0);
+}
+
+static int
+solaris_disk_commit(PedDisk* disk)
+{
+ return (1);
+}
+
+static PedDeviceArchOps solaris_dev_ops = {
+ ._new = solaris_new,
+ .destroy = solaris_destroy,
+ .is_busy = solaris_is_busy,
+ .open = solaris_open,
+ .refresh_open = solaris_refresh_open,
+ .close = solaris_close,
+ .refresh_close = solaris_refresh_close,
+ .read = solaris_read,
+ .write = solaris_write,
+ .check = solaris_check,
+ .sync = solaris_sync,
+ .sync_fast = solaris_sync,
+ .probe_all = solaris_probe_all
+};
+
+PedDiskArchOps solaris_disk_ops = {
+ .partition_get_path = solaris_partition_get_path,
+ .partition_is_busy = solaris_partition_is_busy,
+ .disk_commit = solaris_disk_commit
+};
+
+PedArchitecture ped_solaris_arch = {
+ .dev_ops = &solaris_dev_ops,
+ .disk_ops = &solaris_disk_ops
+};
diff --git a/usr/src/lib/libparted/common/libparted/cs/constraint.c b/usr/src/lib/libparted/common/libparted/cs/constraint.c
new file mode 100644
index 0000000000..5595db3213
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/cs/constraint.c
@@ -0,0 +1,529 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \addtogroup PedConstraint
+ *
+ * \brief Constraint solver interface.
+ *
+ * Constraints are used to communicate restrictions on operations Constraints
+ * are restrictions on the location and alignment of the start and end of a
+ * partition, and the minimum and maximum size.
+ *
+ * Constraints are closed under intersection (for the proof see the source
+ * code). For background information see the Chinese Remainder Theorem.
+ *
+ * This interface consists of construction constraints, finding the intersection
+ * of constraints, and finding solutions to constraints.
+ *
+ * The constraint solver allows you to specify constraints on where a partition
+ * or file system (or any PedGeometry) may be placed/resized/etc. For example,
+ * you might want to make sure that a file system is at least 10 Gb, or that it
+ * starts at the beginning of new cylinder.
+ *
+ * The constraint solver in this file unifies solver in geom.c (which allows you
+ * to specify constraints on ranges) and natmath.c (which allows you to specify
+ * alignment constraints).
+ *
+ * @{
+ */
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+/**
+ * Initializes a pre-allocated piece of memory to contain a constraint
+ * with the supplied default values.
+ *
+ * \return \c 0 on failure.
+ */
+int
+ped_constraint_init (
+ PedConstraint* constraint,
+ const PedAlignment* start_align,
+ const PedAlignment* end_align,
+ const PedGeometry* start_range,
+ const PedGeometry* end_range,
+ PedSector min_size,
+ PedSector max_size)
+{
+ PED_ASSERT (constraint != NULL, return 0);
+ PED_ASSERT (start_range != NULL, return 0);
+ PED_ASSERT (end_range != NULL, return 0);
+ PED_ASSERT (min_size > 0, return 0);
+ PED_ASSERT (max_size > 0, return 0);
+
+ constraint->start_align = ped_alignment_duplicate (start_align);
+ constraint->end_align = ped_alignment_duplicate (end_align);
+ constraint->start_range = ped_geometry_duplicate (start_range);
+ constraint->end_range = ped_geometry_duplicate (end_range);
+ constraint->min_size = min_size;
+ constraint->max_size = max_size;
+
+ return 1;
+}
+
+/**
+ * Convenience wrapper for ped_constraint_init().
+ *
+ * Allocates a new piece of memory and initializes the constraint.
+ *
+ * \return \c NULL on failure.
+ */
+PedConstraint*
+ped_constraint_new (
+ const PedAlignment* start_align,
+ const PedAlignment* end_align,
+ const PedGeometry* start_range,
+ const PedGeometry* end_range,
+ PedSector min_size,
+ PedSector max_size)
+{
+ PedConstraint* constraint;
+
+ constraint = (PedConstraint*) ped_malloc (sizeof (PedConstraint));
+ if (!constraint)
+ goto error;
+ if (!ped_constraint_init (constraint, start_align, end_align,
+ start_range, end_range, min_size, max_size))
+ goto error_free_constraint;
+ return constraint;
+
+error_free_constraint:
+ ped_free (constraint);
+error:
+ return NULL;
+}
+
+/**
+ * Return a constraint that requires a region to be entirely contained inside
+ * \p max, and to entirely contain \p min.
+ *
+ * \return \c NULL on failure.
+ */
+PedConstraint*
+ped_constraint_new_from_min_max (
+ const PedGeometry* min,
+ const PedGeometry* max)
+{
+ PedGeometry start_range;
+ PedGeometry end_range;
+
+ PED_ASSERT (min != NULL, return NULL);
+ PED_ASSERT (max != NULL, return NULL);
+ PED_ASSERT (ped_geometry_test_inside (max, min), return NULL);
+
+ ped_geometry_init (&start_range, min->dev, max->start,
+ min->start - max->start + 1);
+ ped_geometry_init (&end_range, min->dev, min->end,
+ max->end - min->end + 1);
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &start_range, &end_range,
+ min->length, max->length);
+}
+
+/**
+ * Return a constraint that requires a region to entirely contain \p min.
+ *
+ * \return \c NULL on failure.
+ */
+PedConstraint*
+ped_constraint_new_from_min (const PedGeometry* min)
+{
+ PedGeometry full_dev;
+
+ PED_ASSERT (min != NULL, return NULL);
+
+ ped_geometry_init (&full_dev, min->dev, 0, min->dev->length);
+ return ped_constraint_new_from_min_max (min, &full_dev);
+}
+
+/**
+ * Return a constraint that requires a region to be entirely contained inside
+ * \p max.
+ *
+ * \return \c NULL on failure.
+ */
+PedConstraint*
+ped_constraint_new_from_max (const PedGeometry* max)
+{
+ PED_ASSERT (max != NULL, return NULL);
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ max, max, 1, max->length);
+}
+
+/**
+ * Duplicate a constraint.
+ *
+ * \return \c NULL on failure.
+ */
+PedConstraint*
+ped_constraint_duplicate (const PedConstraint* constraint)
+{
+ PED_ASSERT (constraint != NULL, return NULL);
+
+ return ped_constraint_new (
+ constraint->start_align,
+ constraint->end_align,
+ constraint->start_range,
+ constraint->end_range,
+ constraint->min_size,
+ constraint->max_size);
+}
+
+/**
+ * Return a constraint that requires a region to satisfy both \p a and \p b.
+ *
+ * Moreover, any region satisfying \p a and \p b will also satisfy the returned
+ * constraint.
+ *
+ * \return \c NULL if no solution could be found (note that \c NULL is a valid
+ * PedConstraint).
+ */
+PedConstraint*
+ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b)
+{
+ PedAlignment* start_align;
+ PedAlignment* end_align;
+ PedGeometry* start_range;
+ PedGeometry* end_range;
+ PedSector min_size;
+ PedSector max_size;
+ PedConstraint* constraint;
+
+ if (!a || !b)
+ return NULL;
+
+ start_align = ped_alignment_intersect (a->start_align, b->start_align);
+ if (!start_align)
+ goto empty;
+ end_align = ped_alignment_intersect (a->end_align, b->end_align);
+ if (!end_align)
+ goto empty_destroy_start_align;
+ start_range = ped_geometry_intersect (a->start_range, b->start_range);
+ if (!start_range)
+ goto empty_destroy_end_align;
+ end_range = ped_geometry_intersect (a->end_range, b->end_range);
+ if (!end_range)
+ goto empty_destroy_start_range;
+ min_size = PED_MAX (a->min_size, b->min_size);
+ max_size = PED_MIN (a->max_size, b->max_size);
+
+ constraint = ped_constraint_new (
+ start_align, end_align, start_range, end_range,
+ min_size, max_size);
+ if (!constraint)
+ goto empty_destroy_end_range;
+
+ ped_alignment_destroy (start_align);
+ ped_alignment_destroy (end_align);
+ ped_geometry_destroy (start_range);
+ ped_geometry_destroy (end_range);
+ return constraint;
+
+empty_destroy_end_range:
+ ped_geometry_destroy (end_range);
+empty_destroy_start_range:
+ ped_geometry_destroy (start_range);
+empty_destroy_end_align:
+ ped_alignment_destroy (end_align);
+empty_destroy_start_align:
+ ped_alignment_destroy (start_align);
+empty:
+ return NULL;
+}
+
+/**
+ * Release the memory allocated for a PedConstraint constructed with
+ * ped_constraint_init().
+ */
+void
+ped_constraint_done (PedConstraint* constraint)
+{
+ PED_ASSERT (constraint != NULL, return);
+
+ ped_alignment_destroy (constraint->start_align);
+ ped_alignment_destroy (constraint->end_align);
+ ped_geometry_destroy (constraint->start_range);
+ ped_geometry_destroy (constraint->end_range);
+}
+
+/**
+ * Release the memory allocated for a PedConstraint constructed with
+ * ped_constraint_new().
+ */
+void
+ped_constraint_destroy (PedConstraint* constraint)
+{
+ if (constraint) {
+ ped_constraint_done (constraint);
+ ped_free (constraint);
+ }
+}
+
+/*
+ * Return the region within which the start must lie
+ * in order to satisfy a constriant. It takes into account
+ * constraint->start_range, constraint->min_size and constraint->max_size.
+ * All sectors in this range that also satisfy alignment requirements have
+ * an end, such that the (start, end) satisfy the constraint.
+ */
+static PedGeometry*
+_constraint_get_canonical_start_range (const PedConstraint* constraint)
+{
+ PedSector first_end_soln;
+ PedSector last_end_soln;
+ PedSector min_start;
+ PedSector max_start;
+ PedGeometry start_min_max_range;
+
+ if (constraint->min_size > constraint->max_size)
+ return NULL;
+
+ first_end_soln = ped_alignment_align_down (
+ constraint->end_align, constraint->end_range,
+ constraint->end_range->start);
+ last_end_soln = ped_alignment_align_up (
+ constraint->end_align, constraint->end_range,
+ constraint->end_range->end);
+ if (first_end_soln == -1 || last_end_soln == -1
+ || first_end_soln > last_end_soln
+ || last_end_soln < constraint->min_size)
+ return NULL;
+
+ min_start = first_end_soln - constraint->max_size + 1;
+ if (min_start < 0)
+ min_start = 0;
+ max_start = last_end_soln - constraint->min_size + 1;
+ if (max_start < 0)
+ return NULL;
+
+ ped_geometry_init (
+ &start_min_max_range, constraint->start_range->dev,
+ min_start, max_start - min_start + 1);
+
+ return ped_geometry_intersect (&start_min_max_range,
+ constraint->start_range);
+}
+
+/*
+ * Return the nearest start that will have at least one other end that
+ * together satisfy the constraint.
+ */
+static PedSector
+_constraint_get_nearest_start_soln (const PedConstraint* constraint,
+ PedSector start)
+{
+ PedGeometry* start_range;
+ PedSector result;
+
+ start_range = _constraint_get_canonical_start_range (constraint);
+ if (!start_range)
+ return -1;
+ result = ped_alignment_align_nearest (
+ constraint->start_align, start_range, start);
+ ped_geometry_destroy (start_range);
+ return result;
+}
+
+/*
+ * Given a constraint and a start ("half of the solution"), find the
+ * range of all possible ends, such that all (start, end) are solutions
+ * to constraint (subject to additional alignment requirements).
+ */
+static PedGeometry*
+_constraint_get_end_range (const PedConstraint* constraint, PedSector start)
+{
+ PedDevice* dev = constraint->end_range->dev;
+ PedSector first_min_max_end;
+ PedSector last_min_max_end;
+ PedGeometry end_min_max_range;
+
+ if (start + constraint->min_size - 1 > dev->length - 1)
+ return NULL;
+
+ first_min_max_end = start + constraint->min_size - 1;
+ last_min_max_end = start + constraint->max_size - 1;
+ if (last_min_max_end > dev->length - 1)
+ last_min_max_end = dev->length - 1;
+
+ ped_geometry_init (&end_min_max_range, dev,
+ first_min_max_end,
+ last_min_max_end - first_min_max_end + 1);
+
+ return ped_geometry_intersect (&end_min_max_range,
+ constraint->end_range);
+}
+
+/*
+ * Given "constraint" and "start", find the end that is nearest to
+ * "end", such that ("start", the end) together form a solution to
+ * "constraint".
+ */
+static PedSector
+_constraint_get_nearest_end_soln (const PedConstraint* constraint,
+ PedSector start, PedSector end)
+{
+ PedGeometry* end_range;
+ PedSector result;
+
+ end_range = _constraint_get_end_range (constraint, start);
+ if (!end_range)
+ return -1;
+
+ result = ped_alignment_align_nearest (constraint->end_align, end_range,
+ end);
+ ped_geometry_destroy (end_range);
+ return result;
+}
+
+/**
+ * Return the nearest region to \p geom that satisfy a \p constraint.
+ *
+ * Note that "nearest" is somewhat ambiguous. This function makes
+ * no guarantees about how this ambiguity is resovled.
+ *
+ * \return PedGeometry, or NULL when a \p constrain cannot be satisfied
+ */
+PedGeometry*
+ped_constraint_solve_nearest (
+ const PedConstraint* constraint, const PedGeometry* geom)
+{
+ PedSector start;
+ PedSector end;
+ PedGeometry* result;
+
+ if (constraint == NULL)
+ return NULL;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (constraint->start_range->dev == geom->dev, return NULL);
+
+ start = _constraint_get_nearest_start_soln (constraint, geom->start);
+ if (start == -1)
+ return NULL;
+ end = _constraint_get_nearest_end_soln (constraint, start, geom->end);
+ if (end == -1)
+ return NULL;
+
+ result = ped_geometry_new (geom->dev, start, end - start + 1);
+ if (!result)
+ return NULL;
+ PED_ASSERT (ped_constraint_is_solution (constraint, result),
+ return NULL);
+ return result;
+}
+
+/**
+ * Find the largest region that satisfies a constraint.
+ *
+ * There might be more than one solution. This function makes no
+ * guarantees about which solution it will choose in this case.
+ */
+PedGeometry*
+ped_constraint_solve_max (const PedConstraint* constraint)
+{
+ PedDevice* dev;
+ PedGeometry full_dev;
+
+ if (!constraint)
+ return NULL;
+ dev = constraint->start_range->dev;
+ ped_geometry_init (&full_dev, dev, 0, dev->length - 1);
+ return ped_constraint_solve_nearest (constraint, &full_dev);
+}
+
+/**
+ * Check whether \p geom satisfies the given constraint.
+ *
+ * \return \c 1 if it does.
+ **/
+int
+ped_constraint_is_solution (const PedConstraint* constraint,
+ const PedGeometry* geom)
+{
+ PED_ASSERT (constraint != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+
+ if (!ped_alignment_is_aligned (constraint->start_align, NULL,
+ geom->start))
+ return 0;
+ if (!ped_alignment_is_aligned (constraint->end_align, NULL, geom->end))
+ return 0;
+ if (!ped_geometry_test_sector_inside (constraint->start_range,
+ geom->start))
+ return 0;
+ if (!ped_geometry_test_sector_inside (constraint->end_range, geom->end))
+ return 0;
+ if (geom->length < constraint->min_size)
+ return 0;
+ if (geom->length > constraint->max_size)
+ return 0;
+ return 1;
+}
+
+/**
+ * Return a constraint that any region on the given device will satisfy.
+ */
+PedConstraint*
+ped_constraint_any (const PedDevice* dev)
+{
+ PedGeometry full_dev;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length))
+ return NULL;
+
+ return ped_constraint_new (
+ ped_alignment_any,
+ ped_alignment_any,
+ &full_dev,
+ &full_dev,
+ 1,
+ dev->length);
+}
+
+/**
+ * Return a constraint that only the given region will satisfy.
+ */
+PedConstraint*
+ped_constraint_exact (const PedGeometry* geom)
+{
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry start_sector;
+ PedGeometry end_sector;
+
+ ped_alignment_init (&start_align, geom->start, 0);
+ ped_alignment_init (&end_align, geom->end, 0);
+ ped_geometry_init (&start_sector, geom->dev, geom->start, 1);
+ ped_geometry_init (&end_sector, geom->dev, geom->end, 1);
+
+ return ped_constraint_new (&start_align, &end_align,
+ &start_sector, &end_sector, 1,
+ geom->dev->length);
+}
+
+/**
+ * @}
+ */
+
diff --git a/usr/src/lib/libparted/common/libparted/cs/geom.c b/usr/src/lib/libparted/common/libparted/cs/geom.c
new file mode 100644
index 0000000000..620fac88ce
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/cs/geom.c
@@ -0,0 +1,474 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file geom.c */
+
+
+/**
+ * \addtogroup PedGeometry
+ *
+ * \brief PedGeometry represents a continuous region on a device. All addressing
+ * through a PedGeometry object is in terms of the start of the continuous
+ * region.
+ *
+ * The following conditions are always true on a PedGeometry object manipulated
+ * with the GNU Parted API:
+ *
+ * - <tt>start + length - 1 == end</tt>
+ * - <tt>length > 0</tt>
+ * - <tt>start >= 0</tt>
+ * - <tt>end < dev->length</tt>
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/**
+ * Initialize the previously allocated PedGeometry \p geom.
+ */
+int
+ped_geometry_init (PedGeometry* geom, const PedDevice* dev,
+ PedSector start, PedSector length)
+{
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (dev != NULL, return 0);
+
+ geom->dev = (PedDevice*) dev;
+ return ped_geometry_set (geom, start, length);
+}
+
+/**
+ * Create a new PedGeometry object on \p disk, starting at \p start with a
+ * size of \p length sectors.
+ *
+ * \return NULL on failure.
+ */
+PedGeometry*
+ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length)
+{
+ PedGeometry* geom;
+
+ PED_ASSERT (dev != NULL, return NULL);
+
+ geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry));
+ if (!geom)
+ goto error;
+ if (!ped_geometry_init (geom, dev, start, length))
+ goto error_free_geom;
+ return geom;
+
+error_free_geom:
+ ped_free (geom);
+error:
+ return NULL;
+}
+
+/**
+ * Duplicate a PedGeometry object.
+ *
+ * This function constructs a PedGeometry object that is an identical but
+ * independent copy of \p geom. Both the input, \p geom, and the output
+ * should be destroyed with ped_geometry_destroy() when they are no
+ * longer needed.
+ *
+ * \return NULL on failure.
+ */
+PedGeometry*
+ped_geometry_duplicate (const PedGeometry* geom)
+{
+ PED_ASSERT (geom != NULL, return NULL);
+ return ped_geometry_new (geom->dev, geom->start, geom->length);
+}
+
+/**
+ * Return a PedGeometry object that refers to the intersection of
+ * \p a and \p b.
+ *
+ * This function constructs a PedGeometry object that describes the
+ * region that is common to both a and b. If there is no such common
+ * region, it returns NULL. (This situation is not treated as an
+ * error by much of GNU Parted.)
+ */
+PedGeometry*
+ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b)
+{
+ PedSector start;
+ PedSector end;
+
+ if (!a || !b || a->dev != b->dev)
+ return NULL;
+
+ start = PED_MAX (a->start, b->start);
+ end = PED_MIN (a->end, b->end);
+ if (start > end)
+ return NULL;
+
+ return ped_geometry_new (a->dev, start, end - start + 1);
+}
+
+/**
+ * Destroy a PedGeometry object.
+ */
+void
+ped_geometry_destroy (PedGeometry* geom)
+{
+ PED_ASSERT (geom != NULL, return);
+
+ ped_free (geom);
+}
+
+/**
+ * Assign a new \p start, \p end (implicitly) and \p length to \p geom.
+ *
+ * \p geom->end is calculated from \p start and \p length.
+ */
+int
+ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length)
+{
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (geom->dev != NULL, return 0);
+
+ if (length < 1) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have the end before the start!"));
+ return 0;
+ }
+ if (start < 0 || start + length - 1 >= geom->dev->length) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have a partition outside the disk!"));
+ return 0;
+ }
+
+ geom->start = start;
+ geom->length = length;
+ geom->end = start + length - 1;
+
+ return 1;
+}
+
+/**
+ * Assign a new start to \p geom without changing \p geom->end.
+ *
+ * \p geom->length is updated accordingly.
+ */
+int
+ped_geometry_set_start (PedGeometry* geom, PedSector start)
+{
+ return ped_geometry_set (geom, start, geom->end - start + 1);
+}
+
+/**
+ * Assign a new end to \p geom without changing \p geom->start.
+ *
+ * \p geom->length is updated accordingly.
+ */
+int
+ped_geometry_set_end (PedGeometry* geom, PedSector end)
+{
+ return ped_geometry_set (geom, geom->start, end - geom->start + 1);
+}
+/**
+ * Test if \p a overlaps with \p b.
+ *
+ * That is, they lie on the same physical device, and they share
+ * the same physical region at least partially.
+ *
+ * \return 1 if \p a and \p b overlap.
+ */
+int
+ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b)
+{
+ PED_ASSERT (a != NULL, return 0);
+ PED_ASSERT (b != NULL, return 0);
+
+ if (a->dev != b->dev)
+ return 0;
+
+ if (a->start < b->start)
+ return a->end >= b->start;
+ else
+ return b->end >= a->start;
+}
+
+/**
+ * Tests if \p b lies completely within \p a. That is, they lie on the same
+ * physical device, and all of the \p b's region is contained inside
+ * \p a's.
+ *
+ * \return 1 if the region \p b describes is contained entirely inside \p a
+*/
+int
+ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b)
+{
+ PED_ASSERT (a != NULL, return 0);
+ PED_ASSERT (b != NULL, return 0);
+
+ if (a->dev != b->dev)
+ return 0;
+
+ return b->start >= a->start && b->end <= a->end;
+}
+
+/**
+ * Tests if \a a and \p b refer to the same physical region.
+ *
+ * \return 1 if \p a and \p b describe the same regions
+ *
+ */
+int
+ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b)
+{
+ PED_ASSERT (a != NULL, return 0);
+ PED_ASSERT (b != NULL, return 0);
+
+ return a->dev == b->dev
+ && a->start == b->start
+ && a->end == b->end;
+}
+
+/**
+ * Tests if \p sector is inside \p geom.
+ *
+ * \return 1 if sector lies within the \p region that \p geom describes
+ */
+int
+ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector)
+{
+ PED_ASSERT (geom != NULL, return 0);
+
+ return sector >= geom->start && sector <= geom->end;
+}
+
+/**
+ * Reads data from the region represented by \p geom. \p offset is the
+ * location from within the region, not from the start of the disk.
+ * \p count sectors are read into \p buffer.
+ * This is essentially equivalent to:
+ * \code
+ * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count)
+ * \endcode
+ *
+ * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of
+ * partition
+ *
+ * \return 0 on failure
+ */
+int
+ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset,
+ PedSector count)
+{
+ PedSector real_start;
+
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (buffer != NULL, return 0);
+ PED_ASSERT (offset >= 0, return 0);
+ PED_ASSERT (count >= 0, return 0);
+
+ real_start = geom->start + offset;
+
+ if (real_start + count - 1 > geom->end)
+ return 0;
+
+ if (!ped_device_read (geom->dev, buffer, real_start, count))
+ return 0;
+ return 1;
+}
+
+/**
+ * Flushes the cache on \p geom.
+ *
+ * This function flushes all write-behind caches that might be holding
+ * writes made by ped_geometry_write() to \p geom. It is slow, because
+ * it guarantees cache coherency among all relevant caches.
+ *
+ * \return 0 on failure
+ */
+int
+ped_geometry_sync (PedGeometry* geom)
+{
+ PED_ASSERT (geom != NULL, return 0);
+ return ped_device_sync (geom->dev);
+}
+
+/**
+ * Flushes the cache on \p geom.
+ *
+ * This function flushes all write-behind caches that might be holding writes
+ * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency
+ * with other caches that cache data in the region described by \p geom.
+ * If you need cache coherency, use ped_geometry_sync() instead.
+ *
+ * \return 0 on failure
+ */
+int
+ped_geometry_sync_fast (PedGeometry* geom)
+{
+ PED_ASSERT (geom != NULL, return 0);
+ return ped_device_sync_fast (geom->dev);
+}
+
+/**
+ * Writes data into the region represented by \p geom. \p offset is the
+ * location from within the region, not from the start of the disk.
+ * \p count sectors are written.
+ *
+ * \return 0 on failure
+ */
+int
+ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset,
+ PedSector count)
+{
+ int exception_status;
+ PedSector real_start;
+
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (buffer != NULL, return 0);
+ PED_ASSERT (offset >= 0, return 0);
+ PED_ASSERT (count >= 0, return 0);
+
+ real_start = geom->start + offset;
+
+ if (real_start + count - 1 > geom->end) {
+ exception_status = ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Attempt to write sectors %ld-%ld outside of "
+ "partition on %s."),
+ (long) offset, (long) (offset + count - 1),
+ geom->dev->path);
+ return exception_status == PED_EXCEPTION_IGNORE;
+ }
+
+ if (!ped_device_write (geom->dev, buffer, real_start, count))
+ return 0;
+ return 1;
+}
+
+/**
+ * Checks for physical disk errors. \todo use ped_device_check()
+ *
+ * Checks a region for physical defects on \p geom. \p buffer is used
+ * for temporary storage for ped_geometry_check(), and has an undefined
+ * value. \p buffer is \p buffer_size sectors long.
+ * The region checked starts at \p offset sectors inside the
+ * region represented by \p geom, and is \p count sectors long.
+ * \p granularity specificies how sectors should be grouped
+ * together. The first bad sector to be returned will always be in
+ * the form:
+ * <tt>offset + n * granularity</tt>
+ *
+ * \return the first bad sector, or 0 if there were no physical errors
+ */
+PedSector
+ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size,
+ PedSector offset, PedSector granularity, PedSector count,
+ PedTimer* timer)
+{
+ PedSector group;
+ PedSector i;
+ PedSector read_len;
+
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (buffer != NULL, return 0);
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("checking for bad blocks"));
+
+retry:
+ ped_exception_fetch_all();
+ for (group = offset; group < offset + count; group += buffer_size) {
+ ped_timer_update (timer, 1.0 * (group - offset) / count);
+ read_len = PED_MIN (buffer_size, offset + count - group);
+ if (!ped_geometry_read (geom, buffer, group, read_len))
+ goto found_error;
+ }
+ ped_exception_leave_all();
+ ped_timer_update (timer, 1.0);
+ return 0;
+
+found_error:
+ ped_exception_catch();
+ for (i = group; i + granularity < group + count; i += granularity) {
+ if (!ped_geometry_read (geom, buffer, i, granularity)) {
+ ped_exception_catch();
+ ped_exception_leave_all();
+ return i;
+ }
+ }
+ ped_exception_leave_all();
+ goto retry; /* weird: failure on group read, but not individually */
+}
+
+/**
+ * This function takes a \p sector inside the region described by src, and
+ * returns that sector's address inside dst. This means that
+ *
+ * \code
+ * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1)
+ * \endcode
+ *
+ * does the same thing as
+ *
+ * \code
+ * ped_geometry_read (src, buf, sector, 1)
+ * \endcode
+ *
+ * Clearly, this will only work if \p src and \p dst overlap.
+ *
+ * \return -1 if \p sector is not within \p dst's space,
+ * or \p sector's address inside \p dst
+ *
+ */
+PedSector
+ped_geometry_map (const PedGeometry* dst, const PedGeometry* src,
+ PedSector sector)
+{
+ PedSector result;
+
+ PED_ASSERT (dst != NULL, return 0);
+ PED_ASSERT (src != NULL, return 0);
+
+ if (!ped_geometry_test_sector_inside (src, sector))
+ return -1;
+ if (dst->dev != src->dev)
+ return -1;
+
+ result = src->start + sector - dst->start;
+ if (result < 0 || result > dst->length)
+ return -1;
+
+ return result;
+}
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/libparted/cs/natmath.c b/usr/src/lib/libparted/common/libparted/cs/natmath.c
new file mode 100644
index 0000000000..3a04aaa140
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/cs/natmath.c
@@ -0,0 +1,503 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * \file natmath.c
+ */
+
+/**
+ * \addtogroup PedAlignment
+ *
+ * \brief Alignment constraint model.
+ *
+ * This part of libparted models alignment constraints.
+ *
+ * @{
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/natmath.h>
+
+/* Arrrghhh! Why doesn't C have tuples? */
+typedef struct {
+ PedSector gcd; /* "converges" to the gcd */
+ PedSector x;
+ PedSector y;
+} EuclidTriple;
+
+static const PedAlignment _any = {
+ .offset = 0,
+ .grain_size = 1
+};
+
+const PedAlignment* ped_alignment_any = &_any;
+const PedAlignment* ped_alignment_none = NULL;
+
+/* This function returns "a mod b", the way C should have done it!
+ * Mathematicians prefer -3 mod 4 to be 3. Reason: division by N
+ * is all about adding or subtracting N, and we like our remainders
+ * to be between 0 and N - 1.
+ */
+PedSector
+abs_mod (PedSector a, PedSector b)
+{
+ if (a < 0)
+ return a % b + b;
+ else
+ return a % b;
+}
+
+/* Rounds a number down to the closest number that is a multiple of
+ * grain_size.
+ */
+PedSector
+ped_round_down_to (PedSector sector, PedSector grain_size)
+{
+ return sector - abs_mod (sector, grain_size);
+}
+
+#ifdef __sun
+extern PedSector
+#else
+extern inline PedSector
+#endif
+ped_div_round_up (PedSector numerator, PedSector divisor)
+{
+ return (numerator + divisor - 1) / divisor;
+}
+
+#ifdef __sun
+extern PedSector
+#else
+extern inline PedSector
+#endif
+ped_div_round_to_nearest (PedSector numerator, PedSector divisor)
+{
+ return (numerator + divisor/2) / divisor;
+}
+
+/* Rounds a number up to the closest number that is a multiple of
+ * grain_size.
+ */
+PedSector
+ped_round_up_to (PedSector sector, PedSector grain_size)
+{
+ if (sector % grain_size)
+ return ped_round_down_to (sector, grain_size) + grain_size;
+ else
+ return sector;
+}
+
+/* Rounds a number to the closest number that is a multiple of grain_size. */
+PedSector
+ped_round_to_nearest (PedSector sector, PedSector grain_size)
+{
+ if (sector % grain_size > grain_size/2)
+ return ped_round_up_to (sector, grain_size);
+ else
+ return ped_round_down_to (sector, grain_size);
+}
+
+/* This function returns the largest number that divides both a and b.
+ * It uses the ancient Euclidean algorithm.
+ */
+PedSector
+ped_greatest_common_divisor (PedSector a, PedSector b)
+{
+ PED_ASSERT (a >= 0, return 0);
+ PED_ASSERT (b >= 0, return 0);
+
+ /* Put the arguments in the "right" format. (Recursive calls made by
+ * this function are always in the right format.)
+ */
+ if (b > a)
+ return ped_greatest_common_divisor (b, a);
+
+ if (b)
+ return ped_greatest_common_divisor (b, a % b);
+ else
+ return a;
+}
+
+/**
+ * Initialize a preallocated piece of memory for an alignment object
+ * (used by PedConstraint).
+ *
+ * The object will represent all sectors \e s for which the equation
+ * <tt>s = offset + X * grain_size</tt> holds.
+ */
+int
+ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size)
+{
+ PED_ASSERT (align != NULL, return 0);
+
+ if (grain_size < 0)
+ return 0;
+
+ if (grain_size)
+ align->offset = abs_mod (offset, grain_size);
+ else
+ align->offset = offset;
+ align->grain_size = grain_size;
+
+ return 1;
+}
+
+/**
+ * Return an alignment object (used by PedConstraint), representing all
+ * PedSector's that are of the form <tt>offset + X * grain_size</tt>.
+ */
+PedAlignment*
+ped_alignment_new (PedSector offset, PedSector grain_size)
+{
+ PedAlignment* align;
+
+ align = (PedAlignment*) ped_malloc (sizeof (PedAlignment));
+ if (!align)
+ goto error;
+
+ if (!ped_alignment_init (align, offset, grain_size))
+ goto error_free_align;
+
+ return align;
+
+error_free_align:
+ ped_free (align);
+error:
+ return NULL;
+}
+
+/**
+ * Free up memory associated with \p align.
+ */
+void
+ped_alignment_destroy (PedAlignment* align)
+{
+ if (align)
+ ped_free (align);
+}
+
+/**
+ * Return a duplicate of \p align.
+ */
+PedAlignment*
+ped_alignment_duplicate (const PedAlignment* align)
+{
+ if (!align)
+ return NULL;
+ return ped_alignment_new (align->offset, align->grain_size);
+}
+
+/* the extended Euclid algorithm.
+ *
+ * input:
+ * a and b, a > b
+ *
+ * output:
+ * gcd, x and y, such that:
+ *
+ * gcd = greatest common divisor of a and b
+ * gcd = x*a + y*b
+ */
+EuclidTriple
+extended_euclid (int a, int b)
+{
+ EuclidTriple result;
+ EuclidTriple tmp;
+
+ if (b == 0) {
+ result.gcd = a;
+ result.x = 1;
+ result.y = 0;
+ return result;
+ }
+
+ tmp = extended_euclid (b, a % b);
+ result.gcd = tmp.gcd;
+ result.x = tmp.y;
+ result.y = tmp.x - (a/b) * tmp.y;
+ return result;
+}
+
+/**
+ * This function computes a PedAlignment object that describes the
+ * intersection of two alignments. That is, a sector satisfies the
+ * new alignment object if and only if it satisfies both of the original
+ * ones. (See ped_alignment_is_aligned() for the meaning of "satisfies")
+ *
+ * Apart from the trivial cases (where one or both of the alignment objects
+ * constraints have no sectors that satisfy them), this is what we're trying to
+ * do:
+ * - two input constraints: \p a and \p b.
+ * - the new grain_size is going to be the lowest common multiple of
+ * \p a->grain_size and \p b->grain_size
+ * - hard part - solve the simultaneous equations, for offset, where offset,
+ * X and Y are variables. (Note: offset can be obtained from either X or Y,
+ * by substituing into either equation)
+ *
+ * \code
+ * offset = \p a->offset + X * \p a->grain_size (1)
+ * offset = \p b->offset + Y * \p b->grain_size (2)
+ * \endcode
+ *
+ * or, abbreviated:
+ *
+ * \code
+ * o = Ao + X*Ag (1)
+ * o = Bo + Y*Bg (2)
+ *
+ * => Ao + X*Ag = Bo + Y*Bg (1) = (2)
+ * X*Ag - Y*Bg = Bo - Ao (3)
+ * \endcode
+ *
+ * As it turns out, there only exists a solution if (Bo - Ao) is a multiple
+ * of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are
+ * multiples of the GCD.
+ *
+ * Proof:
+ *
+ * \code
+ * A * Ag + B * Bg
+ * = A * (\p a * gcd) + B * (\p b * gcd)
+ * = gcd * (A * \p a + B * \p b)
+ * \endcode
+ *
+ * gcd is a factor of the linear combination. QED
+ *
+ * Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd)
+ * with Euclid's extended algorithm. Then, we just multiply through by
+ * (Bo - Ao) / gcd to get (3).
+ *
+ * i.e.
+ * \code
+ * A * Ag + B * Bg = gcd
+ * A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd
+ * X*Ag - Y*Bg = Bo - Ao (3)
+ *
+ * X = A*(Bo-Ao)/gcd
+ * Y = - B*(Bo-Ao)/gcd
+ * \endcode
+ *
+ * then:
+ * \code
+ * o = Ao + X*Ag (1)
+ * = Ao + A*(Bo-Ao)/gcd*Ag
+ * o = Bo + Y*Bg (2)
+ * = Bo - B*(Bo-Ao)/gcd*Ag
+ * \endcode
+ *
+ * Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring
+ * this algorithm out :-)
+ *
+ * \note Returned \c NULL is a valid PedAlignment object, and can be used
+ for ped_alignment_*() function.
+ *
+ * \return a PedAlignment on success, \c NULL on failure
+ */
+PedAlignment*
+ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b)
+{
+ PedSector new_grain_size;
+ PedSector new_offset;
+ PedSector delta_on_gcd;
+ EuclidTriple gcd_factors;
+
+
+ if (!a || !b)
+ return NULL;
+
+ /*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)",
+ a->offset, a->grain_size, b->offset, b->grain_size);
+ */
+
+ if (a->grain_size < b->grain_size) {
+ const PedAlignment* tmp;
+ tmp = a; a = b; b = tmp;
+ }
+
+ /* weird/trivial case: where the solution space for "a" or "b" is
+ * either empty or contains exactly one solution
+ */
+ if (a->grain_size == 0 && b->grain_size == 0) {
+ if (a->offset == b->offset)
+ return ped_alignment_duplicate (a);
+ else
+ return NULL;
+ }
+
+ /* general case */
+ gcd_factors = extended_euclid (a->grain_size, b->grain_size);
+
+ delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd;
+ new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size;
+ new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd;
+
+ /* inconsistency => no solution */
+ if (new_offset
+ != b->offset - gcd_factors.y * delta_on_gcd * b->grain_size)
+ return NULL;
+
+ return ped_alignment_new (new_offset, new_grain_size);
+}
+
+/* This function returns the sector closest to "sector" that lies inside
+ * geom and satisfies the alignment constraint.
+ */
+static PedSector
+_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector)
+{
+ PED_ASSERT (align != NULL, return -1);
+
+ if (!align->grain_size) {
+ if (ped_alignment_is_aligned (align, geom, sector)
+ && (!geom || ped_geometry_test_sector_inside (geom,
+ sector)))
+ return sector;
+ else
+ return -1;
+ }
+
+ if (sector < geom->start)
+ sector += ped_round_up_to (geom->start - sector,
+ align->grain_size);
+ if (sector > geom->end)
+ sector -= ped_round_up_to (sector - geom->end,
+ align->grain_size);
+
+ if (!ped_geometry_test_sector_inside (geom, sector))
+ return -1;
+ return sector;
+}
+
+/**
+ * This function returns the closest sector to \p sector that lies inside
+ * \p geom that satisfies the given alignment constraint \p align. It prefers
+ * sectors that are beyond \p sector (are not smaller than \p sector),
+ * but does not guarantee that this.
+ *
+ * \return a PedSector on success, \c -1 on failure
+ */
+PedSector
+ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector)
+{
+ PedSector result;
+
+ PED_ASSERT (align != NULL, return -1);
+
+ if (!align->grain_size)
+ result = align->offset;
+ else
+ result = ped_round_up_to (sector - align->offset,
+ align->grain_size)
+ + align->offset;
+
+ if (geom)
+ result = _closest_inside_geometry (align, geom, result);
+ return result;
+}
+
+/**
+ * This function returns the closest sector to \p sector that lies inside
+ * \p geom that satisfies the given alignment constraint \p align. It prefers
+ * sectors that are before \p sector (are not larger than \p sector),
+ * but does not guarantee that this.
+ *
+ * \return a PedSector on success, \c -1 on failure
+ */
+PedSector
+ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector)
+{
+ PedSector result;
+
+ PED_ASSERT (align != NULL, return -1);
+
+ if (!align->grain_size)
+ result = align->offset;
+ else
+ result = ped_round_down_to (sector - align->offset,
+ align->grain_size)
+ + align->offset;
+
+ if (geom)
+ result = _closest_inside_geometry (align, geom, result);
+ return result;
+}
+
+/* Returns either a or b, depending on which is closest to "sector". */
+static PedSector
+closest (PedSector sector, PedSector a, PedSector b)
+{
+ if (a == -1)
+ return b;
+ if (b == -1)
+ return a;
+
+ if (abs (sector - a) < abs (sector - b))
+ return a;
+ else
+ return b;
+}
+
+/**
+ * This function returns the sector that is closest to \p sector,
+ * satisfies the \p align constraint and lies inside \p geom.
+ *
+ * \return a PedSector on success, \c -1 on failure
+ */
+PedSector
+ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector)
+{
+ PED_ASSERT (align != NULL, return -1);
+
+ return closest (sector, ped_alignment_align_up (align, geom, sector),
+ ped_alignment_align_down (align, geom, sector));
+}
+
+/**
+ * This function returns 1 if \p sector satisfies the alignment
+ * constraint \p align and lies inside \p geom.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+int
+ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom,
+ PedSector sector)
+{
+ if (!align)
+ return 0;
+
+ if (geom && !ped_geometry_test_sector_inside (geom, sector))
+ return 0;
+
+ if (align->grain_size)
+ return (sector - align->offset) % align->grain_size == 0;
+ else
+ return sector == align->offset;
+}
+
+/**
+ * @}
+ */
+
diff --git a/usr/src/lib/libparted/common/libparted/debug.c b/usr/src/lib/libparted/common/libparted/debug.c
new file mode 100644
index 0000000000..4d45e4c132
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/debug.c
@@ -0,0 +1,120 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#ifdef DEBUG
+
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+static void default_handler ( const int level, const char* file, int line,
+ const char* function, const char* msg );
+static PedDebugHandler* debug_handler = &default_handler;
+
+
+/**
+ * Default debug handler.
+ * Will print all information to stderr.
+ */
+static void default_handler ( const int level, const char* file, int line,
+ const char* function, const char* msg )
+{
+ fprintf ( stderr, "[%d] %s:%d (%s): %s\n",
+ level, file, line, function, msg );
+}
+
+/**
+ * Send a debug message.
+ * Do not call this directly -- use PED_DEBUG() instead.
+ *
+ * level log level, 0 ~= "print definitely"
+ */
+void ped_debug ( const int level, const char* file, int line,
+ const char* function, const char* msg, ... )
+{
+ va_list arg_list;
+ char* msg_concat = ped_malloc(8192);
+
+ va_start ( arg_list, msg );
+ vsnprintf ( msg_concat, 8192, msg, arg_list );
+ va_end ( arg_list );
+
+ debug_handler ( level, file, line, function, msg_concat );
+
+ ped_free ( msg_concat );
+}
+
+/*
+ * handler debug handler; NULL for default handler
+ */
+void ped_debug_set_handler ( PedDebugHandler* handler )
+{
+ debug_handler = ( handler ? handler : default_handler );
+}
+
+/*
+ * Check an assertion.
+ * Do not call this directly -- use PED_ASSERT() instead.
+ */
+int ped_assert ( int cond, const char* cond_text,
+ const char* file, int line, const char* function )
+{
+ PedExceptionOption opt;
+
+ if ( cond )
+ return 1;
+
+#if HAVE_BACKTRACE
+ /* Print backtrace stack */
+ void *stack[20];
+ char **strings, **string;
+ int size = backtrace(stack, 20);
+ strings = backtrace_symbols(stack, size);
+
+ if (strings) {
+ printf(_("Backtrace has %d calls on stack:\n"), size);
+ for (string = strings; size > 0; size--, string++)
+ printf(" %d: %s\n", size, *string);
+
+ free(strings);
+ }
+#endif
+
+ /* Throw the exception */
+ opt = ped_exception_throw (
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Assertion (%s) at %s:%d in function %s() failed."),
+ cond_text, file, line, function );
+
+ return ( opt == PED_EXCEPTION_IGNORE );
+}
+
+#endif /* DEBUG */
+
diff --git a/usr/src/lib/libparted/common/libparted/device.c b/usr/src/lib/libparted/common/libparted/device.c
new file mode 100644
index 0000000000..07e0a0fe6a
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/device.c
@@ -0,0 +1,445 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999 - 2001, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file device.c */
+
+/**
+ * \addtogroup PedDevice
+ *
+ * \brief Device access.
+ *
+ * When ped_device_probe_all() is called, libparted attempts to detect all
+ * devices. It constructs a list which can be accessed with
+ * ped_device_get_next().
+ *
+ * If you want to use a device that isn't on the list, use
+ * ped_device_get(). Also, there may be OS-specific constructors, for creating
+ * devices from file descriptors, stores, etc. For example,
+ * ped_device_new_from_store().
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#include <limits.h>
+#include <unistd.h>
+#include <errno.h>
+
+static PedDevice* devices; /* legal advice says: initialized to NULL,
+ under section 6.7.8 part 10
+ of ISO/EIC 9899:1999 */
+
+#ifndef HAVE_CANONICALIZE_FILE_NAME
+char *
+canonicalize_file_name (const char *name)
+{
+ char * buf;
+ int size;
+ char * result;
+
+#ifdef PATH_MAX
+ size = PATH_MAX;
+#else
+ /* Bigger is better; realpath has no way todo bounds checking. */
+ size = 4096;
+#endif
+
+ /* Just in case realpath does not NULL terminate the string
+ * or it just fits in SIZE without a NULL terminator. */
+ buf = calloc (size + 1, sizeof(char));
+ if (! buf) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ result = realpath (name, buf);
+ if (! result)
+ free (buf);
+
+ return result;
+}
+#else
+extern char *canonicalize_file_name (const char *name);
+#endif /* !HAVE_CANONICALIZE_FILE_NAME */
+
+static void
+_device_register (PedDevice* dev)
+{
+ PedDevice* walk;
+ for (walk = devices; walk && walk->next; walk = walk->next);
+ if (walk)
+ walk->next = dev;
+ else
+ devices = dev;
+ dev->next = NULL;
+}
+
+static void
+_device_unregister (PedDevice* dev)
+{
+ PedDevice* walk;
+ PedDevice* last = NULL;
+
+ for (walk = devices; walk != NULL; last = walk, walk = walk->next) {
+ if (walk == dev) break;
+ }
+
+ if (last)
+ last->next = dev->next;
+ else
+ devices = dev->next;
+}
+
+/**
+ * Returns the next device that was detected by ped_device_probe_all(), or
+ * calls to ped_device_get_next().
+ * If dev is NULL, returns the first device.
+ *
+ * \return NULL if dev is the last device.
+ */
+PedDevice*
+ped_device_get_next (const PedDevice* dev)
+{
+ if (dev)
+ return dev->next;
+ else
+ return devices;
+}
+
+void
+_ped_device_probe (const char* path)
+{
+ PedDevice* dev;
+
+ PED_ASSERT (path != NULL, return);
+
+ ped_exception_fetch_all ();
+ dev = ped_device_get (path);
+ if (!dev)
+ ped_exception_catch ();
+ ped_exception_leave_all ();
+}
+
+/**
+ * Attempts to detect all devices.
+ */
+void
+ped_device_probe_all ()
+{
+ ped_architecture->dev_ops->probe_all ();
+}
+
+/**
+ * Close/free all devices.
+ * Called by ped_done(), so you do not need to worry about it.
+ */
+void
+ped_device_free_all ()
+{
+ while (devices)
+ ped_device_destroy (devices);
+}
+
+/**
+ * Gets the device "name", where name is usually the block device, e.g.
+ * /dev/sdb. If the device wasn't detected with ped_device_probe_all(),
+ * an attempt will be made to detect it again. If it is found, it will
+ * be added to the list.
+ */
+PedDevice*
+ped_device_get (const char* path)
+{
+ PedDevice* walk;
+ char* normal_path;
+
+ PED_ASSERT (path != NULL, return NULL);
+ normal_path = canonicalize_file_name (path);
+ if (!normal_path)
+ /* Well, maybe it is just that the file does not exist.
+ * Try it anyway. */
+ normal_path = strdup (path);
+ if (!normal_path)
+ return NULL;
+
+ for (walk = devices; walk != NULL; walk = walk->next) {
+ if (!strcmp (walk->path, normal_path)) {
+ ped_free (normal_path);
+ return walk;
+ }
+ }
+
+ walk = ped_architecture->dev_ops->_new (normal_path);
+ ped_free (normal_path);
+ if (!walk)
+ return NULL;
+
+ _device_register (walk);
+ return walk;
+}
+
+/**
+ * Destroys a device and removes it from the device list, and frees
+ * all resources associated with the device (all resources allocated
+ * when the device was created).
+ */
+void
+ped_device_destroy (PedDevice* dev)
+{
+ _device_unregister (dev);
+
+ while (dev->open_count) {
+ if (!ped_device_close (dev))
+ break;
+ }
+
+ ped_architecture->dev_ops->destroy (dev);
+}
+
+void
+ped_device_cache_remove(PedDevice *dev)
+{
+ _device_unregister (dev);
+}
+
+int
+ped_device_is_busy (PedDevice* dev)
+{
+ return ped_architecture->dev_ops->is_busy (dev);
+}
+
+/**
+ * Attempt to open a device to allow use of read, write and sync functions.
+ *
+ * The meaning of "open" is architecture-dependent. Apart from requesting
+ * access to the device from the operating system, it does things like flushing
+ * caches.
+ * \note May allocate resources. Any resources allocated here will
+ * be freed by a final ped_device_close(). (ped_device_open() may be
+ * called multiple times -- it's a ref-count-like mechanism)
+ *
+ * \return zero on failure
+ */
+int
+ped_device_open (PedDevice* dev)
+{
+ int status;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+
+ if (dev->open_count)
+ status = ped_architecture->dev_ops->refresh_open (dev);
+ else
+ status = ped_architecture->dev_ops->open (dev);
+ if (status)
+ dev->open_count++;
+ return status;
+}
+
+/**
+ * Close dev.
+ * If this is the final close, then resources allocated by
+ * ped_device_open() are freed.
+ *
+ * \return zero on failure
+ */
+int
+ped_device_close (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ if (--dev->open_count)
+ return ped_architecture->dev_ops->refresh_close (dev);
+ else
+ return ped_architecture->dev_ops->close (dev);
+}
+
+/**
+ * Begins external access mode. External access mode allows you to
+ * safely do IO on the device. If a PedDevice is open, then you should
+ * not do any IO on that device, e.g. by calling an external program
+ * like e2fsck, unless you put it in external access mode. You should
+ * not use any libparted commands that do IO to a device, e.g.
+ * ped_file_system_{open|resize|copy}, ped_disk_{read|write}), while
+ * a device is in external access mode.
+ * Also, you should not ped_device_close() a device, while it is
+ * in external access mode.
+ * Note: ped_device_begin_external_access_mode() does things like
+ * tell the kernel to flush its caches.
+ *
+ * Close a device while pretending it is still open.
+ * This is useful for temporarily suspending libparted access to the device
+ * in order for an external program to access it.
+ * (Running external programs while the device is open can cause cache
+ * coherency problems.)
+ *
+ * In particular, this function keeps track of dev->open_count, so that
+ * reference counting isn't screwed up.
+ *
+ * \return zero on failure.
+ */
+int
+ped_device_begin_external_access (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+
+ dev->external_mode = 1;
+ if (dev->open_count)
+ return ped_architecture->dev_ops->close (dev);
+ else
+ return 1;
+}
+
+/**
+ * \brief Complementary function to ped_device_begin_external_access.
+ *
+ * \note does things like tell the kernel to flush the device's cache.
+ *
+ * \return zero on failure.
+ */
+int
+ped_device_end_external_access (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (dev->external_mode, return 0);
+
+ dev->external_mode = 0;
+ if (dev->open_count)
+ return ped_architecture->dev_ops->open (dev);
+ else
+ return 1;
+}
+
+/**
+ * \internal Read count sectors from dev into buffer, beginning with sector
+ * start.
+ *
+ * \return zero on failure.
+ */
+int
+ped_device_read (const PedDevice* dev, void* buffer, PedSector start,
+ PedSector count)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (buffer != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ return (ped_architecture->dev_ops->read) (dev, buffer, start, count);
+}
+
+/**
+ * \internal Write count sectors from buffer to dev, starting at sector
+ * start.
+ *
+ * \return zero on failure.
+ *
+ * \sa PedDevice::sector_size
+ * \sa PedDevice::phys_sector_size
+ */
+int
+ped_device_write (PedDevice* dev, const void* buffer, PedSector start,
+ PedSector count)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (buffer != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ return (ped_architecture->dev_ops->write) (dev, buffer, start, count);
+}
+
+PedSector
+ped_device_check (PedDevice* dev, void* buffer, PedSector start,
+ PedSector count)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ return (ped_architecture->dev_ops->check) (dev, buffer, start, count);
+}
+
+/**
+ * \internal Flushes all write-behind caches that might be holding up
+ * writes.
+ * It is slow because it guarantees cache coherency among all relevant caches.
+ *
+ * \return zero on failure
+ */
+int
+ped_device_sync (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ return ped_architecture->dev_ops->sync (dev);
+}
+
+/**
+ * \internal Flushes all write-behind caches that might be holding writes.
+ * \warning Does NOT ensure cache coherency with other caches.
+ * If you need cache coherency, use ped_device_sync() instead.
+ *
+ * \return zero on failure
+ */
+int
+ped_device_sync_fast (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (!dev->external_mode, return 0);
+ PED_ASSERT (dev->open_count > 0, return 0);
+
+ return ped_architecture->dev_ops->sync_fast (dev);
+}
+
+/**
+ * Get a constraint that represents hardware requirements on alignment and
+ * geometry.
+ * This is, for example, important for media that have a physical sector
+ * size that is a multiple of the logical sector size.
+ *
+ * \warning This function is experimental for physical sector sizes not equal to
+ * 2^9.
+ */
+PedConstraint*
+ped_device_get_constraint (PedDevice* dev)
+{
+ int multiplier = dev->phys_sector_size / dev->sector_size;
+
+ PedAlignment* start_align = ped_alignment_new (multiplier, multiplier);
+
+ PedConstraint* c = ped_constraint_new (
+ start_align, ped_alignment_any,
+ ped_geometry_new (dev, 0, dev->length),
+ ped_geometry_new (dev, 0, dev->length),
+ 1, dev->length);
+
+ return c;
+}
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/libparted/disk.c b/usr/src/lib/libparted/common/libparted/disk.c
new file mode 100644
index 0000000000..1582f26e05
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/disk.c
@@ -0,0 +1,2265 @@
+ /*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2007
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file disk.c */
+
+/**
+ * \addtogroup PedDisk
+ *
+ * \brief Disk label access.
+ *
+ * Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get
+ * anything done. A PedDisk is always associated with a device and has a
+ * partition table. There are different types of partition tables (or disk
+ * labels). These are represented by the PedDiskType enumeration.
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+# define N_(String) (String)
+#else
+# define _(String) (String)
+# define N_(String) (String)
+#endif /* ENABLE_NLS */
+
+/* UPDATE MODE functions */
+#ifdef DEBUG
+static int _disk_check_sanity (PedDisk* disk);
+#endif
+static void _disk_push_update_mode (PedDisk* disk);
+static void _disk_pop_update_mode (PedDisk* disk);
+static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc,
+ PedPartition* part);
+static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc,
+ PedPartition* part);
+static int _disk_raw_remove (PedDisk* disk, PedPartition* part);
+static int _disk_raw_add (PedDisk* disk, PedPartition* part);
+
+static PedDiskType* disk_types = NULL;
+
+void
+ped_disk_type_register (PedDiskType* disk_type)
+{
+ PED_ASSERT (disk_type != NULL, return);
+ PED_ASSERT (disk_type->ops != NULL, return);
+ PED_ASSERT (disk_type->name != NULL, return);
+
+ /* pretend that "next" isn't part of the struct :-) */
+ ((struct _PedDiskType*) disk_type)->next = disk_types;
+ disk_types = (struct _PedDiskType*) disk_type;
+}
+
+void
+ped_disk_type_unregister (PedDiskType* disk_type)
+{
+ PedDiskType* walk;
+ PedDiskType* last = NULL;
+
+ PED_ASSERT (disk_types != NULL, return);
+ PED_ASSERT (disk_type != NULL, return);
+
+ for (walk = disk_types; walk && walk != disk_type;
+ last = walk, walk = walk->next);
+
+ PED_ASSERT (walk != NULL, return);
+ if (last)
+ ((struct _PedDiskType*) last)->next = disk_type->next;
+ else
+ disk_types = disk_type->next;
+}
+
+/**
+ * Deprecated: use ped_disk_type_regiser.
+ */
+void
+ped_register_disk_type (PedDiskType* disk_type)
+{
+ ped_disk_type_register (disk_type);
+}
+
+/**
+ * Deprecated: use ped_disk_type_unregiser.
+ */
+void
+ped_unregister_disk_type (PedDiskType* disk_type)
+{
+ ped_disk_type_unregister (disk_type);
+}
+
+/**
+ * Return the next disk type registers, after "type". If "type" is
+ * NULL, returns the first disk type.
+ *
+ * \return Next disk; NULL if "type" is the last registered disk type.
+ */
+PedDiskType*
+ped_disk_type_get_next (PedDiskType* type)
+{
+ if (type)
+ return type->next;
+ else
+ return disk_types;
+}
+
+/**
+ * Return the disk type with a name of "name".
+ *
+ * \return Disk type; NULL if no match.
+ */
+PedDiskType*
+ped_disk_type_get (const char* name)
+{
+ PedDiskType* walk = NULL;
+
+ PED_ASSERT (name != NULL, return NULL);
+
+ for (walk = ped_disk_type_get_next (NULL); walk;
+ walk = ped_disk_type_get_next (walk))
+ if (strcasecmp (walk->name, name) == 0)
+ break;
+
+ return walk;
+}
+
+/**
+ * Return the type of partition table detected on "dev".
+ *
+ * \return Type; NULL if none was detected.
+ */
+PedDiskType*
+ped_disk_probe (PedDevice* dev)
+{
+ PedDiskType *walk = NULL;
+
+ PED_ASSERT (dev != NULL, return NULL);
+
+ if (!ped_device_open (dev))
+ return NULL;
+
+ ped_exception_fetch_all ();
+ for (walk = ped_disk_type_get_next (NULL); walk;
+ walk = ped_disk_type_get_next (walk)) {
+ if (walk->ops->probe (dev))
+ break;
+ }
+
+ if (ped_exception)
+ ped_exception_catch ();
+ ped_exception_leave_all ();
+
+ ped_device_close (dev);
+ return walk;
+}
+
+/**
+ * Read the partition table off a device (if one is found).
+ *
+ * \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors
+ * if the partition table indicates that the existing values
+ * are incorrect.
+ *
+ * \return A new \link _PedDisk PedDisk \endlink object;
+ * NULL on failure (e.g. partition table not detected).
+ */
+PedDisk*
+ped_disk_new (PedDevice* dev)
+{
+ PedDiskType* type;
+ PedDisk* disk;
+
+ PED_ASSERT (dev != NULL, return NULL);
+
+ if (!ped_device_open (dev))
+ goto error;
+
+ type = ped_disk_probe (dev);
+ if (!type) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s: unrecognised disk label"),
+ dev->path);
+ goto error_close_dev;
+ }
+ disk = ped_disk_new_fresh (dev, type);
+ if (!disk)
+ goto error_close_dev;
+ if (!type->ops->read (disk))
+ goto error_destroy_disk;
+ disk->needs_clobber = 0;
+ ped_device_close (dev);
+ return disk;
+
+error_destroy_disk:
+ ped_disk_destroy (disk);
+error_close_dev:
+ ped_device_close (dev);
+error:
+ return NULL;
+}
+
+static int
+_add_duplicate_part (PedDisk* disk, PedPartition* old_part)
+{
+ PedPartition* new_part;
+ PedConstraint* constraint_exact;
+
+ new_part = disk->type->ops->partition_duplicate (old_part);
+ if (!new_part)
+ goto error;
+ new_part->disk = disk;
+
+ constraint_exact = ped_constraint_exact (&new_part->geom);
+ if (!constraint_exact)
+ goto error_destroy_new_part;
+ if (!ped_disk_add_partition (disk, new_part, constraint_exact))
+ goto error_destroy_constraint_exact;
+ ped_constraint_destroy (constraint_exact);
+ return 1;
+
+error_destroy_constraint_exact:
+ ped_constraint_destroy (constraint_exact);
+error_destroy_new_part:
+ ped_partition_destroy (new_part);
+error:
+ return 0;
+}
+
+/**
+ * Clone a \link _PedDisk PedDisk \endlink object.
+ *
+ * \return Deep copy of \p old_disk, NULL on failure.
+ */
+PedDisk*
+ped_disk_duplicate (const PedDisk* old_disk)
+{
+ PedDisk* new_disk;
+ PedPartition* old_part;
+
+ PED_ASSERT (old_disk != NULL, return NULL);
+ PED_ASSERT (!old_disk->update_mode, return NULL);
+ PED_ASSERT (old_disk->type->ops->duplicate != NULL, return NULL);
+ PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL,
+ return NULL);
+
+ new_disk = old_disk->type->ops->duplicate (old_disk);
+ if (!new_disk)
+ goto error;
+
+ _disk_push_update_mode (new_disk);
+ for (old_part = ped_disk_next_partition (old_disk, NULL); old_part;
+ old_part = ped_disk_next_partition (old_disk, old_part)) {
+ if (ped_partition_is_active (old_part)) {
+ if (!_add_duplicate_part (new_disk, old_part))
+ goto error_destroy_new_disk;
+ }
+ }
+ _disk_pop_update_mode (new_disk);
+ return new_disk;
+
+error_destroy_new_disk:
+ ped_disk_destroy (new_disk);
+error:
+ return NULL;
+}
+
+/**
+ * Remove all identifying signatures of a partition table,
+ * except for partition tables of a given type.
+ *
+ * \return 0 on error, 1 otherwise.
+ *
+ * \sa ped_disk_clobber()
+ */
+int
+ped_disk_clobber_exclude (PedDevice* dev, const PedDiskType* exclude)
+{
+ PedDiskType* walk;
+
+ PED_ASSERT (dev != NULL, goto error);
+
+ if (!ped_device_open (dev))
+ goto error;
+
+ for (walk = ped_disk_type_get_next (NULL); walk;
+ walk = ped_disk_type_get_next (walk)) {
+ int probed;
+
+ if (walk == exclude)
+ continue;
+
+ ped_exception_fetch_all ();
+ probed = walk->ops->probe (dev);
+ if (!probed)
+ ped_exception_catch ();
+ ped_exception_leave_all ();
+
+ if (probed && walk->ops->clobber) {
+ if (!walk->ops->clobber (dev))
+ goto error_close_dev;
+ }
+ }
+ ped_device_close (dev);
+ return 1;
+
+error_close_dev:
+ ped_device_close (dev);
+error:
+ return 0;
+}
+
+/**
+ * Remove all identifying signatures of a partition table,
+ *
+ * \return 0 on error, 1 otherwise.
+ *
+ * \sa ped_disk_clobber_exclude()
+ */
+int
+ped_disk_clobber (PedDevice* dev)
+{
+ return ped_disk_clobber_exclude (dev, NULL);
+}
+
+/**
+ * Create a new partition table on \p dev.
+ *
+ * This new partition table is only created in-memory, and nothing is written
+ * to disk until ped_disk_commit_to_dev() is called.
+ *
+ * \return The newly constructed \link _PedDisk PedDisk \endlink,
+ * NULL on failure.
+ */
+PedDisk*
+ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type)
+{
+ PedDisk* disk;
+
+ PED_ASSERT (dev != NULL, return NULL);
+ PED_ASSERT (type != NULL, return NULL);
+ PED_ASSERT (type->ops->alloc != NULL, return NULL);
+
+ disk = type->ops->alloc (dev);
+ if (!disk)
+ goto error;
+ _disk_pop_update_mode (disk);
+ PED_ASSERT (disk->update_mode == 0, goto error_destroy_disk);
+
+ disk->needs_clobber = 1;
+ return disk;
+
+error_destroy_disk:
+ ped_disk_destroy (disk);
+error:
+ return NULL;
+}
+
+PedDisk*
+_ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type)
+{
+ PedDisk* disk;
+
+ disk = (PedDisk*) ped_malloc (sizeof (PedDisk));
+ if (!disk)
+ goto error;
+
+ disk->dev = (PedDevice*)dev;
+ disk->type = disk_type;
+ disk->update_mode = 1;
+ disk->part_list = NULL;
+ return disk;
+
+ ped_free (disk);
+error:
+ return NULL;
+}
+
+void
+_ped_disk_free (PedDisk* disk)
+{
+ _disk_push_update_mode (disk);
+ ped_disk_delete_all (disk);
+ ped_free (disk);
+}
+
+/**
+ * Close \p disk.
+ *
+ * What this function does depends on the PedDiskType of \p disk,
+ * but you can generally assume that outstanding writes are flushed
+ * (this mainly means that _ped_disk_free is called).
+ */
+void
+ped_disk_destroy (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return);
+ PED_ASSERT (!disk->update_mode, return);
+
+ disk->type->ops->free (disk);
+}
+
+/**
+ * Tell the operating system kernel about the partition table layout
+ * of \p disk.
+ *
+ * This is rather loosely defined: for example, on old versions of Linux,
+ * it simply calls the BLKRRPART ioctl, which tells the kernel to
+ * reread the partition table. On newer versions (2.4.x), it will
+ * use the new blkpg interface to tell Linux where each partition
+ * starts/ends, etc. In this case, Linux does not need to have support for
+ * a specific type of partition table.
+ *
+ * \return 0 on failure, 1 otherwise.
+ */
+int
+ped_disk_commit_to_os (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return 0);
+
+ if (!ped_device_open (disk->dev))
+ goto error;
+ if (!ped_architecture->disk_ops->disk_commit (disk))
+ goto error_close_dev;
+ ped_device_close (disk->dev);
+ return 1;
+
+error_close_dev:
+ ped_device_close (disk->dev);
+error:
+ return 0;
+}
+
+/**
+ * Write the changes made to the in-memory description
+ * of a partition table to the device.
+ *
+ * \return 0 on failure, 1 otherwise.
+ */
+int
+ped_disk_commit_to_dev (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, goto error);
+ PED_ASSERT (!disk->update_mode, goto error);
+
+ if (!disk->type->ops->write) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("This libparted doesn't have write support for "
+ "%s. Perhaps it was compiled read-only."),
+ disk->type->name);
+ goto error;
+ }
+
+ if (!ped_device_open (disk->dev))
+ goto error;
+
+ if (disk->needs_clobber) {
+ if (!ped_disk_clobber_exclude (disk->dev, disk->type))
+ goto error_close_dev;
+ disk->needs_clobber = 0;
+ }
+ if (!disk->type->ops->write (disk))
+ goto error_close_dev;
+ ped_device_close (disk->dev);
+ return 1;
+
+error_close_dev:
+ ped_device_close (disk->dev);
+error:
+ return 0;
+}
+
+/*
+ * This function writes the in-memory changes to a partition table to
+ * disk and informs the operating system of the changes.
+ *
+ * \note Equivalent to calling first ped_disk_commit_to_dev(), then
+ * ped_disk_commit_to_os().
+ *
+ * \return 0 on failure, 1 otherwise.
+ */
+int
+ped_disk_commit (PedDisk* disk)
+{
+ if (!ped_disk_commit_to_dev (disk))
+ return 0;
+ return ped_disk_commit_to_os (disk);
+}
+
+/**
+ * \addtogroup PedPartition
+ *
+ * @{
+ */
+
+/**
+ * Check whether a partition is mounted or busy in some
+ * other way.
+ *
+ * \note An extended partition is busy if any logical partitions are mounted.
+ *
+ * \return \c 1 if busy.
+ */
+int
+ped_partition_is_busy (const PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return 1);
+
+ return ped_architecture->disk_ops->partition_is_busy (part);
+}
+
+/**
+ * Return a path that can be used to address the partition in the
+ * operating system.
+ */
+char*
+ped_partition_get_path (const PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return NULL);
+
+ return ped_architecture->disk_ops->partition_get_path (part);
+}
+
+/** @} */
+
+/**
+ * \addtogroup PedDisk
+ *
+ * @{
+ */
+
+/**
+ * Perform a sanity check on a partition table.
+ *
+ * \note The check performed is generic (i.e. it does not depends on the label
+ * type of the disk.
+ *
+ * \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file
+ * system on it.
+ *
+ * \return 0 if the check fails, 1 otherwise.
+ */
+int
+ped_disk_check (const PedDisk* disk)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ const PedFileSystemType* fs_type = walk->fs_type;
+ PedGeometry* geom;
+ PedSector length_error;
+ PedSector max_length_error;
+
+ if (!ped_partition_is_active (walk) || !fs_type)
+ continue;
+
+ geom = ped_file_system_probe_specific (fs_type, &walk->geom);
+ if (!geom)
+ continue;
+
+ length_error = abs (walk->geom.length - geom->length);
+ max_length_error = PED_MAX (4096, walk->geom.length / 100);
+ if (!ped_geometry_test_inside (&walk->geom, geom)
+ || length_error > max_length_error) {
+ char* part_size = ped_unit_format (disk->dev, walk->geom.length);
+ char* fs_size = ped_unit_format (disk->dev, geom->length);
+ PedExceptionOption choice;
+
+ choice = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Partition %d is %s, but the file system is "
+ "%s."),
+ walk->num, part_size, fs_size);
+
+ ped_free (part_size);
+ ped_free (fs_size);
+
+ if (choice != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * This function checks if a particular type of partition table supports
+ * a feature.
+ *
+ * \return 1 if \p disk_type supports \p feature, 0 otherwise.
+ */
+int
+ped_disk_type_check_feature (const PedDiskType* disk_type,
+ PedDiskTypeFeature feature)
+{
+ return (disk_type->features & feature) != 0;
+}
+
+/**
+ * Get the number of primary partitions.
+ */
+int
+ped_disk_get_primary_partition_count (const PedDisk* disk)
+{
+ PedPartition* walk;
+ int count = 0;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (ped_partition_is_active (walk)
+ && ! (walk->type & PED_PARTITION_LOGICAL))
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * Get the highest partition number on \p disk.
+ */
+int
+ped_disk_get_last_partition_num (const PedDisk* disk)
+{
+ PedPartition* walk;
+ int highest = -1;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (walk->num > highest)
+ highest = walk->num;
+ }
+
+ return highest;
+}
+
+/**
+ * Get the maximum number of (primary) partitions the disk label supports.
+ *
+ * For example, MacIntosh partition maps can have different sizes,
+ * and accordingly support a different number of partitions.
+ */
+int
+ped_disk_get_max_primary_partition_count (const PedDisk* disk)
+{
+ PED_ASSERT (disk->type != NULL, return 0);
+ PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL,
+ return 0);
+
+ return disk->type->ops->get_max_primary_partition_count (disk);
+}
+
+/**
+ * \internal We turned a really nasty bureaucracy problem into an elegant maths
+ * problem :-) Basically, there are some constraints to a partition's
+ * geometry:
+ *
+ * (1) it must start and end on a "disk" block, determined by the disk label
+ * (not the hardware). (constraint represented by a PedAlignment)
+ *
+ * (2) if we're resizing a partition, we MIGHT need to keep each block aligned.
+ * Eg: if an ext2 file system has 4k blocks, then we can only move the start
+ * by a multiple of 4k. (constraint represented by a PedAlignment)
+ *
+ * (3) we need to keep the start and end within the device's physical
+ * boundaries. (constraint represented by a PedGeometry)
+ *
+ * Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-) See
+ * ped_alignment_intersect()
+ *
+ * The application of these constraints is in disk_*.c's *_partition_align()
+ * function.
+ */
+static int
+_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ const PedDiskType* disk_type;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->num != -1, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ disk_type = part->disk->type;
+ PED_ASSERT (disk_type != NULL, return 0);
+ PED_ASSERT (disk_type->ops->partition_align != NULL, return 0);
+ PED_ASSERT (part->disk->update_mode, return 0);
+
+ return disk_type->ops->partition_align (part, constraint);
+}
+
+static int
+_partition_enumerate (PedPartition* part)
+{
+ const PedDiskType* disk_type;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ disk_type = part->disk->type;
+ PED_ASSERT (disk_type != NULL, return 0);
+ PED_ASSERT (disk_type->ops->partition_enumerate != NULL, return 0);
+
+ return disk_type->ops->partition_enumerate (part);
+}
+
+/**
+ * Gives all the (active) partitions a number. It should preserve the numbers
+ * and orders as much as possible.
+ */
+static int
+ped_disk_enumerate_partitions (PedDisk* disk)
+{
+ PedPartition* walk;
+ int i;
+ int end;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+/* first "sort" already-numbered partitions. (e.g. if a logical partition
+ * is removed, then all logical partitions that were number higher MUST be
+ * renumbered)
+ */
+ end = ped_disk_get_last_partition_num (disk);
+ for (i=1; i<=end; i++) {
+ walk = ped_disk_get_partition (disk, i);
+ if (walk) {
+ if (!_partition_enumerate (walk))
+ return 0;
+ }
+ }
+
+/* now, number un-numbered partitions */
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (ped_partition_is_active (walk) && walk->num == -1) {
+ if (!_partition_enumerate (walk))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+_disk_remove_metadata (PedDisk* disk)
+{
+ PedPartition* walk = NULL;
+ PedPartition* next;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ next = ped_disk_next_partition (disk, walk);
+
+ while (next) {
+ walk = next;
+ while (1) {
+ next = ped_disk_next_partition (disk, next);
+ if (!next || next->type & PED_PARTITION_METADATA)
+ break;
+ }
+ if (walk->type & PED_PARTITION_METADATA)
+ ped_disk_delete_partition (disk, walk);
+ }
+ return 1;
+}
+
+static int
+_disk_alloc_metadata (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return 0);
+
+ if (!disk->update_mode)
+ _disk_remove_metadata (disk);
+
+ return disk->type->ops->alloc_metadata (disk);
+}
+
+static int
+_disk_remove_freespace (PedDisk* disk)
+{
+ PedPartition* walk;
+ PedPartition* next;
+
+ walk = ped_disk_next_partition (disk, NULL);
+ for (; walk; walk = next) {
+ next = ped_disk_next_partition (disk, walk);
+
+ if (walk->type & PED_PARTITION_FREESPACE) {
+ _disk_raw_remove (disk, walk);
+ ped_partition_destroy (walk);
+ }
+ }
+
+ return 1;
+}
+
+static int
+_alloc_extended_freespace (PedDisk* disk)
+{
+ PedSector last_end;
+ PedPartition* walk;
+ PedPartition* last;
+ PedPartition* free_space;
+ PedPartition* extended_part;
+
+ extended_part = ped_disk_extended_partition (disk);
+ if (!extended_part)
+ return 1;
+
+ last_end = extended_part->geom.start;
+ last = NULL;
+
+ for (walk = extended_part->part_list; walk; walk = walk->next) {
+ if (walk->geom.start > last_end + 1) {
+ free_space = ped_partition_new (
+ disk,
+ PED_PARTITION_FREESPACE
+ | PED_PARTITION_LOGICAL,
+ NULL,
+ last_end + 1, walk->geom.start - 1);
+ _disk_raw_insert_before (disk, walk, free_space);
+ }
+
+ last = walk;
+ last_end = last->geom.end;
+ }
+
+ if (last_end < extended_part->geom.end) {
+ free_space = ped_partition_new (
+ disk,
+ PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL,
+ NULL,
+ last_end + 1, extended_part->geom.end);
+
+ if (last)
+ return _disk_raw_insert_after (disk, last, free_space);
+ else
+ extended_part->part_list = free_space;
+ }
+
+ return 1;
+}
+
+static int
+_disk_alloc_freespace (PedDisk* disk)
+{
+ PedSector last_end;
+ PedPartition* walk;
+ PedPartition* last;
+ PedPartition* free_space;
+
+ if (!_disk_remove_freespace (disk))
+ return 0;
+ if (!_alloc_extended_freespace (disk))
+ return 0;
+
+ last = NULL;
+ last_end = -1;
+
+ for (walk = disk->part_list; walk; walk = walk->next) {
+ if (walk->geom.start > last_end + 1) {
+ free_space = ped_partition_new (disk,
+ PED_PARTITION_FREESPACE, NULL,
+ last_end + 1, walk->geom.start - 1);
+ _disk_raw_insert_before (disk, walk, free_space);
+ }
+
+ last = walk;
+ last_end = last->geom.end;
+ }
+
+ if (last_end < disk->dev->length - 1) {
+ free_space = ped_partition_new (disk,
+ PED_PARTITION_FREESPACE, NULL,
+ last_end + 1, disk->dev->length - 1);
+ if (last)
+ return _disk_raw_insert_after (disk, last, free_space);
+ else
+ disk->part_list = free_space;
+ }
+
+ return 1;
+}
+
+/**
+ * Update mode: used when updating the internal representation of the partition
+ * table. In update mode, the metadata and freespace placeholder/virtual
+ * partitions are removed, making it much easier for various manipulation
+ * routines...
+ */
+static void
+_disk_push_update_mode (PedDisk* disk)
+{
+ if (!disk->update_mode) {
+#ifdef DEBUG
+ _disk_check_sanity (disk);
+#endif
+
+ _disk_remove_freespace (disk);
+ disk->update_mode++;
+ _disk_remove_metadata (disk);
+
+#ifdef DEBUG
+ _disk_check_sanity (disk);
+#endif
+ } else {
+ disk->update_mode++;
+ }
+}
+
+static void
+_disk_pop_update_mode (PedDisk* disk)
+{
+ PED_ASSERT (disk->update_mode, return);
+
+ if (disk->update_mode == 1) {
+ /* re-allocate metadata BEFORE leaving update mode, to prevent infinite
+ * recursion (metadata allocation requires update mode)
+ */
+#ifdef DEBUG
+ _disk_check_sanity (disk);
+#endif
+
+ _disk_alloc_metadata (disk);
+ disk->update_mode--;
+ _disk_alloc_freespace (disk);
+
+#ifdef DEBUG
+ _disk_check_sanity (disk);
+#endif
+ } else {
+ disk->update_mode--;
+ }
+}
+
+/** @} */
+
+/**
+ * \addtogroup PedPartition
+ *
+ * \brief Partition access.
+ *
+ * @{
+ */
+
+PedPartition*
+_ped_partition_alloc (const PedDisk* disk, PedPartitionType type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ part = (PedPartition*) ped_malloc (sizeof (PedPartition));
+ if (!part)
+ goto error;
+
+ part->prev = NULL;
+ part->next = NULL;
+
+ part->disk = (PedDisk*) disk;
+ if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1))
+ goto error_free_part;
+
+ part->num = -1;
+ part->type = type;
+ part->part_list = NULL;
+ part->fs_type = fs_type;
+
+ return part;
+
+error_free_part:
+ ped_free (part);
+error:
+ return NULL;
+}
+
+void
+_ped_partition_free (PedPartition* part)
+{
+ ped_free (part);
+}
+
+int
+_ped_partition_attempt_align (PedPartition* part,
+ const PedConstraint* external,
+ PedConstraint* internal)
+{
+ PedConstraint* intersection;
+ PedGeometry* solution;
+
+ intersection = ped_constraint_intersect (external, internal);
+ ped_constraint_destroy (internal);
+ if (!intersection)
+ goto fail;
+
+ solution = ped_constraint_solve_nearest (intersection, &part->geom);
+ if (!solution)
+ goto fail_free_intersection;
+ ped_geometry_set (&part->geom, solution->start, solution->length);
+ ped_geometry_destroy (solution);
+ ped_constraint_destroy (intersection);
+ return 1;
+
+fail_free_intersection:
+ ped_constraint_destroy (intersection);
+fail:
+ return 0;
+}
+
+/**
+ * Create a new \link _PedPartition PedPartition \endlink on \p disk.
+ *
+ * \param type One of \p PED_PARTITION_NORMAL, \p PED_PARTITION_EXTENDED,
+ * \p PED_PARTITION_LOGICAL.
+ *
+ * \note The constructed partition is not added to <tt>disk</tt>'s
+ * partition table. Use ped_disk_add_partition() to do this.
+ *
+ * \return A new \link _PedPartition PedPartition \endlink object,
+ * NULL on failure.
+ *
+ * \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the
+ * label does not support this concept.
+ */
+PedPartition*
+ped_partition_new (const PedDisk* disk, PedPartitionType type,
+ const PedFileSystemType* fs_type, PedSector start,
+ PedSector end)
+{
+ int supports_extended;
+ PedPartition* part;
+
+ PED_ASSERT (disk != NULL, return NULL);
+ PED_ASSERT (disk->type->ops->partition_new != NULL, return NULL);
+
+ supports_extended = ped_disk_type_check_feature (disk->type,
+ PED_DISK_TYPE_EXTENDED);
+
+ if (!supports_extended
+ && (type == PED_PARTITION_EXTENDED
+ || type == PED_PARTITION_LOGICAL)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s disk labels do not support extended "
+ "partitions."),
+ disk->type->name);
+ goto error;
+ }
+
+ part = disk->type->ops->partition_new (disk, type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (fs_type || part->type == PED_PARTITION_EXTENDED) {
+ if (!ped_partition_set_system (part, fs_type))
+ goto error_destroy_part;
+ }
+ return part;
+
+error_destroy_part:
+ ped_partition_destroy (part);
+error:
+ return NULL;
+}
+
+/**
+ * Destroy a \link _PedPartition PedPartition \endlink object.
+ *
+ * \note Should not be called on a partition that is in a partition table.
+ * Use ped_disk_delete_partition() instead.
+ */
+void
+ped_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+ PED_ASSERT (part->disk != NULL, return);
+ PED_ASSERT (part->disk->type->ops->partition_new != NULL, return);
+
+ part->disk->type->ops->partition_destroy (part);
+}
+
+
+/**
+ * Return whether or not the partition is "active".
+ *
+ * A partition is active if \p part->type is neither \p PED_PARTITION_METADATA
+ * nor \p PED_PARTITION_FREE.
+ */
+int
+ped_partition_is_active (const PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ return !(part->type & PED_PARTITION_FREESPACE
+ || part->type & PED_PARTITION_METADATA);
+}
+
+/**
+ * Set the state (\c 1 or \c 0) of a flag on a partition.
+ *
+ * Flags are disk label specific, although they have a global
+ * "namespace": the flag PED_PARTITION_BOOT, for example, roughly means
+ * "this" partition is bootable". But this means different things on different
+ * disk labels (and may not be defined on some disk labels). For example,
+ * on MS-DOS disk labels, there can only be one boot partition, and this
+ * refers to the partition that will be booted from on startup. On PC98
+ * disk labels, the user can choose from any bootable partition on startup.
+ *
+ * \note It is an error to call this on an unavailable flag -- use
+ * ped_partition_is_flag_available() to determine which flags are available
+ * for a given disk label.
+ *
+ * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this
+ * label.
+ */
+int
+ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ PedDiskOps* ops;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+
+ ops = part->disk->type->ops;
+ PED_ASSERT (ops->partition_set_flag != NULL, return 0);
+ PED_ASSERT (ops->partition_is_flag_available != NULL, return 0);
+
+ if (!ops->partition_is_flag_available (part, flag)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ "The flag '%s' is not available for %s disk labels.",
+ ped_partition_flag_get_name (flag),
+ part->disk->type->name);
+ return 0;
+ }
+
+ return ops->partition_set_flag (part, flag, state);
+}
+
+/**
+ * Get the state (\c 1 or \c 0) of a flag on a partition.
+ *
+ * See ped_partition_set_flag() for conditions that must hold.
+ *
+ * \todo Where's the check for flag availability?
+ */
+int
+ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL,
+ return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+
+ return part->disk->type->ops->partition_get_flag (part, flag);
+}
+
+/**
+ * Check whether a given flag is available on a partition.
+ *
+ * \return \c 1 if the flag is available.
+ */
+int
+ped_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL,
+ return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+
+ return part->disk->type->ops->partition_is_flag_available (part, flag);
+}
+
+/**
+ * Sets the system type on the partition to \p fs_type.
+ *
+ * \note The file system may be opened, to get more information about the
+ * file system, e.g. to determine if it's FAT16 or FAT32.
+ *
+ * \return \c 0 on failure.
+ */
+int
+ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ const PedDiskType* disk_type;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ disk_type = part->disk->type;
+ PED_ASSERT (disk_type != NULL, return 0);
+ PED_ASSERT (disk_type->ops != NULL, return 0);
+ PED_ASSERT (disk_type->ops->partition_set_system != NULL, return 0);
+
+ return disk_type->ops->partition_set_system (part, fs_type);
+}
+
+static int
+_assert_partition_name_feature (const PedDiskType* disk_type)
+{
+ if (!ped_disk_type_check_feature (
+ disk_type, PED_DISK_TYPE_PARTITION_NAME)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ "%s disk labels do not support partition names.",
+ disk_type->name);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Sets the name of a partition.
+ *
+ * \note This will only work if the disk label supports it.
+ * You can use
+ * \code
+ * ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME);
+ * \endcode
+ * to check whether this feature is enabled for a label.
+ *
+ * \note \p name will not be modified by libparted. It can be freed
+ * by the caller immediately after ped_partition_set_name() is called.
+ *
+ * \return \c 1 on success, \c 0 otherwise.
+ */
+int
+ped_partition_set_name (PedPartition* part, const char* name)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+ PED_ASSERT (name != NULL, return 0);
+
+ if (!_assert_partition_name_feature (part->disk->type))
+ return 0;
+
+ PED_ASSERT (part->disk->type->ops->partition_set_name != NULL,
+ return 0);
+ part->disk->type->ops->partition_set_name (part, name);
+ return 1;
+}
+
+/**
+ * Returns the name of a partition \p part. This will only work if the disk
+ * label supports it.
+ *
+ * \note The returned string should not be modified. It should
+ * not be referenced after the partition is destroyed.
+ */
+const char*
+ped_partition_get_name (const PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return NULL);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+
+ if (!_assert_partition_name_feature (part->disk->type))
+ return NULL;
+
+ PED_ASSERT (part->disk->type->ops->partition_get_name != NULL,
+ return NULL);
+ return part->disk->type->ops->partition_get_name (part);
+}
+
+/** @} */
+
+/**
+ * \addtogroup PedDisk
+ *
+ * @{
+ */
+
+PedPartition*
+ped_disk_extended_partition (const PedDisk* disk)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk; walk = walk->next) {
+ if (walk->type == PED_PARTITION_EXTENDED)
+ break;
+ }
+ return walk;
+}
+
+/**
+ * Return the next partition after \p part on \p disk. If \p part is \c NULL,
+ * return the first partition. If \p part is the last partition, returns
+ * \c NULL. If \p part is an extended partition, returns the first logical
+ * partition. If this is called repeatedly passing the return value as \p part,
+ * a depth-first traversal is executed.
+ *
+ * \return The next partition, \c NULL if no more partitions left.
+ */
+PedPartition*
+ped_disk_next_partition (const PedDisk* disk, const PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+
+ if (!part)
+ return disk->part_list;
+ if (part->type == PED_PARTITION_EXTENDED)
+ return part->part_list ? part->part_list : part->next;
+ if (part->next)
+ return part->next;
+ if (part->type & PED_PARTITION_LOGICAL)
+ return ped_disk_extended_partition (disk)->next;
+ return NULL;
+}
+
+/** @} */
+
+#ifdef DEBUG
+static int
+_disk_check_sanity (PedDisk* disk)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk; walk = walk->next) {
+ PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL), return 0);
+ PED_ASSERT (!walk->prev || walk->prev->next == walk, return 0);
+ }
+
+ if (!ped_disk_extended_partition (disk))
+ return 1;
+
+ for (walk = ped_disk_extended_partition (disk)->part_list; walk;
+ walk = walk->next) {
+ PED_ASSERT (walk->type & PED_PARTITION_LOGICAL, return 0);
+ if (walk->prev)
+ PED_ASSERT (walk->prev->next == walk, return 0);
+ }
+ return 1;
+}
+#endif
+
+/**
+ * Returns the partition numbered \p num.
+ *
+ * \return \c NULL if the specified partition does not exist.
+ */
+PedPartition*
+ped_disk_get_partition (const PedDisk* disk, int num)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE))
+ return walk;
+ }
+
+ return NULL;
+}
+
+/**
+ * Returns the partition that contains sect. If sect lies within a logical
+ * partition, then the logical partition is returned (not the extended
+ * partition).
+ */
+PedPartition*
+ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ for (walk = disk->part_list; walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (ped_geometry_test_sector_inside (&walk->geom, sect)
+ && walk->type != PED_PARTITION_EXTENDED)
+ return walk;
+ }
+
+ /* should never get here, unless sect is outside of disk's useable
+ * part, or we're in "update mode", and the free space place-holders
+ * have been removed with _disk_remove_freespace()
+ */
+ return NULL;
+}
+
+/* I'm beginning to agree with Sedgewick :-/ */
+static int
+_disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (loc != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ part->prev = loc->prev;
+ part->next = loc;
+ if (part->prev) {
+ part->prev->next = part;
+ } else {
+ if (loc->type & PED_PARTITION_LOGICAL)
+ ped_disk_extended_partition (disk)->part_list = part;
+ else
+ disk->part_list = part;
+ }
+ loc->prev = part;
+
+ return 1;
+}
+
+static int
+_disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (loc != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ part->prev = loc;
+ part->next = loc->next;
+ if (loc->next)
+ loc->next->prev = part;
+ loc->next = part;
+
+ return 1;
+}
+
+static int
+_disk_raw_remove (PedDisk* disk, PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ if (part->prev) {
+ part->prev->next = part->next;
+ if (part->next)
+ part->next->prev = part->prev;
+ } else {
+ if (part->type & PED_PARTITION_LOGICAL) {
+ ped_disk_extended_partition (disk)->part_list
+ = part->next;
+ } else {
+ disk->part_list = part->next;
+ }
+ if (part->next)
+ part->next->prev = NULL;
+ }
+
+ return 1;
+}
+
+/*
+ *UPDATE MODE ONLY
+ */
+static int
+_disk_raw_add (PedDisk* disk, PedPartition* part)
+{
+ PedPartition* walk;
+ PedPartition* last;
+ PedPartition* ext_part;
+
+ PED_ASSERT (disk->update_mode, return 0);
+
+ ext_part = ped_disk_extended_partition (disk);
+
+ last = NULL;
+ walk = (part->type & PED_PARTITION_LOGICAL) ?
+ ext_part->part_list : disk->part_list;
+
+ for (; walk; last = walk, walk = walk->next) {
+ if (walk->geom.start > part->geom.end)
+ break;
+ }
+
+ if (walk) {
+ return _disk_raw_insert_before (disk, walk, part);
+ } else {
+ if (last) {
+ return _disk_raw_insert_after (disk, last, part);
+ } else {
+ if (part->type & PED_PARTITION_LOGICAL)
+ ext_part->part_list = part;
+ else
+ disk->part_list = part;
+ }
+ }
+
+ return 1;
+}
+
+static PedConstraint*
+_partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom)
+{
+ PedSector min_start;
+ PedSector max_end;
+ PedPartition* walk;
+ PedGeometry free_space;
+
+ PED_ASSERT (part->disk->update_mode, return NULL);
+ PED_ASSERT (part->geom.dev == geom->dev, return NULL);
+
+ if (part->type & PED_PARTITION_LOGICAL) {
+ PedPartition* ext_part;
+
+ ext_part = ped_disk_extended_partition (part->disk);
+ PED_ASSERT (ext_part != NULL, return NULL);
+
+ min_start = ext_part->geom.start;
+ max_end = ext_part->geom.end;
+ walk = ext_part->part_list;
+ } else {
+ min_start = 0;
+ max_end = part->disk->dev->length - 1;
+ walk = part->disk->part_list;
+ }
+
+ while (walk != NULL
+ && (walk->geom.start < geom->start
+ || min_start >= walk->geom.start)) {
+ if (walk != part)
+ min_start = walk->geom.end + 1;
+ walk = walk->next;
+ }
+
+ if (walk == part)
+ walk = walk->next;
+
+ if (walk)
+ max_end = walk->geom.start - 1;
+
+ if (min_start >= max_end)
+ return NULL;
+
+ ped_geometry_init (&free_space, part->disk->dev,
+ min_start, max_end - min_start + 1);
+ return ped_constraint_new_from_max (&free_space);
+}
+
+/*
+ * Returns \c 0 if the partition, \p part overlaps with any partitions on the
+ * \p disk. The geometry of \p part is taken to be \p geom, NOT \p part->geom
+ * (the idea here is to check if \p geom is valid, before changing \p part).
+ *
+ * This is useful for seeing if a resized partitions new geometry is going to
+ * fit, without the existing geomtry getting in the way.
+ *
+ * Note: overlap with an extended partition is also allowed, provided that
+ * \p geom lies completely inside the extended partition.
+ */
+static int
+_disk_check_part_overlaps (PedDisk* disk, PedPartition* part)
+{
+ PedPartition* walk;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ for (walk = ped_disk_next_partition (disk, NULL); walk;
+ walk = ped_disk_next_partition (disk, walk)) {
+ if (walk->type & PED_PARTITION_FREESPACE)
+ continue;
+ if (walk == part)
+ continue;
+ if (part->type & PED_PARTITION_EXTENDED
+ && walk->type & PED_PARTITION_LOGICAL)
+ continue;
+
+ if (ped_geometry_test_overlap (&walk->geom, &part->geom)) {
+ if (walk->type & PED_PARTITION_EXTENDED
+ && part->type & PED_PARTITION_LOGICAL
+ && ped_geometry_test_inside (&walk->geom,
+ &part->geom))
+ continue;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+_partition_check_basic_sanity (PedDisk* disk, PedPartition* part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+
+ PED_ASSERT (part->disk == disk, return 0);
+
+ PED_ASSERT (part->geom.start >= 0, return 0);
+ PED_ASSERT (part->geom.end < disk->dev->length, return 0);
+ PED_ASSERT (part->geom.start <= part->geom.end, return 0);
+
+ if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED)
+ && (part->type == PED_PARTITION_EXTENDED
+ || part->type == PED_PARTITION_LOGICAL)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s disk labels don't support logical or extended "
+ "partitions."),
+ disk->type->name);
+ return 0;
+ }
+
+ if (ped_partition_is_active (part)
+ && ! (part->type & PED_PARTITION_LOGICAL)) {
+ if (ped_disk_get_primary_partition_count (disk) + 1
+ > ped_disk_get_max_primary_partition_count (disk)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Too many primary partitions."));
+ return 0;
+ }
+ }
+
+ if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't add a logical partition to %s, because "
+ "there is no extended partition."),
+ disk->dev->path);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+_check_extended_partition (PedDisk* disk, PedPartition* part)
+{
+ PedPartition* walk;
+ PedPartition* ext_part;
+
+ PED_ASSERT (disk != NULL, return 0);
+ ext_part = ped_disk_extended_partition (disk);
+ if (!ext_part) ext_part = part;
+ PED_ASSERT (ext_part != NULL, return 0);
+
+ if (part != ext_part) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have more than one extended partition on %s."),
+ disk->dev->path);
+ return 0;
+ }
+
+ for (walk = ext_part->part_list; walk; walk = walk->next) {
+ if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have logical partitions outside of "
+ "the extended partition."));
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+_check_partition (PedDisk* disk, PedPartition* part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+
+ PED_ASSERT (part->geom.start <= part->geom.end, return 0);
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ if (!_check_extended_partition (disk, part))
+ return 0;
+ }
+
+ if (part->type & PED_PARTITION_LOGICAL
+ && !ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have a logical partition outside of the "
+ "extended partition on %s."),
+ disk->dev->path);
+ return 0;
+ }
+
+ if (!_disk_check_part_overlaps (disk, part)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have overlapping partitions."));
+ return 0;
+ }
+
+ if (! (part->type & PED_PARTITION_LOGICAL)
+ && ext_part && ext_part != part
+ && ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Can't have a primary partition inside an extended "
+ "partition."));
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Adds PedPartition \p part to PedPartition \p disk.
+ *
+ * \warning The partition's geometry may be changed, subject to \p constraint.
+ * You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>,
+ * but many partition table schemes have special requirements on the start
+ * and end of partitions. Therefore, having an overly strict constraint
+ * will probably mean that this function will fail (in which
+ * case \p part will be left unmodified)
+ * \p part is assigned a number (\p part->num) in this process.
+ *
+ * \return \c 0 on failure.
+ */
+int
+ped_disk_add_partition (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint)
+{
+ PedConstraint* overlap_constraint = NULL;
+ PedConstraint* constraints = NULL;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ if (!_partition_check_basic_sanity (disk, part))
+ return 0;
+
+ _disk_push_update_mode (disk);
+
+ if (ped_partition_is_active (part)) {
+ overlap_constraint
+ = _partition_get_overlap_constraint (part, &part->geom);
+ constraints = ped_constraint_intersect (overlap_constraint,
+ constraint);
+
+ if (!constraints && constraint) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have overlapping partitions."));
+ goto error;
+ }
+
+ if (!_partition_enumerate (part))
+ goto error;
+ if (!_partition_align (part, constraints))
+ goto error;
+ }
+ if (!_check_partition (disk, part))
+ goto error;
+ if (!_disk_raw_add (disk, part))
+ goto error;
+
+ ped_constraint_destroy (overlap_constraint);
+ ped_constraint_destroy (constraints);
+ _disk_pop_update_mode (disk);
+#ifdef DEBUG
+ if (!_disk_check_sanity (disk))
+ return 0;
+#endif
+ return 1;
+
+error:
+ ped_constraint_destroy (overlap_constraint);
+ ped_constraint_destroy (constraints);
+ _disk_pop_update_mode (disk);
+ return 0;
+}
+
+/**
+ * Removes PedPartition \p part from PedDisk \p disk.
+ *
+ * If \p part is an extended partition, it must not contain any logical
+ * partitions. \p part is *NOT* destroyed. The caller must call
+ * ped_partition_destroy(), or use ped_disk_delete_partition() instead.
+ *
+ * \return \c 0 on error.
+ */
+int
+ped_disk_remove_partition (PedDisk* disk, PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ _disk_push_update_mode (disk);
+ PED_ASSERT (part->part_list == NULL, goto error);
+ _disk_raw_remove (disk, part);
+ _disk_pop_update_mode (disk);
+ ped_disk_enumerate_partitions (disk);
+ return 1;
+
+error:
+ _disk_pop_update_mode (disk);
+ return 0;
+}
+
+static int
+ped_disk_delete_all_logical (PedDisk* disk);
+
+/**
+ * Removes \p part from \p disk, and destroys \p part.
+ *
+ * \return \c 0 on failure.
+ */
+int
+ped_disk_delete_partition (PedDisk* disk, PedPartition* part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ _disk_push_update_mode (disk);
+ if (part->type == PED_PARTITION_EXTENDED)
+ ped_disk_delete_all_logical (disk);
+ ped_disk_remove_partition (disk, part);
+ ped_partition_destroy (part);
+ _disk_pop_update_mode (disk);
+
+ return 1;
+}
+
+static int
+ped_disk_delete_all_logical (PedDisk* disk)
+{
+ PedPartition* walk;
+ PedPartition* next;
+ PedPartition* ext_part;
+
+ PED_ASSERT (disk != NULL, return 0);
+ ext_part = ped_disk_extended_partition (disk);
+ PED_ASSERT (ext_part != NULL, return 0);
+
+ for (walk = ext_part->part_list; walk; walk = next) {
+ next = walk->next;
+
+ if (!ped_disk_delete_partition (disk, walk))
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * Removes and destroys all partitions on \p disk.
+ *
+ * \return \c 0 on failure.
+ */
+int
+ped_disk_delete_all (PedDisk* disk)
+{
+ PedPartition* walk;
+ PedPartition* next;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ _disk_push_update_mode (disk);
+
+ for (walk = disk->part_list; walk; walk = next) {
+ next = walk->next;
+
+ if (!ped_disk_delete_partition (disk, walk))
+ return 0;
+ }
+
+ _disk_pop_update_mode (disk);
+
+ return 1;
+}
+
+/**
+ * Sets the geometry of \p part (i.e. change a partitions location). This can
+ * fail for many reasons, e.g. can't overlap with other partitions. If it
+ * does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's
+ * geometry may be set to something different from \p start and \p end subject
+ * to \p constraint.
+ *
+ * \warning The constraint warning from ped_disk_add_partition() applies.
+ *
+ * \note this function does not modify the contents of the partition. You need
+ * to call ped_file_system_resize() separately.
+ */
+int
+ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint,
+ PedSector start, PedSector end)
+{
+ PedConstraint* overlap_constraint = NULL;
+ PedConstraint* constraints = NULL;
+ PedGeometry old_geom;
+ PedGeometry new_geom;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk == disk, return 0);
+
+ old_geom = part->geom;
+ ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1);
+
+ _disk_push_update_mode (disk);
+
+ overlap_constraint
+ = _partition_get_overlap_constraint (part, &new_geom);
+ constraints = ped_constraint_intersect (overlap_constraint, constraint);
+ if (!constraints && constraint) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't have overlapping partitions."));
+ goto error_pop_update_mode;
+ }
+
+ part->geom = new_geom;
+ if (!_partition_align (part, constraints))
+ goto error_pop_update_mode;
+ if (!_check_partition (disk, part))
+ goto error_pop_update_mode;
+
+ /* remove and add, to ensure the ordering gets updated if necessary */
+ _disk_raw_remove (disk, part);
+ _disk_raw_add (disk, part);
+
+ _disk_pop_update_mode (disk);
+
+ ped_constraint_destroy (overlap_constraint);
+ ped_constraint_destroy (constraints);
+ return 1;
+
+error_pop_update_mode:
+ _disk_pop_update_mode (disk);
+ ped_constraint_destroy (overlap_constraint);
+ ped_constraint_destroy (constraints);
+ part->geom = old_geom;
+ return 0;
+}
+
+/**
+ * Grow PedPartition \p part geometry to the maximum possible subject to
+ * \p constraint. The new geometry will be a superset of the old geometry.
+ *
+ * \return 0 on failure
+ */
+int
+ped_disk_maximize_partition (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint)
+{
+ PedGeometry old_geom;
+ PedSector global_min_start;
+ PedSector global_max_end;
+ PedSector new_start;
+ PedSector new_end;
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedConstraint* constraint_any;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ if (part->type & PED_PARTITION_LOGICAL) {
+ PED_ASSERT (ext_part != NULL, return 0);
+ global_min_start = ext_part->geom.start;
+ global_max_end = ext_part->geom.end;
+ } else {
+ global_min_start = 0;
+ global_max_end = disk->dev->length - 1;
+ }
+
+ old_geom = part->geom;
+
+ _disk_push_update_mode (disk);
+
+ if (part->prev)
+ new_start = part->prev->geom.end + 1;
+ else
+ new_start = global_min_start;
+
+ if (part->next)
+ new_end = part->next->geom.start - 1;
+ else
+ new_end = global_max_end;
+
+ if (!ped_disk_set_partition_geom (disk, part, constraint, new_start,
+ new_end))
+ goto error;
+
+ _disk_pop_update_mode (disk);
+ return 1;
+
+error:
+ constraint_any = ped_constraint_any (disk->dev);
+ ped_disk_set_partition_geom (disk, part, constraint_any,
+ old_geom.start, old_geom.end);
+ ped_constraint_destroy (constraint_any);
+ _disk_pop_update_mode (disk);
+ return 0;
+}
+
+/**
+ * Get the maximum geometry \p part can be grown to, subject to
+ * \p constraint.
+ *
+ * \return \c NULL on failure.
+ */
+PedGeometry*
+ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part,
+ const PedConstraint* constraint)
+{
+ PedGeometry old_geom;
+ PedGeometry* max_geom;
+ PedConstraint* constraint_exact;
+
+ PED_ASSERT(disk != NULL, return NULL);
+ PED_ASSERT(part != NULL, return NULL);
+ PED_ASSERT(ped_partition_is_active (part), return NULL);
+
+ old_geom = part->geom;
+ if (!ped_disk_maximize_partition (disk, part, constraint))
+ return NULL;
+ max_geom = ped_geometry_duplicate (&part->geom);
+
+ constraint_exact = ped_constraint_exact (&old_geom);
+ ped_disk_set_partition_geom (disk, part, constraint_exact,
+ old_geom.start, old_geom.end);
+ ped_constraint_destroy (constraint_exact);
+
+ /* this assertion should never fail, because the old
+ * geometry was valid
+ */
+ PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom),
+ return NULL);
+
+ return max_geom;
+}
+
+/**
+ * Reduce the size of the extended partition to a minimum while still wrapping
+ * its logical partitions. If there are no logical partitions, remove the
+ * extended partition.
+ *
+ * \return 0 on failure.
+ */
+int
+ped_disk_minimize_extended_partition (PedDisk* disk)
+{
+ PedPartition* first_logical;
+ PedPartition* last_logical;
+ PedPartition* walk;
+ PedPartition* ext_part;
+ PedConstraint* constraint;
+ int status;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ ext_part = ped_disk_extended_partition (disk);
+ if (!ext_part)
+ return 1;
+
+ _disk_push_update_mode (disk);
+
+ first_logical = ext_part->part_list;
+ if (!first_logical) {
+ _disk_pop_update_mode (disk);
+ return ped_disk_delete_partition (disk, ext_part);
+ }
+
+ for (walk = first_logical; walk->next; walk = walk->next);
+ last_logical = walk;
+
+ constraint = ped_constraint_any (disk->dev);
+ status = ped_disk_set_partition_geom (disk, ext_part, constraint,
+ first_logical->geom.start,
+ last_logical->geom.end);
+ ped_constraint_destroy (constraint);
+
+ _disk_pop_update_mode (disk);
+ return status;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * \addtogroup PedPartition
+ *
+ * @{
+ */
+
+/**
+ * Returns a name that seems mildly appropriate for a partition type \p type.
+ *
+ * Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it
+ * will return "free". This isn't to be taken too seriously - it's just
+ * useful for user interfaces, so you can show the user something ;-)
+ *
+ * \note The returned string will be in English. However,
+ * translations are provided, so the caller can call
+ * dgettext("parted", RESULT) on the result.
+ *
+ */
+const char*
+ped_partition_type_get_name (PedPartitionType type)
+{
+ if (type & PED_PARTITION_METADATA)
+ return N_("metadata");
+ else if (type & PED_PARTITION_FREESPACE)
+ return N_("free");
+ else if (type & PED_PARTITION_EXTENDED)
+ return N_("extended");
+ else if (type & PED_PARTITION_LOGICAL)
+ return N_("logical");
+ else
+ return N_("primary");
+}
+
+
+/**
+ * Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot".
+ *
+ * \note The returned string will be in English. However,
+ * translations are provided, so the caller can call
+ * dgettext("parted", RESULT) on the result.
+ */
+const char*
+ped_partition_flag_get_name (PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ return N_("boot");
+ case PED_PARTITION_ROOT:
+ return N_("root");
+ case PED_PARTITION_SWAP:
+ return N_("swap");
+ case PED_PARTITION_HIDDEN:
+ return N_("hidden");
+ case PED_PARTITION_RAID:
+ return N_("raid");
+ case PED_PARTITION_LVM:
+ return N_("lvm");
+ case PED_PARTITION_LBA:
+ return N_("lba");
+ case PED_PARTITION_HPSERVICE:
+ return N_("hp-service");
+ case PED_PARTITION_PALO:
+ return N_("palo");
+ case PED_PARTITION_PREP:
+ return N_("prep");
+ case PED_PARTITION_MSFT_RESERVED:
+ return N_("msftres");
+
+ default:
+ ped_exception_throw (
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("Unknown partition flag, %d."),
+ flag);
+ return NULL;
+ }
+}
+
+/**
+ * Iterates through all flags.
+ *
+ * ped_partition_flag_next(0) returns the first flag
+ *
+ * \return the next flag, or 0 if there are no more flags
+ */
+PedPartitionFlag
+ped_partition_flag_next (PedPartitionFlag flag)
+{
+ return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1);
+}
+
+/**
+ * Returns the flag associated with \p name.
+ *
+ * \p name can be the English
+ * string, or the translation for the native language.
+ */
+PedPartitionFlag
+ped_partition_flag_get_by_name (const char* name)
+{
+ PedPartitionFlag flag;
+ const char* flag_name;
+
+ for (flag = ped_partition_flag_next (0); flag;
+ flag = ped_partition_flag_next (flag)) {
+ flag_name = ped_partition_flag_get_name (flag);
+ if (strcasecmp (name, flag_name) == 0
+ || strcasecmp (name, _(flag_name)) == 0)
+ return flag;
+ }
+
+ return 0;
+}
+
+static void
+ped_partition_print (const PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ printf (" %-10s %02d (%d->%d)\n",
+ ped_partition_type_get_name (part->type),
+ part->num,
+ (int) part->geom.start, (int) part->geom.end);
+}
+
+/** @} */
+
+/**
+ * \addtogroup PedDisk
+ *
+ * @{
+ */
+
+/**
+ * Prints a summary of disk's partitions. Useful for debugging.
+ */
+void
+ped_disk_print (const PedDisk* disk)
+{
+ PedPartition* part;
+
+ PED_ASSERT (disk != NULL, return);
+
+ for (part = disk->part_list; part;
+ part = ped_disk_next_partition (disk, part))
+ ped_partition_print (part);
+}
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/libparted/exception.c b/usr/src/lib/libparted/common/libparted/exception.c
new file mode 100644
index 0000000000..0940d65456
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/exception.c
@@ -0,0 +1,312 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file exception.c */
+
+/**
+ * \addtogroup PedException
+ *
+ * \brief Exception handling.
+ *
+ * There are a few types of exceptions: PED_EXCEPTION_INFORMATION,
+ * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL,
+ * PED_EXCEPTION_BUG.
+ *
+ * They are "thrown" when one of the above events occur while executing
+ * a libparted function. For example, if ped_device_open() fails
+ * because the device doesn't exist, an exception will be thrown.
+ * Exceptions contain text describing what the event was. It will give
+ * at least one option for resolving the exception: PED_EXCEPTION_FIX,
+ * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY,
+ * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown,
+ * there are two things that can happen:
+ *
+ * -# an exception handler is called, which selects how the exception should be
+ * resolved (usually by asking the user). Also note: an exception handler may
+ * choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action
+ * will be taken by libparted (respectively the code that threw the
+ * exception). In general, a default action will be "safe".
+ * -# the exception is not handled, because the caller of the function wants to
+ * handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is
+ * returned.
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#define N_(String) String
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+int ped_exception = 0;
+
+static PedExceptionOption default_handler (PedException* ex);
+
+static PedExceptionHandler* ex_handler = default_handler;
+static PedException* ex = NULL;
+static int ex_fetch_count = 0;
+
+static char* type_strings [] = {
+ N_("Information"),
+ N_("Warning"),
+ N_("Error"),
+ N_("Fatal"),
+ N_("Bug"),
+ N_("No Implementation")
+};
+
+static char* option_strings [] = {
+ N_("Fix"),
+ N_("Yes"),
+ N_("No"),
+ N_("OK"),
+ N_("Retry"),
+ N_("Ignore"),
+ N_("Cancel")
+};
+
+/**
+ * Return a string describing an exception type.
+ */
+char*
+ped_exception_get_type_string (PedExceptionType ex_type)
+{
+ return type_strings [ex_type - 1];
+}
+
+/* FIXME: move this out to the prospective math.c */
+/* FIXME: this can probably be done more efficiently */
+static int
+ped_log2 (int n)
+{
+ int x;
+
+ PED_ASSERT (n > 0, return -1);
+
+ for (x=0; 1 << x <= n; x++);
+
+ return x - 1;
+}
+
+/**
+ * Return a string describing an exception option.
+ */
+char*
+ped_exception_get_option_string (PedExceptionOption ex_opt)
+{
+ return option_strings [ped_log2 (ex_opt)];
+}
+
+static PedExceptionOption
+default_handler (PedException* e)
+{
+ if (e->type == PED_EXCEPTION_BUG)
+ fprintf (stderr,
+ _("A bug has been detected in GNU Parted. "
+ "Refer to the web site of parted "
+ "http://www.gnu.org/software/parted/parted.html "
+ "for more informations of what could be useful "
+ "for bug submitting! "
+ "Please email a bug report to "
+ "bug-parted@gnu.org containing at least the "
+ "version (%s) and the following message: "),
+ VERSION);
+ else
+ fprintf (stderr, "%s: ",
+ ped_exception_get_type_string (e->type));
+ fprintf (stderr, "%s\n", e->message);
+
+ switch (e->options) {
+ case PED_EXCEPTION_OK:
+ case PED_EXCEPTION_CANCEL:
+ case PED_EXCEPTION_IGNORE:
+ return e->options;
+
+ default:
+ return PED_EXCEPTION_UNHANDLED;
+ }
+}
+
+/**
+ * Set the exception handler.
+ *
+ * The exception handler should return ONE of the options set in ex->options,
+ * indicating the way the event should be resolved.
+ */
+void
+ped_exception_set_handler (PedExceptionHandler* handler)
+{
+ if (handler)
+ ex_handler = handler;
+ else
+ ex_handler = default_handler;
+}
+
+/**
+ * Get the current exception handler.
+ */
+PedExceptionHandler *
+ped_exception_get_handler (void)
+{
+ if (ex_handler)
+ return ex_handler;
+ return default_handler;
+}
+
+/**
+ * Assert that the current exception has been resolved.
+ */
+void
+ped_exception_catch ()
+{
+ if (ped_exception) {
+ ped_exception = 0;
+
+ ped_free (ex->message);
+ ped_free (ex);
+ ex = NULL;
+ }
+}
+
+static PedExceptionOption
+do_throw ()
+{
+ PedExceptionOption ex_opt;
+
+ ped_exception = 1;
+
+ if (ex_fetch_count) {
+ return PED_EXCEPTION_UNHANDLED;
+ } else {
+ ex_opt = ex_handler (ex);
+ ped_exception_catch ();
+ return ex_opt;
+ }
+}
+
+/**
+ * Throw an exception.
+ *
+ * You can also use this in a program using libparted.
+ * "message" is a printf-like format string, so you can do
+ *
+ * \code
+ * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL,
+ * "Can't open %s", file_name);
+ * \endcode
+ *
+ * Returns the option selected to resolve the exception. If the exception was
+ * unhandled, PED_EXCEPTION_UNHANDLED is returned.
+ */
+PedExceptionOption
+ped_exception_throw (PedExceptionType ex_type,
+ PedExceptionOption ex_opts, const char* message, ...)
+{
+ va_list arg_list;
+ int result;
+ static int size = 1000;
+
+ if (ex)
+ ped_exception_catch ();
+
+ ex = (PedException*) malloc (sizeof (PedException));
+ if (!ex)
+ goto no_memory;
+
+ ex->type = ex_type;
+ ex->options = ex_opts;
+
+ while (1) {
+ ex->message = (char*) malloc (size);
+ if (!ex->message)
+ goto no_memory;
+
+ va_start (arg_list, message);
+ result = vsnprintf (ex->message, size, message, arg_list);
+ va_end (arg_list);
+
+ if (result > -1 && result < size)
+ break;
+
+ size += 10;
+ }
+
+ return do_throw ();
+
+no_memory:
+ fputs ("Out of memory in exception handler!\n", stderr);
+
+ va_start (arg_list, message);
+ vfprintf (stderr, message, arg_list);
+ va_end (arg_list);
+
+ return PED_EXCEPTION_UNHANDLED;
+}
+
+/**
+ * Rethrow an unhandled exception.
+ * This means repeating the last ped_exception_throw() statement.
+ */
+PedExceptionOption
+ped_exception_rethrow ()
+{
+ return do_throw ();
+}
+
+/**
+ * Indicates that exceptions should not go to the exception handler, but
+ * passed up to the calling function(s). All calls to
+ * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED.
+ */
+void
+ped_exception_fetch_all ()
+{
+ ex_fetch_count++;
+}
+
+/**
+ * Indicates that the calling function does not want to accept any
+ * responsibility for exceptions any more.
+ *
+ * \note a caller of that function may still want responsibility, so
+ * ped_exception_throw() may not invoke the exception handler.
+ *
+ * \warning every call to this function must have a preceding
+ * ped_exception_fetch_all().
+ */
+void
+ped_exception_leave_all ()
+{
+ PED_ASSERT (ex_fetch_count > 0, return);
+ ex_fetch_count--;
+}
+
+/** @} */
+
diff --git a/usr/src/lib/libparted/common/libparted/filesys.c b/usr/src/lib/libparted/common/libparted/filesys.c
new file mode 100644
index 0000000000..986f0ca35e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/filesys.c
@@ -0,0 +1,782 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file filesys.c */
+
+/**
+ * \addtogroup PedFileSystem
+ *
+ * \note File systems exist on a PedGeometry - NOT a PedPartition.
+ *
+ * @{
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define BUFFER_SIZE 4096 /* in sectors */
+
+static PedFileSystemType* fs_types = NULL;
+
+void
+ped_file_system_type_register (PedFileSystemType* fs_type)
+{
+ PED_ASSERT (fs_type != NULL, return);
+ PED_ASSERT (fs_type->ops != NULL, return);
+ PED_ASSERT (fs_type->name != NULL, return);
+
+ /* pretend that "next" isn't part of the struct :-) */
+ ((struct _PedFileSystemType*) fs_type)->next = fs_types;
+ fs_types = (struct _PedFileSystemType*) fs_type;
+}
+
+void
+ped_file_system_type_unregister (PedFileSystemType* fs_type)
+{
+ PedFileSystemType* walk;
+ PedFileSystemType* last = NULL;
+
+ PED_ASSERT (fs_types != NULL, return);
+ PED_ASSERT (fs_type != NULL, return);
+
+ for (walk = fs_types; walk && walk != fs_type;
+ last = walk, walk = walk->next);
+
+ PED_ASSERT (walk != NULL, return);
+ if (last)
+ ((struct _PedFileSystemType*) last)->next = fs_type->next;
+ else
+ fs_types = fs_type->next;
+}
+
+/**
+ * Get a PedFileSystemType by its @p name.
+ *
+ * @return @c NULL if none found.
+ */
+PedFileSystemType*
+ped_file_system_type_get (const char* name)
+{
+ PedFileSystemType* walk;
+
+ PED_ASSERT (name != NULL, return NULL);
+
+ for (walk = fs_types; walk != NULL; walk = walk->next) {
+ if (!strcasecmp (walk->name, name))
+ break;
+ }
+ return walk;
+}
+
+/**
+ * Get the next PedFileSystemType after @p fs_type.
+ *
+ * @return @c NULL if @p fs_type is the last item in the list.
+ */
+PedFileSystemType*
+ped_file_system_type_get_next (const PedFileSystemType* fs_type)
+{
+ if (fs_type)
+ return fs_type->next;
+ else
+ return fs_types;
+}
+
+/**
+ * Attempt to find a file system and return the region it occupies.
+ *
+ * @param fs_type The file system type to probe for.
+ * @param geom The region to be searched.
+ *
+ * @return @p NULL if @p fs_type file system wasn't detected
+ */
+PedGeometry*
+ped_file_system_probe_specific (
+ const PedFileSystemType* fs_type, PedGeometry* geom)
+{
+ PedGeometry* result;
+
+ PED_ASSERT (fs_type != NULL, return NULL);
+ PED_ASSERT (fs_type->ops->probe != NULL, return NULL);
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!ped_device_open (geom->dev))
+ return 0;
+ result = fs_type->ops->probe (geom);
+ ped_device_close (geom->dev);
+ return result;
+}
+
+static int
+_test_open (PedFileSystemType* fs_type, PedGeometry* geom)
+{
+ PedFileSystem* fs;
+
+ ped_exception_fetch_all ();
+ fs = fs_type->ops->open (geom);
+ if (fs)
+ fs_type->ops->close (fs);
+ else
+ ped_exception_catch ();
+ ped_exception_leave_all ();
+ return fs != NULL;
+}
+
+static PedFileSystemType*
+_probe_with_open (PedGeometry* geom, int detected_count,
+ PedFileSystemType* detected[])
+{
+ int i;
+ PedFileSystemType* open_detected = NULL;
+
+ ped_device_open (geom->dev);
+
+ /* If one and only one file system that Parted is able to open
+ * can be successfully opened on this geometry, return it.
+ * If more than one can be, return NULL.
+ */
+ for (i=0; i<detected_count; i++) {
+ if (!detected[i]->ops->open || !_test_open (detected [i], geom))
+ continue;
+
+ if (open_detected) {
+ ped_device_close (geom->dev);
+ return NULL;
+ } else {
+ open_detected = detected [i];
+ }
+ }
+
+ /* If no file system has been successfully opened, and
+ * if Parted has detected at most one unopenable file system,
+ * return it.
+ */
+ if (!open_detected)
+ for (i=0; i<detected_count; i++) {
+ if (detected[i]->ops->open)
+ continue;
+ if (open_detected) {
+ ped_device_close (geom->dev);
+ return NULL;
+ } else {
+ open_detected = detected [i];
+ }
+ }
+
+ ped_device_close (geom->dev);
+ return open_detected;
+}
+
+static int
+_geometry_error (const PedGeometry* a, const PedGeometry* b)
+{
+ PedSector start_delta = a->start - b->start;
+ PedSector end_delta = a->end - b->end;
+
+ return abs (start_delta) + abs (end_delta);
+}
+
+static PedFileSystemType*
+_best_match (const PedGeometry* geom, PedFileSystemType* detected [],
+ const int detected_error [], int detected_count)
+{
+ int best_match = 0;
+ int i;
+ PedSector min_error;
+
+ min_error = PED_MAX (4096, geom->length / 100);
+
+ for (i = 1; i < detected_count; i++) {
+ if (detected_error [i] < detected_error [best_match])
+ best_match = i;
+ }
+
+ /* make sure the best match is significantly better than all the
+ * other matches
+ */
+ for (i = 0; i < detected_count; i++) {
+ if (i == best_match)
+ continue;
+
+ if (abs (detected_error [best_match] - detected_error [i])
+ < min_error)
+ return NULL;
+ }
+
+ return detected [best_match];
+}
+
+
+/**
+ * Attempt to detect a file system in region \p geom.
+ * This function tries to be clever at dealing with ambiguous
+ * situations, such as when one file system was not completely erased before a
+ * new file system was created on top of it.
+ *
+ * \return a new PedFileSystem on success, \c NULL on failure
+ */
+PedFileSystemType*
+ped_file_system_probe (PedGeometry* geom)
+{
+ PedFileSystemType* detected[32];
+ int detected_error[32];
+ int detected_count = 0;
+ PedFileSystemType* walk = NULL;
+
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!ped_device_open (geom->dev))
+ return NULL;
+
+ ped_exception_fetch_all ();
+ while ( (walk = ped_file_system_type_get_next (walk)) ) {
+ PedGeometry* probed;
+
+ probed = ped_file_system_probe_specific (walk, geom);
+ if (probed) {
+ detected [detected_count] = walk;
+ detected_error [detected_count]
+ = _geometry_error (geom, probed);
+ detected_count++;
+ ped_geometry_destroy (probed);
+ } else {
+ ped_exception_catch ();
+ }
+ }
+ ped_exception_leave_all ();
+
+ ped_device_close (geom->dev);
+
+ if (!detected_count)
+ return NULL;
+ walk = _best_match (geom, detected, detected_error, detected_count);
+ if (walk)
+ return walk;
+ return _probe_with_open (geom, detected_count, detected);
+}
+
+/**
+ * This function erases all file system signatures that indicate that a
+ * file system occupies a given region described by \p geom.
+ * After this operation ped_file_system_probe() won't detect any file system.
+ *
+ * \note ped_file_system_create() calls this before creating a new file system.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+int
+ped_file_system_clobber (PedGeometry* geom)
+{
+ PedFileSystemType* fs_type = NULL;
+
+ PED_ASSERT (geom != NULL, return 0);
+
+ if (!ped_device_open (geom->dev))
+ goto error;
+
+ ped_exception_fetch_all ();
+ while ((fs_type = ped_file_system_type_get_next (fs_type))) {
+ PedGeometry* probed;
+
+ if (!fs_type->ops->clobber)
+ continue;
+
+ probed = ped_file_system_probe_specific (fs_type, geom);
+ if (!probed) {
+ ped_exception_catch ();
+ continue;
+ }
+ ped_geometry_destroy (probed);
+
+ if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) {
+ ped_exception_leave_all ();
+ goto error_close_dev;
+ }
+ }
+ ped_device_close (geom->dev);
+ ped_exception_leave_all ();
+ return 1;
+
+error_close_dev:
+ ped_device_close (geom->dev);
+error:
+ return 0;
+}
+
+/* This function erases all signatures that indicate the presence of
+ * a file system in a particular region, without erasing any data
+ * contained inside the "exclude" region.
+ */
+static int
+ped_file_system_clobber_exclude (PedGeometry* geom,
+ const PedGeometry* exclude)
+{
+ PedGeometry* clobber_geom;
+ int status;
+
+ if (ped_geometry_test_sector_inside (exclude, geom->start))
+ return 1;
+
+ clobber_geom = ped_geometry_duplicate (geom);
+ if (ped_geometry_test_overlap (clobber_geom, exclude))
+ ped_geometry_set_end (clobber_geom, exclude->start - 1);
+
+ status = ped_file_system_clobber (clobber_geom);
+ ped_geometry_destroy (clobber_geom);
+ return status;
+}
+
+/**
+ * This function opens the file system stored on \p geom, if it
+ * can find one.
+ * It is often called in the following manner:
+ * \code
+ * fs = ped_file_system_open (&part.geom)
+ * \endcode
+ *
+ * \throws PED_EXCEPTION_ERROR if file system could not be detected
+ * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume
+ * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on
+ * \p geom is not implemented
+ *
+ * \return a PedFileSystem on success, \c NULL on failure.
+ */
+PedFileSystem*
+ped_file_system_open (PedGeometry* geom)
+{
+ PedFileSystemType* type;
+ PedFileSystem* fs;
+ PedGeometry* probed_geom;
+
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!ped_device_open (geom->dev))
+ goto error;
+
+ type = ped_file_system_probe (geom);
+ if (!type) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Could not detect file system."));
+ goto error_close_dev;
+ }
+
+ probed_geom = ped_file_system_probe_specific (type, geom);
+ if (!probed_geom)
+ goto error_close_dev;
+ if (!ped_geometry_test_inside (geom, probed_geom)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The file system is bigger than its volume!"))
+ != PED_EXCEPTION_IGNORE)
+ goto error_destroy_probed_geom;
+ }
+
+ if (!type->ops->open) {
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for opening %s file systems "
+ "is not implemented yet."),
+ type->name);
+ goto error_destroy_probed_geom;
+ }
+
+ fs = type->ops->open (probed_geom);
+ if (!fs)
+ goto error_destroy_probed_geom;
+ ped_geometry_destroy (probed_geom);
+ return fs;
+
+error_destroy_probed_geom:
+ ped_geometry_destroy (probed_geom);
+error_close_dev:
+ ped_device_close (geom->dev);
+error:
+ return 0;
+}
+
+/**
+ * This function initializes a new file system of type \p type on
+ * a region described by \p geom, writing out appropriate metadata and
+ * signatures. If \p timer is non-NULL, it is used as the progress meter.
+ *
+ * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type
+ * is not implemented yet
+ *
+ * \return a PedFileSystem on success, \c NULL on failure
+ */
+PedFileSystem*
+ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type,
+ PedTimer* timer)
+{
+ PedFileSystem* fs;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (type != NULL, return NULL);
+
+ if (!type->ops->create) {
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for creating %s file systems "
+ "is not implemented yet."),
+ type->name);
+ goto error;
+ }
+
+ if (!ped_device_open (geom->dev))
+ goto error;
+
+ if (!ped_file_system_clobber (geom))
+ goto error_close_dev;
+ fs = type->ops->create (geom, timer);
+ if (!fs)
+ goto error_close_dev;
+ return fs;
+
+error_close_dev:
+ ped_device_close (geom->dev);
+error:
+ return 0;
+}
+
+/**
+ * Close file system \p fs.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+int
+ped_file_system_close (PedFileSystem* fs)
+{
+ PedDevice* dev = fs->geom->dev;
+
+ PED_ASSERT (fs != NULL, goto error_close_dev);
+
+ if (!fs->type->ops->close (fs))
+ goto error_close_dev;
+ ped_device_close (dev);
+ return 1;
+
+error_close_dev:
+ ped_device_close (dev);
+ return 0;
+}
+
+/**
+ * Check \p fs file system for errors.
+ *
+ * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is
+ * not implemented yet
+ *
+ * \return \c 0 on failure (i.e. unfixed errors)
+ */
+int
+ped_file_system_check (PedFileSystem* fs, PedTimer* timer)
+{
+ PED_ASSERT (fs != NULL, return 0);
+
+ if (!fs->type->ops->check) {
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for checking %s file systems "
+ "is not implemented yet."),
+ fs->type->name);
+ return 0;
+ }
+ return fs->type->ops->check (fs, timer);
+}
+
+static int
+_raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer)
+{
+ char* buf;
+ PedSector pos;
+
+ PED_ASSERT (src != NULL, goto error);
+ PED_ASSERT (dest != NULL, goto error);
+ PED_ASSERT (src->length <= dest->length, goto error);
+
+ buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */
+ if (!buf)
+ goto error;
+
+ if (!ped_device_open (src->dev))
+ goto error_free_buf;
+ if (!ped_device_open (dest->dev))
+ goto error_close_src;
+
+ for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) {
+ ped_timer_update (timer, 1.0 * pos / src->length);
+ if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE))
+ goto error_close_dest;
+ if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE))
+ goto error_close_dest;
+ }
+ if (pos < src->length) {
+ ped_timer_update (timer, 1.0 * pos / src->length);
+ if (!ped_geometry_read (src, buf, pos, src->length - pos))
+ goto error_close_dest;
+ if (!ped_geometry_write (dest, buf, pos, src->length - pos))
+ goto error_close_dest;
+ }
+ ped_timer_update (timer, 1.0);
+
+ ped_device_close (src->dev);
+ ped_device_close (dest->dev);
+ ped_free (buf);
+ return 1;
+
+error_close_dest:
+ ped_device_close (dest->dev);
+error_close_src:
+ ped_device_close (src->dev);
+error_free_buf:
+ ped_free (buf);
+error:
+ return 0;
+}
+
+static PedFileSystem*
+_raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom,
+ PedTimer* timer)
+{
+ PedFileSystem* new_fs;
+ PedTimer* sub_timer = NULL;
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("raw block copying"));
+
+ sub_timer = ped_timer_new_nested (timer, 0.95);
+ if (!_raw_copy (fs->geom, geom, sub_timer))
+ goto error;
+ ped_timer_destroy_nested (sub_timer);
+
+ new_fs = ped_file_system_open (geom);
+ if (!new_fs)
+ goto error;
+
+ ped_timer_set_state_name (timer, _("growing file system"));
+
+ sub_timer = ped_timer_new_nested (timer, 0.05);
+ if (!ped_file_system_resize (new_fs, geom, sub_timer))
+ goto error_close_new_fs;
+ ped_timer_destroy_nested (sub_timer);
+ return new_fs;
+
+error_close_new_fs:
+ ped_file_system_close (new_fs);
+error:
+ ped_timer_destroy_nested (sub_timer);
+ return NULL;
+}
+
+/**
+ * Create a new file system (of the same type) on \p geom, and
+ * copy the contents of \p fs into the new filesystem.
+ * If \p timer is non-NULL, it is used as the progress meter.
+ *
+ * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition
+ * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs
+ * is not implemented yet
+ *
+ * \return a new PedFileSystem on success, \c NULL on failure
+ */
+PedFileSystem*
+ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ PedFileSystem* new_fs;
+
+ PED_ASSERT (fs != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+
+ if (!ped_device_open (geom->dev))
+ goto error;
+
+ if (ped_geometry_test_overlap (fs->geom, geom)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Can't copy onto an overlapping partition."));
+ goto error_close_dev;
+ }
+
+ if (!fs->checked && fs->type->ops->check) {
+ if (!ped_file_system_check (fs, timer))
+ goto error_close_dev;
+ }
+
+ if (!ped_file_system_clobber_exclude (geom, fs->geom))
+ goto error_close_dev;
+
+ if (!fs->type->ops->copy) {
+ if (fs->type->ops->resize) {
+ if (fs->geom->length <= geom->length)
+ return _raw_copy_and_resize (
+ fs, (PedGeometry*) geom,
+ timer);
+
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Direct support for copying file systems is "
+ "not yet implemented for %s. However, "
+ "support for resizing is implemented. "
+ "Therefore, the file system can be copied if "
+ "the new partition is at least as big as the "
+ "old one. So, either shrink the partition "
+ "you are trying to copy, or copy to a bigger "
+ "partition."),
+ fs->type->name);
+ goto error_close_dev;
+ } else {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for copying %s file systems is not "
+ "implemented yet."),
+ fs->type->name);
+ goto error_close_dev;
+ }
+ }
+ new_fs = fs->type->ops->copy (fs, geom, timer);
+ if (!new_fs)
+ goto error_close_dev;
+ return new_fs;
+
+error_close_dev:
+ ped_device_close (geom->dev);
+error:
+ return NULL;;
+}
+
+/**
+ * Resize \p fs to new geometry \p geom.
+ *
+ * \p geom should satisfy the ped_file_system_get_resize_constraint().
+ * (This isn't asserted, so it's not a bug not to... just it's likely
+ * to fail ;) If \p timer is non-NULL, it is used as the progress meter.
+ *
+ * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs
+ * is not implemented yet
+ *
+ * \return \c 0 on failure
+ */
+int
+ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ PED_ASSERT (fs != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+
+ if (!fs->type->ops->resize) {
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for resizing %s file systems "
+ "is not implemented yet."),
+ fs->type->name);
+ return 0;
+ }
+ if (!fs->checked && fs->type->ops->check) {
+ if (!ped_file_system_check (fs, timer))
+ return 0;
+ }
+ if (!ped_file_system_clobber_exclude (geom, fs->geom))
+ return 0;
+
+ return fs->type->ops->resize (fs, geom, timer);
+}
+
+/**
+ * This function returns a constraint on the region that all file systems
+ * of a particular type \p fs_type created on device \p dev with
+ * ped_file_system_create() must satisfy. For example, FAT16 file systems must
+ * be at least 32 megabytes.
+ *
+ * \return \c NULL on failure
+ */
+PedConstraint*
+ped_file_system_get_create_constraint (const PedFileSystemType* fs_type,
+ const PedDevice* dev)
+{
+ PED_ASSERT (fs_type != NULL, return NULL);
+ PED_ASSERT (dev != NULL, return NULL);
+
+ if (!fs_type->ops->get_create_constraint)
+ return NULL;
+ return fs_type->ops->get_create_constraint (dev);
+}
+/**
+ * Return a constraint, that represents all of the possible ways the
+ * file system \p fs can be resized with ped_file_system_resize().
+ * This takes into account the amount of used space on
+ * the filesystem \p fs and the capabilities of the resize algorithm.
+ * Hints:
+ * -# if constraint->start_align->grain_size == 0, or
+ * constraint->start_geom->length == 1, then the start can not be moved
+ * -# constraint->min_size is the minimum size you can resize the partition
+ * to. You might want to tell the user this ;-).
+ *
+ * \return a PedConstraint on success, \c NULL on failure
+ */
+PedConstraint*
+ped_file_system_get_resize_constraint (const PedFileSystem* fs)
+{
+ PED_ASSERT (fs != NULL, return 0);
+
+ if (!fs->type->ops->get_resize_constraint)
+ return NULL;
+ return fs->type->ops->get_resize_constraint (fs);
+}
+
+/**
+ * Get the constraint on copying \p fs with ped_file_system_copy()
+ * to somewhere on \p dev.
+ *
+ * \return a PedConstraint on success, \c NULL on failure
+ */
+PedConstraint*
+ped_file_system_get_copy_constraint (const PedFileSystem* fs,
+ const PedDevice* dev)
+{
+ PedGeometry full_dev;
+
+ PED_ASSERT (fs != NULL, return NULL);
+ PED_ASSERT (dev != NULL, return NULL);
+
+ if (fs->type->ops->get_copy_constraint)
+ return fs->type->ops->get_copy_constraint (fs, dev);
+
+ if (fs->type->ops->resize) {
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ fs->geom->length, dev->length);
+ }
+
+ return NULL;
+}
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/affs.c b/usr/src/lib/libparted/common/libparted/fs/amiga/affs.c
new file mode 100644
index 0000000000..0f5fd96a60
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/affs.c
@@ -0,0 +1,462 @@
+/*
+ affs.c -- parted support for affs file systems
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#include "amiga.h"
+#include "affs.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+static int
+_affs_probe_root (uint32_t *block, int blocksize) {
+ int i;
+ uint32_t sum;
+
+ if (PED_BE32_TO_CPU (block[0]) != 2) return 0;
+ if (PED_BE32_TO_CPU (block[128*blocksize-1]) != 1) return 0;
+ for (i = 0, sum = 0; i < 128*blocksize; i++)
+ sum += PED_BE32_TO_CPU (block[i]);
+ if (sum) return 0;
+ return 1;
+}
+
+static PedGeometry*
+_generic_affs_probe (PedGeometry* geom, uint32_t kind)
+{
+ uint32_t *block;
+ PedSector root, len, pos;
+ struct PartitionBlock * part;
+ int blocksize = 1, reserved = 2, prealloc = 0;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (geom->dev != NULL, return NULL);
+
+ /* Finds the blocksize, prealloc and reserved values of the partition block */
+ if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate partition block\n"), __func__);
+ goto error_part;
+ }
+ if (amiga_find_part(geom, part) != NULL) {
+ prealloc = PED_BE32_TO_CPU (part->de_PreAlloc);
+ reserved = PED_BE32_TO_CPU (part->de_Reserved);
+ reserved = reserved == 0 ? 1 : reserved;
+ blocksize = PED_BE32_TO_CPU (part->de_SizeBlock)
+ * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128;
+ }
+ ped_free (part);
+
+ /* Test boot block */
+ if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate block\n"), __func__);
+ goto error_block;
+ }
+ if (!ped_device_read (geom->dev, block, geom->start, blocksize)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read boot block %llu\n"), __func__, geom->start);
+ goto error;
+ }
+ if (PED_BE32_TO_CPU (block[0]) != kind) {
+ goto error;
+ }
+
+ /* Find and test the root block */
+ len = geom->length / blocksize - reserved;
+ pos = (len - 1) / 2;
+ root = geom->start + (pos + reserved) * blocksize;
+ printf ("Pralloc = %d, Reserved = %d, blocksize = %d, root block at %llu\n",
+ prealloc, reserved, blocksize, root);
+
+ if (!ped_device_read (geom->dev, block, root, blocksize)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read root block %llu\n"), __func__, root);
+ goto error;
+ }
+ if (_affs_probe_root(block, blocksize) == 1) {
+ ped_free (block);
+ return ped_geometry_duplicate (geom);
+ }
+
+error:
+ ped_free (block);
+error_block:
+error_part:
+ return NULL;
+}
+static PedGeometry*
+_affs0_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5300);
+}
+static PedGeometry*
+_affs1_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5301);
+}
+static PedGeometry*
+_affs2_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5302);
+}
+static PedGeometry*
+_affs3_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5303);
+}
+static PedGeometry*
+_affs4_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5304);
+}
+static PedGeometry*
+_affs5_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5305);
+}
+static PedGeometry*
+_affs6_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5306);
+}
+static PedGeometry*
+_affs7_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x444f5307);
+}
+static PedGeometry*
+_amufs_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754653);
+}
+static PedGeometry*
+_amufs0_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754600);
+}
+static PedGeometry*
+_amufs1_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754601);
+}
+static PedGeometry*
+_amufs2_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754602);
+}
+static PedGeometry*
+_amufs3_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754603);
+}
+static PedGeometry*
+_amufs4_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754604);
+}
+static PedGeometry*
+_amufs5_probe (PedGeometry* geom) {
+ return _generic_affs_probe (geom, 0x6d754605);
+}
+
+static PedFileSystemOps _affs0_ops = {
+ .probe = _affs0_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs1_ops = {
+ .probe = _affs1_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs2_ops = {
+ .probe = _affs2_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs3_ops = {
+ .probe = _affs3_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs4_ops = {
+ .probe = _affs4_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs5_ops = {
+ .probe = _affs5_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs6_ops = {
+ .probe = _affs6_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _affs7_ops = {
+ .probe = _affs7_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs_ops = {
+ .probe = _amufs_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs0_ops = {
+ .probe = _amufs0_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs1_ops = {
+ .probe = _amufs1_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs2_ops = {
+ .probe = _amufs2_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs3_ops = {
+ .probe = _amufs3_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs4_ops = {
+ .probe = _amufs4_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _amufs5_ops = {
+ .probe = _amufs5_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+
+#define AFFS_BLOCK_SIZES ((int[5]){512, 1024, 2048, 4096, 0})
+#define AMUFS_BLOCK_SIZES ((int[2]){512, 0})
+
+
+PedFileSystemType _affs0_type = {
+ .next = NULL,
+ .ops = &_affs0_ops,
+ .name = "affs0",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs1_type = {
+ .next = NULL,
+ .ops = &_affs1_ops,
+ .name = "affs1",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs2_type = {
+ .next = NULL,
+ .ops = &_affs2_ops,
+ .name = "affs2",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs3_type = {
+ .next = NULL,
+ .ops = &_affs3_ops,
+ .name = "affs3",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs4_type = {
+ .next = NULL,
+ .ops = &_affs4_ops,
+ .name = "affs4",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs5_type = {
+ .next = NULL,
+ .ops = &_affs5_ops,
+ .name = "affs5",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs6_type = {
+ .next = NULL,
+ .ops = &_affs6_ops,
+ .name = "affs6",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _affs7_type = {
+ .next = NULL,
+ .ops = &_affs7_ops,
+ .name = "affs7",
+ .block_sizes = AFFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs_type = {
+ .next = NULL,
+ .ops = &_amufs_ops,
+ .name = "amufs",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs0_type = {
+ .next = NULL,
+ .ops = &_amufs0_ops,
+ .name = "amufs0",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs1_type = {
+ .next = NULL,
+ .ops = &_amufs1_ops,
+ .name = "amufs1",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs2_type = {
+ .next = NULL,
+ .ops = &_amufs2_ops,
+ .name = "amufs2",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs3_type = {
+ .next = NULL,
+ .ops = &_amufs3_ops,
+ .name = "amufs3",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs4_type = {
+ .next = NULL,
+ .ops = &_amufs4_ops,
+ .name = "amufs4",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
+PedFileSystemType _amufs5_type = {
+ .next = NULL,
+ .ops = &_amufs5_ops,
+ .name = "amufs5",
+ .block_sizes = AMUFS_BLOCK_SIZES
+};
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/affs.h b/usr/src/lib/libparted/common/libparted/fs/amiga/affs.h
new file mode 100644
index 0000000000..4138061445
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/affs.h
@@ -0,0 +1,19 @@
+
+/*
+ affs.h -- parted suppoer for affs filesystems header files
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.c b/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.c
new file mode 100644
index 0000000000..3983f54226
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.c
@@ -0,0 +1,349 @@
+/*
+ libparted/fs_amiga - amiga file system support.
+ Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Sven Luther <luther@debian.org>
+*/
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#include "amiga.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */
+#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */
+#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */
+#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */
+#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */
+#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */
+#define IDNAME_FREE (uint32_t)0xffffffff
+
+static const char *
+_amiga_block_id (uint32_t id) {
+ switch (id) {
+ case IDNAME_RIGIDDISK :
+ return "RDSK";
+ case IDNAME_BADBLOCK :
+ return "BADB";
+ case IDNAME_PARTITION :
+ return "PART";
+ case IDNAME_FILESYSHEADER :
+ return "FSHD";
+ case IDNAME_LOADSEG :
+ return "LSEG";
+ case IDNAME_BOOT :
+ return "BOOT";
+ case IDNAME_FREE :
+ return "<free>";
+ default :
+ return "<unknown>";
+ }
+}
+
+struct AmigaIds *
+_amiga_add_id (uint32_t id, struct AmigaIds *ids) {
+ struct AmigaIds *newid;
+
+ if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate id list element\n"), __func__);
+ return 0;
+ }
+ newid->ID = id;
+ newid->next = ids;
+ return newid;
+}
+
+void
+_amiga_free_ids (struct AmigaIds *ids) {
+ struct AmigaIds *current, *next;
+
+ for (current = ids; current != NULL; current = next) {
+ next = current->next;
+ ped_free (current);
+ }
+}
+int
+_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) {
+ struct AmigaIds *current;
+
+ for (current = ids; current != NULL; current = current->next) {
+ if (id == current->ID)
+ return 1;
+ }
+ return 0;
+}
+
+#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff)
+
+struct AmigaBlock {
+ uint32_t amiga_ID; /* Identifier 32 bit word */
+ uint32_t amiga_SummedLongss; /* Size of the structure for checksums */
+ int32_t amiga_ChkSum; /* Checksum of the structure */
+};
+#define AMIGA(pos) ((struct AmigaBlock *)(pos))
+
+struct RigidDiskBlock {
+ uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */
+ uint32_t rdb_SummedLongs; /* Size of the structure for checksums */
+ int32_t rdb_ChkSum; /* Checksum of the structure */
+ uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t rdb_BlockBytes; /* Size of disk blocks */
+ uint32_t rdb_Flags; /* RDB Flags */
+ /* block list heads */
+ uint32_t rdb_BadBlockList; /* Bad block list */
+ uint32_t rdb_PartitionList; /* Partition list */
+ uint32_t rdb_FileSysHeaderList; /* File system header list */
+ uint32_t rdb_DriveInit; /* Drive specific init code */
+ uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */
+ uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */
+ /* physical drive characteristics */
+ uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */
+ uint32_t rdb_Sectors; /* Number of sectors of the drive */
+ uint32_t rdb_Heads; /* Number of heads of the drive */
+ uint32_t rdb_Interleave; /* Interleave */
+ uint32_t rdb_Park; /* Head parking cylinder */
+ uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */
+ uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */
+ uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */
+ uint32_t rdb_StepRate; /* Step rate of the drive */
+ uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */
+ /* logical drive characteristics */
+ uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */
+ uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */
+ uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */
+ uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */
+ uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */
+ uint32_t rdb_AutoParkSeconds; /* zero for no auto park */
+ uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */
+ /* (not including replacement bad blocks) */
+ uint32_t rdb_Reserved4;
+ /* drive identification */
+ char rdb_DiskVendor[8];
+ char rdb_DiskProduct[16];
+ char rdb_DiskRevision[4];
+ char rdb_ControllerVendor[8];
+ char rdb_ControllerProduct[16];
+ char rdb_ControllerRevision[4];
+ uint32_t rdb_Reserved5[10];
+};
+
+#define AMIGA_MAX_PARTITIONS 128
+#define RDB_LOCATION_LIMIT 16
+#define RDSK(pos) ((struct RigidDiskBlock *)(pos))
+
+static int
+_amiga_checksum (struct AmigaBlock *blk) {
+ uint32_t *rdb = (uint32_t *) blk;
+ uint32_t sum;
+ int i, end;
+
+ sum = PED_BE32_TO_CPU (rdb[0]);
+ end = PED_BE32_TO_CPU (rdb[1]);
+
+ if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT;
+
+ for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]);
+
+ return sum;
+}
+
+static void
+_amiga_calculate_checksum (struct AmigaBlock *blk) {
+
+ blk->amiga_ChkSum = PED_CPU_TO_BE32(
+ PED_BE32_TO_CPU(blk->amiga_ChkSum) -
+ _amiga_checksum((struct AmigaBlock *) blk));
+ return;
+}
+
+
+static struct AmigaBlock *
+_amiga_read_block (PedDevice *dev, struct AmigaBlock *blk, PedSector block, struct AmigaIds *ids) {
+ if (!ped_device_read (dev, blk, block, 1)) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read block %llu\n"), __func__, block))
+ {
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return NULL;
+ }
+ }
+ if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids))
+ return NULL;
+ if (_amiga_checksum (blk) != 0) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL,
+ _("%s : Bad checksum on block %llu of type %s\n"),
+ __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID))))
+ {
+ case PED_EXCEPTION_CANCEL :
+ return NULL;
+ case PED_EXCEPTION_FIX :
+ _amiga_calculate_checksum(AMIGA(blk));
+ if (!ped_device_write (dev, blk, block, 1)) {
+ switch (ped_exception_throw(PED_EXCEPTION_FATAL,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't write block %d\n"), __func__, block))
+ {
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return NULL;
+ }
+ }
+ case PED_EXCEPTION_IGNORE :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return blk;
+ }
+ }
+ return blk;
+}
+
+static uint32_t
+_amiga_find_rdb (PedDevice *dev, struct RigidDiskBlock *rdb) {
+ int i;
+ struct AmigaIds *ids;
+
+ ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL);
+
+ for (i = 0; i<RDB_LOCATION_LIMIT; i++) {
+ if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) {
+ continue;
+ }
+ if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) {
+ _amiga_free_ids (ids);
+ return i;
+ }
+ }
+ _amiga_free_ids (ids);
+ return AMIGA_RDB_NOT_FOUND;
+}
+
+static int
+_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max)
+{
+ uint32_t i;
+
+ for (i = 0; i < max; i++)
+ if (block == blocklist[i]) {
+ /* We are looping, let's stop. */
+ return 1;
+ }
+ blocklist[max] = block;
+ return 0;
+}
+
+/* We have already allocated a rdb, we are now reading it from the disk */
+struct PartitionBlock *
+amiga_find_part (PedGeometry *geom, struct PartitionBlock *part)
+{
+ struct RigidDiskBlock *rdb;
+ uint32_t partblock;
+ uint32_t partlist[AMIGA_MAX_PARTITIONS];
+ int i;
+
+ PED_ASSERT(geom!= NULL, return NULL);
+ PED_ASSERT(geom->dev!= NULL, return NULL);
+
+ if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate disk_specific rdb block\n"), __func__))
+ {
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return NULL;
+ }
+ }
+ if (_amiga_find_rdb (geom->dev, rdb) == AMIGA_RDB_NOT_FOUND) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Didn't find rdb block, should never happen\n"), __func__))
+ {
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ ped_free(rdb);
+ return NULL;
+ }
+ }
+
+ /* We initialize the hardblock free list to detect loops */
+ for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = IDNAME_FREE;
+
+ for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList);
+ i < AMIGA_MAX_PARTITIONS && partblock != IDNAME_FREE;
+ i++, partblock = PED_BE32_TO_CPU(part->pb_Next))
+ {
+ PedSector start, end;
+ PedSector cylblocks;
+
+ /* Let's look for loops in the partition table */
+ if (_amiga_loop_check(partblock, partlist, i)) {
+ ped_free (rdb);
+ return NULL;
+ }
+ /* Let's read a partition block to get its geometry*/
+ if (!ped_device_read (geom->dev, part, (PedSector)partblock, 1)) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to read partition block %llu\n"),
+ __func__, (PedSector)partblock))
+ {
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ ped_free(rdb);
+ return NULL;
+ }
+ }
+
+ /* Current block is not a Partition Block */
+ if (part->pb_ID != IDNAME_PARTITION) {
+ ped_free (rdb);
+ return NULL;
+ }
+
+ /* Calculate the geometry of the partition */
+ cylblocks = ((PedSector) PED_BE32_TO_CPU (part->de_Surfaces)) *
+ ((PedSector) PED_BE32_TO_CPU (part->de_BlocksPerTrack));
+ start = ((PedSector) PED_BE32_TO_CPU (part->de_LowCyl)) * cylblocks;
+ end = ((((PedSector) PED_BE32_TO_CPU (part->de_HighCyl))+1) * (cylblocks))-1;
+
+ /* And check if it is the one we are searching for */
+ if (start == geom->start && end == geom->end) {
+ ped_free (rdb);
+ return part;
+ }
+ }
+
+ ped_free (rdb);
+ return NULL;
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.h b/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.h
new file mode 100644
index 0000000000..da46fdc689
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/amiga.h
@@ -0,0 +1,70 @@
+/*
+ util.h -- amiga partition table headers.
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+struct PartitionBlock {
+ uint32_t pb_ID; /* Identifier 32 bit word : 'PART' */
+ uint32_t pb_SummedLongs; /* Size of the structure for checksums */
+ int32_t pb_ChkSum; /* Checksum of the structure */
+ uint32_t pb_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t pb_Next; /* Block number of the next PartitionBlock */
+ uint32_t pb_Flags; /* Part Flags (NOMOUNT and BOOTABLE) */
+ uint32_t pb_Reserved1[2];
+ uint32_t pb_DevFlags; /* Preferred flags for OpenDevice */
+ uint8_t pb_DriveName[32]; /* Preferred DOS device name: BSTR form */
+ uint32_t pb_Reserved2[15];
+ uint32_t de_TableSize; /* Size of Environment vector */
+ uint32_t de_SizeBlock; /* Size of the blocks in 32 bit words, usually 128 */
+ uint32_t de_SecOrg; /* Not used; must be 0 */
+ uint32_t de_Surfaces; /* Number of heads (surfaces) */
+ uint32_t de_SectorPerBlock; /* Disk sectors per block, used with SizeBlock, usually 1 */
+ uint32_t de_BlocksPerTrack; /* Blocks per track. drive specific */
+ uint32_t de_Reserved; /* DOS reserved blocks at start of partition. */
+ uint32_t de_PreAlloc; /* DOS reserved blocks at end of partition */
+ uint32_t de_Interleave; /* Not used, usually 0 */
+ uint32_t de_LowCyl; /* First cylinder of the partition */
+ uint32_t de_HighCyl; /* Last cylinder of the partition */
+ uint32_t de_NumBuffers; /* Initial # DOS of buffers. */
+ uint32_t de_BufMemType; /* Type of mem to allocate for buffers */
+ uint32_t de_MaxTransfer; /* Max number of bytes to transfer at a time */
+ uint32_t de_Mask; /* Address Mask to block out certain memory */
+ int32_t de_BootPri; /* Boot priority for autoboot */
+ uint32_t de_DosType; /* Dostype of the file system */
+ uint32_t de_Baud; /* Baud rate for serial handler */
+ uint32_t de_Control; /* Control word for handler/filesystem */
+ uint32_t de_BootBlocks; /* Number of blocks containing boot code */
+ uint32_t pb_EReserved[12];
+};
+
+#define PART(pos) ((struct PartitionBlock *)(pos))
+
+#define PBFB_BOOTABLE 0 /* this partition is intended to be bootable */
+#define PBFF_BOOTABLE 1L /* (expected directories and files exist) */
+#define PBFB_NOMOUNT 1 /* do not mount this partition (e.g. manually */
+#define PBFF_NOMOUNT 2L /* mounted, but space reserved here) */
+
+struct PartitionBlock * amiga_find_part (PedGeometry *geom, struct PartitionBlock *part);
+
+struct AmigaIds {
+ uint32_t ID;
+ struct AmigaIds *next;
+};
+
+struct AmigaIds * _amiga_add_id (uint32_t id, struct AmigaIds *ids);
+void _amiga_free_ids (struct AmigaIds *ids);
+int _amiga_id_in_list (uint32_t id, struct AmigaIds *ids);
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.c b/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.c
new file mode 100644
index 0000000000..912b72bc8b
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.c
@@ -0,0 +1,150 @@
+/*
+ apfs.c -- parted support for apfs file systems
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#include "amiga.h"
+#include "apfs.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+static int
+_apfs_probe_root (uint32_t *block, uint32_t blocksize, uint32_t kind) {
+ if (PED_BE32_TO_CPU (block[0]) != kind) return 0;
+ return 1;
+}
+
+static PedGeometry*
+_generic_apfs_probe (PedGeometry* geom, uint32_t kind)
+{
+ uint32_t *block;
+ PedSector root;
+ struct PartitionBlock * part;
+ uint32_t blocksize = 1, reserved = 2, prealloc = 0;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (geom->dev != NULL, return NULL);
+
+ /* Finds the blocksize, prealloc and reserved values of the partition block */
+ if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate partition block\n"), __func__);
+ goto error_part;
+ }
+ if (amiga_find_part(geom, part) != NULL) {
+ prealloc = PED_BE32_TO_CPU (part->de_PreAlloc);
+ reserved = PED_BE32_TO_CPU (part->de_Reserved);
+ blocksize = PED_BE32_TO_CPU (part->de_SizeBlock)
+ * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128;
+ }
+ ped_free (part);
+
+ /* Test boot block */
+ if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate block\n"), __func__);
+ goto error_block;
+ }
+ if (!ped_device_read (geom->dev, block, geom->start, blocksize)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read boot block %llu\n"), __func__, geom->start);
+ goto error;
+ }
+ if (PED_BE32_TO_CPU (block[0]) != kind) {
+ goto error;
+ }
+
+ /* Find and test the root block */
+ root = geom->start+reserved*blocksize;
+ if (!ped_device_read (geom->dev, block, root, blocksize)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read root block %llu\n"), __func__, root);
+ goto error;
+ }
+ if (_apfs_probe_root(block, blocksize, kind) == 1) {
+ ped_free(block);
+ return ped_geometry_duplicate (geom);
+ }
+
+error:
+ ped_free (block);
+error_block:
+error_part:
+ return NULL;
+}
+
+static PedGeometry*
+_apfs1_probe (PedGeometry* geom) {
+ return _generic_apfs_probe (geom, 0x50463101);
+}
+
+static PedGeometry*
+_apfs2_probe (PedGeometry* geom) {
+ return _generic_apfs_probe (geom, 0x50463102);
+}
+
+static PedFileSystemOps _apfs1_ops = {
+ .probe = _apfs1_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+static PedFileSystemOps _apfs2_ops = {
+ .probe = _apfs2_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+
+#define APFS_BLOCK_SIZES ((int[2]){512, 0})
+
+PedFileSystemType _apfs1_type = {
+ .next = NULL,
+ .ops = &_apfs1_ops,
+ .name = "apfs1",
+ .block_sizes = APFS_BLOCK_SIZES
+};
+PedFileSystemType _apfs2_type = {
+ .next = NULL,
+ .ops = &_apfs2_ops,
+ .name = "apfs2",
+ .block_sizes = APFS_BLOCK_SIZES
+};
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.h b/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.h
new file mode 100644
index 0000000000..7dadb91d2c
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/apfs.h
@@ -0,0 +1,17 @@
+/*
+ apfs.h -- parted support for apfs file systems header files
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.c b/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.c
new file mode 100644
index 0000000000..774a2c3c83
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.c
@@ -0,0 +1,142 @@
+/*
+ asfs.c -- parted asfs filesystem support
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#include "amiga.h"
+#include "asfs.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+static int
+_asfs_probe_root (PedGeometry *geom, uint32_t *block, int blocksize, PedSector root) {
+ int i, sum;
+ PedSector start, end;
+
+ if (PED_BE32_TO_CPU (block[0]) != 0x53465300) return 0;
+ for (i = 0, sum = 1; i < 128*blocksize; i++) sum += PED_BE32_TO_CPU (block[i]);
+ if (sum != 0) return 0;
+ if (PED_BE32_TO_CPU (block[2]) * blocksize + geom->start != root) {
+ return 0;
+ }
+ start = ((((PedSector) PED_BE32_TO_CPU (block[8])) << 32)
+ + (PedSector) PED_BE32_TO_CPU (block[9])) / 512;
+ end = (((((PedSector) PED_BE32_TO_CPU (block[10])) << 32)
+ + (PedSector) PED_BE32_TO_CPU (block[11])) / 512) - 1;
+ if (start != geom->start || end != geom->end) return 0;
+ return 1;
+}
+
+static PedGeometry*
+_asfs_probe (PedGeometry* geom)
+{
+ uint32_t *block;
+ struct PartitionBlock * part;
+ int blocksize = 1, reserved = 1, prealloc = 1;
+ PedSector root;
+ int found = 0;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (geom->dev != NULL, return NULL);
+
+ /* Finds the blocksize, prealloc and reserved values of the partition block */
+ if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate partition block\n"), __func__);
+ goto error_part;
+ }
+ if (amiga_find_part(geom, part) != NULL) {
+ prealloc = PED_BE32_TO_CPU (part->de_PreAlloc) == 0 ?
+ 1 : PED_BE32_TO_CPU (part->de_PreAlloc);
+ reserved = PED_BE32_TO_CPU (part->de_Reserved) == 0 ?
+ 1 : PED_BE32_TO_CPU (part->de_Reserved);
+ blocksize = PED_BE32_TO_CPU (part->de_SizeBlock)
+ * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128;
+ }
+ ped_free (part);
+
+ /* Test boot block */
+ if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Failed to allocate block\n"), __func__);
+ goto error_block;
+ }
+ root = geom->start;
+ if (!ped_device_read (geom->dev, block, root, blocksize)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read root block %llu\n"), __func__, root);
+ goto error;
+ }
+ if (PED_BE32_TO_CPU (block[0]) != 0x53465300) {
+ goto error;
+ }
+
+ /* Find and test the root blocks */
+ if (_asfs_probe_root(geom, block, blocksize, root)) {
+ found++;
+ }
+ root = geom->end - blocksize - (geom->length % blocksize) + 1;
+ if (!ped_device_read (geom->dev, block, root, 1)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Couldn't read root block %llu\n"), __func__, root);
+ goto error;
+ }
+ if (_asfs_probe_root(geom, block, blocksize, root)) {
+ found++;
+ }
+ if (found != 0) {
+ ped_free (block);
+ return ped_geometry_duplicate (geom);
+ }
+
+error:
+ ped_free (block);
+error_block:
+error_part:
+ return NULL;
+}
+
+static PedFileSystemOps _asfs_ops = {
+ .probe = _asfs_probe,
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+};
+
+PedFileSystemType _asfs_type = {
+ .next = NULL,
+ .ops = &_asfs_ops,
+ .name = "asfs",
+ .block_sizes = ((int[2]){512, 0})
+};
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.h b/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.h
new file mode 100644
index 0000000000..141677e1be
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/asfs.h
@@ -0,0 +1,17 @@
+/*
+ asfs.h -- parted asfs filesystem support header files
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
diff --git a/usr/src/lib/libparted/common/libparted/fs/amiga/interface.c b/usr/src/lib/libparted/common/libparted/fs/amiga/interface.c
new file mode 100644
index 0000000000..4bbe5a4d25
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/amiga/interface.c
@@ -0,0 +1,87 @@
+/*
+ interface.c -- parted support amiga file systems
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+extern PedFileSystemType _affs0_type;
+extern PedFileSystemType _affs1_type;
+extern PedFileSystemType _affs2_type;
+extern PedFileSystemType _affs3_type;
+extern PedFileSystemType _affs4_type;
+extern PedFileSystemType _affs5_type;
+extern PedFileSystemType _affs6_type;
+extern PedFileSystemType _affs7_type;
+extern PedFileSystemType _amufs_type;
+extern PedFileSystemType _amufs0_type;
+extern PedFileSystemType _amufs1_type;
+extern PedFileSystemType _amufs2_type;
+extern PedFileSystemType _amufs3_type;
+extern PedFileSystemType _amufs4_type;
+extern PedFileSystemType _amufs5_type;
+extern PedFileSystemType _asfs_type;
+extern PedFileSystemType _apfs1_type;
+extern PedFileSystemType _apfs2_type;
+
+void ped_file_system_amiga_init ()
+{
+ ped_file_system_type_register (&_affs0_type);
+ ped_file_system_type_register (&_affs1_type);
+ ped_file_system_type_register (&_affs2_type);
+ ped_file_system_type_register (&_affs3_type);
+ ped_file_system_type_register (&_affs4_type);
+ ped_file_system_type_register (&_affs5_type);
+ ped_file_system_type_register (&_affs6_type);
+ ped_file_system_type_register (&_affs7_type);
+ ped_file_system_type_register (&_amufs_type);
+ ped_file_system_type_register (&_amufs0_type);
+ ped_file_system_type_register (&_amufs1_type);
+ ped_file_system_type_register (&_amufs2_type);
+ ped_file_system_type_register (&_amufs3_type);
+ ped_file_system_type_register (&_amufs4_type);
+ ped_file_system_type_register (&_amufs5_type);
+ ped_file_system_type_register (&_asfs_type);
+ ped_file_system_type_register (&_apfs1_type);
+ ped_file_system_type_register (&_apfs2_type);
+}
+
+void ped_file_system_amiga_done ()
+{
+ ped_file_system_type_unregister (&_affs0_type);
+ ped_file_system_type_unregister (&_affs1_type);
+ ped_file_system_type_unregister (&_affs2_type);
+ ped_file_system_type_unregister (&_affs3_type);
+ ped_file_system_type_unregister (&_affs4_type);
+ ped_file_system_type_unregister (&_affs5_type);
+ ped_file_system_type_unregister (&_affs6_type);
+ ped_file_system_type_unregister (&_affs7_type);
+ ped_file_system_type_unregister (&_amufs_type);
+ ped_file_system_type_unregister (&_amufs0_type);
+ ped_file_system_type_unregister (&_amufs1_type);
+ ped_file_system_type_unregister (&_amufs2_type);
+ ped_file_system_type_unregister (&_amufs3_type);
+ ped_file_system_type_unregister (&_amufs4_type);
+ ped_file_system_type_unregister (&_amufs5_type);
+ ped_file_system_type_unregister (&_asfs_type);
+ ped_file_system_type_unregister (&_apfs1_type);
+ ped_file_system_type_unregister (&_apfs2_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.c
new file mode 100644
index 0000000000..618fca9c4a
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.c
@@ -0,0 +1,792 @@
+/*
+ ext2.c -- generic ext2 stuff
+ Copyright (C) 1998, 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <uuid/uuid.h>
+#include "ext2.h"
+
+/* ext2 stuff ****************************************************************/
+
+unsigned char _bitmap[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+int ext2_copy_block(struct ext2_fs *fs, blk_t from, blk_t to)
+{
+ unsigned char* buf = ped_malloc (fs->blocksize);
+
+ if (!ext2_bcache_flush(fs, from)) return 0;
+ if (!ext2_bcache_flush(fs, to)) return 0;
+
+ if (!ext2_read_blocks(fs, buf, from, 1)) return 0;
+ if (!ext2_write_blocks(fs, buf, to, 1)) return 0;
+
+ return 1;
+}
+
+int ext2_get_block_state(struct ext2_fs *fs, blk_t block)
+{
+ struct ext2_buffer_head *bh;
+ int group;
+ int offset;
+ int state;
+
+ block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
+ state = bh->data[offset>>3] & _bitmap[offset&7];
+ ext2_brelse(bh, 0);
+
+ return state;
+}
+
+blk_t ext2_find_free_block(struct ext2_fs *fs)
+{
+ int i;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i]))
+ {
+ blk_t j;
+ blk_t offset;
+
+ offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ for (j=fs->adminblocks;
+ j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ j++)
+ if (ext2_is_data_block(fs, offset + j) &&
+ !ext2_get_block_state(fs, offset + j))
+ return offset + j;
+
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Inconsistent group descriptors!"));
+ }
+
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system full!"));
+ return 0;
+}
+
+ino_t ext2_find_free_inode(struct ext2_fs *fs)
+{
+ int i;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i]))
+ {
+ ino_t j;
+ ino_t offset;
+
+ offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
+ for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
+ if (!ext2_get_inode_state(fs, offset + j))
+ return offset + j;
+
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Inconsistent group descriptors!"));
+ }
+
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system full!"));
+ return 0;
+}
+
+int ext2_move_blocks(struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest)
+{
+ unsigned char *buf;
+ blk_t i;
+
+ ped_exception_fetch_all();
+ if ((buf = ped_malloc(num << fs->logsize)) != NULL)
+ {
+ ped_exception_leave_all();
+
+ if (!ext2_bcache_flush_range(fs, src, num)) return 0;
+ if (!ext2_bcache_flush_range(fs, dest, num)) return 0;
+
+ if (!ext2_read_blocks(fs, buf, src, num)) return 0;
+ if (!ext2_write_blocks(fs, buf, dest, num)) return 0;
+
+ ped_free(buf);
+ return 1;
+ }
+ ped_exception_catch();
+ ped_exception_leave_all();
+
+ if (src > dest)
+ {
+ for (i=0;i<num;i++)
+ if (!ext2_copy_block(fs, src+i, dest+i))
+ return 0;
+ }
+ else
+ {
+ for (i=num;i>0;i--)
+ if (!ext2_copy_block(fs, src+i, dest+i))
+ return 0;
+ }
+ return 1;
+}
+
+int ext2_read_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
+{
+ return fs->devhandle->ops->read(fs->devhandle->cookie, ptr, block, num);
+}
+
+int ext2_set_block_state(struct ext2_fs *fs, blk_t block, int state, int updatemetadata)
+{
+ struct ext2_buffer_head *bh;
+ int group;
+ int offset;
+
+ block -= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ group = block / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ offset = block % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
+ bh->dirty = 1;
+ if (state)
+ bh->data[offset>>3] |= _bitmap[offset&7];
+ else
+ bh->data[offset>>3] &= ~_bitmap[offset&7];
+ ext2_brelse(bh, 0);
+
+ if (updatemetadata)
+ {
+ int diff;
+
+ diff = state ? -1 : 1;
+
+ fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16
+ (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) + diff);
+ fs->sb.s_free_blocks_count = PED_CPU_TO_LE32
+ (EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + diff);
+ fs->metadirty |= EXT2_META_SB | EXT2_META_GD;
+ }
+ return 1;
+}
+
+int ext2_write_blocks(struct ext2_fs *fs, void *ptr, blk_t block, blk_t num)
+{
+ return fs->devhandle->ops->write(fs->devhandle->cookie, ptr, block, num);
+}
+
+int ext2_zero_blocks(struct ext2_fs *fs, blk_t block, blk_t num)
+{
+ unsigned char *buf;
+ blk_t i;
+
+ ped_exception_fetch_all();
+ buf = ped_malloc (num << fs->logsize);
+ if (buf)
+ {
+ ped_exception_leave_all();
+
+ memset(buf, 0, num << fs->logsize);
+ if (!ext2_bcache_flush_range(fs, block, num))
+ goto error_free_buf;
+ if (!ext2_write_blocks(fs, buf, block, num))
+ goto error_free_buf;
+ ped_free(buf);
+ return 1;
+ }
+ ped_exception_catch();
+
+ buf = ped_malloc (fs->blocksize);
+ if (buf)
+ {
+ ped_exception_leave_all();
+
+ memset(buf, 0, fs->blocksize);
+
+ for (i=0;i<num;i++)
+ {
+ if (!ext2_bcache_flush(fs, block+i))
+ goto error_free_buf;
+ if (!ext2_write_blocks(fs, buf, block+i, 1))
+ goto error_free_buf;
+ }
+
+ ped_free(buf);
+ return 1;
+ }
+ ped_exception_catch();
+ ped_exception_leave_all();
+
+ for (i=0;i<num;i++)
+ {
+ struct ext2_buffer_head *bh;
+
+ bh = ext2_bcreate(fs, block+i);
+ if (!bh)
+ goto error;
+ bh->dirty = 1;
+ if (!ext2_brelse(bh, 1))
+ goto error;
+ }
+ return 1;
+
+error_free_buf:
+ ped_free(buf);
+error:
+ return 0;
+}
+
+off_t ext2_get_inode_offset(struct ext2_fs *fs, ino_t inode, blk_t *block)
+{
+ int group;
+ int offset;
+
+ inode--;
+
+ group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+ offset = (inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb))
+ * sizeof(struct ext2_inode);
+
+ *block = EXT2_GROUP_INODE_TABLE(fs->gd[group])
+ + (offset >> fs->logsize);
+
+ return offset & (fs->blocksize - 1);
+}
+
+int ext2_get_inode_state(struct ext2_fs *fs, ino_t inode)
+{
+ struct ext2_buffer_head *bh;
+ int group;
+ int offset;
+ int ret;
+
+ inode--;
+ group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+ offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
+ ret = bh->data[offset>>3] & _bitmap[offset&7];
+ ext2_brelse(bh, 0);
+
+ return ret;
+}
+
+int ext2_read_inode(struct ext2_fs *fs, ino_t inode, struct ext2_inode *data)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blk;
+ off_t off;
+
+ off = ext2_get_inode_offset(fs, inode, &blk);
+
+ bh = ext2_bread(fs, blk);
+ if (!bh)
+ return 0;
+
+ memcpy(data, bh->data + off, sizeof(struct ext2_inode));
+ ext2_brelse(bh, 0);
+ return 1;
+}
+
+int ext2_set_inode_state(struct ext2_fs *fs, ino_t inode, int state, int updatemetadata)
+{
+ struct ext2_buffer_head *bh;
+ int group;
+ int offset;
+
+ inode--;
+ group = inode / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+ offset = inode % EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
+ if (!bh)
+ return 0;
+ bh->dirty = 1;
+ if (state)
+ bh->data[offset>>3] |= _bitmap[offset&7];
+ else
+ bh->data[offset>>3] &= ~_bitmap[offset&7];
+ ext2_brelse(bh, 0);
+
+ if (updatemetadata)
+ {
+ int diff;
+
+ diff = state ? -1 : 1;
+
+ fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16
+ (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) + diff);
+ fs->sb.s_free_inodes_count = PED_CPU_TO_LE32
+ (EXT2_SUPER_FREE_INODES_COUNT(fs->sb) + diff);
+ fs->metadirty = EXT2_META_SB | EXT2_META_GD;
+ }
+ return 1;
+}
+
+static void
+_inode_update_size(struct ext2_fs *fs, struct ext2_inode *inode, int delta)
+{
+ int i512perblock = 1 << (fs->logsize - 9);
+ uint64_t size;
+
+ /* i_blocks is in 512 byte blocks */
+ inode->i_blocks = PED_CPU_TO_LE32(EXT2_INODE_BLOCKS(*inode)
+ + delta * i512perblock);
+ size = EXT2_INODE_SIZE(*inode) + delta * fs->blocksize;
+ inode->i_size = PED_CPU_TO_LE32(size % (1LL << 32));
+ inode->i_size_high = PED_CPU_TO_LE32(size / (1LL << 32));
+ inode->i_mtime = PED_CPU_TO_LE32(time(NULL));
+}
+
+int ext2_do_inode(struct ext2_fs *fs, struct ext2_inode *inode, blk_t block,
+ int action)
+{
+ struct ext2_buffer_head *bh;
+ uint32_t *udata;
+ blk_t count = 0;
+ int i;
+ int u32perblock = fs->blocksize >> 2;
+ int i512perblock = 1 << (fs->logsize - 9);
+
+ if (block == 0 || EXT2_INODE_MODE(*inode) == 0)
+ return -1;
+
+ if (fs->opt_debug)
+ switch (action)
+ {
+ case EXT2_ACTION_ADD:
+ fprintf(stderr,"adding 0x%04x to inode\n",
+ block);
+ break;
+ case EXT2_ACTION_DELETE:
+ fprintf(stderr,"deleting 0x%04x from inode\n",
+ block);
+ break;
+ case EXT2_ACTION_FIND:
+ fprintf(stderr,"finding 0x%04x in inode\n",
+ block);
+ break;
+ }
+
+ /* Direct blocks for first 12 blocks */
+ for (i = 0; i < EXT2_NDIR_BLOCKS; i++)
+ {
+ if (action == EXT2_ACTION_ADD && !EXT2_INODE_BLOCK(*inode, i))
+ {
+ inode->i_block[i] = PED_CPU_TO_LE32(block);
+ _inode_update_size (fs, inode, 1);
+ ext2_set_block_state(fs, block, 1, 1);
+ return i;
+ }
+ if (EXT2_INODE_BLOCK(*inode, i) == block)
+ {
+ if (action == EXT2_ACTION_DELETE)
+ {
+ inode->i_block[i] = 0;
+ _inode_update_size (fs, inode, -1);
+ ext2_set_block_state(fs, block, 0, 1);
+ }
+ return i;
+ }
+ if (EXT2_INODE_BLOCK(*inode, i))
+ count += i512perblock;
+ }
+
+ count += EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ? i512perblock : 0;
+ count += EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ? i512perblock : 0;
+ count += EXT2_INODE_BLOCK(*inode, EXT2_TIND_BLOCK) ? i512perblock : 0;
+
+ if (!EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK) ||
+ (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
+ return -1;
+
+ bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_IND_BLOCK));
+ udata = (uint32_t *)bh->data;
+
+ /* Indirect blocks for next 256/512/1024 blocks (for 1k/2k/4k blocks) */
+ for (i = 0; i < u32perblock; i++) {
+ if (action == EXT2_ACTION_ADD && !udata[i]) {
+ bh->dirty = 1;
+ udata[i] = PED_CPU_TO_LE32(block);
+ _inode_update_size (fs, inode, 1);
+ ext2_set_block_state(fs, block, 1, 1);
+ ext2_brelse(bh, 0);
+ return EXT2_NDIR_BLOCKS + i;
+ }
+ if (PED_LE32_TO_CPU(udata[i]) == block) {
+ if (action == EXT2_ACTION_DELETE) {
+ bh->dirty = 1;
+ udata[i] = 0;
+ _inode_update_size (fs, inode, -1);
+ ext2_set_block_state(fs, block, 0, 1);
+ }
+ ext2_brelse(bh, 0);
+ return EXT2_NDIR_BLOCKS + i;
+ }
+ if (udata[i])
+ {
+ count += i512perblock;
+ if (count >= EXT2_INODE_BLOCKS(*inode) &&
+ action != EXT2_ACTION_ADD)
+ return -1;
+ }
+ }
+
+ ext2_brelse(bh, 0);
+
+ if (!EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK) ||
+ (count >= EXT2_INODE_BLOCKS(*inode) && action != EXT2_ACTION_ADD))
+ return -1;
+ bh = ext2_bread(fs, EXT2_INODE_BLOCK(*inode, EXT2_DIND_BLOCK));
+ udata = (uint32_t *)bh->data;
+
+ /* Double indirect blocks for next 2^16/2^18/2^20 1k/2k/4k blocks */
+ for (i = 0; i < u32perblock; i++) {
+ struct ext2_buffer_head *bh2;
+ uint32_t *udata2;
+ int j;
+
+ if (!udata[i]) {
+ ext2_brelse(bh, 0);
+ return -1;
+ }
+ bh2 = ext2_bread(fs, PED_LE32_TO_CPU(udata[i]));
+ udata2 = (uint32_t *)bh2->data;
+ count += i512perblock;
+
+ for (j = 0; j < u32perblock; j++) {
+ if (action == EXT2_ACTION_ADD && !udata2[j]) {
+ bh2->dirty = 1;
+ udata2[j] = PED_CPU_TO_LE32(block);
+ _inode_update_size (fs, inode, 1);
+ ext2_set_block_state(fs, block, 1, 1);
+ ext2_brelse(bh, 0);
+ ext2_brelse(bh2, 0);
+ return EXT2_NDIR_BLOCKS + i * u32perblock + j;
+ }
+ if (PED_LE32_TO_CPU(udata2[j]) == block) {
+ if (action == EXT2_ACTION_DELETE) {
+ bh2->dirty = 1;
+ udata2[j] = 0;
+ _inode_update_size (fs, inode, -1);
+ ext2_set_block_state(fs, block, 0, 1);
+ }
+ ext2_brelse(bh, 0);
+ ext2_brelse(bh2, 0);
+ return EXT2_NDIR_BLOCKS + i * u32perblock + j;
+ }
+ if (udata2[j])
+ {
+ count += i512perblock;
+ if (count >= EXT2_INODE_BLOCKS(*inode) &&
+ action != EXT2_ACTION_ADD)
+ return -1;
+ }
+ }
+ ext2_brelse(bh2, 0);
+ }
+ ext2_brelse(bh, 0);
+
+ /* FIXME: we should check for triple-indirect blocks here, but it
+ * would be nice to have a better routine to traverse blocks, and
+ * file systems that need triple-indirect blocks for the resize
+ * inode are too big to worry about yet.
+ */
+
+ return -1;
+}
+
+int ext2_write_inode(struct ext2_fs *fs, ino_t inode, const struct ext2_inode *data)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blk;
+ off_t off;
+
+ off = ext2_get_inode_offset(fs, inode, &blk);
+
+ bh = ext2_bread(fs, blk);
+ if (!bh)
+ return 0;
+ bh->dirty = 1;
+ memcpy(bh->data + off, data, sizeof(struct ext2_inode));
+ ext2_brelse(bh, 0);
+
+ return 1;
+}
+
+int ext2_zero_inode(struct ext2_fs *fs, ino_t inode)
+{
+ struct ext2_inode buf;
+
+ memset(&buf, 0, sizeof(struct ext2_inode));
+ return ext2_write_inode(fs, inode, &buf);
+}
+
+
+
+
+
+/* check whether y is root of x
+ * (formula grabbed from linux ext2 kernel source) */
+static int is_root(int x, int y)
+{
+ if (!x)
+ return 1;
+
+ while (1)
+ {
+ if (x == 1)
+ return 1;
+
+ if (x % y)
+ return 0;
+
+ x /= y;
+ }
+}
+
+/* check whether group contains a superblock copy on file systems
+ * where not all groups have one (sparse superblock feature) */
+int ext2_is_group_sparse(struct ext2_fs *fs, int group)
+{
+ if (!fs->sparse)
+ return 1;
+
+ if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7))
+ return 1;
+
+ return 0;
+}
+
+void ext2_close(struct ext2_fs *fs)
+{
+ ext2_commit_metadata(fs, EXT2_META_PRIMARY | EXT2_META_BACKUP);
+ ext2_sync(fs);
+
+ ext2_bcache_deinit(fs);
+
+ fs->devhandle->ops->close(fs->devhandle->cookie);
+
+ ped_free(fs->gd);
+ ped_free(fs);
+}
+
+int ext2_commit_metadata(struct ext2_fs *fs, int copies)
+{
+ int i;
+ int num;
+ int wmeta = fs->metadirty & copies;
+ unsigned char* sb = ped_malloc(fs->blocksize);
+ struct ext2_super_block *sb_for_io;
+ int sb_block;
+
+ /* See if there is even anything to write... */
+ if (wmeta == EXT2_META_CLEAN)
+ return 1;
+
+ fs->sb.s_r_blocks_count = PED_CPU_TO_LE32 (
+ fs->r_frac * (loff_t)EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ / 100);
+
+ if (!ext2_read_blocks (fs, sb, 0, 1))
+ return 0;
+
+ if (EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)) {
+ memcpy(sb, &fs->sb, 1024);
+ sb_for_io = (struct ext2_super_block *) sb;
+ } else {
+ memcpy(sb+1024, &fs->sb, 1024);
+ sb_for_io = (struct ext2_super_block *) (sb + 1024);
+ }
+
+ num = copies & EXT2_META_BACKUP ? fs->numgroups : 1;
+
+ for (i = 0, sb_block = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb); i < num;
+ i++, sb_block += EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ {
+
+ if (!ext2_is_group_sparse(fs, i))
+ continue;
+
+ if (fs->dynamic_version)
+ sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i);
+
+ if ((i == 0 && wmeta & EXT2_META_PRIMARY_SB) ||
+ (i != 0 && wmeta & EXT2_META_SB))
+ {
+ if (!ext2_bcache_flush_range(fs, sb_block, 1))
+ return 0;
+ if (!ext2_write_blocks(fs, sb, sb_block, 1))
+ return 0;
+ }
+ if ((i == 0 && wmeta & EXT2_META_PRIMARY_GD) ||
+ (i != 0 && wmeta & EXT2_META_GD))
+ {
+ if (!ext2_bcache_flush_range(fs, sb_block + 1,
+ fs->gdblocks))
+ return 0;
+ if (!ext2_write_blocks(fs, fs->gd, sb_block + 1,
+ fs->gdblocks))
+ return 0;
+ }
+ }
+
+ sb_for_io->s_block_group_nr = 0;
+
+ /* Clear the flags of the components we just finished writing. */
+ fs->metadirty &= ~copies;
+
+ return 1;
+}
+
+int ext2_sync(struct ext2_fs *fs)
+{
+ if (!ext2_commit_metadata(fs, EXT2_META_PRIMARY)) return 0;
+ if (!ext2_bcache_sync(fs)) return 0;
+ if (!fs->devhandle->ops->sync(fs->devhandle->cookie)) return 0;
+ return 1;
+}
+
+struct ext2_fs *ext2_open(struct ext2_dev_handle *handle, int state)
+{
+ struct ext2_fs *fs;
+
+ if ((fs = (struct ext2_fs *) ped_malloc(sizeof(struct ext2_fs)))
+ == NULL)
+ goto error;
+
+ handle->ops->set_blocksize(handle->cookie, 10);
+
+ if (!handle->ops->read(handle->cookie, &fs->sb, 1, 1)
+ || EXT2_SUPER_MAGIC(fs->sb) != EXT2_SUPER_MAGIC_CONST)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Invalid superblock. Are you sure this is an ext2 "
+ "file system?"));
+ goto error_free_fs;
+ }
+
+
+ fs->opt_debug = 1;
+ fs->opt_safe = 1;
+ fs->opt_verbose = 0;
+
+ if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS & ~(state & EXT2_ERROR_FS))
+ {
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL,
+ _("File system has errors! You should run e2fsck."))
+ == PED_EXCEPTION_CANCEL)
+ goto error_free_fs;
+ }
+
+ if (!((EXT2_SUPER_STATE(fs->sb) | state) & EXT2_VALID_FS)
+ || (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
+ & EXT3_FEATURE_INCOMPAT_RECOVER))
+ {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
+ _("File system was not cleanly unmounted! "
+ "You should run e2fsck. Modifying an unclean "
+ "file system could cause severe corruption."))
+ != PED_EXCEPTION_IGNORE)
+ goto error_free_fs;
+ }
+
+ fs->dynamic_version = EXT2_SUPER_REV_LEVEL (fs->sb) > 0;
+
+ if ((EXT2_SUPER_FEATURE_COMPAT(fs->sb)
+ & ~(EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+ EXT2_FEATURE_COMPAT_HAS_DIR_INDEX)) ||
+ (EXT2_SUPER_FEATURE_INCOMPAT(fs->sb)
+ & ~(EXT2_FEATURE_INCOMPAT_FILETYPE |
+ EXT3_FEATURE_INCOMPAT_RECOVER)) ||
+ (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
+ & ~(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE)))
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an incompatible feature enabled."));
+ goto error_free_fs;
+ }
+
+ fs->devhandle = handle;
+ fs->logsize = EXT2_SUPER_LOG_BLOCK_SIZE(fs->sb) + 10;
+ handle->ops->set_blocksize(handle->cookie, fs->logsize);
+
+ if (!ext2_bcache_init(fs))
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Error allocating buffer cache."));
+ goto error_free_fs;
+ }
+
+ fs->blocksize = 1 << fs->logsize;
+
+ fs->numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ fs->gdblocks = ped_div_round_up (fs->numgroups
+ * sizeof(struct ext2_group_desc),
+ fs->blocksize);
+ fs->inodeblocks = ped_div_round_up (EXT2_SUPER_INODES_PER_GROUP(fs->sb)
+ * sizeof(struct ext2_inode),
+ fs->blocksize);
+ fs->r_frac = ped_div_round_up (100 * (loff_t)EXT2_SUPER_R_BLOCKS_COUNT(fs->sb),
+ EXT2_SUPER_BLOCKS_COUNT(fs->sb));
+ fs->adminblocks = 3 + fs->gdblocks + fs->inodeblocks;
+
+ fs->sparse = 0;
+ if (EXT2_SUPER_FEATURE_RO_COMPAT(fs->sb)
+ & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
+ fs->sparse = 1;
+
+ fs->has_journal = 0 < (EXT2_SUPER_FEATURE_COMPAT(fs->sb)
+ & EXT3_FEATURE_COMPAT_HAS_JOURNAL);
+ fs->has_internal_journal
+ = fs->has_journal
+ && uuid_is_null(EXT2_SUPER_JOURNAL_UUID(fs->sb))
+ && EXT2_SUPER_JOURNAL_INUM(fs->sb);
+
+ fs->gd = ped_malloc (fs->numgroups * sizeof (struct ext2_group_desc)
+ + fs->blocksize);
+ if (!fs->gd)
+ goto error_deinit_bcache;
+
+ ext2_read_blocks(fs, fs->gd, EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) + 1,
+ fs->gdblocks);
+
+ fs->metadirty = 0;
+ return fs;
+
+ ped_free(fs->gd);
+error_deinit_bcache:
+ ext2_bcache_deinit(fs);
+error_free_fs:
+ ped_free(fs);
+error:
+ return NULL;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.h b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.h
new file mode 100644
index 0000000000..2e9db64ae5
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2.h
@@ -0,0 +1,249 @@
+/*
+ ext2.h -- ext2 header
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _EXT2_H
+#define _EXT2_H
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <sys/types.h>
+#include "tune.h"
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+
+/* Ehrm.... sorry, pedanticists! :-) */
+#ifndef offsetof
+# define offsetof(type, field) ((size_t)(&(((type *)0)->field)))
+#endif
+
+#ifdef __BEOS__
+ typedef off_t loff_t;
+#endif
+
+#if defined(__sun)
+typedef off_t loff_t;
+typedef uint32_t blk_t;
+#else
+typedef u_int32_t blk_t;
+#endif
+
+#ifdef HAVE_LINUX_EXT2_FS_H
+#define _LINUX_TYPES_H
+#define i_version i_generation
+#include <linux/ext2_fs.h>
+#else
+#include "ext2_fs.h"
+#endif
+
+extern unsigned char _bitmap[8];
+
+struct ext2_buffer_cache
+{
+ struct ext2_buffer_head *cache;
+ struct ext2_buffer_head *heads;
+ struct ext2_buffer_head **hash;
+ struct ext2_fs *fs;
+
+ int size;
+ int numalloc;
+ unsigned char *buffermem;
+};
+
+struct ext2_buffer_head
+{
+ struct ext2_buffer_head *next;
+ struct ext2_buffer_head *prev;
+ unsigned char *data;
+ blk_t block;
+
+ int usecount;
+ int dirty;
+
+ struct ext2_buffer_cache *bc;
+ int alloc;
+};
+
+struct ext2_dev_ops
+{
+ int (*close)(void *cookie);
+ blk_t (*get_size)(void *cookie);
+ int (*read)(void *cookie, void *ptr, blk_t block, blk_t num);
+ int (*set_blocksize)(void *cookie, int logsize);
+ int (*sync)(void *cookie);
+ int (*write)(void *cookie, void *ptr, blk_t block, blk_t num);
+};
+
+struct ext2_dev_handle
+{
+ struct ext2_dev_ops *ops;
+ void *cookie;
+};
+
+struct ext2_fs
+{
+ struct ext2_dev_handle *devhandle;
+
+ struct ext2_super_block sb;
+ struct ext2_group_desc *gd;
+ struct ext2_buffer_cache *bc;
+ int metadirty; /* 0:all sb&gd copies clean
+ 1:all sb&gd copies dirty
+ 2:only first sb&gd copy clean */
+
+ int dynamic_version;
+ int sparse; /* sparse superblocks */
+ int has_journal; /* journal */
+ int has_internal_journal;
+
+ int blocksize;
+ int logsize;
+ blk_t adminblocks;
+ blk_t gdblocks;
+ blk_t itoffset;
+ blk_t inodeblocks;
+ int numgroups;
+ int r_frac; /* reserved % of blocks */
+
+ unsigned char *relocator_pool;
+ unsigned char *relocator_pool_end;
+
+ int opt_debug;
+ int opt_safe;
+ int opt_verbose;
+
+ void *journal;
+};
+
+
+#define EXT2_ACTION_ADD 1
+#define EXT2_ACTION_DELETE 2
+#define EXT2_ACTION_FIND 3
+
+#define EXT2_META_CLEAN 0
+#define EXT2_META_PRIMARY_SB 1
+#define EXT2_META_BACKUP_SB 2
+#define EXT2_META_PRIMARY_GD 4
+#define EXT2_META_BACKUP_GD 8
+
+#define EXT2_META_PRIMARY (EXT2_META_PRIMARY_SB | EXT2_META_PRIMARY_GD)
+#define EXT2_META_BACKUP (EXT2_META_BACKUP_SB | EXT2_META_BACKUP_GD)
+#define EXT2_META_SB (EXT2_META_PRIMARY_SB | EXT2_META_BACKUP_SB)
+#define EXT2_META_GD (EXT2_META_PRIMARY_GD | EXT2_META_BACKUP_GD)
+
+/* generic stuff */
+int ext2_copy_block (struct ext2_fs *fs, blk_t from, blk_t to);
+void ext2_close (struct ext2_fs *fs);
+int ext2_commit_metadata (struct ext2_fs *fs, int copies);
+off_t ext2_get_inode_offset (struct ext2_fs *fs, ino_t inode, blk_t *block);
+blk_t ext2_find_free_block (struct ext2_fs *fs);
+ino_t ext2_find_free_inode (struct ext2_fs *fs);
+int ext2_get_inode_state (struct ext2_fs *fs, ino_t inode);
+int ext2_is_group_sparse (struct ext2_fs *fs, int group);
+int ext2_move_blocks (struct ext2_fs *fs, blk_t src, blk_t num, blk_t dest);
+struct ext2_fs *ext2_open (struct ext2_dev_handle *handle, int state);
+int ext2_read_blocks (struct ext2_fs *fs, void *ptr, blk_t block, blk_t numblocks);
+int ext2_read_inode (struct ext2_fs *fs, ino_t inode, struct ext2_inode *inodep);
+int ext2_set_inode_state (struct ext2_fs *fs, ino_t inode, int state, int updatemetadata);
+int ext2_do_inode (struct ext2_fs *fs, struct ext2_inode *inode, blk_t block, int action);
+int ext2_sync (struct ext2_fs *fs);
+int ext2_write_blocks (struct ext2_fs *fs, void *ptr, blk_t block, blk_t numblocks);
+int ext2_write_inode (struct ext2_fs *fs, ino_t inode, const struct ext2_inode *inodep);
+int ext2_zero_blocks (struct ext2_fs *fs, blk_t block, blk_t num);
+int ext2_zero_inode (struct ext2_fs *fs, ino_t inode);
+
+/* block related */
+void ext2_bgbitmap_cache_deinit (struct ext2_fs *fs);
+int ext2_bgbitmap_cache_flush (struct ext2_fs *fs);
+int ext2_bgbitmap_cache_init (struct ext2_fs *fs);
+int ext2_get_block_state (struct ext2_fs *, blk_t block);
+int ext2_set_block_state (struct ext2_fs *, blk_t block, int state, int updatemetadata);
+
+/* block relocator */
+int ext2_block_relocate (struct ext2_fs *fs, blk_t newsize);
+
+/* buffer */
+void ext2_bcache_deinit (struct ext2_fs *fs);
+void ext2_bcache_dump (struct ext2_fs *fs);
+int ext2_bcache_flush (struct ext2_fs *fs, blk_t block);
+int ext2_bcache_flush_range (struct ext2_fs *fs, blk_t first, blk_t last);
+int ext2_bcache_init (struct ext2_fs *fs);
+int ext2_bcache_sync (struct ext2_fs *fs);
+struct ext2_buffer_head *ext2_bcreate (struct ext2_fs *fs, blk_t block);
+struct ext2_buffer_head *ext2_bread (struct ext2_fs *fs, blk_t block);
+int ext2_brelse (struct ext2_buffer_head *bh, int forget);
+
+/* inode relocator */
+int ext2_inode_relocate (struct ext2_fs *fs, int newgroups);
+
+/* journalling */
+void ext2_journal_deinit (struct ext2_fs *fs);
+int ext2_journal_init (struct ext2_fs *fs);
+
+/* metadata mover */
+int ext2_metadata_push (struct ext2_fs *fs, blk_t newsize);
+
+/* fs creation */
+struct ext2_fs *ext2_mkfs (struct ext2_dev_handle *handle, blk_t numblocks, int log_block_size, blk_t blocks_per_group, int inodes_per_group, int sparse_sb, int reserved_block_percentage, PedTimer* timer);
+
+/* resize */
+int ext2_resize_fs (struct ext2_fs *fs, blk_t newsize, PedTimer* timer);
+
+/* unix I/O */
+struct ext2_dev_handle *ext2_make_dev_handle_from_file(char *dev);
+
+
+
+
+static __inline__ int ext2_is_data_block(struct ext2_fs *fs, blk_t block)
+{
+ blk_t blk;
+ int group;
+
+ PED_ASSERT (block >= EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb), return 0);
+ PED_ASSERT (block < EXT2_SUPER_BLOCKS_COUNT(fs->sb), return 0);
+
+ blk = block - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+
+ group = blk / EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ blk %= EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ if (ext2_is_group_sparse(fs, group) && blk <= fs->gdblocks)
+ return 0;
+
+ if (block == EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]) ||
+ block == EXT2_GROUP_INODE_BITMAP(fs->gd[group]))
+ return 0;
+
+ if (block >= EXT2_GROUP_INODE_TABLE(fs->gd[group]) &&
+ block < EXT2_GROUP_INODE_TABLE(fs->gd[group]) + fs->inodeblocks)
+ return 0;
+
+ return 1;
+}
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_block_relocator.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_block_relocator.c
new file mode 100644
index 0000000000..b75a4c5b2d
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_block_relocator.c
@@ -0,0 +1,927 @@
+/*
+ ext2_block_relocator.c -- ext2 block relocator
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "ext2.h"
+
+
+/* This struct describes a single block that will be relocated. The
+ * block's original location is "num", and its new location is "dest".
+ * The block is presumebly referred to by some other block in the file
+ * system, which is recorded as "refblock". (Only one reference to
+ * the block is allowed by the block relocator.) "refoffset" describes
+ * the location within the refblock in which the block is referenced.
+ * "isindirect" is 0 for direct, 1 for single-indirect, 2 for
+ * double-indirect, etc.
+ *
+ * The algorithms in the file fill the entries of this struct in this order:
+ * num, refblock/refoffset/isindirectblock, dest.
+ */
+struct ext2_block_entry
+{
+ blk_t num;
+ blk_t dest;
+ blk_t refblock;
+ unsigned refoffset:16;
+ unsigned isindirectblock:16;
+};
+
+/* This struct contains all data structures relevant to the block relocator.
+ * - newallocoffset is the distance between the start of a block group,
+ * and the first data block in the group. This can change when a
+ * filesystem is resized, because the size of the group descriptors is
+ * proportional to the size of the filesystem.
+ *
+ * - allocentries is the size of the "block" array. It is a tuneable
+ * parameter that determines how many blocks can be moved in each
+ * pass.
+ *
+ * - usedentries says how many entries of the "block" array have been
+ * used. That is, how many blocks have been scheduled so far to
+ * be moved.
+ *
+ * - resolvedentries is the number of blocks whose referencing block
+ * has been found and recorded in block[.]->refblock, etc.
+ *
+ * - block is an array that records which blocks need to be moved, and
+ * where they will be moved to, etc. At some point in the algorithm, this
+ * array gets sorted (grep for qsort!) by indirectness.
+ *
+ * - start: each entry in this array corresponds to a level of
+ * indirectness (0-3). Each level has two items: dst and num. "num"
+ * is the number of blocks inside "block" of that level of indirectness.
+ * After doscan() is finished, and the level of indirectness of each
+ * block is known, "block" is sorted (see above). The "dst" pointer
+ * is a pointer inside "block" that indicates the start of the portion
+ * of the array containg blocks of that level of indirectness.
+ */
+struct ext2_block_relocator_state
+{
+ blk_t newallocoffset;
+ blk_t allocentries;
+ blk_t usedentries;
+ blk_t resolvedentries;
+ struct ext2_block_entry *block;
+
+ struct {
+ struct ext2_block_entry *dst;
+ int num;
+ } start[4];
+};
+
+
+
+static int compare_block_entries(const void *x0, const void *x1)
+{
+ const struct ext2_block_entry *b0;
+ const struct ext2_block_entry *b1;
+
+ b0 = (const struct ext2_block_entry *)x0;
+ b1 = (const struct ext2_block_entry *)x1;
+
+ if (b0->num < b1->num)
+ return -1;
+
+ if (b0->num > b1->num)
+ return 1;
+
+ return 0;
+}
+
+static int compare_block_entries_ind(const void *x0, const void *x1)
+{
+ const struct ext2_block_entry *b0;
+ const struct ext2_block_entry *b1;
+
+ b0 = (const struct ext2_block_entry *)x0;
+ b1 = (const struct ext2_block_entry *)x1;
+
+ if (b0->isindirectblock > b1->isindirectblock)
+ return -1;
+
+ if (b0->isindirectblock < b1->isindirectblock)
+ return 1;
+
+ return 0;
+}
+
+static int compare_block_entries_ref(const void *x0, const void *x1)
+{
+ const struct ext2_block_entry *b0;
+ const struct ext2_block_entry *b1;
+
+ b0 = (const struct ext2_block_entry *)x0;
+ b1 = (const struct ext2_block_entry *)x1;
+
+ if (b0->refblock < b1->refblock)
+ return -1;
+
+ if (b0->refblock > b1->refblock)
+ return 1;
+
+ return 0;
+}
+
+struct ext2_block_entry *findit(struct ext2_block_relocator_state *state, blk_t block)
+{
+ int min;
+ int max;
+ struct ext2_block_entry *retv;
+ int t;
+ blk_t tval;
+
+ max = state->usedentries - 1;
+ min = 0;
+ retv = NULL;
+
+ repeat:
+ if (min > max)
+ goto out;
+
+ t = (min + max) >> 1;
+ tval = state->block[t].num;
+
+ if (tval > block)
+ max = t - 1;
+
+ if (tval < block)
+ min = t + 1;
+
+ if (tval != block)
+ goto repeat;
+
+ retv = &state->block[t];
+
+ out:
+ return retv;
+}
+
+/* This function adds records a reference to a block ("blk"), if that
+ * block is scheduled to be moved.
+ */
+static int doblock(struct ext2_fs *fs,
+ struct ext2_block_relocator_state *state,
+ blk_t blk,
+ blk_t refblock,
+ off_t refoffset,
+ int indirect)
+{
+ struct ext2_block_entry *ent;
+
+ if ((ent = findit(state, blk)) == NULL)
+ return 1;
+
+ if (ent->refblock)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Cross-linked blocks found! Better go run e2fsck "
+ "first!"));
+ return 0;
+ }
+
+ ent->refblock = refblock;
+ ent->refoffset = refoffset;
+ ent->isindirectblock = indirect;
+
+ state->resolvedentries++;
+ state->start[indirect].num++;
+
+ return 1;
+}
+
+static int doindblock(struct ext2_fs *fs,
+ struct ext2_block_relocator_state *state,
+ blk_t blk,
+ blk_t refblock,
+ off_t refoffset)
+{
+ struct ext2_buffer_head *bh;
+ int i;
+ uint32_t *uptr;
+
+ if (!doblock(fs, state, blk, refblock, refoffset, 1))
+ return 0;
+
+ bh = ext2_bread(fs, blk);
+ if (!bh)
+ return 0;
+ uptr = (uint32_t *)bh->data;
+
+ for (i=0;i<(fs->blocksize >> 2);i++)
+ if (uptr[i])
+ if (!doblock(fs, state, PED_LE32_TO_CPU(uptr[i]), blk,
+ i<<2, 0))
+ return 0;
+
+ if (!ext2_brelse(bh, 0))
+ return 0;
+
+ return 1;
+}
+
+static int dodindblock(struct ext2_fs *fs,
+ struct ext2_block_relocator_state *state,
+ blk_t blk,
+ blk_t refblock,
+ off_t refoffset)
+{
+ struct ext2_buffer_head *bh;
+ int i;
+ uint32_t *uptr;
+
+ if (!doblock(fs, state, blk, refblock, refoffset, 2))
+ return 0;
+
+ bh = ext2_bread(fs, blk);
+ if (!bh)
+ return 0;
+ uptr = (uint32_t *)bh->data;
+
+ for (i=0;i<(fs->blocksize >> 2);i++)
+ if (uptr[i])
+ if (!doindblock(fs, state, PED_LE32_TO_CPU(uptr[i]),
+ blk, i<<2))
+ return 0;
+
+ if (!ext2_brelse(bh, 0))
+ return 0;
+
+ return 1;
+}
+
+static int dotindblock(struct ext2_fs *fs,
+ struct ext2_block_relocator_state *state,
+ blk_t blk,
+ blk_t refblock,
+ off_t refoffset)
+{
+ struct ext2_buffer_head *bh;
+ int i;
+ uint32_t *uptr;
+
+ if (!doblock(fs, state, blk, refblock, refoffset, 3))
+ return 0;
+
+ bh = ext2_bread(fs, blk);
+ if (!bh)
+ return 0;
+ uptr = (uint32_t *)bh->data;
+
+ for (i=0;i<(fs->blocksize >> 2);i++)
+ if (uptr[i])
+ if (!dodindblock(fs, state, PED_LE32_TO_CPU(uptr[i]),
+ blk, i<<2))
+ return 0;
+
+ if (!ext2_brelse(bh, 0))
+ return 0;
+
+ return 1;
+}
+
+
+/* This function records any block references from an inode to blocks that are
+ * scheduled to be moved.
+ */
+static int doinode(struct ext2_fs *fs, struct ext2_block_relocator_state *state, int inode)
+{
+ struct ext2_inode buf;
+
+ if (!ext2_read_inode(fs, inode, &buf))
+ return 0;
+
+ if (EXT2_INODE_BLOCKS(buf))
+ {
+ blk_t blk;
+ int i;
+ off_t inodeoffset;
+ blk_t inodeblock;
+
+ inodeoffset = ext2_get_inode_offset(fs, inode, &inodeblock);
+
+ /* do Hurd block, if there is one... */
+ if (EXT2_SUPER_CREATOR_OS(fs->sb) == EXT2_OS_HURD
+ && EXT2_INODE_TRANSLATOR(buf)) {
+ if (!doblock(fs,
+ state,
+ EXT2_INODE_TRANSLATOR(buf),
+ inodeblock,
+ inodeoffset + offsetof(struct ext2_inode,
+ osd1.hurd1.h_i_translator),
+ 0))
+ return 0;
+ }
+
+ for (i=0;i<EXT2_NDIR_BLOCKS;i++)
+ if ((blk = EXT2_INODE_BLOCK(buf, i)) != 0)
+ if (!doblock(fs,
+ state,
+ blk,
+ inodeblock,
+ inodeoffset + offsetof(struct ext2_inode, i_block[i]),
+ 0))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_IND_BLOCK)) != 0)
+ if (!doindblock(fs,
+ state,
+ blk,
+ inodeblock,
+ inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_IND_BLOCK])))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_DIND_BLOCK)) != 0)
+ if (!dodindblock(fs,
+ state,
+ blk,
+ inodeblock,
+ inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_DIND_BLOCK])))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_TIND_BLOCK)) != 0)
+ if (!dotindblock(fs,
+ state,
+ blk,
+ inodeblock,
+ inodeoffset + offsetof(struct ext2_inode, i_block[EXT2_TIND_BLOCK])))
+ return 0;
+
+ }
+
+ return 1;
+}
+
+/* This function scans the entire filesystem, to find all references to blocks
+ * that are scheduled to be moved.
+ */
+static int doscan(struct ext2_fs *fs, struct ext2_block_relocator_state *state)
+{
+ int i;
+
+ state->start[0].num = 0;
+ state->start[1].num = 0;
+ state->start[2].num = 0;
+ state->start[3].num = 0;
+
+ for (i=0;i<fs->numgroups;i++)
+ {
+ struct ext2_buffer_head *bh;
+ unsigned int j;
+ int offset;
+
+ if (fs->opt_verbose)
+ {
+ fprintf(stderr, " scanning group %i.... ", i);
+ fflush(stderr);
+ }
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i]));
+ if (!bh)
+ return 0;
+ offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
+
+ for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
+ if (bh->data[j>>3] & _bitmap[j&7])
+ {
+ if (!doinode(fs, state, offset + j))
+ {
+ ext2_brelse(bh, 0);
+ return 0;
+ }
+
+ if (state->resolvedentries == state->usedentries)
+ break;
+ }
+
+ ext2_brelse(bh, 0);
+
+ if (fs->opt_verbose)
+ {
+ fprintf(stderr, "%i/%i blocks resolved\r",
+ state->resolvedentries,
+ state->usedentries);
+ fflush(stderr);
+ }
+
+ if (state->resolvedentries == state->usedentries)
+ break;
+ }
+
+ if (fs->opt_verbose)
+ fputc('\n', stderr);
+
+ state->start[3].dst = state->block;
+ state->start[2].dst = state->start[3].dst + state->start[3].num;
+ state->start[1].dst = state->start[2].dst + state->start[2].num;
+ state->start[0].dst = state->start[1].dst + state->start[1].num;
+
+ return 1;
+}
+
+
+
+
+
+static int ext2_block_relocator_copy(struct ext2_fs *fs, struct ext2_block_relocator_state *state)
+{
+ unsigned char *buf;
+
+ ped_exception_fetch_all();
+ buf = (unsigned char *) ped_malloc(MAXCONT << fs->logsize);
+ if (buf)
+ {
+ int num;
+ int numleft;
+ struct ext2_block_entry *ptr;
+
+ ped_exception_leave_all();
+
+ numleft = state->usedentries;
+ ptr = state->block;
+ while (numleft)
+ {
+ num = PED_MIN(numleft, MAXCONT);
+ while (num != 1)
+ {
+ if (ptr[0].num + num-1 == ptr[num-1].num &&
+ ptr[0].dest + num-1 == ptr[num-1].dest)
+ break;
+
+ num >>= 1;
+ }
+
+ if (!ext2_bcache_flush_range(fs, ptr[0].num, num))
+ goto error_free_buf;
+ if (!ext2_bcache_flush_range(fs, ptr[0].dest, num))
+ goto error_free_buf;
+
+ if (!ext2_read_blocks(fs, buf, ptr[0].num, num))
+ goto error_free_buf;
+ if (!ext2_write_blocks(fs, buf, ptr[0].dest, num))
+ goto error_free_buf;
+
+ ptr += num;
+ numleft -= num;
+
+ if (fs->opt_verbose)
+ {
+ fprintf(stderr, "copied %i/%i blocks\r",
+ state->usedentries - numleft,
+ state->usedentries);
+ fflush(stderr);
+ }
+ }
+
+ ped_free(buf);
+
+ if (fs->opt_safe)
+ ext2_sync(fs);
+
+ if (fs->opt_verbose)
+ fputc('\n', stderr);
+ }
+ else
+ {
+ blk_t i;
+
+ ped_exception_catch();
+ ped_exception_leave_all();
+
+ for (i=0;i<state->usedentries;i++)
+ {
+ struct ext2_block_entry *block;
+
+ block = &state->block[i];
+ if (!ext2_copy_block(fs, block->num, block->dest))
+ goto error;
+ }
+ }
+
+ return 1;
+
+error_free_buf:
+ ped_free(buf);
+error:
+ return 0;
+}
+
+static int ext2_block_relocator_ref(struct ext2_fs *fs, struct ext2_block_relocator_state *state, struct ext2_block_entry *block)
+{
+ struct ext2_buffer_head *bh;
+ static int numerrors = 0;
+
+ if (!(block->refblock || block->refoffset))
+ {
+ ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+ _("Block %i has no reference? Weird."),
+ block->num);
+ return 0;
+ }
+
+ bh = ext2_bread(fs, block->refblock);
+ if (!bh)
+ return 0;
+
+ if (fs->opt_debug)
+ {
+ if (PED_LE32_TO_CPU(*((uint32_t *)(bh->data + block->refoffset)))
+ != block->num) {
+ fprintf(stderr,
+ "block %i ref error! (->%i {%i, %i})\n",
+ block->num,
+ block->dest,
+ block->refblock,
+ block->refoffset);
+ ext2_brelse(bh, 0);
+
+ if (numerrors++ < 4)
+ return 1;
+
+ fputs("all is not well!\n", stderr);
+ return 0;
+ }
+ }
+
+ *((uint32_t *)(bh->data + block->refoffset))
+ = PED_LE32_TO_CPU(block->dest);
+ bh->dirty = 1;
+ ext2_brelse(bh, 0);
+
+ ext2_set_block_state(fs, block->dest, 1, 1);
+ ext2_set_block_state(fs, block->num, 0, 1);
+
+ if (block->isindirectblock)
+ {
+ struct ext2_block_entry *dst;
+ int i;
+ int num;
+
+ dst = state->start[block->isindirectblock-1].dst;
+ num = state->start[block->isindirectblock-1].num;
+
+ for (i=0;i<num;i++)
+ if (dst[i].refblock == block->num)
+ dst[i].refblock = block->dest;
+ }
+
+ return 1;
+}
+
+/* This function allocates new locations for blocks that are scheduled to move
+ * (inside state->blocks).
+ *
+ * FIXME: doesn't seem to handle sparse block groups. That is, there might be
+ * some free space that could be exploited in resizing that currently isn't...
+ *
+ * FIXME: should throw an exception if it fails to allocate blocks.
+ */
+static int ext2_block_relocator_grab_blocks(struct ext2_fs *fs, struct ext2_block_relocator_state *state)
+{
+ int i;
+ blk_t ptr;
+
+ ptr = 0;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[i]))
+ {
+ struct ext2_buffer_head *bh;
+ unsigned int j;
+ int offset;
+
+ bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]));
+ offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+
+ for (j=state->newallocoffset;
+ j<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ j++)
+ if (!(bh->data[j>>3] & _bitmap[j&7]))
+ {
+ state->block[ptr++].dest = offset + j;
+
+ if (ptr == state->usedentries)
+ {
+ ext2_brelse(bh, 0);
+ return 1;
+ }
+ }
+
+ ext2_brelse(bh, 0);
+ }
+
+ return 0;
+}
+
+static int ext2_block_relocator_flush(struct ext2_fs *fs, struct ext2_block_relocator_state *state)
+{
+ int i;
+
+ if (!state->usedentries)
+ return 1;
+
+ if (fs->opt_verbose)
+ fputs("ext2_block_relocator_flush\n", stderr);
+
+ if (fs->opt_debug)
+ {
+ again:
+
+ for (i=0; (unsigned int) i < state->usedentries-1; i++)
+ if (state->block[i].num >= state->block[i+1].num)
+ {
+ fputs("ext2_block_relocator_flush: "
+ "blocks not in order!\n", stderr);
+
+ qsort(state->block,
+ state->usedentries,
+ sizeof(struct ext2_block_entry),
+ compare_block_entries);
+ goto again;
+ }
+ }
+
+ if (!doscan(fs, state))
+ return 0;
+
+ if (!ext2_block_relocator_grab_blocks(fs, state))
+ return 0;
+
+ if (!ext2_block_relocator_copy(fs, state))
+ return 0;
+
+ qsort(state->block,
+ state->usedentries,
+ sizeof(struct ext2_block_entry),
+ compare_block_entries_ind);
+
+ for (i=3;i>=0;i--)
+ {
+ struct ext2_block_entry *dst;
+ int j;
+ int num;
+
+ dst = state->start[i].dst;
+ num = state->start[i].num;
+
+ if (!num)
+ continue;
+
+ if (fs->opt_verbose)
+ {
+ /* FIXXXME gross hack */
+ fprintf(stderr, "relocating %s blocks",
+ ((char *[4]){"direct",
+ "singly indirect",
+ "doubly indirect",
+ "triply indirect"})[i]);
+ fflush(stderr);
+ }
+
+ qsort(dst,
+ num,
+ sizeof(struct ext2_block_entry),
+ compare_block_entries_ref);
+
+ for (j=0;j<num;j++)
+ if (!ext2_block_relocator_ref(fs, state, &dst[j]))
+ return 0;
+
+ if (fs->opt_safe) {
+ if (!ext2_sync(fs))
+ return 0;
+ }
+
+ if (fs->opt_verbose)
+ fputc('\n', stderr);
+ }
+
+ state->usedentries = 0;
+ state->resolvedentries = 0;
+
+ return 1;
+}
+
+static int ext2_block_relocator_mark(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t block)
+{
+ int i;
+
+ if (fs->opt_debug)
+ {
+ if (!ext2_get_block_state(fs, block) ||
+ !ext2_is_data_block(fs, block))
+ {
+ ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("Block %i shouldn't have been marked "
+ "(%d, %d)!"), block,
+ ext2_get_block_state(fs, block),
+ ext2_is_data_block(fs, block));
+ }
+ }
+
+ if (state->usedentries == state->allocentries - 1)
+ if (!ext2_block_relocator_flush(fs, state))
+ return 0;
+
+ i = state->usedentries;
+ state->block[i].num = block;
+ state->block[i].dest = 0;
+ state->block[i].refblock = 0;
+ state->block[i].refoffset = 0;
+
+ state->usedentries++;
+ return 1;
+}
+
+static int ext2_block_relocate_grow(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t newsize)
+{
+ blk_t newgdblocks;
+ blk_t newitoffset;
+ int i;
+
+ newgdblocks = ped_div_round_up (newsize
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ newgdblocks = ped_div_round_up (newgdblocks
+ * sizeof(struct ext2_group_desc),
+ fs->blocksize);
+ if (newgdblocks == fs->gdblocks)
+ return 1;
+
+ newitoffset = newgdblocks + 3;
+ state->newallocoffset = newitoffset + fs->inodeblocks;
+
+ for (i=0;i<fs->numgroups;i++)
+ {
+ struct ext2_buffer_head *bh;
+ blk_t diff;
+ blk_t j;
+ blk_t start;
+ int sparse;
+
+ bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]));
+ start = (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ sparse = ext2_is_group_sparse(fs, i);
+
+ if (EXT2_GROUP_INODE_TABLE(fs->gd[i]) < start + newitoffset
+ || (sparse && ((EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])
+ < start + newitoffset - 2)
+ || (EXT2_GROUP_INODE_BITMAP(fs->gd[i])
+ < start + newitoffset - 1))))
+ {
+ diff = newitoffset - (EXT2_GROUP_INODE_TABLE(fs->gd[i])
+ - start);
+
+ for (j=0;j<diff;j++)
+ {
+ blk_t block;
+ blk_t k;
+
+ k = EXT2_GROUP_INODE_TABLE(fs->gd[i])
+ + fs->inodeblocks + j;
+ block = k % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ if (bh->data[block>>3] & _bitmap[block&7]) {
+ k += EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ if (!ext2_block_relocator_mark(fs,
+ state, k))
+ {
+ ext2_brelse(bh, 0);
+ return 0;
+ }
+ }
+ }
+ }
+
+ ext2_brelse(bh, 0);
+ }
+
+ if (!ext2_block_relocator_flush(fs, state))
+ return 0;
+
+ return 1;
+}
+
+static int ext2_block_relocate_shrink(struct ext2_fs *fs, struct ext2_block_relocator_state *state, blk_t newsize)
+{
+ int diff;
+ int i;
+
+ diff = ped_div_round_up (newsize - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ diff = ped_div_round_up (diff * sizeof(struct ext2_group_desc),
+ fs->blocksize);
+ diff = fs->gdblocks - diff;
+
+ state->newallocoffset = fs->itoffset + fs->inodeblocks;
+
+ for (i=0;i<fs->numgroups;i++)
+ {
+ struct ext2_buffer_head *bh;
+ blk_t groupsize;
+ blk_t j;
+ blk_t offset;
+ int sparse;
+ blk_t start;
+ int type;
+
+ offset = i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ sparse = ext2_is_group_sparse(fs, i);
+
+ if (newsize >= offset + EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ continue; /* group will survive */
+
+ bh = ext2_bread(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]));
+
+ if (newsize <= offset)
+ type = 2; /* group is fully chopped off */
+ else
+ type = 1; /* group is partly chopped off */
+
+ if (!sparse && type == 2)
+ {
+ for (j=EXT2_GROUP_INODE_BITMAP(fs->gd[i])+1;
+ j<EXT2_GROUP_INODE_TABLE(fs->gd[i]);
+ j++)
+ {
+ blk_t k;
+
+ k = j - offset;
+ if (bh->data[k>>3] & _bitmap[k&7])
+ if (!ext2_block_relocator_mark(fs, state, j))
+ {
+ ext2_brelse(bh, 0);
+ return 0;
+ }
+ }
+ }
+
+ start = newsize;
+ if (type == 2)
+ start = EXT2_GROUP_INODE_TABLE(fs->gd[i])
+ + fs->inodeblocks;
+
+ start -= offset;
+
+ groupsize = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ if (offset + groupsize > EXT2_SUPER_BLOCKS_COUNT(fs->sb))
+ groupsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - offset;
+
+ for (j=start;j<groupsize;j++)
+ if (bh->data[j>>3] & _bitmap[j&7])
+ if (!ext2_block_relocator_mark(fs, state,
+ offset + j))
+ {
+ ext2_brelse(bh, 0);
+ return 0;
+ }
+
+ ext2_brelse(bh, 0);
+ }
+
+ return ext2_block_relocator_flush(fs, state);
+}
+
+int ext2_block_relocate(struct ext2_fs *fs, blk_t newsize)
+{
+ struct ext2_block_relocator_state state;
+
+ if (fs->opt_verbose)
+ fputs("relocating blocks....\n", stderr);
+
+ state.newallocoffset = 0;
+ state.allocentries = (ext2_relocator_pool_size << 10) /
+ sizeof(struct ext2_block_entry);
+ state.usedentries = 0;
+ state.resolvedentries = 0;
+ state.block = (struct ext2_block_entry *)fs->relocator_pool;
+
+ if (newsize < EXT2_SUPER_BLOCKS_COUNT(fs->sb))
+ return ext2_block_relocate_shrink(fs, &state, newsize);
+
+ return ext2_block_relocate_grow(fs, &state, newsize);
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_buffer.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_buffer.c
new file mode 100644
index 0000000000..3a5f29c991
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_buffer.c
@@ -0,0 +1,446 @@
+/*
+ ext2_buffer.c -- ext2 buffer cache
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ext2.h"
+
+/* pseudo-header */
+
+static __inline__ int ext2_block_hash(blk_t block)
+{
+ unsigned long x;
+
+ x = block ^ (block >> 8) ^ (block >> 16) ^ (block >> 24);
+ return x & ((1 << ext2_hash_bits) - 1);
+}
+
+static struct ext2_buffer_head *ext2_bh_alloc (struct ext2_buffer_cache *, blk_t);
+static void ext2_bh_dealloc (struct ext2_buffer_head *);
+static struct ext2_buffer_head *ext2_bh_find (struct ext2_buffer_cache *, blk_t);
+static int ext2_bh_do_read (struct ext2_buffer_head *);
+static int ext2_bh_do_write(struct ext2_buffer_head *);
+static void ext2_bh_hash (struct ext2_buffer_head *);
+static void ext2_bh_unhash (struct ext2_buffer_head *);
+
+
+
+static int try_to_flush(struct ext2_buffer_cache *bc)
+{
+ int i;
+
+ for (i=0;i<bc->size;i++)
+ {
+ struct ext2_buffer_head *bh;
+
+ bh = &bc->heads[i];
+
+ if (bh->alloc && !bh->usecount && !bh->dirty)
+ {
+ ext2_bh_dealloc(bh);
+ return 1;
+ }
+ }
+
+ for (i=0;i<bc->size;i++)
+ {
+ struct ext2_buffer_head *bh;
+
+ bh = &bc->heads[i];
+
+ if (bh->alloc && !bh->usecount && bh->dirty)
+ {
+ ext2_bh_do_write(bh);
+ ext2_bh_dealloc(bh);
+ return 1;
+ }
+ }
+
+ if (ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Couldn't flush buffer cache!"))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ return 1;
+}
+
+
+
+
+
+static struct ext2_buffer_head *ext2_bh_alloc(struct ext2_buffer_cache *bc, blk_t block)
+{
+ struct ext2_buffer_head *bh;
+ int i;
+
+ bh = NULL;
+
+ tryagain:
+ for (i=0;i<bc->size;i++)
+ {
+ bh = &bc->heads[i];
+
+ if (!bh->alloc)
+ break;
+ }
+
+ if (i == bc->size)
+ {
+ try_to_flush(bc);
+ goto tryagain;
+ }
+
+ bh = &bc->heads[i];
+
+ bh->next = NULL;
+ bh->prev = NULL;
+ bh->block = block;
+ bh->usecount = 0;
+ bh->dirty = 0;
+ bh->alloc = 1;
+ bc->numalloc++;
+
+ ext2_bh_hash(bh);
+
+ return bh;
+}
+
+static void ext2_bh_dealloc(struct ext2_buffer_head *bh)
+{
+ if (bh->dirty)
+ ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_IGNORE,
+ "deallocing() a dirty buffer! %i\n", bh->block);
+
+ ext2_bh_unhash(bh);
+ bh->alloc = 0;
+ bh->bc->numalloc--;
+}
+
+static struct ext2_buffer_head *ext2_bh_find(struct ext2_buffer_cache *bc, blk_t block)
+{
+ struct ext2_buffer_head *a;
+ struct ext2_buffer_head *b;
+ int hash;
+
+ hash = ext2_block_hash(block);
+ a = bc->hash[hash];
+
+ if (a != NULL)
+ {
+ b = a;
+ do
+ {
+ if (a->block == block)
+ return a;
+
+ a = a->next;
+ } while (a != b);
+ }
+
+ return NULL;
+}
+
+static int ext2_bh_do_read(struct ext2_buffer_head *bh)
+{
+ return ext2_read_blocks(bh->bc->fs, bh->data, bh->block, 1);
+}
+
+static int ext2_bh_do_write(struct ext2_buffer_head *bh)
+{
+ if (!bh->alloc) {
+ ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+ "Attempt to write unallocated buffer.");
+ return 0;
+ }
+
+ ext2_write_blocks(bh->bc->fs, bh->data, bh->block, 1);
+ bh->dirty = 0;
+ return 1;
+}
+
+static void ext2_bh_hash(struct ext2_buffer_head *bh)
+{
+ int hash;
+
+ hash = ext2_block_hash(bh->block);
+ if (bh->bc->hash[hash] != NULL)
+ {
+ bh->next = bh->bc->hash[hash];
+ bh->prev = bh->next->prev;
+ bh->next->prev = bh;
+ bh->prev->next = bh;
+ return;
+ }
+
+ bh->bc->hash[hash] = bh;
+ bh->next = bh->prev = bh;
+}
+
+static void ext2_bh_unhash(struct ext2_buffer_head *bh)
+{
+ int hash;
+
+ hash = ext2_block_hash(bh->block);
+
+ bh->prev->next = bh->next;
+ bh->next->prev = bh->prev;
+
+ if (bh->bc->hash[hash] == bh)
+ {
+ if (bh->next != bh)
+ bh->bc->hash[hash] = bh->next;
+ else
+ bh->bc->hash[hash] = NULL;
+ }
+
+ bh->next = NULL;
+ bh->prev = NULL;
+}
+
+
+
+
+
+
+
+static int breadimmhits = 0;
+static int breadindhits = 0;
+static int breadmisses = 0;
+
+void ext2_bcache_deinit(struct ext2_fs *fs)
+{
+ ext2_bcache_sync(fs);
+ ped_free(fs->bc->buffermem);
+ ped_free(fs->bc->hash);
+ ped_free(fs->bc->heads);
+ ped_free(fs->bc);
+
+ if (fs->opt_verbose)
+ fprintf(stderr,
+ "direct hits: %i, indirect hits: %i, misses: %i\n",
+ breadimmhits,
+ breadindhits,
+ breadmisses);
+}
+
+void ext2_bcache_dump(struct ext2_fs *fs)
+{
+ int i;
+
+ fputs ("buffer cache dump:\n", stderr);
+
+ for (i=0;i<(1<<ext2_hash_bits);i++)
+ if (fs->bc->hash[i] != NULL)
+ {
+ struct ext2_buffer_head *a;
+ struct ext2_buffer_head *b;
+
+ fprintf(stderr, "%i: ", i);
+
+ a = b = fs->bc->hash[i];
+ do
+ {
+ fprintf(stderr, "%i ", a->block);
+ a = a->next;
+ } while (a != b);
+
+ fputc ('\n', stderr);
+ }
+}
+
+int ext2_bcache_flush(struct ext2_fs *fs, blk_t block)
+{
+ struct ext2_buffer_head *bh;
+
+ if ((bh = ext2_bh_find(fs->bc, block)) == NULL)
+ return 1;
+
+ if (bh->usecount) {
+ ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+ "Attempt to flush a buffer that's in use! [%i,%i]",
+ bh->block, bh->usecount);
+ return 0;
+ }
+
+ if (bh->dirty) {
+ if (!ext2_bh_do_write(bh))
+ return 0;
+ }
+
+ ext2_bh_dealloc(bh);
+ return 1;
+}
+
+int ext2_bcache_flush_range(struct ext2_fs *fs, blk_t block, blk_t num)
+{
+ blk_t end = block + num;
+
+ for (; block < end; block++) {
+ if (!ext2_bcache_flush(fs, block))
+ return 0;
+ }
+ return 1;
+}
+
+int ext2_bcache_init(struct ext2_fs *fs)
+{
+ struct ext2_buffer_cache *bc;
+ int i;
+ int size;
+
+ size = ext2_buffer_cache_pool_size >> (fs->logsize - 10);
+
+ if ((bc = (struct ext2_buffer_cache *) ped_malloc(sizeof(struct ext2_buffer_cache))) == NULL)
+ return 0;
+
+ if ((bc->heads = (struct ext2_buffer_head *) ped_malloc(size * sizeof(struct ext2_buffer_head))) == NULL)
+ return 0;
+
+ if ((bc->hash = (struct ext2_buffer_head **) ped_malloc(sizeof(struct ext2_buffer_head *) << ext2_hash_bits)) == NULL)
+ {
+ ped_free(bc->heads);
+ ped_free(bc);
+ return 0;
+ }
+
+ if ((bc->buffermem = (unsigned char *) ped_malloc(ext2_buffer_cache_pool_size << 10)) == NULL)
+ {
+ ped_free(bc->hash);
+ ped_free(bc->heads);
+ ped_free(bc);
+ return 0;
+ }
+
+ bc->cache = &bc->heads[0];
+ bc->fs = fs;
+ bc->size = size;
+ bc->numalloc = 0;
+
+ for (i=0;i<size;i++)
+ {
+ bc->heads[i].data = bc->buffermem + (i << fs->logsize);
+ bc->heads[i].bc = bc;
+ bc->heads[i].alloc = 0;
+ }
+
+ for (i=0;i<(1<<ext2_hash_bits);i++)
+ bc->hash[i] = NULL;
+
+ fs->bc = bc;
+
+ return 1;
+}
+
+int ext2_bcache_sync(struct ext2_fs *fs)
+{
+ int i;
+
+ for (i=0;i<fs->bc->size;i++)
+ {
+ struct ext2_buffer_head *bh;
+
+ bh = &fs->bc->heads[i];
+
+ if (bh->alloc && bh->dirty) {
+ if (!ext2_bh_do_write(bh))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+
+
+
+
+
+
+struct ext2_buffer_head *ext2_bcreate(struct ext2_fs *fs, blk_t block)
+{
+ struct ext2_buffer_head *bh;
+
+ if ((bh = ext2_bh_find(fs->bc, block)) != NULL)
+ {
+ bh->usecount++;
+ }
+ else
+ {
+ bh = ext2_bh_alloc(fs->bc, block);
+ bh->usecount = 1;
+ }
+
+ memset(bh->data, 0, fs->blocksize);
+ bh->dirty = 1;
+
+ return bh;
+}
+
+struct ext2_buffer_head *ext2_bread(struct ext2_fs *fs, blk_t block)
+{
+ struct ext2_buffer_head *bh;
+
+ if ((bh = fs->bc->cache)->block == block)
+ {
+ breadimmhits++;
+ bh->usecount++;
+ return bh;
+ }
+
+ if ((bh = ext2_bh_find(fs->bc, block)) != NULL)
+ {
+ fs->bc->cache = bh;
+ breadindhits++;
+ bh->usecount++;
+ return bh;
+ }
+
+ breadmisses++;
+
+ bh = ext2_bh_alloc(fs->bc, block);
+ fs->bc->cache = bh;
+ bh->usecount = 1;
+ if (!ext2_bh_do_read(bh)) {
+ ext2_bh_dealloc(bh);
+ return NULL;
+ }
+
+ return bh;
+}
+
+int ext2_brelse(struct ext2_buffer_head *bh, int forget)
+{
+ if (bh->usecount-- == 1 && forget)
+ {
+ if (bh->dirty) {
+ if (!ext2_bh_do_write(bh))
+ return 0;
+ }
+
+ ext2_bh_dealloc(bh);
+ }
+ return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_fs.h b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_fs.h
new file mode 100644
index 0000000000..713cc21af3
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_fs.h
@@ -0,0 +1,323 @@
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * 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/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * EXT2_*_*() convienience macros added by Andrew Clausen <clausen@gnu.org>
+ * Copyright (C) 2000 Free Software Foundation, Inc.
+ */
+
+#ifndef _EXT2_FS_H
+#define _EXT2_FS_H
+
+#include <parted/endian.h>
+#include <stdint.h>
+
+/*
+ * The second extended file system constants/structures
+ */
+
+#define EXT2_SUPER_MAGIC_CONST 0xEF53
+#define EXT2_MIN_BLOCK_SIZE 1024
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
+#define EXT2_VALID_FS 0x0001
+#define EXT2_ERROR_FS 0x0002
+#define EXT2_RESERVED_INODE_COUNT 11
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX 0
+#define EXT2_OS_HURD 1
+#define EXT2_OS_MASIX 2
+#define EXT2_OS_FREEBSD 3
+#define EXT2_OS_LITES 4
+
+/*
+ * Feature set definitions
+ */
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
+#define EXT2_FEATURE_COMPAT_HAS_DIR_INDEX 0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
+
+/*
+ * Special inodes numbers
+ */
+#define EXT2_BAD_INO 1 /* Bad blocks inode */
+#define EXT2_ROOT_INO 2 /* Root inode */
+#define EXT2_ACL_IDX_INO 3 /* ACL inode */
+#define EXT2_ACL_DATA_INO 4 /* ACL inode */
+#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
+
+/*
+ * Ext2 directory file types. Only the low 3 bits are used. The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN 0
+#define EXT2_FT_REG_FILE 1
+#define EXT2_FT_DIR 2
+#define EXT2_FT_CHRDEV 3
+#define EXT2_FT_BLKDEV 4
+#define EXT2_FT_FIFO 5
+#define EXT2_FT_SOCK 6
+#define EXT2_FT_SYMLINK 7
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */
+#define EXT2_ERRORS_RO 2 /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC 3 /* Panic */
+#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE
+
+struct ext2_dir_entry_2
+{
+ uint32_t inode;
+ uint16_t rec_len;
+ uint8_t name_len;
+ uint8_t file_type;
+ char name[255];
+};
+
+struct ext2_group_desc
+{
+ uint32_t bg_block_bitmap;
+ uint32_t bg_inode_bitmap;
+ uint32_t bg_inode_table;
+ uint16_t bg_free_blocks_count;
+ uint16_t bg_free_inodes_count;
+ uint16_t bg_used_dirs_count;
+ uint16_t bg_pad;
+ uint32_t bg_reserved[3];
+};
+
+struct ext2_inode
+{
+ uint16_t i_mode; /* File mode */
+ uint16_t i_uid; /* Owner Uid */
+ uint32_t i_size; /* Size in bytes */
+ uint32_t i_atime; /* Access time */
+ uint32_t i_ctime; /* Creation time */
+ uint32_t i_mtime; /* Modification time */
+ uint32_t i_dtime; /* Deletion Time */
+ uint16_t i_gid; /* Group Id */
+ uint16_t i_links_count; /* Links count */
+ uint32_t i_blocks; /* Blocks count */
+ uint32_t i_flags; /* File flags */
+ union {
+ struct {
+ uint32_t l_i_reserved1;
+ } linux1;
+ struct {
+ uint32_t h_i_translator;
+ } hurd1;
+ struct {
+ uint32_t m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ uint32_t i_generation; /* File version (for NFS) */
+ uint32_t i_file_acl; /* File ACL */
+ uint32_t i_dir_acl; /* Directory ACL */
+ uint32_t i_faddr; /* Fragment address */
+ union {
+ struct {
+ uint8_t l_i_frag; /* Fragment number */
+ uint8_t l_i_fsize; /* Fragment size */
+ uint16_t i_pad1;
+ uint32_t l_i_reserved2[2];
+ } linux2;
+ struct {
+ uint8_t h_i_frag; /* Fragment number */
+ uint8_t h_i_fsize; /* Fragment size */
+ uint16_t h_i_mode_high;
+ uint16_t h_i_uid_high;
+ uint16_t h_i_gid_high;
+ uint32_t h_i_author;
+ } hurd2;
+ struct {
+ uint8_t m_i_frag; /* Fragment number */
+ uint8_t m_i_fsize; /* Fragment size */
+ uint16_t m_pad1;
+ uint32_t m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+
+#define i_size_high i_dir_acl
+
+struct ext2_super_block
+{
+ uint32_t s_inodes_count; /* Inodes count */
+ uint32_t s_blocks_count; /* Blocks count */
+ uint32_t s_r_blocks_count; /* Reserved blocks count */
+ uint32_t s_free_blocks_count; /* Free blocks count */
+ uint32_t s_free_inodes_count; /* Free inodes count */
+ uint32_t s_first_data_block; /* First Data Block */
+ uint32_t s_log_block_size; /* Block size */
+ int32_t s_log_frag_size; /* Fragment size */
+ uint32_t s_blocks_per_group; /* # Blocks per group */
+ uint32_t s_frags_per_group; /* # Fragments per group */
+ uint32_t s_inodes_per_group; /* # Inodes per group */
+ uint32_t s_mtime; /* Mount time */
+ uint32_t s_wtime; /* Write time */
+ uint16_t s_mnt_count; /* Mount count */
+ int16_t s_max_mnt_count; /* Maximal mount count */
+ uint16_t s_magic; /* Magic signature */
+ uint16_t s_state; /* File system state */
+ uint16_t s_errors; /* Behaviour when detecting errors */
+ uint16_t s_minor_rev_level; /* minor revision level */
+ uint32_t s_lastcheck; /* time of last check */
+ uint32_t s_checkinterval; /* max. time between checks */
+ uint32_t s_creator_os; /* OS */
+ uint32_t s_rev_level; /* Revision level */
+ uint16_t s_def_resuid; /* Default uid for reserved blocks */
+ uint16_t s_def_resgid; /* Default gid for reserved blocks */
+ /*
+ * These fields are for EXT2_DYNAMIC_REV superblocks only.
+ *
+ * Note: the difference between the compatible feature set and
+ * the incompatible feature set is that if there is a bit set
+ * in the incompatible feature set that the kernel doesn't
+ * know about, it should refuse to mount the file system.
+ *
+ * e2fsck's requirements are more strict; if it doesn't know
+ * about a feature in either the compatible or incompatible
+ * feature set, it must abort and not try to meddle with
+ * things it doesn't understand...
+ */
+ uint32_t s_first_ino; /* First non-reserved inode */
+ uint16_t s_inode_size; /* size of inode structure */
+ uint16_t s_block_group_nr; /* block group # of this superblock */
+ uint32_t s_feature_compat; /* compatible feature set */
+ uint32_t s_feature_incompat; /* incompatible feature set */
+ uint32_t s_feature_ro_compat; /* readonly-compatible feature set */
+ uint8_t s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ uint32_t s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_COMPAT_PREALLOC flag is on.
+ */
+ uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ uint16_t s_padding1;
+ /*
+ * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+ */
+ uint8_t s_journal_uuid[16]; /* uuid of journal superblock */
+ uint32_t s_journal_inum; /* inode number of journal file */
+ uint32_t s_journal_dev; /* device number of journal file */
+ uint32_t s_last_orphan; /* start of list of inodes to delete */
+
+ uint32_t s_reserved[197]; /* Padding to the end of the block */
+};
+
+#define EXT2_DIRENT_INODE(dir_ent) (PED_LE32_TO_CPU((dir_ent).inode))
+#define EXT2_DIRENT_REC_LEN(dir_ent) (PED_LE16_TO_CPU((dir_ent).rec_len))
+#define EXT2_DIRENT_NAME_LEN(dir_ent) ((dir_ent).name_len)
+#define EXT2_DIRENT_FILE_TYPE(dir_ent) ((dir_ent).file_type)
+
+#define EXT2_GROUP_BLOCK_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_block_bitmap))
+#define EXT2_GROUP_INODE_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_inode_bitmap))
+#define EXT2_GROUP_INODE_TABLE(gd) (PED_LE32_TO_CPU((gd).bg_inode_table))
+#define EXT2_GROUP_FREE_BLOCKS_COUNT(gd) \
+ (PED_LE16_TO_CPU((gd).bg_free_blocks_count))
+#define EXT2_GROUP_FREE_INODES_COUNT(gd) \
+ (PED_LE16_TO_CPU((gd).bg_free_inodes_count))
+#define EXT2_GROUP_USED_DIRS_COUNT(gd) \
+ (PED_LE16_TO_CPU((gd).bg_used_dirs_count))
+
+#define EXT2_INODE_MODE(inode) (PED_LE16_TO_CPU((inode).i_mode))
+#define EXT2_INODE_UID(inode) (PED_LE16_TO_CPU((inode).i_uid))
+#define EXT2_INODE_SIZE(inode) \
+ ((uint64_t) PED_LE32_TO_CPU((inode).i_size) \
+ + ((uint64_t) PED_LE32_TO_CPU((inode).i_size_high) << 32))
+#define EXT2_INODE_ATIME(inode) (PED_LE32_TO_CPU((inode).i_atime))
+#define EXT2_INODE_CTIME(inode) (PED_LE32_TO_CPU((inode).i_ctime))
+#define EXT2_INODE_MTIME(inode) (PED_LE32_TO_CPU((inode).i_mtime))
+#define EXT2_INODE_DTIME(inode) (PED_LE32_TO_CPU((inode).i_dtime))
+#define EXT2_INODE_GID(inode) (PED_LE16_TO_CPU((inode).i_gid))
+#define EXT2_INODE_LINKS_COUNT(inode) (PED_LE16_TO_CPU((inode).i_links_count))
+#define EXT2_INODE_BLOCKS(inode) (PED_LE32_TO_CPU((inode).i_blocks))
+#define EXT2_INODE_FLAGS(inode) (PED_LE32_TO_CPU((inode).i_flags))
+#define EXT2_INODE_TRANSLATOR(inode) (PED_LE32_TO_CPU((inode).osd1.hurd1.h_i_translator))
+#define EXT2_INODE_BLOCK(inode, blk) (PED_LE32_TO_CPU((inode).i_block[blk]))
+
+#define EXT2_SUPER_INODES_COUNT(sb) (PED_LE32_TO_CPU((sb).s_inodes_count))
+#define EXT2_SUPER_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_blocks_count))
+#define EXT2_SUPER_R_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_r_blocks_count))
+#define EXT2_SUPER_FREE_BLOCKS_COUNT(sb) \
+ (PED_LE32_TO_CPU((sb).s_free_blocks_count))
+#define EXT2_SUPER_FREE_INODES_COUNT(sb) \
+ (PED_LE32_TO_CPU((sb).s_free_inodes_count))
+#define EXT2_SUPER_FIRST_DATA_BLOCK(sb) \
+ (PED_LE32_TO_CPU((sb).s_first_data_block))
+#define EXT2_SUPER_LOG_BLOCK_SIZE(sb) (PED_LE32_TO_CPU((sb).s_log_block_size))
+#define EXT2_SUPER_LOG_FRAG_SIZE(sb) \
+ ((int32_t) PED_LE32_TO_CPU((sb).s_log_frag_size))
+#define EXT2_SUPER_BLOCKS_PER_GROUP(sb) \
+ (PED_LE32_TO_CPU((sb).s_blocks_per_group))
+#define EXT2_SUPER_FRAGS_PER_GROUP(sb) \
+ (PED_LE32_TO_CPU((sb).s_frags_per_group))
+#define EXT2_SUPER_INODES_PER_GROUP(sb) \
+ (PED_LE32_TO_CPU((sb).s_inodes_per_group))
+#define EXT2_SUPER_MTIME(sb) (PED_LE32_TO_CPU((sb).s_mtime))
+#define EXT2_SUPER_WTIME(sb) (PED_LE32_TO_CPU((sb).s_wtime))
+#define EXT2_SUPER_MNT_COUNT(sb) (PED_LE16_TO_CPU((sb).s_mnt_count))
+#define EXT2_SUPER_MAX_MNT_COUNT(sb) \
+ ((int16_t) PED_LE16_TO_CPU((sb).s_max_mnt_count))
+#define EXT2_SUPER_MAGIC(sb) (PED_LE16_TO_CPU((sb).s_magic))
+#define EXT2_SUPER_STATE(sb) (PED_LE16_TO_CPU((sb).s_state))
+#define EXT2_SUPER_ERRORS(sb) (PED_LE16_TO_CPU((sb).s_errors))
+#define EXT2_SUPER_MINOR_REV_LEVEL(sb) \
+ (PED_LE16_TO_CPU((sb).s_minor_rev_level))
+#define EXT2_SUPER_LASTCHECK(sb) (PED_LE32_TO_CPU((sb).s_lastcheck))
+#define EXT2_SUPER_CHECKINTERVAL(sb) (PED_LE32_TO_CPU((sb).s_checkinterval))
+#define EXT2_SUPER_CREATOR_OS(sb) (PED_LE32_TO_CPU((sb).s_creator_os))
+#define EXT2_SUPER_REV_LEVEL(sb) (PED_LE32_TO_CPU((sb).s_rev_level))
+#define EXT2_SUPER_DEF_RESUID(sb) (PED_LE16_TO_CPU((sb).s_def_resuid))
+#define EXT2_SUPER_DEF_RESGID(sb) (PED_LE16_TO_CPU((sb).s_def_resgid))
+
+#define EXT2_SUPER_FIRST_INO(sb) (PED_LE32_TO_CPU((sb).s_first_ino))
+#define EXT2_SUPER_INODE_SIZE(sb) (PED_LE16_TO_CPU((sb).s_inode_size))
+#define EXT2_SUPER_BLOCK_GROUP_NR(sb) (PED_LE16_TO_CPU((sb).s_block_group_nr))
+#define EXT2_SUPER_FEATURE_COMPAT(sb) (PED_LE32_TO_CPU((sb).s_feature_compat))
+#define EXT2_SUPER_FEATURE_INCOMPAT(sb) \
+ (PED_LE32_TO_CPU((sb).s_feature_incompat))
+#define EXT2_SUPER_FEATURE_RO_COMPAT(sb) \
+ (PED_LE32_TO_CPU((sb).s_feature_ro_compat))
+#define EXT2_SUPER_UUID(sb) ((sb).s_uuid)
+#define EXT2_SUPER_VOLUME_NAME(sb) ((sb).s_volume_name)
+#define EXT2_SUPER_LAST_MOUNTED(sb) ((sb).s_last_mounted)
+#define EXT2_SUPER_ALGORITHM_USAGE_BITMAP(sb) \
+ (PED_LE32_TO_CPU((sb).s_algorithm_usage_bitmap))
+
+#define EXT2_SUPER_JOURNAL_UUID(sb) ((sb).s_journal_uuid)
+#define EXT2_SUPER_JOURNAL_INUM(sb) (PED_LE32_TO_CPU((sb).s_journal_inum))
+#define EXT2_SUPER_JOURNAL_DEV(sb) (PED_LE32_TO_CPU((sb).s_journal_dev))
+#define EXT2_SUPER_LAST_ORPHAN(sb) (PED_LE32_TO_CPU((sb).s_last_orphan))
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_inode_relocator.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_inode_relocator.c
new file mode 100644
index 0000000000..45befbf9ce
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_inode_relocator.c
@@ -0,0 +1,599 @@
+/*
+ ext2_inode_relocator.c -- ext2 inode relocator
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h> /* for S_ISDIR */
+#include "ext2.h"
+
+
+
+
+
+
+struct ext2_reference
+{
+ blk_t block;
+ off_t offset;
+};
+
+struct ext2_inode_entry
+{
+ ino_t num;
+ ino_t dest;
+ unsigned numreferences:16;
+ unsigned isdir:1;
+ struct ext2_reference *ref;
+};
+
+struct ext2_inode_relocator_state
+{
+ int usedentries;
+ int resolvedentries;
+ struct ext2_inode_entry *inode;
+ struct ext2_reference *last;
+};
+
+
+
+
+
+static struct ext2_inode_entry *findit(struct ext2_inode_relocator_state *state, ino_t inode)
+{
+ int min;
+ int max;
+ struct ext2_inode_entry *retv;
+ int t;
+ blk_t tval;
+
+ max = state->usedentries - 1;
+ min = 0;
+ retv = NULL;
+
+ repeat:
+ if (min > max)
+ goto out;
+
+ t = (min + max) >> 1;
+ tval = state->inode[t].num;
+
+ t--;
+ if (tval > inode)
+ max = t;
+
+ t += 2;
+ if (tval < inode)
+ min = t;
+
+ t--;
+
+ if (tval != inode)
+ goto repeat;
+
+ retv = &state->inode[t];
+
+ out:
+ return retv;
+}
+
+static int addref(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode, blk_t block, off_t offset)
+{
+ struct ext2_inode_entry *ent;
+ int i;
+
+ if ((ent = findit(state, inode)) == NULL)
+ return 1;
+
+ for (i=0;i<ent->numreferences;i++)
+ if (!ent->ref[i].block)
+ break;
+
+ if (i == ent->numreferences)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Found an inode with a incorrect link count. "
+ "Better go run e2fsck first!"));
+ return 0;
+ }
+
+ if (i == ent->numreferences - 1)
+ state->resolvedentries++;
+
+ ent->ref[i].block = block;
+ ent->ref[i].offset = offset;
+
+ return 1;
+}
+
+static int doblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno)
+{
+ struct ext2_buffer_head *bh;
+ off_t offset;
+
+ bh = ext2_bread(fs, blockno);
+ if (!bh)
+ return 0;
+
+ offset = 0;
+ do
+ {
+ struct ext2_dir_entry_2 *ptr;
+
+ ptr = (struct ext2_dir_entry_2 *)(bh->data + offset);
+
+ if (ptr->name_len)
+ if (!addref(fs, state, EXT2_DIRENT_INODE(*ptr), blockno,
+ offset))
+ return 0;
+
+ PED_ASSERT (ptr->rec_len > 0, return 0);
+ offset += EXT2_DIRENT_REC_LEN (*ptr);
+ } while (offset < fs->blocksize);
+
+ ext2_brelse(bh, 0);
+ return 1;
+}
+
+static int doindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blk;
+ int i;
+
+ bh = ext2_bread(fs, blockno);
+
+ for (i=0;i<(fs->blocksize>>2);i++)
+ if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0)
+ if (!doblock(fs, state, blk))
+ return 0;
+
+ ext2_brelse(bh, 0);
+ return 1;
+}
+
+static int dodindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blk;
+ int i;
+
+ bh = ext2_bread(fs, blockno);
+ if (!bh)
+ return 0;
+
+ for (i=0;i<(fs->blocksize>>2);i++)
+ if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0)
+ if (!doindblock(fs, state, blk))
+ return 0;
+
+ ext2_brelse(bh, 0);
+ return 1;
+}
+
+static int dotindblock(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, blk_t blockno)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blk;
+ int i;
+
+ bh = ext2_bread(fs, blockno);
+ if (!bh)
+ return 0;
+
+ for (i=0;i<(fs->blocksize>>2);i++)
+ if ((blk = PED_LE32_TO_CPU(((uint32_t *)bh->data)[i])) != 0)
+ if (!dodindblock(fs, state, blk))
+ return 0;
+
+ ext2_brelse(bh, 0);
+ return 1;
+}
+
+static int doinode(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode)
+{
+ struct ext2_inode buf;
+ int i;
+
+ if (!ext2_read_inode(fs, inode, &buf))
+ return 0;
+ if (S_ISDIR(EXT2_INODE_MODE(buf)))
+ {
+ blk_t blk;
+
+ for (i=0;i<EXT2_NDIR_BLOCKS;i++)
+ if ((blk = EXT2_INODE_BLOCK(buf, i)) != 0)
+ if (!doblock(fs, state, blk))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_IND_BLOCK)) != 0)
+ if (!doindblock(fs, state, blk))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_DIND_BLOCK)) != 0)
+ if (!dodindblock(fs, state, blk))
+ return 0;
+
+ if ((blk = EXT2_INODE_BLOCK(buf, EXT2_TIND_BLOCK)) != 0)
+ if (!dotindblock(fs, state, blk))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int doscangroup(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, int group)
+{
+ struct ext2_buffer_head *bh;
+ unsigned int i;
+ int offset;
+
+ if (fs->opt_verbose)
+ fprintf(stderr, " scanning group %i.... ", group);
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]));
+ offset = group * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
+
+ for (i=0;i<EXT2_SUPER_INODES_PER_GROUP(fs->sb);i++)
+ if (bh->data[i>>3] & _bitmap[i&7])
+ {
+ if (!doinode(fs, state, offset + i))
+ {
+ ext2_brelse(bh, 0);
+ return 0;
+ }
+
+ if (state->resolvedentries == state->usedentries)
+ break;
+ }
+
+ ext2_brelse(bh, 0);
+
+ if (fs->opt_verbose)
+ fprintf(stderr,
+ "%i/%i inodes resolved\r",
+ state->resolvedentries,
+ state->usedentries);
+
+ return 1;
+}
+
+/* basically: this builds a dependency graph of the inodes in the entire file
+ * system. inodes are only referenced by the directory tree (or the magic
+ * ones implicitly, like the bad blocks inode), so we just walk the directory
+ * tree adding references.
+ */
+static int doscan(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ int i;
+
+ /* while the journal will usually be inode 8 (and therefore will never
+ * need to be moved), we don't have any guarantee (grrr). So, we
+ * need to be prepared to move it... (and update the reference in the
+ * super block)
+ */
+ if (fs->has_internal_journal)
+ addref(fs, state, EXT2_SUPER_JOURNAL_INUM(fs->sb),
+ 1, offsetof(struct ext2_super_block, s_journal_inum));
+
+ if (!doscangroup(fs, state, 0))
+ return 0;
+
+ if (state->resolvedentries != state->usedentries)
+ for (i=fs->numgroups-1;i>0;i--)
+ {
+ if (!doscangroup(fs, state, i))
+ return 0;
+
+ if (state->resolvedentries == state->usedentries)
+ break;
+ }
+
+ if (fs->opt_verbose)
+ fputc ('\n', stderr);
+
+ return 1;
+}
+
+
+
+
+
+
+
+static int ext2_inode_relocator_copy(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ int i;
+
+ for (i=0;i<state->usedentries;i++)
+ {
+ struct ext2_inode buf;
+ struct ext2_inode_entry *entry;
+
+ entry = &state->inode[i];
+
+ if (fs->opt_debug)
+ if (!ext2_get_inode_state(fs, entry->num) ||
+ ext2_get_inode_state(fs, entry->dest))
+ fputs ("inodebitmaperror\n", stderr);
+
+ if (!ext2_read_inode(fs, entry->num, &buf))
+ return 0;
+ if (!ext2_write_inode(fs, entry->dest, &buf))
+ return 0;
+
+ entry->isdir = S_ISDIR(EXT2_INODE_MODE(buf))?1:0;
+ }
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+ return 1;
+}
+
+static int ext2_inode_relocator_finish(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ int i;
+
+ for (i=0;i<state->usedentries;i++)
+ {
+ struct ext2_inode_entry *entry;
+
+ entry = &state->inode[i];
+ ext2_set_inode_state(fs, entry->dest, 1, 1);
+ ext2_set_inode_state(fs, entry->num, 0, 1);
+ ext2_zero_inode(fs, entry->num);
+ }
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+ return 1;
+}
+
+static int ext2_inode_relocator_ref(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ int i;
+ static int numerrors = 0;
+
+ for (i=0;i<state->usedentries;i++)
+ {
+ struct ext2_inode_entry *entry;
+ int j;
+ uint32_t t;
+
+ entry = &state->inode[i];
+ t = entry->dest;
+
+ for (j=0;j<entry->numreferences;j++)
+ {
+ struct ext2_buffer_head *bh;
+
+ bh = ext2_bread(fs, entry->ref[j].block);
+ if (!bh)
+ return 0;
+
+ if (fs->opt_debug)
+ {
+ if (PED_LE32_TO_CPU((*((uint32_t *)(bh->data + entry->ref[j].offset)))) != entry->num)
+ {
+ fprintf(stderr,
+ "inode %li ref error! (->%li, [%i]={%i, %i})\n",
+ (long) entry->num,
+ (long) entry->dest,
+ j,
+ entry->ref[j].block,
+ (int) entry->ref[j].offset);
+ ext2_brelse(bh, 0);
+
+ if (numerrors++ < 4)
+ continue;
+
+ fputs ("all is not well!\n", stderr);
+ return 0;
+ }
+ }
+
+ *((uint32_t *)(bh->data + entry->ref[j].offset))
+ = PED_CPU_TO_LE32(t);
+ bh->dirty = 1;
+
+ ext2_brelse(bh, 0);
+ }
+
+ if (entry->isdir)
+ {
+ int oldgroup;
+ int newgroup;
+
+ oldgroup = (entry->num - 1)
+ / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+ newgroup = (entry->dest - 1)
+ / EXT2_SUPER_INODES_PER_GROUP(fs->sb);
+
+ fs->gd[oldgroup].bg_used_dirs_count = PED_CPU_TO_LE16 (
+ EXT2_GROUP_USED_DIRS_COUNT(fs->gd[oldgroup])
+ - 1);
+ fs->gd[newgroup].bg_used_dirs_count = PED_CPU_TO_LE16 (
+ EXT2_GROUP_USED_DIRS_COUNT(fs->gd[newgroup])
+ + 1);
+
+ fs->metadirty = EXT2_META_GD;
+ }
+ }
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ return 1;
+}
+
+static int ext2_inode_relocator_grab_inodes(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ int i;
+ int ptr;
+
+ ptr = 0;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[i]))
+ {
+ struct ext2_buffer_head *bh;
+ unsigned int j;
+ int offset;
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i]));
+ if (!bh)
+ return 0;
+ offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
+
+ j = i ? 0 : 13;
+ for (;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
+ if (!(bh->data[j>>3] & _bitmap[j&7]))
+ {
+ state->inode[ptr++].dest = offset + j;
+
+ if (ptr == state->usedentries)
+ {
+ ext2_brelse(bh, 0);
+ return 1;
+ }
+ }
+
+ ext2_brelse(bh, 0);
+ }
+
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Not enough free inodes!"));
+ return 0;
+}
+
+static int ext2_inode_relocator_flush(struct ext2_fs *fs, struct ext2_inode_relocator_state *state)
+{
+ if (!state->usedentries)
+ return 1;
+
+ if (!doscan(fs, state))
+ return 0;
+
+ if (!ext2_inode_relocator_grab_inodes(fs, state))
+ return 0;
+
+ if (!ext2_inode_relocator_copy(fs, state))
+ return 0;
+
+ if (!ext2_inode_relocator_ref(fs, state))
+ return 0;
+
+ if (!ext2_inode_relocator_finish(fs, state))
+ return 0;
+
+ state->usedentries = 0;
+ state->resolvedentries = 0;
+ state->last = (struct ext2_reference *)fs->relocator_pool_end;
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ return 1;
+}
+
+static int ext2_inode_relocator_mark(struct ext2_fs *fs, struct ext2_inode_relocator_state *state, ino_t inode)
+{
+ struct ext2_inode buf;
+ struct ext2_inode_entry *ent;
+ int i;
+
+ if (!ext2_read_inode(fs, inode, &buf))
+ return 0;
+
+ {
+ register void *adv;
+ register void *rec;
+
+ adv = state->inode + state->usedentries + 1;
+ rec = state->last - EXT2_INODE_LINKS_COUNT(buf);
+
+ if (adv >= rec)
+ ext2_inode_relocator_flush(fs, state);
+ }
+
+ state->last -= EXT2_INODE_LINKS_COUNT(buf);
+
+ ent = &state->inode[state->usedentries];
+ ent->num = inode;
+ ent->dest = 0;
+ ent->numreferences = EXT2_INODE_LINKS_COUNT(buf);
+ ent->ref = state->last;
+
+ for (i=0;i<ent->numreferences;i++)
+ {
+ ent->ref[i].block = 0;
+ ent->ref[i].offset = 0;
+ }
+
+ state->usedentries++;
+
+ return 1;
+}
+
+
+int ext2_inode_relocate(struct ext2_fs *fs, int newgroups)
+{
+ int i;
+ struct ext2_inode_relocator_state state;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_inode_relocate\n", stderr);
+
+ state.usedentries = 0;
+ state.resolvedentries = 0;
+ state.inode = (struct ext2_inode_entry *)fs->relocator_pool;
+ state.last = (struct ext2_reference *)fs->relocator_pool_end;
+
+ for (i=newgroups;i<fs->numgroups;i++)
+ {
+ struct ext2_buffer_head *bh;
+ unsigned int j;
+ int offset;
+
+ bh = ext2_bread(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[i]));
+ if (!bh)
+ return 0;
+ offset = i * EXT2_SUPER_INODES_PER_GROUP(fs->sb) + 1;
+
+ for (j=0;j<EXT2_SUPER_INODES_PER_GROUP(fs->sb);j++)
+ if (bh->data[j>>3] & _bitmap[j&7])
+ ext2_inode_relocator_mark(fs, &state,
+ offset + j);
+
+ ext2_brelse(bh, 0);
+ }
+
+ if (!ext2_inode_relocator_flush(fs, &state))
+ return 0;
+
+ return 1;
+}
+#endif /* !DISCOVER_ONLY */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_meta.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_meta.c
new file mode 100644
index 0000000000..09fb8ad5e1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_meta.c
@@ -0,0 +1,145 @@
+/*
+ ext2_meta.c -- ext2 metadata mover
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "ext2.h"
+
+int ext2_metadata_push(struct ext2_fs *fs, blk_t newsize)
+{
+ int i;
+ int newgdblocks;
+ blk_t newitoffset;
+
+ newgdblocks = ped_div_round_up (newsize
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ newgdblocks = ped_div_round_up (newgdblocks
+ * sizeof(struct ext2_group_desc),
+ fs->blocksize);
+ newitoffset = newgdblocks + 3;
+
+ if (newitoffset <= fs->itoffset)
+ return 1;
+
+ for (i=0;i<fs->numgroups;i++)
+ {
+ blk_t diff;
+ blk_t j;
+ blk_t fromblock;
+ blk_t start;
+
+ start = (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+
+ if (EXT2_GROUP_INODE_TABLE(fs->gd[i]) >= start + newitoffset
+ && EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) >= start + newitoffset - 2
+ && EXT2_GROUP_INODE_BITMAP(fs->gd[i]) >= start + newitoffset - 1)
+ continue;
+
+ diff = newitoffset - (EXT2_GROUP_INODE_TABLE(fs->gd[i]) - start);
+
+ /* inode table */
+ fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]) + fs->inodeblocks;
+
+ if (fs->opt_debug)
+ {
+ for (j=0;j<diff;j++)
+ if (!ext2_get_block_state(fs, fromblock+j))
+ {
+ fprintf(stderr,
+ "error: block relocator "
+ "should have relocated "
+ "%i\n",
+ fromblock);
+
+ return 0;
+ }
+ }
+
+ for (j=0;j<diff;j++)
+ if (!ext2_set_block_state(fs, fromblock+j, 1, 0))
+ return 0;
+
+ if (!ext2_move_blocks(fs,
+ EXT2_GROUP_INODE_TABLE(fs->gd[i]),
+ fs->inodeblocks,
+ EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff))
+ return 0;
+ fs->gd[i].bg_inode_table = PED_CPU_TO_LE32 (
+ EXT2_GROUP_INODE_TABLE(fs->gd[i]) + diff);
+ fs->metadirty |= EXT2_META_GD;
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ /* block bitmap and inode bitmap */
+ fromblock = EXT2_GROUP_INODE_TABLE(fs->gd[i]);
+ if (ext2_is_group_sparse(fs, i))
+ {
+ if (!ext2_copy_block(fs,
+ EXT2_GROUP_INODE_BITMAP(fs->gd[i]),
+ EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff))
+ return 0;
+ fs->gd[i].bg_inode_bitmap = PED_CPU_TO_LE32 (
+ EXT2_GROUP_INODE_BITMAP(fs->gd[i]) + diff);
+ fs->metadirty |= EXT2_META_GD;
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ if (!ext2_copy_block(fs,
+ EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]),
+ EXT2_GROUP_BLOCK_BITMAP(fs->gd[i])+diff))
+ return 0;
+ fs->gd[i].bg_block_bitmap = PED_CPU_TO_LE32 (
+ EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) + diff);
+ fs->metadirty |= EXT2_META_GD;
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ fromblock = EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]);
+ }
+
+ ext2_zero_blocks(fs, fromblock-diff, diff);
+ for (j=0;j<diff;j++)
+ if (!ext2_set_block_state(fs, fromblock+j-diff, 0, 0))
+ return 0;
+
+ if (fs->opt_verbose)
+ fprintf(stderr,
+ "ext2_metadata_push: group %i/%i\r",
+ i+1, fs->numgroups);
+ }
+
+ fs->itoffset = newitoffset;
+
+ if (fs->opt_verbose)
+ fputc ('\n', stderr);
+
+ return 1;
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_mkfs.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_mkfs.c
new file mode 100644
index 0000000000..19931dd0f3
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_mkfs.c
@@ -0,0 +1,632 @@
+/*
+ ext2_mkfs.c -- ext2 fs creator
+ Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#define USE_EXT2_IS_DATA_BLOCK
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+#include "ext2.h"
+
+/* formula grabbed from linux ext2 kernel source
+ *
+ * returns 1 iff:
+ * x == y^N, N is some natural number
+ * OR x == 0
+ */
+static __inline__ int is_root(int x, int y)
+{
+ if (!x) return 1;
+
+ while (1)
+ {
+ if (x == 1) return 1;
+
+ if (x % y) return 0;
+
+ x /= y;
+ }
+}
+
+static __inline__ int is_group_sparse(int sparsesbfs, int group)
+{
+ if (!sparsesbfs)
+ return 1;
+
+ if (is_root(group, 3) || is_root(group, 5) || is_root(group, 7))
+ return 1;
+
+ return 0;
+}
+
+/* has implicit parameter 'sb' !! */
+#define is_sparse(group) is_group_sparse(EXT2_SUPER_FEATURE_RO_COMPAT(*sb) \
+ & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER, (group))
+
+static int ext2_mkfs_write_main(struct ext2_dev_handle *handle,
+ struct ext2_super_block *sb,
+ struct ext2_group_desc *gd)
+{
+ int freeit;
+ int i;
+ int numgroups;
+ int gdblocks;
+ unsigned char *sbbuf;
+ struct ext2_super_block *sb_for_io;
+
+ freeit = 0;
+ sbbuf = (unsigned char *)sb;
+ sb_for_io = sb;
+ if (EXT2_SUPER_LOG_BLOCK_SIZE(*sb))
+ {
+ sbbuf = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+ if (!(handle->ops->read)(handle->cookie, sbbuf, 0, 1))
+ return 0;
+ memcpy (sbbuf+1024, sb, 1024);
+ freeit = 1;
+ sb_for_io = (struct ext2_super_block*) (sbbuf + 1024);
+ }
+
+ numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(*sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(*sb));
+ gdblocks = ped_div_round_up (numgroups * sizeof(struct ext2_group_desc),
+ 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+
+ for (i=0;i<numgroups;i++)
+ {
+ if (is_sparse(i))
+ {
+ int offset;
+
+ offset = EXT2_SUPER_FIRST_DATA_BLOCK(*sb)
+ + i * EXT2_SUPER_BLOCKS_PER_GROUP(*sb);
+
+ sb_for_io->s_block_group_nr = PED_CPU_TO_LE16 (i);
+
+ if (!handle->ops->write(handle->cookie, sbbuf,
+ offset, 1))
+ return 0;
+ if (!handle->ops->write(handle->cookie, gd, offset+1,
+ gdblocks))
+ return 0;
+ }
+ }
+
+ sb_for_io->s_block_group_nr = 0;
+
+ if (freeit)
+ ped_free(sbbuf);
+ return 1;
+}
+
+static int ext2_mkfs_write_meta(struct ext2_dev_handle *handle,
+ struct ext2_super_block *sb,
+ struct ext2_group_desc *gd,
+ PedTimer* timer)
+{
+ int blocksize;
+ int gdtsize;
+ int i;
+ int itsize;
+ int numgroups;
+ unsigned char *bb;
+ unsigned char *ib;
+ unsigned char *zero;
+
+ blocksize = 1 << (EXT2_SUPER_LOG_BLOCK_SIZE(*sb) + 13);
+
+ numgroups = ped_div_round_up (EXT2_SUPER_BLOCKS_COUNT(*sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(*sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(*sb));
+ itsize = ped_div_round_up (sizeof(struct ext2_inode)
+ * EXT2_SUPER_INODES_PER_GROUP(*sb),
+ (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)));
+ gdtsize = ped_div_round_up (sizeof(struct ext2_group_desc) * numgroups,
+ (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)));
+
+ bb = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+ if (!bb) goto error;
+ ib = ped_malloc(1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+ if (!ib) goto error_free_bb;
+ zero = ped_malloc((1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize);
+ if (!zero) goto error_free_zero;
+
+ memset(zero, 0, (1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb)) * itsize);
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("writing per-group metadata"));
+
+ for (i=0;i<numgroups;i++)
+ {
+ int admin;
+ blk_t bbblock;
+ int groupsize;
+ int groupoffset;
+ blk_t ibblock;
+ int j;
+
+ ped_timer_update (timer, 1.0 * i / numgroups);
+
+ groupoffset = i*EXT2_SUPER_BLOCKS_PER_GROUP(*sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(*sb);
+ groupsize = PED_MIN(EXT2_SUPER_BLOCKS_COUNT(*sb) - groupoffset,
+ EXT2_SUPER_BLOCKS_PER_GROUP(*sb));
+
+ admin = itsize + 2;
+ bbblock = groupoffset;
+ ibblock = groupoffset + 1;
+ if (is_sparse(i))
+ {
+ admin += gdtsize + 1;
+ bbblock = groupoffset + gdtsize + 1;
+ ibblock = groupoffset + gdtsize + 2;
+ }
+
+ {
+ memset(bb, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+ if (is_sparse(i))
+ for (j=0;j<gdtsize+1;j++)
+ bb[j>>3] |= _bitmap[j&7];
+
+ j = bbblock - groupoffset;
+ bb[j>>3] |= _bitmap[j&7];
+
+ j = ibblock - groupoffset;
+ bb[j>>3] |= _bitmap[j&7];
+
+ for (j=0;j<itsize;j++)
+ {
+ int k = j + gdtsize + 3;
+
+ bb[k>>3] |= _bitmap[k&7];
+ }
+
+ for (j=groupsize;j<blocksize;j++)
+ bb[j>>3] |= _bitmap[j&7];
+
+ if (!handle->ops->write(handle->cookie, bb, bbblock, 1))
+ goto error_free_zero;
+ }
+
+ {
+ memset(ib, 0, 1024 << EXT2_SUPER_LOG_BLOCK_SIZE(*sb));
+
+ for (j=EXT2_SUPER_INODES_PER_GROUP(*sb);j<blocksize;j++)
+ bb[j>>3] |= _bitmap[j&7];
+
+ if (!handle->ops->write(handle->cookie, ib, ibblock, 1))
+ goto error_free_zero;
+ }
+
+ if (!handle->ops->write(handle->cookie, zero,
+ groupoffset + gdtsize + 3, itsize))
+ goto error_free_zero;
+
+ gd[i].bg_block_bitmap = PED_CPU_TO_LE32(bbblock);
+ gd[i].bg_inode_bitmap = PED_CPU_TO_LE32(ibblock);
+ gd[i].bg_inode_table = PED_CPU_TO_LE32(groupoffset + gdtsize
+ + 3);
+ gd[i].bg_free_blocks_count = PED_CPU_TO_LE16(groupsize - admin);
+ gd[i].bg_free_inodes_count = PED_CPU_TO_LE16(
+ EXT2_SUPER_INODES_PER_GROUP(*sb));
+ gd[i].bg_used_dirs_count = 0;
+ gd[i].bg_used_dirs_count = 0;
+ gd[i].bg_pad = 0;
+ gd[i].bg_reserved[0] = 0;
+ gd[i].bg_reserved[1] = 0;
+ gd[i].bg_reserved[2] = 0;
+
+ sb->s_free_blocks_count = PED_CPU_TO_LE32 (
+ EXT2_SUPER_FREE_BLOCKS_COUNT(*sb)
+ + EXT2_GROUP_FREE_BLOCKS_COUNT(gd[i]));
+ }
+
+ ped_timer_update (timer, 1.0);
+
+ ped_free(zero);
+ ped_free(ib);
+ ped_free(bb);
+ return 1;
+
+error_free_zero:
+ ped_free(zero);
+ ped_free(ib);
+error_free_bb:
+ ped_free(bb);
+error:
+ return 0;
+}
+
+/* returns the offset into the buffer of the start of the next dir entry */
+static int _set_dirent(void* buf, int offset, int block_size, int is_last,
+ uint32_t inode, char* name, int file_type)
+{
+ struct ext2_dir_entry_2 *dirent = (void*) (((char*)buf) + offset);
+ int name_len = strlen(name);
+ int rec_len;
+
+ if (is_last)
+ rec_len = block_size - offset;
+ else
+ rec_len = ped_round_up_to(name_len + 1 + 8, 4);
+
+ memset (dirent, 0, rec_len);
+
+ dirent->inode = PED_CPU_TO_LE32(inode);
+ dirent->name_len = name_len;
+ dirent->rec_len = PED_CPU_TO_LE16(rec_len);
+ dirent->file_type = file_type;
+ strcpy(dirent->name, name);
+
+ return offset + rec_len;
+}
+
+static int ext2_mkfs_create_lost_and_found_inode(struct ext2_fs *fs)
+{
+ struct ext2_buffer_head *bh;
+ blk_t blocks[12];
+ uint32_t* data = ped_malloc ((fs->blocksize / 4) * sizeof(uint32_t));
+ int i;
+ struct ext2_inode inode;
+ int offset;
+
+ for (i=0;i<12;i++)
+ {
+ if (!(blocks[i] = ext2_find_free_block(fs)))
+ return 0;
+
+ if (!ext2_set_block_state(fs, blocks[i], 1, 1))
+ return 0;
+ }
+
+ /* create the directory entries, preallocating lots of blocks */
+ /* first block contains . and .. */
+ bh = ext2_bcreate(fs, blocks[0]);
+ if (!bh)
+ return 0;
+ memset(bh->data, 0, fs->blocksize);
+ offset = _set_dirent(bh->data, 0, fs->blocksize, 0,
+ 11, ".", EXT2_FT_DIR);
+ offset = _set_dirent(bh->data, offset, fs->blocksize, 1,
+ EXT2_ROOT_INO, "..", EXT2_FT_DIR);
+ bh->dirty = 1;
+ ext2_brelse(bh, 1);
+
+ /* subsequent blocks are empty */
+ memset(data, 0, fs->blocksize);
+ data[0] = 0;
+ data[1] = PED_CPU_TO_LE32(fs->blocksize);
+ for (i=1;i<12;i++)
+ {
+ bh = ext2_bcreate(fs, blocks[i]);
+ memcpy(bh->data, data, fs->blocksize);
+ bh->dirty = 1;
+ ext2_brelse(bh, 1);
+ }
+
+ /* create inode */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755);
+ inode.i_uid = 0;
+ inode.i_size = PED_CPU_TO_LE32(12 * fs->blocksize);
+ inode.i_atime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_ctime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_mtime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_dtime = 0;
+ inode.i_gid = 0;
+ inode.i_links_count = PED_CPU_TO_LE16(2);
+ inode.i_blocks = PED_CPU_TO_LE32((12 * fs->blocksize) >> 9);
+ inode.i_flags = 0;
+ for (i=0;i<12;i++)
+ inode.i_block[i] = PED_CPU_TO_LE32(blocks[i]);
+
+ if (!ext2_write_inode(fs, 11, &inode))
+ return 0;
+ fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16(
+ EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1);
+ fs->metadirty |= EXT2_META_GD;
+
+ return 1;
+}
+
+static int ext2_mkfs_create_root_inode(struct ext2_fs *fs)
+{
+ struct ext2_buffer_head *bh;
+ blk_t block;
+ struct ext2_inode inode;
+ int offset;
+
+ if (!(block = ext2_find_free_block(fs)))
+ return 0;
+ if (!ext2_set_block_state(fs, block, 1, 1))
+ return 0;
+
+ /* create directory entries */
+ bh = ext2_bcreate(fs, block);
+ memset(bh->data, 0, fs->blocksize);
+ offset = _set_dirent(bh->data, 0, fs->blocksize, 0,
+ EXT2_ROOT_INO, ".", EXT2_FT_DIR);
+ offset = _set_dirent(bh->data, offset, fs->blocksize, 0,
+ EXT2_ROOT_INO, "..", EXT2_FT_DIR);
+ offset = _set_dirent(bh->data, offset, fs->blocksize, 1,
+ 11, "lost+found", EXT2_FT_DIR);
+ bh->dirty = 1;
+ if (!ext2_brelse(bh, 1))
+ return 0;
+
+ /* create inode */
+ memset(&inode, 0, sizeof(struct ext2_inode));
+ inode.i_mode = PED_CPU_TO_LE16(S_IFDIR | 0755);
+ inode.i_uid = 0;
+ inode.i_size = PED_CPU_TO_LE32(fs->blocksize);
+ inode.i_atime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_ctime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_mtime = PED_CPU_TO_LE32(time(NULL));
+ inode.i_dtime = 0;
+ inode.i_gid = 0;
+ inode.i_links_count = PED_CPU_TO_LE16(3);
+ inode.i_blocks = PED_CPU_TO_LE32(fs->blocksize >> 9);
+ inode.i_flags = 0;
+ inode.i_block[0] = PED_CPU_TO_LE32(block);
+
+ if (!ext2_write_inode(fs, 2, &inode))
+ return 0;
+ fs->gd[0].bg_used_dirs_count = PED_CPU_TO_LE16 (
+ EXT2_GROUP_USED_DIRS_COUNT(fs->gd[0]) + 1);
+ fs->metadirty |= EXT2_META_GD;
+
+ return 1;
+}
+
+static int ext2_reserve_inodes(struct ext2_fs *fs)
+{
+ int i;
+
+ for (i=1;i<12;i++)
+ if (!ext2_set_inode_state(fs, i, 1, 1))
+ return 0;
+ return 1;
+}
+
+static int ext2_mkfs_init_sb (struct ext2_super_block *sb, blk_t numblocks,
+ int numgroups, int first_block,
+ int log_block_size, blk_t blocks_per_group,
+ int inodes_per_group, int sparse_sb,
+ int reserved_block_percentage)
+{
+ /* catch a bug in gcc 2.95.2 */
+ PED_ASSERT(numgroups != 0, return 0);
+
+ memset(sb, 0, 1024);
+
+ sb->s_inodes_count = PED_CPU_TO_LE32(numgroups * inodes_per_group);
+ sb->s_blocks_count = PED_CPU_TO_LE32(numblocks);
+ sb->s_r_blocks_count = PED_CPU_TO_LE32(((uint64_t)numblocks
+ * reserved_block_percentage) / 100);
+
+ /* hack: this get's inc'd as we go through each group in
+ * ext2_mkfs_write_meta()
+ */
+ sb->s_free_blocks_count = 0;
+ sb->s_free_inodes_count = PED_CPU_TO_LE32 (numgroups
+ * inodes_per_group);
+ sb->s_first_data_block = PED_CPU_TO_LE32(first_block);
+ sb->s_log_block_size = PED_CPU_TO_LE32(log_block_size - 10);
+ sb->s_log_frag_size = sb->s_log_block_size;
+ sb->s_blocks_per_group = PED_CPU_TO_LE32(blocks_per_group);
+ sb->s_frags_per_group = PED_CPU_TO_LE32(blocks_per_group);
+ sb->s_inodes_per_group = PED_CPU_TO_LE32(inodes_per_group);
+ sb->s_mtime = 0;
+ sb->s_wtime = 0;
+ sb->s_mnt_count = 0;
+ sb->s_max_mnt_count = PED_CPU_TO_LE16(30);
+ sb->s_magic = PED_CPU_TO_LE16(0xEF53);
+ sb->s_state = PED_CPU_TO_LE16(EXT2_VALID_FS);
+ sb->s_errors = PED_CPU_TO_LE16(EXT2_ERRORS_DEFAULT);
+ sb->s_minor_rev_level = 0;
+ sb->s_lastcheck = 0;
+ sb->s_checkinterval = 0;
+ sb->s_creator_os = 0;
+ sb->s_rev_level = PED_CPU_TO_LE32(1);
+ sb->s_def_resuid = 0;
+ sb->s_def_resgid = 0;
+ sb->s_first_ino = PED_CPU_TO_LE32(11);
+ sb->s_inode_size = PED_CPU_TO_LE16(128);
+ sb->s_block_group_nr = 0;
+ sb->s_feature_compat = 0;
+ sb->s_feature_incompat = 0;
+ sb->s_feature_ro_compat = 0;
+ if (sparse_sb)
+ sb->s_feature_ro_compat
+ |= PED_CPU_TO_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+
+/* FIXME: let the user decide? _set_dirent() assumes FILETYPE */
+ sb->s_feature_incompat
+ |= PED_CPU_TO_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE);
+
+ uuid_generate(sb->s_uuid);
+ memset(sb->s_volume_name, 0, 16);
+ memset(sb->s_last_mounted, 0, 64);
+ sb->s_algorithm_usage_bitmap = 0;
+ sb->s_prealloc_blocks = 0;
+ sb->s_prealloc_dir_blocks = 0;
+ sb->s_padding1 = 0;
+
+ return 1;
+}
+
+/* Given these five inputs, compute the three outputs. */
+static void
+compute_block_counts (blk_t numblocks, int numgroups, int log_block_size,
+ int sparse_sb, blk_t blocks_per_group,
+ int *last_group_blocks,
+ int *last_group_admin,
+ int *inodes_per_group)
+{
+ int first_block = (log_block_size == 10) ? 1 : 0;
+ size_t block_size = 1 << log_block_size;
+
+ *last_group_blocks = ((numblocks - first_block) % blocks_per_group);
+ if (!*last_group_blocks)
+ *last_group_blocks = blocks_per_group;
+ *inodes_per_group = ped_round_up_to (numblocks / numgroups / 2,
+ (block_size
+ / sizeof(struct ext2_inode)));
+ *last_group_admin = (2 + *inodes_per_group * sizeof(struct ext2_inode)
+ / block_size);
+ if (is_group_sparse(sparse_sb, numgroups - 1)) {
+ *last_group_admin +=
+ (ped_div_round_up (numgroups * sizeof(struct ext2_group_desc),
+ block_size));
+ }
+}
+
+struct ext2_fs *ext2_mkfs(struct ext2_dev_handle *handle,
+ blk_t numblocks,
+ int log_block_size,
+ blk_t blocks_per_group,
+ int inodes_per_group,
+ int sparse_sb,
+ int reserved_block_percentage,
+ PedTimer* timer)
+{
+ struct ext2_fs *fs;
+ struct ext2_super_block sb;
+ struct ext2_group_desc *gd;
+ int numgroups;
+ int first_block;
+ int last_group_blocks;
+ int last_group_admin;
+
+ /* if the FS is > 512Mb, use 4k blocks, otherwise 1k blocks */
+ if (log_block_size == 0) {
+ handle->ops->set_blocksize(handle->cookie, 12);
+ if (handle->ops->get_size(handle->cookie) > (512 * 1024))
+ log_block_size = 12;
+ else
+ log_block_size = 10;
+ }
+
+ /* FIXME: block size must be > MAX(logicalbs, physicalbs)
+ * to avoid modify-on-write.
+ * -- Leslie
+ */
+
+
+ handle->ops->set_blocksize(handle->cookie, log_block_size);
+
+ if (numblocks == 0)
+ numblocks = handle->ops->get_size(handle->cookie);
+ if (numblocks == 0)
+ goto diagnose_fs_too_small;
+
+ if (blocks_per_group == (unsigned int) 0)
+ blocks_per_group = 8 << log_block_size;
+
+ first_block = (log_block_size == 10) ? 1 : 0;
+
+ numgroups = ped_div_round_up (numblocks
+ - first_block, blocks_per_group);
+
+ if (sparse_sb == -1)
+ sparse_sb = 1;
+
+ /* FIXME: 5% not appropriate for modern drive sizes */
+ if (reserved_block_percentage == -1)
+ reserved_block_percentage = 5;
+
+ compute_block_counts (numblocks, numgroups, log_block_size, sparse_sb,
+ blocks_per_group, &last_group_blocks,
+ &last_group_admin, &inodes_per_group);
+
+ int fs_too_small = 0;
+ if (last_group_admin + 1 >= last_group_blocks)
+ {
+ numgroups--;
+ if (numgroups == 0)
+ fs_too_small = 1;
+ else
+ {
+ numblocks -= last_group_blocks;
+ compute_block_counts (numblocks, numgroups, log_block_size,
+ sparse_sb, blocks_per_group,
+ &last_group_blocks, &last_group_admin,
+ &inodes_per_group);
+ }
+ }
+
+ if (numgroups == 1
+ && (last_group_blocks - last_group_admin < 8
+ || inodes_per_group < 16
+ /* This final term ensures that we detect
+ mkpartfs primary ext2 10KB 27650B as invalid. */
+ || (inodes_per_group == 16
+ && last_group_blocks - last_group_admin < 14)))
+ fs_too_small = 1;
+
+ if (fs_too_small) {
+ diagnose_fs_too_small:
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("File system too small for ext2."));
+ goto error;
+ }
+
+ gd = ped_malloc(numgroups * sizeof(struct ext2_group_desc)
+ + (1 << log_block_size));
+ if (!gd)
+ goto error;
+
+ if (!ext2_mkfs_init_sb(&sb, numblocks, numgroups, first_block,
+ log_block_size, blocks_per_group,
+ inodes_per_group, sparse_sb,
+ reserved_block_percentage))
+ goto error_free_gd;
+ if (!ext2_mkfs_write_meta(handle, &sb, gd, timer))
+ goto error_free_gd;
+ if (!ext2_mkfs_write_main(handle, &sb, gd))
+ goto error_free_gd;
+
+ fs = ext2_open(handle, 0);
+ if (!fs) goto error_close_fs;
+ if (!ext2_reserve_inodes(fs)) goto error_close_fs;
+ if (!ext2_mkfs_create_root_inode(fs)) goto error_close_fs;
+ if (!ext2_mkfs_create_lost_and_found_inode(fs))
+ goto error_close_fs;
+ if (!ext2_sync(fs)) goto error_close_fs;
+ ped_free(gd);
+ return fs;
+
+error_close_fs:
+ ext2_close(fs);
+error_free_gd:
+ ped_free (gd);
+error:
+ return NULL;
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_resize.c b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_resize.c
new file mode 100644
index 0000000000..580c46606e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/ext2_resize.c
@@ -0,0 +1,730 @@
+/*
+ ext2_resize.c -- ext2 resizer
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "ext2.h"
+
+static int ext2_add_group(struct ext2_fs *fs, blk_t groupsize)
+{
+ blk_t admin;
+ int group;
+ blk_t groupstart;
+ blk_t newgdblocks;
+ int sparse;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_add_group\n", stderr);
+
+ if (!ped_realloc ((void*) &fs->gd,
+ (fs->numgroups+1) * sizeof(struct ext2_group_desc)
+ + fs->blocksize))
+ return 0;
+
+ if (fs->opt_debug)
+ {
+ if (EXT2_SUPER_BLOCKS_COUNT(fs->sb) !=
+ EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ + fs->numgroups * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ {
+ fputs ("ext2_add_group: last (existing) group "
+ "isn't complete!\n", stderr);
+
+ return 0;
+ }
+ }
+
+ group = fs->numgroups;
+ sparse = ext2_is_group_sparse(fs, group);
+ groupstart = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ + group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ admin = fs->adminblocks;
+ if (!sparse)
+ admin -= fs->gdblocks + 1;
+
+ if (fs->opt_debug)
+ {
+ if (groupsize < fs->adminblocks ||
+ groupsize > EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ {
+ fprintf(stderr,
+ "ext2_add_group: groups of %i blocks are "
+ "impossible!\n", groupsize);
+
+ return 0;
+ }
+ }
+
+ newgdblocks = ped_div_round_up((fs->numgroups + 1)
+ * sizeof(struct ext2_group_desc),
+ fs->blocksize);
+ if (newgdblocks != fs->gdblocks)
+ {
+ int i;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (ext2_is_group_sparse(fs, i))
+ {
+ blk_t start;
+
+ start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ + i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ ext2_set_block_state(fs,
+ start + fs->gdblocks + 1, 1, 1);
+ }
+
+ fs->gdblocks++;
+ fs->adminblocks++;
+ if (sparse)
+ admin++;
+ }
+
+ fs->numgroups++;
+
+ fs->sb.s_inodes_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_INODES_COUNT(fs->sb)
+ + EXT2_SUPER_INODES_PER_GROUP(fs->sb));
+ fs->sb.s_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_BLOCKS_COUNT(fs->sb) + groupsize);
+ fs->sb.s_free_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) + groupsize - admin);
+ fs->sb.s_free_inodes_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_FREE_INODES_COUNT(fs->sb)
+ + EXT2_SUPER_INODES_PER_GROUP(fs->sb));
+ fs->metadirty |= EXT2_META_SB;
+
+ {
+ blk_t off;
+ blk_t sparseoff;
+
+ off = groupstart;
+ sparseoff = off + fs->itoffset - 2;
+
+ if (sparse)
+ {
+ fs->gd[group].bg_block_bitmap
+ = PED_CPU_TO_LE32(sparseoff);
+ fs->gd[group].bg_inode_bitmap
+ = PED_CPU_TO_LE32(sparseoff + 1);
+ }
+ else
+ {
+ fs->gd[group].bg_block_bitmap
+ = PED_CPU_TO_LE32(off);
+ fs->gd[group].bg_inode_bitmap
+ = PED_CPU_TO_LE32(off + 1);
+ }
+
+ /* Hey, I don't know _why_ either */
+ fs->gd[group].bg_inode_table = PED_CPU_TO_LE32(sparseoff + 2);
+ }
+
+ fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16(groupsize - admin);
+ fs->gd[group].bg_free_inodes_count = PED_CPU_TO_LE16(
+ EXT2_SUPER_INODES_PER_GROUP(fs->sb));
+ fs->gd[group].bg_used_dirs_count = 0;
+ fs->metadirty |= EXT2_META_SB | EXT2_META_GD;
+
+ {
+ struct ext2_buffer_head *bh;
+ blk_t i;
+
+ bh = ext2_bcreate(fs, EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]));
+ if (!bh)
+ return 0;
+
+ if (sparse)
+ {
+ bh->data[0] |= _bitmap[0];
+ for (i=1;i<=fs->gdblocks;i++)
+ bh->data[i>>3] |= _bitmap[i&7];
+ }
+
+ i = EXT2_GROUP_BLOCK_BITMAP(fs->gd[group]) - groupstart;
+ bh->data[i>>3] |= _bitmap[i&7];
+
+ i = EXT2_GROUP_INODE_BITMAP(fs->gd[group]) - groupstart;
+ bh->data[i>>3] |= _bitmap[i&7];
+
+ for (i=0;i<fs->inodeblocks;i++)
+ {
+ blk_t j;
+
+ j = EXT2_GROUP_INODE_TABLE(fs->gd[group])
+ - groupstart + i;
+ bh->data[j>>3] |= _bitmap[j&7];
+ }
+
+ for (i=groupsize;i<EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);i++)
+ bh->data[i>>3] |= _bitmap[i&7];
+
+ ext2_brelse(bh, 0); /* this is a block bitmap */
+ }
+
+ if (!ext2_zero_blocks(fs, EXT2_GROUP_INODE_BITMAP(fs->gd[group]), 1))
+ return 0;
+ if (!ext2_zero_blocks(fs, EXT2_GROUP_INODE_TABLE(fs->gd[group]),
+ fs->inodeblocks))
+ return 0;
+
+ if (fs->opt_safe)
+ if (!ext2_sync(fs))
+ return 0;
+
+ return 1;
+}
+
+static int ext2_del_group(struct ext2_fs *fs)
+{
+ blk_t admin;
+ int group;
+ blk_t groupsize;
+ blk_t newgdblocks;
+ int sparse;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_del_group\n", stderr);
+
+ group = fs->numgroups - 1;
+ sparse = ext2_is_group_sparse(fs, group);
+
+ admin = fs->adminblocks;
+ if (!sparse)
+ admin -= fs->gdblocks + 1;
+
+ groupsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ - group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ if (EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) < groupsize - admin)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system is too full to remove a group!"));
+
+ return 0;
+ }
+
+ if (EXT2_SUPER_FREE_INODES_COUNT(fs->sb)
+ < EXT2_SUPER_INODES_PER_GROUP(fs->sb))
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has too many allocated inodes to "
+ "remove a group!"));
+ return 0;
+ }
+
+ if (fs->opt_debug)
+ {
+ if (EXT2_GROUP_FREE_INODES_COUNT(fs->gd[group]) !=
+ EXT2_SUPER_INODES_PER_GROUP(fs->sb))
+ {
+ fputs ("ext2_del_group: this should not "
+ "happen anymore!\n", stderr);
+
+ return 0;
+ }
+ }
+
+ newgdblocks = ped_div_round_up((fs->numgroups - 1) *
+ sizeof(struct ext2_group_desc), fs->blocksize);
+
+ if (newgdblocks != fs->gdblocks)
+ {
+ int i;
+
+ for (i=0;i<fs->numgroups;i++)
+ if (ext2_is_group_sparse(fs, i))
+ {
+ blk_t start;
+
+ start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) +
+ i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ ext2_set_block_state(fs,
+ start + fs->gdblocks,
+ 0, 1);
+ }
+
+ fs->gdblocks--;
+ fs->adminblocks--;
+ if (sparse)
+ admin--;
+ }
+
+ if (fs->opt_debug)
+ {
+ if (EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group])
+ != groupsize - admin)
+ {
+ blk_t i;
+ blk_t num;
+ blk_t offset;
+
+ offset = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) +
+ group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ num = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ for (i=0;i<num;i++)
+ if (ext2_is_data_block(fs, offset+i) &&
+ ext2_get_block_state(fs, offset+i))
+ {
+ fprintf(stderr,
+ "error: block relocator "
+ "should have relocated "
+ "%i\n",
+ offset+i);
+
+ return 0;
+ }
+ }
+ }
+
+ fs->numgroups--;
+
+ fs->sb.s_inodes_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_INODES_COUNT(fs->sb)
+ - EXT2_SUPER_INODES_PER_GROUP(fs->sb));
+ fs->sb.s_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupsize);
+ fs->sb.s_free_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) - (groupsize - admin));
+ fs->sb.s_free_inodes_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_FREE_INODES_COUNT(fs->sb)
+ - EXT2_SUPER_INODES_PER_GROUP(fs->sb));
+ fs->metadirty |= EXT2_META_SB;
+
+ if (fs->opt_safe)
+ ext2_sync(fs);
+
+ ped_realloc ((void*) &fs->gd,
+ fs->numgroups * sizeof(struct ext2_group_desc)
+ + fs->blocksize);
+
+ return 1;
+}
+
+static int ext2_grow_group(struct ext2_fs *fs, blk_t newsize)
+{
+ int group;
+ blk_t groupoff;
+ blk_t gblocks;
+ blk_t i;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_grow_group\n", stderr);
+
+ group = fs->numgroups - 1;
+ groupoff = group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ gblocks = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupoff;
+
+ if (fs->opt_debug)
+ {
+ if (newsize < gblocks)
+ {
+ fputs ("ext2_grow_group: called to shrink group!\n",
+ stderr);
+
+ return 0;
+ }
+
+ if (gblocks == newsize)
+ {
+ fputs ("ext2_grow_group: nothing to do!\n", stderr);
+ return 0;
+ }
+ }
+
+ for (i=gblocks;i<newsize;i++)
+ ext2_set_block_state(fs, groupoff + i, 0, 1);
+
+ fs->sb.s_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_BLOCKS_COUNT(fs->sb) + newsize - gblocks);
+ fs->metadirty |= EXT2_META_SB;
+
+ if (fs->opt_safe)
+ ext2_sync(fs);
+
+ return 1;
+}
+
+static int ext2_shrink_group(struct ext2_fs *fs, blk_t newsize)
+{
+ blk_t admin;
+ int group;
+ blk_t groupoff;
+ blk_t gblocks;
+ blk_t i;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_shrink_group\n", stderr);
+
+ group = fs->numgroups - 1;
+
+ admin = fs->adminblocks;
+ if (!ext2_is_group_sparse(fs, group))
+ admin -= fs->gdblocks + 1;
+
+ groupoff = group * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb)
+ + EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+ gblocks = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - groupoff;
+
+ if (fs->opt_debug)
+ {
+ if (newsize < admin)
+ {
+ fprintf(stderr,
+ "ext2_shrink_group: cant shrink a group "
+ "to %i blocks\n", newsize);
+
+ return 0;
+ }
+
+ if (newsize > gblocks)
+ {
+ fputs ("ext2_shrink_group: called to grow group!\n",
+ stderr);
+
+ return 0;
+ }
+
+ if (gblocks == newsize)
+ {
+ fputs ("ext2_shrink_group: nothing to do!\n",
+ stderr);
+
+ return 0;
+ }
+ }
+
+ for (i=newsize;i<gblocks;i++)
+ {
+ if (fs->opt_debug && ext2_get_block_state(fs, groupoff + i))
+ {
+ fprintf(stderr,
+ "error: block relocator should have relocated "
+ "%i\n",
+ groupoff + i);
+
+ return 0;
+ }
+
+ ext2_set_block_state(fs, groupoff + i, 1, 0);
+ }
+
+ i = gblocks - newsize;
+ fs->sb.s_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_BLOCKS_COUNT(fs->sb) - i);
+ fs->sb.s_free_blocks_count = PED_CPU_TO_LE32(
+ EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) - i);
+ fs->gd[group].bg_free_blocks_count = PED_CPU_TO_LE16(
+ EXT2_GROUP_FREE_BLOCKS_COUNT(fs->gd[group]) - i);
+
+ fs->metadirty |= EXT2_META_SB | EXT2_META_GD;
+
+ if (fs->opt_safe)
+ ext2_sync(fs);
+
+ return 1;
+}
+
+
+
+
+
+
+static int ext2_grow_fs(struct ext2_fs *fs, blk_t newsize, PedTimer* timer)
+{
+ blk_t diff;
+ blk_t sizelast;
+ blk_t origsize = EXT2_SUPER_BLOCKS_COUNT(fs->sb);
+
+ if (fs->opt_verbose)
+ fputs ("ext2_grow_fs\n", stderr);
+
+ if (!ext2_block_relocate(fs, newsize))
+ return 0;
+
+ if (!ext2_metadata_push(fs, newsize))
+ return 0;
+
+ diff = newsize - EXT2_SUPER_BLOCKS_COUNT(fs->sb);
+ sizelast = EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ - (fs->numgroups-1) * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ if (sizelast != EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ {
+ blk_t growto;
+
+ growto = sizelast + diff;
+ if (growto > EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb))
+ growto = EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ if (!ext2_grow_group(fs, growto))
+ return 0;
+
+ diff -= growto - sizelast;
+ }
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("adding groups"));
+
+ while (diff)
+ {
+ ped_timer_update (timer,
+ 1.0 - 1.0 * diff / (newsize - origsize));
+
+ sizelast = PED_MIN(diff, EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ if (!ext2_add_group(fs, sizelast))
+ return 0;
+
+ diff -= sizelast;
+ }
+
+ ped_timer_update (timer, 1.0);
+
+ return 1;
+}
+
+static int ext2_shrink_fs(struct ext2_fs *fs, blk_t newsize,
+ PedTimer* timer)
+{
+ blk_t origsize = EXT2_SUPER_BLOCKS_COUNT (fs->sb);
+ blk_t diff;
+ int newgroups;
+ blk_t sizelast;
+
+ if (fs->opt_verbose)
+ fputs ("ext2_shrink_fs\n", stderr);
+
+ newgroups = ped_div_round_up (newsize
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb),
+ EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ if (EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ - EXT2_SUPER_FREE_BLOCKS_COUNT(fs->sb) > newsize)
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Your file system is too full to resize it to %i "
+ "blocks. Sorry."), newsize);
+ return 0;
+ }
+
+ if (EXT2_SUPER_INODES_COUNT(fs->sb)
+ - EXT2_SUPER_FREE_INODES_COUNT(fs->sb)
+ > newgroups * EXT2_SUPER_INODES_PER_GROUP(fs->sb))
+ {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Your file system has too many occupied inodes to "
+ "resize it to %i blocks. Sorry."), newsize);
+ return 0;
+ }
+
+ if (!ext2_inode_relocate(fs, newgroups))
+ return 0;
+
+ if (!ext2_block_relocate(fs, newsize))
+ return 0;
+
+ diff = EXT2_SUPER_BLOCKS_COUNT(fs->sb) - newsize;
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("shrinking"));
+
+ while (diff > 0)
+ {
+ ped_timer_update (timer,
+ 1.0 - 1.0 * diff / (origsize - newsize));
+
+ sizelast = EXT2_SUPER_BLOCKS_COUNT(fs->sb)
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb) -
+ (fs->numgroups - 1)
+ * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+
+ if (diff < sizelast)
+ {
+ if (!ext2_shrink_group(fs, sizelast - diff))
+ return 0;
+
+ diff = 0;
+ }
+ else
+ {
+ if (!ext2_del_group(fs))
+ return 0;
+
+ diff -= sizelast;
+ }
+ }
+
+ ped_timer_update (timer, 1.0);
+
+ return 1;
+}
+
+int ext2_determine_itoffset(struct ext2_fs *fs)
+{
+ int i;
+
+ fs->itoffset = EXT2_GROUP_INODE_TABLE(fs->gd[0])
+ - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb);
+
+ /*PED_DEBUG (0x20, "itoffset is %d", fs->itoffset);
+
+ PED_DEBUG (0x20, "walking %d groups", fs->numgroups);*/
+
+ for (i=0;i<fs->numgroups;i++)
+ {
+ blk_t start;
+ blk_t bb;
+ blk_t ib;
+ blk_t it;
+
+ start = EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb)
+ + (i * EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb));
+ it = start + fs->itoffset;
+
+ /*PED_DEBUG (0x21, "start = %d, it = %d", start, it);*/
+
+ if (ext2_is_group_sparse(fs, i))
+ {
+ /*PED_DEBUG (0x21, "%d has a superblock copy", i);*/
+ bb = it - 2;
+ ib = it - 1;
+ }
+ else
+ {
+ /*PED_DEBUG (0x21, "%d doesn't have a superblock copy",
+ i);*/
+ bb = start;
+ ib = start + 1;
+ }
+
+ if (EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]) != bb ||
+ EXT2_GROUP_INODE_BITMAP(fs->gd[i]) != ib ||
+ EXT2_GROUP_INODE_TABLE(fs->gd[i]) != it)
+ {
+ /* ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("This ext2 file system has a rather strange layout! "
+ "Parted can't resize this (yet)."));*/
+
+ /* PED_DEBUG (0x21, "calculated block bitmap to be %d, "
+ "but fs says %d.", bb,
+ EXT2_GROUP_BLOCK_BITMAP(fs->gd[i]));
+ PED_DEBUG (0x21, "calculated inode bitmap to be %d, "
+ "but fs says %d.", ib,
+ EXT2_GROUP_INODE_BITMAP(fs->gd[i]));
+ PED_DEBUG (0x21, "calculated inode table to be %d, "
+ "but fs says %d.", it,
+ EXT2_GROUP_INODE_TABLE(fs->gd[i]));*/
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int ext2_resize_fs(struct ext2_fs *fs, blk_t newsize, PedTimer* timer)
+{
+ blk_t residue;
+ int status;
+
+ if (EXT2_SUPER_STATE(fs->sb) & EXT2_ERROR_FS)
+ {
+ ped_exception_throw (
+ PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL,
+ _("File system has errors! You should run e2fsck."));
+ return 0;
+ }
+
+ if (!(EXT2_SUPER_STATE(fs->sb) & EXT2_VALID_FS))
+ {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system was not cleanly unmounted! "
+ "You should run e2fsck."));
+ return 0;
+ }
+
+ if (EXT2_SUPER_FEATURE_COMPAT(fs->sb)
+ & EXT2_FEATURE_COMPAT_HAS_DIR_INDEX) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING, PED_EXCEPTION_IGNORE_CANCEL,
+ _("The file system has the 'dir_index' feature "
+ "enabled. Parted can only resize the file system "
+ "if it disables this feature. You can enable it "
+ "later by running 'tune2fs -O dir_index DEVICE' "
+ "and then 'e2fsck -fD DEVICE'."))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ fs->sb.s_feature_compat
+ = PED_CPU_TO_LE32(EXT2_SUPER_FEATURE_COMPAT(fs->sb)
+ & ~EXT2_FEATURE_COMPAT_HAS_DIR_INDEX);
+ fs->metadirty |= EXT2_META_SB;
+ }
+
+ if (!ext2_determine_itoffset(fs) && ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK_CANCEL,
+ _("A resize operation on this file system will "
+ "use EXPERIMENTAL code\n"
+ "that MAY CORRUPT it (although no one has "
+ "reported any such damage yet).\n"
+ "You should at least backup your data first, "
+ "and run 'e2fsck -f' afterwards."))
+ == PED_EXCEPTION_CANCEL)
+ {
+ return 0;
+ }
+
+ if (fs->opt_verbose)
+ fputs ("ext2_resize_fs\n", stderr);
+
+ residue = (newsize - EXT2_SUPER_FIRST_DATA_BLOCK(fs->sb))
+ % EXT2_SUPER_BLOCKS_PER_GROUP(fs->sb);
+ if (residue && residue <= fs->adminblocks)
+ newsize -= residue;
+
+ if (newsize == EXT2_SUPER_BLOCKS_COUNT(fs->sb))
+ return 1;
+
+ fs->relocator_pool
+ = (unsigned char *)ped_malloc(ext2_relocator_pool_size << 10);
+ if (!fs->relocator_pool)
+ return 0;
+ fs->relocator_pool_end
+ = fs->relocator_pool + (ext2_relocator_pool_size << 10);
+
+ if (newsize < EXT2_SUPER_BLOCKS_COUNT(fs->sb))
+ status = ext2_shrink_fs(fs, newsize, timer);
+ else
+ status = ext2_grow_fs(fs, newsize, timer);
+
+ ped_free(fs->relocator_pool);
+ fs->relocator_pool = NULL;
+ fs->relocator_pool_end = NULL;
+
+ return status;
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/interface.c b/usr/src/lib/libparted/common/libparted/fs/ext2/interface.c
new file mode 100644
index 0000000000..043f948de6
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/interface.c
@@ -0,0 +1,352 @@
+/*
+ interface.c -- parted binding glue to libext2resize
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* VERSION: libext2resize 1.1.6 (by Lennert)
+ * merged 1.1.11 changes (by Andrew)
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include "ext2.h"
+#include "parted_io.h"
+
+static PedFileSystemType _ext2_type;
+static PedFileSystemType _ext3_type;
+
+struct ext2_dev_handle* ext2_make_dev_handle_from_parted_geometry(PedGeometry* geom);
+
+static PedGeometry*
+_ext2_generic_probe (PedGeometry* geom, int expect_ext3)
+{
+ struct ext2_super_block sb;
+
+ if (!ped_geometry_read(geom, &sb, 2, 2))
+ return NULL;
+
+ if (EXT2_SUPER_MAGIC(sb) == EXT2_SUPER_MAGIC_CONST) {
+ PedSector block_size = 1 << (EXT2_SUPER_LOG_BLOCK_SIZE(sb) + 1);
+ PedSector block_count = EXT2_SUPER_BLOCKS_COUNT(sb);
+ PedSector group_blocks = EXT2_SUPER_BLOCKS_PER_GROUP(sb);
+ PedSector group_nr = EXT2_SUPER_BLOCK_GROUP_NR(sb);
+ PedSector first_data_block = EXT2_SUPER_FIRST_DATA_BLOCK(sb);
+ int version = EXT2_SUPER_REV_LEVEL(sb);
+ int is_ext3 = (EXT2_SUPER_FEATURE_COMPAT(sb)
+ & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0;
+
+ if (expect_ext3 != is_ext3)
+ return NULL;
+
+ if (version > 0 && group_nr > 0) {
+ PedSector start;
+ PedGeometry probe_geom;
+
+ start = geom->start
+ - group_blocks * group_nr
+ - first_data_block;
+
+ if (start < 0)
+ return NULL;
+ ped_geometry_init (&probe_geom, geom->dev,
+ start, block_count * block_size);
+ return _ext2_generic_probe (&probe_geom, expect_ext3);
+ } else {
+ return ped_geometry_new (geom->dev, geom->start,
+ block_count * block_size);
+ }
+ }
+ return NULL;
+}
+
+static PedGeometry*
+_ext2_probe (PedGeometry* geom)
+{
+ return _ext2_generic_probe (geom, 0);
+}
+
+static PedGeometry*
+_ext3_probe (PedGeometry* geom)
+{
+ return _ext2_generic_probe (geom, 1);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+_ext2_clobber (PedGeometry* geom)
+{
+ struct ext2_super_block sb;
+
+ if (!ped_geometry_read(geom, &sb, 2, 2))
+ return 0;
+ if (EXT2_SUPER_MAGIC(sb) != EXT2_SUPER_MAGIC_CONST)
+ return 1;
+
+ sb.s_magic = 0;
+ return ped_geometry_write(geom, &sb, 2, 2);
+}
+
+static PedFileSystem*
+_ext2_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ struct ext2_fs* fs_info;
+ struct ext2_dev_handle* handle;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs) goto error;
+
+ fs->type = &_ext2_type;
+ fs->geom = ped_geometry_duplicate (geom);
+ fs->checked = 1;
+
+ handle = ext2_make_dev_handle_from_parted_geometry(fs->geom);
+ if (!handle) goto error_free_fs;
+
+ fs_info = (struct ext2_fs*) ext2_open(handle, 0);
+ if (!fs_info) goto error_free_handle;
+
+ fs->type_specific = (void*) fs_info;
+ fs_info->opt_verbose = 0;
+
+ return fs;
+
+error_free_handle:
+ ext2_destroy_dev_handle(handle);
+error_free_fs:
+ ped_free(fs);
+error:
+ return NULL;
+}
+
+static PedFileSystem*
+_ext2_create (PedGeometry* geom, PedTimer* timer)
+{
+ PedFileSystem* fs;
+ struct ext2_fs* fs_info;
+ struct ext2_dev_handle* handle;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs) goto error;
+
+ fs->type = &_ext2_type;
+ fs->geom = ped_geometry_duplicate (geom);
+
+ handle = ext2_make_dev_handle_from_parted_geometry(fs->geom);
+ if (!handle) goto error_free_fs;
+
+ fs_info = ext2_mkfs (handle, 0, 0, 0, 0, -1, -1, timer);
+ if (!fs_info) goto error_free_handle;
+
+ fs->type_specific = (void*) fs_info;
+ fs_info->opt_verbose = 0;
+
+ return fs;
+
+error_free_handle:
+ ext2_destroy_dev_handle(handle);
+error_free_fs:
+ ped_free(fs);
+error:
+ return NULL;
+}
+
+static int
+_ext2_close (PedFileSystem *fs)
+{
+ struct ext2_dev_handle* handle;
+
+ handle = ((struct ext2_fs*)fs->type_specific)->devhandle;
+ ext2_close(fs->type_specific);
+ ext2_destroy_dev_handle(handle);
+
+ ped_free(fs);
+ return 1;
+}
+
+static int
+_ext2_check (PedFileSystem *fs, PedTimer* timer)
+{
+ ped_exception_throw (PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK,
+ _("The ext2 file system passed a basic check. For a more "
+ "comprehensive check, use the e2fsck program."));
+ return 1;
+}
+
+static int
+_ext2_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ struct ext2_fs* f;
+ PedSector old_length = fs->geom->length;
+
+ PED_ASSERT (fs->geom->dev == geom->dev, return 0);
+
+ if (fs->geom->start != geom->start)
+ {
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Sorry, can't move the start of ext2 partitions yet!"));
+ return 0;
+ }
+
+ geom->dev->boot_dirty = 1;
+
+ f = (struct ext2_fs *) fs->type_specific;
+
+/* ensure that the geometry contains the new and old geometry */
+ if (old_length > geom->length) {
+ if (!ext2_resize_fs(f, geom->length >> (f->logsize - 9),
+ timer))
+ goto error;
+
+ fs->geom->length = geom->length;
+ fs->geom->end = fs->geom->start + geom->length - 1;
+ } else {
+ fs->geom->length = geom->length;
+ fs->geom->end = fs->geom->start + geom->length - 1;
+
+ if (!ext2_resize_fs(f, geom->length >> (f->logsize - 9),
+ timer))
+ goto error;
+ }
+ return 1;
+
+error:
+ return 0;
+}
+
+static PedConstraint*
+_ext2_get_create_constraint (const PedDevice* dev)
+{
+ PedGeometry full_dev;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ 64, dev->length);
+}
+
+static PedConstraint*
+_ext2_get_resize_constraint (const PedFileSystem* fs)
+{
+ struct ext2_fs* f = (struct ext2_fs *) fs->type_specific;
+ PedDevice* dev = fs->geom->dev;
+ PedAlignment start_align;
+ PedGeometry start_sector;
+ PedGeometry full_dev;
+ PedSector min_size;
+
+ if (!ped_alignment_init (&start_align, fs->geom->start, 0))
+ return NULL;
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+ if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
+ return NULL;
+ min_size = (EXT2_SUPER_BLOCKS_COUNT(f->sb)
+ - EXT2_SUPER_FREE_BLOCKS_COUNT(f->sb))
+ * (f->blocksize / dev->sector_size);
+
+ return ped_constraint_new (&start_align, ped_alignment_any,
+ &start_sector, &full_dev, min_size,
+ dev->length);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps _ext2_ops = {
+ .probe = _ext2_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = _ext2_clobber,
+ .open = _ext2_open,
+ .create = _ext2_create,
+ .close = _ext2_close,
+ .check = _ext2_check,
+ .resize = _ext2_resize,
+ .copy = NULL,
+ .get_create_constraint = _ext2_get_create_constraint,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = _ext2_get_resize_constraint
+#else /* !DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+#endif /* !DISCOVER_ONLY */
+};
+
+static PedFileSystemOps _ext3_ops = {
+ .probe = _ext3_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = _ext2_clobber,
+ .open = _ext2_open,
+ .create = NULL,
+ .close = _ext2_close,
+ .check = _ext2_check,
+ .resize = _ext2_resize,
+ .copy = NULL,
+ .get_create_constraint = _ext2_get_create_constraint,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = _ext2_get_resize_constraint
+#else /* !DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_copy_constraint = NULL,
+ .get_resize_constraint = NULL
+#endif /* !DISCOVER_ONLY */
+};
+
+#define EXT23_BLOCK_SIZES ((int[6]){512, 1024, 2048, 4096, 8192, 0})
+
+static PedFileSystemType _ext2_type = {
+ .next = NULL,
+ .ops = &_ext2_ops,
+ .name = "ext2",
+ .block_sizes = EXT23_BLOCK_SIZES
+};
+
+static PedFileSystemType _ext3_type = {
+ .next = NULL,
+ .ops = &_ext3_ops,
+ .name = "ext3",
+ .block_sizes = EXT23_BLOCK_SIZES
+};
+
+void ped_file_system_ext2_init ()
+{
+ ped_file_system_type_register (&_ext2_type);
+ ped_file_system_type_register (&_ext3_type);
+}
+
+void ped_file_system_ext2_done ()
+{
+ ped_file_system_type_unregister (&_ext2_type);
+ ped_file_system_type_unregister (&_ext3_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.c b/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.c
new file mode 100644
index 0000000000..7018cde022
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.c
@@ -0,0 +1,135 @@
+/*
+ parted_io.c -- parted I/O code interface for libext2resize
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+#include <parted/parted.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ext2.h"
+
+/* pseudo-header.... */
+
+loff_t llseek(unsigned int fd, loff_t offset, unsigned int whence);
+
+struct my_cookie
+{
+ int logsize;
+ PedGeometry* geom;
+};
+
+/* ...then this must be pseudo-code :-) */
+
+static int do_close (void *cookie);
+static int do_sync (void *cookie);
+static blk_t do_get_size (void *cookie);
+static int do_read (void *cookie, void *ptr, blk_t block, blk_t numblocks);
+static int do_set_blocksize(void *cookie, int logsize);
+static int do_write (void *cookie, void *ptr, blk_t block, blk_t numblocks);
+
+struct ext2_dev_ops ops =
+{
+ .close = do_close,
+ .get_size = do_get_size,
+ .read = do_read,
+ .set_blocksize = do_set_blocksize,
+ .sync = do_sync,
+ .write = do_write
+};
+
+
+
+static int do_close(void *cookie)
+{
+ struct my_cookie *monster = cookie;
+
+ return ped_geometry_sync(monster->geom);
+}
+
+static int do_sync(void *cookie)
+{
+ struct my_cookie *monster = cookie;
+
+ return ped_geometry_sync(monster->geom);
+}
+
+static blk_t do_get_size(void *cookie)
+{
+ struct my_cookie *monster = cookie;
+
+ return monster->geom->length >> (monster->logsize - 9);
+}
+
+static int do_read(void *cookie, void *ptr, blk_t block, blk_t num)
+{
+ struct my_cookie *monster = cookie;
+
+ return ped_geometry_read(monster->geom, ptr, block << (monster->logsize - 9), num << (monster->logsize - 9));
+}
+
+static int do_set_blocksize(void *cookie, int logsize)
+{
+ struct my_cookie *monster = cookie;
+
+ monster->logsize = logsize;
+ return 1;
+}
+
+static int do_write(void *cookie, void *ptr, blk_t block, blk_t num)
+{
+ struct my_cookie *monster = cookie;
+
+ return ped_geometry_write(monster->geom, ptr,
+ block << (monster->logsize - 9),
+ num << (monster->logsize - 9));
+}
+
+
+struct ext2_dev_handle *ext2_make_dev_handle_from_parted_geometry(PedGeometry* geom)
+{
+ struct ext2_dev_handle *dh;
+ struct my_cookie *monster;
+
+ if ((dh = ped_malloc(sizeof(struct ext2_dev_handle))) == NULL)
+ goto error;
+
+ if ((monster = ped_malloc(sizeof(struct my_cookie))) == NULL)
+ goto error_free_dh;
+
+ dh->ops = &ops;
+ dh->cookie = monster;
+ monster->logsize = 9;
+ monster->geom = geom;
+
+ return dh;
+
+error_free_dh:
+ ped_free(dh);
+error:
+ return NULL;
+}
+
+void ext2_destroy_dev_handle(struct ext2_dev_handle *handle)
+{
+ ped_geometry_destroy(((struct my_cookie *)handle->cookie)->geom);
+ ped_free(handle->cookie);
+ ped_free(handle);
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.h b/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.h
new file mode 100644
index 0000000000..a860b71d24
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/parted_io.h
@@ -0,0 +1,27 @@
+/*
+ parted_io.h
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PARTED_IO_H
+#define _PARTED_IO_H
+
+#include "ext2.h"
+
+void ext2_destroy_dev_handle(struct ext2_dev_handle *handle);
+
+#endif
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/tune.c b/usr/src/lib/libparted/common/libparted/fs/ext2/tune.c
new file mode 100644
index 0000000000..258d86ed07
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/tune.c
@@ -0,0 +1,39 @@
+/*
+ tune.c -- tuneable stuff
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#ifndef DISCOVER_ONLY
+
+/*
+ * maybe i'll make this all command-line configurable one day
+ */
+
+/* The size of the buffer cache in kilobytes. Note that this is only
+ the actual buffer memory. On top of this amount additional memory
+ will be allocated for buffer cache bookkeeping. */
+int ext2_buffer_cache_pool_size = 512;
+
+/* The size of the buffer cache hash table (log2 of # of buckets). */
+int ext2_hash_bits = 8;
+
+/* The block/inode relocator pool size in kilobytes. Make this as big
+ as you can. The smaller this is, the more disk I/O is required for
+ doing relocations. */
+int ext2_relocator_pool_size = 4096;
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/ext2/tune.h b/usr/src/lib/libparted/common/libparted/fs/ext2/tune.h
new file mode 100644
index 0000000000..27c526fc17
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ext2/tune.h
@@ -0,0 +1,29 @@
+/*
+ tune.h -- ext2 tunables header
+ Copyright (C) 1998-2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TUNE_H
+#define _TUNE_H
+
+#define MAXCONT 256
+
+extern int ext2_buffer_cache_pool_size;
+extern int ext2_hash_bits;
+extern int ext2_max_groups;
+extern int ext2_relocator_pool_size;
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.c b/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.c
new file mode 100644
index 0000000000..2bed1fd970
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.c
@@ -0,0 +1,452 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2002, 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* Reads in the boot sector (superblock), and does a minimum of sanity
+ * checking. The goals are:
+ * - to detect fat file systems, even if they are damaged [i.e. not
+ * return an error / throw an exception]
+ * - to fail detection if there's not enough information for
+ * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero)
+ */
+int
+fat_boot_sector_read (FatBootSector* bs, const PedGeometry *geom)
+{
+ PED_ASSERT (bs != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+
+ if (!ped_geometry_read (geom, bs, 0, 1))
+ return 0;
+
+ if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid signature for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->system_id[0]) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid signature for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->sector_size
+ || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid sector size for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->cluster_size) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid cluster size for a FAT "
+ "file system."));
+ return 0;
+ }
+
+ if (!bs->reserved) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid number of reserved "
+ "sectors for a FAT file system."));
+ return 0;
+ }
+
+ if (bs->fats < 1 || bs->fats > 4) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("File system has an invalid number of FATs."));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ Don't trust the FAT12, FAT16 or FAT32 label string.
+ */
+FatType
+fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom)
+{
+ PedSector logical_sector_size;
+ PedSector first_cluster_sector;
+ FatCluster cluster_count;
+
+ if (!PED_LE16_TO_CPU (bs->dir_entries))
+ return FAT_TYPE_FAT32;
+
+ logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+ first_cluster_sector
+ = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size
+ + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size
+ + PED_LE16_TO_CPU (bs->dir_entries)
+ / (512 / sizeof (FatDirEntry));
+ cluster_count = (geom->length - first_cluster_sector)
+ / bs->cluster_size / logical_sector_size;
+ if (cluster_count > MAX_FAT12_CLUSTERS)
+ return FAT_TYPE_FAT16;
+ else
+ return FAT_TYPE_FAT12;
+}
+
+/* Analyses the boot sector, and sticks appropriate numbers in
+ fs->type_specific.
+
+ Note: you need to subtract (2 * cluster_sectors) off cluster offset,
+ because the first cluster is number 2. (0 and 1 are not real clusters,
+ and referencing them is a bug)
+ */
+int
+fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int fat_entry_size;
+
+ PED_ASSERT (bs != NULL, return 0);
+
+ if (PED_LE16_TO_CPU (bs->sector_size) != 512) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("This file system has a logical sector size of %d. "
+ "GNU Parted is known not to work properly with sector "
+ "sizes other than 512 bytes."),
+ (int) PED_LE16_TO_CPU (bs->sector_size))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+
+ fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512;
+
+ fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track);
+ fs_info->heads = PED_LE16_TO_CPU (bs->heads);
+ if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63
+ || fs_info->heads < 1 || fs_info->heads > 255) {
+ PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom;
+ int cyl_count = 0;
+
+ if (fs_info->heads > 0 && fs_info->sectors_per_track > 0)
+ cyl_count = fs->geom->dev->length / fs_info->heads
+ / fs_info->sectors_per_track;
+
+ switch (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX + PED_EXCEPTION_IGNORE
+ + PED_EXCEPTION_CANCEL,
+ _("The file system's CHS geometry is (%d, %d, %d), "
+ "which is invalid. The partition table's CHS "
+ "geometry is (%d, %d, %d). If you select Ignore, "
+ "the file system's CHS geometry will be left "
+ "unchanged. If you select Fix, the file system's "
+ "CHS geometry will be set to match the partition "
+ "table's CHS geometry."),
+ cyl_count, fs_info->heads, fs_info->sectors_per_track,
+ bios_geom->cylinders, bios_geom->heads,
+ bios_geom->sectors)) {
+
+ case PED_EXCEPTION_FIX:
+ fs_info->sectors_per_track = bios_geom->sectors;
+ fs_info->heads = bios_geom->heads;
+ bs->secs_track
+ = PED_CPU_TO_LE16 (fs_info->sectors_per_track);
+ bs->heads = PED_CPU_TO_LE16 (fs_info->heads);
+ if (!fat_boot_sector_write (bs, fs))
+ return 0;
+ break;
+
+ case PED_EXCEPTION_CANCEL:
+ return 0;
+
+ case PED_EXCEPTION_IGNORE:
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (bs->sectors)
+ fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors)
+ * fs_info->logical_sector_size;
+ else
+ fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count)
+ * fs_info->logical_sector_size;
+
+ fs_info->fat_table_count = bs->fats;
+ fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries);
+ fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved)
+ * fs_info->logical_sector_size;
+ fs_info->cluster_sectors = bs->cluster_size
+ * fs_info->logical_sector_size;
+ fs_info->cluster_size = fs_info->cluster_sectors * 512;
+
+ if (fs_info->logical_sector_size == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says logical sector size is 0. "
+ "This is weird. "));
+ return 0;
+ }
+ if (fs_info->fat_table_count == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says there are no FAT tables. This "
+ "is weird. "));
+ return 0;
+ }
+ if (fs_info->cluster_sectors == 0) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("FAT boot sector says clusters are 0 sectors. This "
+ "is weird. "));
+ return 0;
+ }
+
+ fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom);
+ if (fs_info->fat_type == FAT_TYPE_FAT12) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("File system is FAT12, which is unsupported."));
+ return 0;
+ }
+ if (fs_info->fat_type == FAT_TYPE_FAT16) {
+ fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length)
+ * fs_info->logical_sector_size;
+ fs_info->serial_number
+ = PED_LE32_TO_CPU (bs->u.fat16.serial_number);
+ fs_info->root_cluster = 0;
+ fs_info->root_dir_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ fs_info->root_dir_sector_count
+ = fs_info->root_dir_entry_count * sizeof (FatDirEntry)
+ / (512 * fs_info->logical_sector_size);
+ fs_info->cluster_offset
+ = fs_info->root_dir_offset
+ + fs_info->root_dir_sector_count;
+ }
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length)
+ * fs_info->logical_sector_size;
+ fs_info->serial_number
+ = PED_LE32_TO_CPU (bs->u.fat32.serial_number);
+ fs_info->info_sector_offset
+ = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.info_sector)
+ * fs_info->logical_sector_size;
+ fs_info->boot_sector_backup_offset
+ = PED_LE16_TO_CPU (fs_info->boot_sector.u.fat32.backup_sector)
+ * fs_info->logical_sector_size;
+ fs_info->root_cluster
+ = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster);
+ fs_info->root_dir_offset = 0;
+ fs_info->root_dir_sector_count = 0;
+ fs_info->cluster_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ }
+
+ fs_info->cluster_count
+ = (fs_info->sector_count - fs_info->cluster_offset)
+ / fs_info->cluster_sectors;
+
+ fat_entry_size = fat_table_entry_size (fs_info->fat_type);
+ if (fs_info->cluster_count + 2
+ > fs_info->fat_sectors * 512 / fat_entry_size)
+ fs_info->cluster_count
+ = fs_info->fat_sectors * 512 / fat_entry_size - 2;
+
+ fs_info->dir_entries_per_cluster
+ = fs_info->cluster_size / sizeof (FatDirEntry);
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+int
+fat_boot_sector_set_boot_code (FatBootSector* bs)
+{
+ PED_ASSERT (bs != NULL, return 0);
+
+ memset (bs, 0, 512);
+ memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3);
+ memcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE, FAT_BOOT_CODE_LENGTH);
+ return 1;
+}
+
+int
+fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (bs != NULL, return 0);
+
+ memcpy (bs->system_id, "MSWIN4.1", 8);
+ bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512);
+ bs->cluster_size = fs_info->cluster_sectors
+ / fs_info->logical_sector_size;
+ bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset
+ / fs_info->logical_sector_size);
+ bs->fats = fs_info->fat_table_count;
+
+ bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16)
+ ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count)
+ : 0;
+
+ if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff
+ || fs_info->fat_type == FAT_TYPE_FAT32) {
+ bs->sectors = 0;
+ bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count
+ / fs_info->logical_sector_size);
+ } else {
+ bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count
+ / fs_info->logical_sector_size);
+ bs->sector_count = 0;
+ }
+
+ bs->media = 0xf8;
+
+ bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track);
+ bs->heads = PED_CPU_TO_LE16 (fs_info->heads);
+ bs->hidden = PED_CPU_TO_LE32 (fs->geom->start);
+
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ bs->fat_length = 0;
+ bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors
+ / fs_info->logical_sector_size);
+ bs->u.fat32.flags = 0; /* FIXME: what the hell are these? */
+ bs->u.fat32.version = 0; /* must be 0, for Win98 bootstrap */
+ bs->u.fat32.root_dir_cluster
+ = PED_CPU_TO_LE32 (fs_info->root_cluster);
+ bs->u.fat32.info_sector
+ = PED_CPU_TO_LE16 (fs_info->info_sector_offset
+ / fs_info->logical_sector_size);
+ bs->u.fat32.backup_sector
+ = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset
+ / fs_info->logical_sector_size);
+
+ bs->u.fat32.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */
+
+ memset (bs->u.fat32.empty_1, 0, 12);
+
+ bs->u.fat32.ext_signature = 0x29;
+ bs->u.fat32.serial_number
+ = PED_CPU_TO_LE32 (fs_info->serial_number);
+ memcpy (bs->u.fat32.volume_name, "NO NAME ", 11);
+ memcpy (bs->u.fat32.fat_name, "FAT32 ", 8);
+ } else {
+ bs->fat_length
+ = PED_CPU_TO_LE16 (fs_info->fat_sectors
+ / fs_info->logical_sector_size);
+
+ bs->u.fat16.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */
+
+ bs->u.fat16.ext_signature = 0x29;
+ bs->u.fat16.serial_number
+ = PED_CPU_TO_LE32 (fs_info->serial_number);
+ memcpy (bs->u.fat16.volume_name, "NO NAME ", 11);
+ memcpy (bs->u.fat16.fat_name, "FAT16 ", 8);
+ }
+
+ bs->boot_sign = PED_CPU_TO_LE16 (0xaa55);
+
+ return 1;
+}
+
+int
+fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (bs != NULL, return 0);
+
+ if (!ped_geometry_write (fs->geom, bs, 0, 1))
+ return 0;
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ if (!ped_geometry_write (fs->geom, bs,
+ fs_info->boot_sector_backup_offset, 1))
+ return 0;
+ }
+ return ped_geometry_sync (fs->geom);
+}
+
+int
+fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int status;
+
+ PED_ASSERT (is != NULL, return 0);
+
+ if (!ped_geometry_read (fs->geom, is, fs_info->info_sector_offset, 1))
+ return 0;
+
+ if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) {
+ status = ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The information sector has the wrong "
+ "signature (%x). Select cancel for now, "
+ "and send in a bug report. If you're "
+ "desperate, it's probably safe to ignore."),
+ PED_LE32_TO_CPU (is->signature_2));
+ if (status == PED_EXCEPTION_CANCEL) return 0;
+ }
+ return 1;
+}
+
+int
+fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (is != NULL, return 0);
+
+ fat_table_count_stats (fs_info->fat);
+
+ memset (is, 0, 512);
+
+ is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1);
+ is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2);
+ is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count);
+ is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc);
+ is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3);
+
+ return 1;
+}
+
+int
+fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (is != NULL, return 0);
+
+ if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1))
+ return 0;
+ return ped_geometry_sync (fs->geom);
+}
+#endif /* !DISCOVER_ONLY */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.h b/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.h
new file mode 100644
index 0000000000..38fc3129e9
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/bootsector.h
@@ -0,0 +1,142 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_FAT_BOOTSECTOR_H
+#define PED_FAT_BOOTSECTOR_H
+
+typedef struct _FatBootSector FatBootSector;
+typedef struct _FatInfoSector FatInfoSector;
+
+#include "fat.h"
+
+#define FAT32_INFO_MAGIC1 0x41615252
+#define FAT32_INFO_MAGIC2 0x61417272
+#define FAT32_INFO_MAGIC3 0xaa55
+
+/* stolen from mkdosfs, by Dave Hudson */
+
+#define FAT_BOOT_MESSAGE \
+"This partition does not have an operating system loader installed on it.\n\r"\
+"Press a key to reboot..."
+
+#define FAT_BOOT_JUMP "\xeb\x58\x90" /* jmp +5a */
+
+#define FAT_BOOT_CODE "\x0e" /* push cs */ \
+ "\x1f" /* pop ds */ \
+ "\xbe\x74\x7e" /* mov si, offset message */ \
+ /* write_msg_loop: */ \
+ "\xac" /* lodsb */ \
+ "\x22\xc0" /* and al, al */ \
+ "\x74\x06" /* jz done (+8) */ \
+ "\xb4\x0e" /* mov ah, 0x0e */ \
+ "\xcd\x10" /* int 0x10 */ \
+ "\xeb\xf5" /* jmp write_msg_loop */ \
+ /* done: */ \
+ "\xb4\x00" /* mov ah, 0x00 */ \
+ "\xcd\x16" /* int 0x16 */ \
+ "\xb4\x00" /* mov ah, 0x00 */ \
+ "\xcd\x19" /* int 0x19 */ \
+ "\xeb\xfe" /* jmp +0 - in case int 0x19 */ \
+ /* doesn't work */ \
+ /* message: */ \
+ FAT_BOOT_MESSAGE
+
+#define FAT_BOOT_CODE_LENGTH 128
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _FatBootSector {
+ uint8_t boot_jump[3]; /* 00: Boot strap short or near jump */
+ uint8_t system_id[8]; /* 03: system name */
+ uint16_t sector_size; /* 0b: bytes per logical sector */
+ uint8_t cluster_size; /* 0d: sectors/cluster */
+ uint16_t reserved; /* 0e: reserved sectors */
+ uint8_t fats; /* 10: number of FATs */
+ uint16_t dir_entries; /* 11: number of root directory entries */
+ uint16_t sectors; /* 13: if 0, total_sect supersedes */
+ uint8_t media; /* 15: media code */
+ uint16_t fat_length; /* 16: sectors/FAT for FAT12/16 */
+ uint16_t secs_track; /* 18: sectors per track */
+ uint16_t heads; /* 1a: number of heads */
+ uint32_t hidden; /* 1c: hidden sectors (partition start) */
+ uint32_t sector_count; /* 20: no. of sectors (if sectors == 0) */
+
+ union __attribute__ ((packed)) {
+ /* FAT16 fields */
+ struct __attribute__ ((packed)) {
+ uint8_t drive_num; /* 24: */
+ uint8_t empty_1; /* 25: */
+ uint8_t ext_signature; /* 26: always 0x29 */
+ uint32_t serial_number; /* 27: */
+ uint8_t volume_name [11]; /* 2b: */
+ uint8_t fat_name [8]; /* 36: */
+ uint8_t boot_code[448]; /* 3f: Boot code (or message) */
+ } fat16;
+ /* FAT32 fields */
+ struct __attribute__ ((packed)) {
+ uint32_t fat_length; /* 24: size of FAT in sectors */
+ uint16_t flags; /* 28: bit8: fat mirroring, low4: active fat */
+ uint16_t version; /* 2a: minor * 256 + major */
+ uint32_t root_dir_cluster; /* 2c: */
+ uint16_t info_sector; /* 30: */
+ uint16_t backup_sector; /* 32: */
+ uint8_t empty_1 [12]; /* 34: */
+ uint16_t drive_num; /* 40: */
+ uint8_t ext_signature; /* 42: always 0x29 */
+ uint32_t serial_number; /* 43: */
+ uint8_t volume_name [11]; /* 47: */
+ uint8_t fat_name [8]; /* 52: */
+ uint8_t boot_code[420]; /* 5a: Boot code (or message) */
+ } fat32;
+ } u;
+
+ uint16_t boot_sign; /* 1fe: always 0xAA55 */
+};
+
+struct __attribute__ ((packed)) _FatInfoSector {
+ uint32_t signature_1; /* should be 0x41615252 */
+ uint8_t unused [480];
+ uint32_t signature_2; /* should be 0x61417272 */
+ uint32_t free_clusters;
+ uint32_t next_cluster; /* most recently allocated cluster */
+ uint8_t unused2 [0xe];
+ uint16_t signature_3; /* should be 0xaa55 */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+int fat_boot_sector_read (FatBootSector* bs, const PedGeometry* geom);
+FatType fat_boot_sector_probe_type (const FatBootSector* bs,
+ const PedGeometry* geom);
+int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs);
+int fat_boot_sector_set_boot_code (FatBootSector* bs);
+int fat_boot_sector_generate (FatBootSector* bs, const PedFileSystem* fs);
+int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs);
+
+int fat_info_sector_read (FatInfoSector* is, const PedFileSystem* fs);
+int fat_info_sector_generate (FatInfoSector* is, const PedFileSystem* fs);
+int fat_info_sector_write (const FatInfoSector* is, PedFileSystem* fs);
+
+#endif /* PED_FAT_BOOTSECTOR_H */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/calc.c b/usr/src/lib/libparted/common/libparted/fs/fat/calc.c
new file mode 100644
index 0000000000..e7c2862a8c
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/calc.c
@@ -0,0 +1,435 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+/* returns the minimum size of clusters for a given file system type */
+PedSector
+fat_min_cluster_size (FatType fat_type) {
+ switch (fat_type) {
+ case FAT_TYPE_FAT12: return 1;
+ case FAT_TYPE_FAT16: return 1024/512;
+ case FAT_TYPE_FAT32: return 4096/512;
+ }
+ return 0;
+}
+
+static PedSector
+_smallest_power2_over (PedSector ceiling)
+{
+ PedSector result = 1;
+
+ while (result < ceiling)
+ result *= 2;
+
+ return result;
+}
+
+/* returns the minimum size of clusters for a given file system type */
+PedSector
+fat_recommend_min_cluster_size (FatType fat_type, PedSector size) {
+ switch (fat_type) {
+ case FAT_TYPE_FAT12: return 1;
+ case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type);
+ case FAT_TYPE_FAT32:
+ return PED_MAX(_smallest_power2_over(size
+ / MAX_FAT32_CLUSTERS),
+ fat_min_cluster_size (fat_type));
+ }
+ return 0;
+}
+
+/* returns the maxmimum size of clusters for a given file system type */
+PedSector
+fat_max_cluster_size (FatType fat_type) {
+ switch (fat_type) {
+ case FAT_TYPE_FAT12: return 1; /* dunno... who cares? */
+ case FAT_TYPE_FAT16: return 32768/512;
+ case FAT_TYPE_FAT32: return 65536/512;
+ }
+ return 0;
+}
+
+/* returns the minimum number of clusters for a given file system type */
+FatCluster
+fat_min_cluster_count (FatType fat_type) {
+ switch (fat_type) {
+ case FAT_TYPE_FAT12:
+ case FAT_TYPE_FAT16:
+ return fat_max_cluster_count (fat_type) / 2;
+
+ case FAT_TYPE_FAT32: return 0xfff0;
+ }
+ return 0;
+}
+
+/* returns the maximum number of clusters for a given file system type */
+FatCluster
+fat_max_cluster_count (FatType fat_type) {
+ switch (fat_type) {
+ case FAT_TYPE_FAT12: return 0xff0;
+ case FAT_TYPE_FAT16: return 0xfff0;
+ case FAT_TYPE_FAT32: return 0x0ffffff0;
+ }
+ return 0;
+}
+
+/* what is this supposed to be? What drugs are M$ on? (Can I have some? :-) */
+PedSector
+fat_min_reserved_sector_count (FatType fat_type)
+{
+ return (fat_type == FAT_TYPE_FAT32) ? 32 : 1;
+}
+
+int
+fat_check_resize_geometry (const PedFileSystem* fs,
+ const PedGeometry* geom,
+ PedSector new_cluster_sectors,
+ FatCluster new_cluster_count)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector free_space;
+ PedSector min_free_space;
+ PedSector total_space;
+ PedSector new_total_space;
+ PedSector dir_space;
+
+ PED_ASSERT (geom != NULL, return 0);
+
+ dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors;
+ free_space = fs_info->fat->free_cluster_count
+ * fs_info->cluster_sectors;
+ total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors;
+ new_total_space = new_cluster_count * new_cluster_sectors;
+ min_free_space = total_space - new_total_space + dir_space;
+
+ PED_ASSERT (new_cluster_count
+ <= fat_max_cluster_count (FAT_TYPE_FAT32),
+ return 0);
+
+ if (free_space < min_free_space) {
+ char* needed = ped_unit_format (geom->dev, min_free_space);
+ char* have = ped_unit_format (geom->dev, free_space);
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("You need %s of free disk space to shrink this "
+ "partition to this size. Currently, only %s is "
+ "free."),
+ needed, have);
+ ped_free (needed);
+ ped_free (have);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/******************************************************************************/
+
+/* DO NOT EDIT THIS ALGORITHM!
+ * As far as I can tell, this is the same algorithm used by Microsoft to
+ * calculate the size of the file allocaion tables, and the number of clusters.
+ * I have not verified this by dissassembling Microsoft code - I came to this
+ * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE).
+ *
+ * If you think this code makes no sense, then you are right. I will restrain
+ * the urge to inflict serious bodily harm on Microsoft people.
+ */
+
+static int
+entries_per_sector (FatType fat_type)
+{
+ switch (fat_type) {
+ case FAT_TYPE_FAT12:
+ return 512 * 3 / 2;
+ case FAT_TYPE_FAT16:
+ return 512 / 2;
+ case FAT_TYPE_FAT32:
+ return 512 / 4;
+ }
+ return 0;
+}
+
+static int
+calc_sizes (PedSector size, PedSector align, FatType fat_type,
+ PedSector root_dir_sectors, PedSector cluster_sectors,
+ FatCluster* out_cluster_count, PedSector* out_fat_size)
+{
+ PedSector data_fat_space; /* space available to clusters + FAT */
+ PedSector fat_space; /* space taken by each FAT */
+ PedSector cluster_space; /* space taken by clusters */
+ FatCluster cluster_count;
+ int i;
+
+ PED_ASSERT (out_cluster_count != NULL, return 0);
+ PED_ASSERT (out_fat_size != NULL, return 0);
+
+ data_fat_space = size - fat_min_reserved_sector_count (fat_type)
+ - align;
+ if (fat_type == FAT_TYPE_FAT16)
+ data_fat_space -= root_dir_sectors;
+
+ fat_space = 0;
+ for (i = 0; i < 2; i++) {
+ if (fat_type == FAT_TYPE_FAT32)
+ cluster_space = data_fat_space - fat_space;
+ else
+ cluster_space = data_fat_space - 2 * fat_space;
+
+ cluster_count = cluster_space / cluster_sectors;
+ fat_space = ped_div_round_up (cluster_count + 2,
+ entries_per_sector (fat_type));
+ }
+
+ cluster_space = data_fat_space - 2 * fat_space;
+ cluster_count = cluster_space / cluster_sectors;
+
+ /* looks like this should be part of the loop condition?
+ * Need to build the Big Table TM again to check
+ */
+ if (fat_space < ped_div_round_up (cluster_count + 2,
+ entries_per_sector (fat_type))) {
+ fat_space = ped_div_round_up (cluster_count + 2,
+ entries_per_sector (fat_type));
+ }
+
+ if (cluster_count > fat_max_cluster_count (fat_type)
+ || cluster_count < fat_min_cluster_count (fat_type))
+ return 0;
+
+ *out_cluster_count = cluster_count;
+ *out_fat_size = fat_space;
+
+ return 1;
+}
+
+/****************************************************************************/
+
+int
+fat_calc_sizes (PedSector size, PedSector align, FatType fat_type,
+ PedSector root_dir_sectors,
+ PedSector* out_cluster_sectors, FatCluster* out_cluster_count,
+ PedSector* out_fat_size)
+{
+ PedSector cluster_sectors;
+
+ PED_ASSERT (out_cluster_sectors != NULL, return 0);
+ PED_ASSERT (out_cluster_count != NULL, return 0);
+ PED_ASSERT (out_fat_size != NULL, return 0);
+
+ for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
+ cluster_sectors <= fat_max_cluster_size (fat_type);
+ cluster_sectors *= 2) {
+ if (calc_sizes (size, align, fat_type, root_dir_sectors,
+ cluster_sectors,
+ out_cluster_count, out_fat_size)) {
+ *out_cluster_sectors = cluster_sectors;
+ return 1;
+ }
+ }
+
+ for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size);
+ cluster_sectors >= fat_min_cluster_size (fat_type);
+ cluster_sectors /= 2) {
+ if (calc_sizes (size, align, fat_type, root_dir_sectors,
+ cluster_sectors,
+ out_cluster_count, out_fat_size)) {
+ *out_cluster_sectors = cluster_sectors;
+ return 1;
+ }
+ }
+
+ /* only make the cluster size really small (<4k) if a bigger one is
+ * isn't possible. Windows never makes FS's like this, but it
+ * seems to work... (do more tests!)
+ */
+ for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) {
+ if (calc_sizes (size, align, fat_type, root_dir_sectors,
+ cluster_sectors,
+ out_cluster_count, out_fat_size)) {
+ *out_cluster_sectors = cluster_sectors;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Same as fat_calc_sizes, except it only attempts to match a particular
+ * cluster size. This is useful, because the FAT resizer can only shrink the
+ * cluster size.
+ */
+int
+fat_calc_resize_sizes (
+ const PedGeometry* geom,
+ PedSector align,
+ FatType fat_type,
+ PedSector root_dir_sectors,
+ PedSector cluster_sectors,
+ PedSector* out_cluster_sectors,
+ FatCluster* out_cluster_count,
+ PedSector* out_fat_size)
+{
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (out_cluster_sectors != NULL, return 0);
+ PED_ASSERT (out_cluster_count != NULL, return 0);
+ PED_ASSERT (out_fat_size != NULL, return 0);
+
+/* libparted can only reduce the cluster size at this point */
+ for (*out_cluster_sectors = cluster_sectors;
+ *out_cluster_sectors >= fat_min_cluster_size (fat_type);
+ *out_cluster_sectors /= 2) {
+ if (calc_sizes (geom->length, align, fat_type, root_dir_sectors,
+ *out_cluster_sectors,
+ out_cluster_count, out_fat_size))
+ return 1;
+ }
+ return 0;
+}
+
+/* Calculates the number of sectors needed to be added to cluster_offset,
+ to make the cluster on the new file system match up with the ones
+ on the old file system.
+ However, some space is reserved by fat_calc_resize_sizes() and
+ friends, to allow room for this space. If too much of this space is left
+ over, everyone will complain, so we have to be greedy, and use it all up...
+ */
+PedSector
+fat_calc_align_sectors (const PedFileSystem* new_fs,
+ const PedFileSystem* old_fs)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
+ PedSector raw_old_meta_data_end;
+ PedSector new_meta_data_size;
+ PedSector min_new_meta_data_end;
+ PedSector new_data_size;
+ PedSector new_clusters_size;
+ PedSector align;
+
+ new_meta_data_size
+ = fat_min_reserved_sector_count (new_fs_info->fat_type)
+ + new_fs_info->fat_sectors * 2;
+
+ if (new_fs_info->fat_type == FAT_TYPE_FAT16)
+ new_meta_data_size += new_fs_info->root_dir_sector_count;
+
+ raw_old_meta_data_end = old_fs->geom->start
+ + old_fs_info->cluster_offset;
+
+ min_new_meta_data_end = new_fs->geom->start + new_meta_data_size;
+
+ if (raw_old_meta_data_end > min_new_meta_data_end)
+ align = (raw_old_meta_data_end - min_new_meta_data_end)
+ % new_fs_info->cluster_sectors;
+ else
+ align = (new_fs_info->cluster_sectors
+ - ( (min_new_meta_data_end - raw_old_meta_data_end)
+ % new_fs_info->cluster_sectors ))
+ % new_fs_info->cluster_sectors;
+
+ new_data_size = new_fs->geom->length - new_meta_data_size;
+ new_clusters_size = new_fs_info->cluster_count
+ * new_fs_info->cluster_sectors;
+
+ while (new_clusters_size + align + new_fs_info->cluster_sectors
+ <= new_data_size)
+ align += new_fs_info->cluster_sectors;
+
+ return align;
+}
+
+int
+fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ return sector >= fs_info->cluster_offset
+ && sector < fs_info->cluster_offset
+ + fs_info->cluster_sectors * fs_info->cluster_count;
+}
+
+FatFragment
+fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
+ return 0);
+
+ return (cluster - 2) * fs_info->cluster_frags;
+}
+
+FatCluster
+fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
+
+ return frag / fs_info->cluster_frags + 2;
+}
+
+PedSector
+fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
+
+ return frag * fs_info->frag_sectors + fs_info->cluster_offset;
+}
+
+FatFragment
+fat_sector_to_frag (const PedFileSystem* fs, PedSector sector)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
+
+ return (sector - fs_info->cluster_offset) / fs_info->frag_sectors;
+}
+
+PedSector
+fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
+ return 0);
+
+ return (cluster - 2) * fs_info->cluster_sectors
+ + fs_info->cluster_offset;
+}
+
+FatCluster
+fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (sector >= fs_info->cluster_offset, return 0);
+
+ return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors
+ + 2;
+}
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/calc.h b/usr/src/lib/libparted/common/libparted/fs/fat/calc.h
new file mode 100644
index 0000000000..9af08ebc83
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/calc.h
@@ -0,0 +1,77 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_FAT_CALC_H
+#define PED_FAT_CALC_H
+
+extern PedSector fat_min_cluster_size (FatType fat_type);
+extern PedSector fat_max_cluster_size (FatType fat_type);
+extern FatCluster fat_min_cluster_count (FatType fat_type);
+extern FatCluster fat_max_cluster_count (FatType fat_type);
+
+extern PedSector fat_min_reserved_sector_count (FatType fat_type);
+
+extern int fat_check_resize_geometry (const PedFileSystem* fs,
+ const PedGeometry* geom,
+ PedSector new_cluster_sectors,
+ FatCluster new_cluster_count);
+
+extern int fat_calc_sizes (PedSector size,
+ PedSector align,
+ FatType fat_type,
+ PedSector root_dir_sectors,
+ PedSector* out_cluster_sectors,
+ FatCluster* out_cluster_count,
+ PedSector* out_fat_size);
+
+extern int fat_calc_resize_sizes (const PedGeometry* geom,
+ PedSector align,
+ FatType fat_type,
+ PedSector root_dir_sectors,
+ PedSector cluster_sectors,
+ PedSector* out_cluster_sectors,
+ FatCluster* out_cluster_count,
+ PedSector* out_fat_size);
+
+extern PedSector
+fat_calc_align_sectors (const PedFileSystem* new_fs,
+ const PedFileSystem* old_fs);
+
+extern int
+fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector);
+
+extern FatFragment
+fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster);
+
+extern FatCluster
+fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag);
+
+extern PedSector
+fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag);
+
+extern FatFragment
+fat_sector_to_frag (const PedFileSystem* fs, PedSector sector);
+
+extern PedSector
+fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster);
+
+extern FatCluster
+fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector);
+
+#endif /* PED_FAT_CALC_H */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.c b/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.c
new file mode 100644
index 0000000000..f3151fee5e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.c
@@ -0,0 +1,424 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <string.h>
+
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+static int
+needs_duplicating (const FatOpContext* ctx, FatFragment frag)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatCluster cluster = fat_frag_to_cluster (ctx->old_fs, frag);
+ FatClusterFlag flag;
+
+ PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2,
+ return 0);
+
+ flag = fat_get_fragment_flag (ctx->old_fs, frag);
+ switch (flag) {
+ case FAT_FLAG_FREE:
+ return 0;
+
+ case FAT_FLAG_DIRECTORY:
+ return 1;
+
+ case FAT_FLAG_FILE:
+ return fat_op_context_map_static_fragment (ctx, frag) == -1;
+
+ case FAT_FLAG_BAD:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+search_next_fragment (FatOpContext* ctx)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs);
+
+ for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) {
+ if (needs_duplicating (ctx, ctx->buffer_offset))
+ return 1;
+ }
+ return 0; /* all done! */
+}
+
+static int
+read_marked_fragments (FatOpContext* ctx, FatFragment length)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs);
+ int status;
+ FatFragment i;
+
+ ped_exception_fetch_all ();
+ status = fat_read_fragments (ctx->old_fs, fs_info->buffer,
+ ctx->buffer_offset, length);
+ ped_exception_leave_all ();
+ if (status)
+ return 1;
+
+ ped_exception_catch ();
+
+/* something bad happened, so read fragments one by one. (The error may
+ have occurred on an unused fragment: who cares) */
+ for (i = 0; i < length; i++) {
+ if (ctx->buffer_map [i]) {
+ if (!fat_read_fragment (ctx->old_fs,
+ fs_info->buffer + i * fs_info->frag_size,
+ ctx->buffer_offset + i))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+fetch_fragments (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatFragment fetch_length = 0;
+ FatFragment frag;
+
+ for (frag = 0; frag < ctx->buffer_frags; frag++)
+ ctx->buffer_map [frag] = -1;
+
+ for (frag = 0;
+ frag < ctx->buffer_frags
+ && ctx->buffer_offset + frag < old_fs_info->frag_count;
+ frag++) {
+ if (needs_duplicating (ctx, ctx->buffer_offset + frag)) {
+ ctx->buffer_map [frag] = 1;
+ fetch_length = frag + 1;
+ }
+ }
+
+ if (!read_marked_fragments (ctx, fetch_length))
+ return 0;
+
+ return 1;
+}
+
+/*****************************************************************************
+ * here starts the write code. All assumes that ctx->buffer_map [first] and
+ * ctx->buffer_map [last] are occupied by fragments that need to be duplicated.
+ *****************************************************************************/
+
+/* finds the first fragment that is not going to get overwritten (that needs to
+ get read in) */
+static FatFragment
+get_first_underlay (const FatOpContext* ctx, int first, int last)
+{
+ int old;
+ FatFragment new;
+
+ PED_ASSERT (first <= last, return 0);
+
+ new = ctx->buffer_map [first];
+ for (old = first + 1; old <= last; old++) {
+ if (ctx->buffer_map [old] == -1)
+ continue;
+ new++;
+ if (ctx->buffer_map [old] != new)
+ return new;
+ }
+ return -1;
+}
+
+/* finds the last fragment that is not going to get overwritten (that needs to
+ get read in) */
+static FatFragment
+get_last_underlay (const FatOpContext* ctx, int first, int last)
+{
+ int old;
+ FatFragment new;
+
+ PED_ASSERT (first <= last, return 0);
+
+ new = ctx->buffer_map [last];
+ for (old = last - 1; old >= first; old--) {
+ if (ctx->buffer_map [old] == -1)
+ continue;
+ new--;
+ if (ctx->buffer_map [old] != new)
+ return new;
+ }
+ return -1;
+}
+
+/* "underlay" refers to the "static" fragments, that remain unchanged.
+ * when writing large chunks at a time, we don't want to clobber these,
+ * so we read them in, and write them back again. MUCH quicker that way.
+ */
+static int
+quick_group_write_read_underlay (FatOpContext* ctx, int first, int last)
+{
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatFragment first_underlay;
+ FatFragment last_underlay;
+ FatFragment underlay_length;
+
+ PED_ASSERT (first <= last, return 0);
+
+ first_underlay = get_first_underlay (ctx, first, last);
+ if (first_underlay == -1)
+ return 1;
+ last_underlay = get_last_underlay (ctx, first, last);
+
+ PED_ASSERT (first_underlay <= last_underlay, return 0);
+
+ underlay_length = last_underlay - first_underlay + 1;
+ if (!fat_read_fragments (ctx->new_fs,
+ new_fs_info->buffer
+ + (first_underlay - ctx->buffer_map [first])
+ * new_fs_info->frag_size,
+ first_underlay,
+ underlay_length))
+ return 0;
+ return 1;
+}
+
+/* quick_group_write() makes no attempt to recover from errors - just
+ * does things fast. If there is an error, slow_group_write() is
+ * called.
+ * Note: we do syncing writes, to make sure there isn't any
+ * error writing out. It's rather difficult recovering from errors
+ * further on.
+ */
+static int
+quick_group_write (FatOpContext* ctx, int first, int last)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ int active_length;
+ int i;
+ int offset;
+
+ PED_ASSERT (first <= last, return 0);
+
+ ped_exception_fetch_all ();
+ if (!quick_group_write_read_underlay (ctx, first, last))
+ goto error;
+
+ for (i = first; i <= last; i++) {
+ if (ctx->buffer_map [i] == -1)
+ continue;
+
+ offset = ctx->buffer_map [i] - ctx->buffer_map [first];
+ memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size,
+ old_fs_info->buffer + i * new_fs_info->frag_size,
+ new_fs_info->frag_size);
+ }
+
+ active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1;
+ if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer,
+ ctx->buffer_map [first], active_length))
+ goto error;
+
+ ped_exception_leave_all ();
+ return 1;
+
+error:
+ ped_exception_catch ();
+ ped_exception_leave_all ();
+ return 0;
+}
+
+/* Writes fragments out, one at a time, avoiding errors on redundant writes
+ * on damaged parts of the disk we already know about. If there's an error
+ * on one of the required fragments, it gets marked as bad, and a replacement
+ * is found.
+ */
+static int
+slow_group_write (FatOpContext* ctx, int first, int last)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ int i;
+
+ PED_ASSERT (first <= last, return 0);
+
+ for (i = first; i <= last; i++) {
+ if (ctx->buffer_map [i] == -1)
+ continue;
+
+ while (!fat_write_sync_fragment (ctx->new_fs,
+ old_fs_info->buffer + i * old_fs_info->frag_size,
+ ctx->buffer_map [i])) {
+ fat_table_set_bad (new_fs_info->fat,
+ ctx->buffer_map [i]);
+ ctx->buffer_map [i] = fat_table_alloc_cluster
+ (new_fs_info->fat);
+ if (ctx->buffer_map [i] == 0)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+update_remap (FatOpContext* ctx, int first, int last)
+{
+ int i;
+
+ PED_ASSERT (first <= last, return 0);
+
+ for (i = first; i <= last; i++) {
+ if (ctx->buffer_map [i] == -1)
+ continue;
+ ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i];
+ }
+
+ return 1;
+}
+
+static int
+group_write (FatOpContext* ctx, int first, int last)
+{
+ PED_ASSERT (first <= last, return 0);
+
+ if (!quick_group_write (ctx, first, last)) {
+ if (!slow_group_write (ctx, first, last))
+ return 0;
+ }
+ if (!update_remap (ctx, first, last))
+ return 0;
+ return 1;
+}
+
+/* assumes fragment size and new_fs's cluster size are equal */
+static int
+write_fragments (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ int group_start;
+ int group_end = -1; /* shut gcc up! */
+ FatFragment mapped_length;
+ FatFragment i;
+ FatCluster new_cluster;
+
+ PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count, return 0);
+
+ group_start = -1;
+ for (i = 0; i < ctx->buffer_frags; i++) {
+ if (ctx->buffer_map [i] == -1)
+ continue;
+
+ ctx->frags_duped++;
+
+ new_cluster = fat_table_alloc_cluster (new_fs_info->fat);
+ if (!new_cluster)
+ return 0;
+ fat_table_set_eof (new_fs_info->fat, new_cluster);
+ ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs,
+ new_cluster);
+
+ if (group_start == -1)
+ group_start = group_end = i;
+
+ PED_ASSERT (ctx->buffer_map [i]
+ >= ctx->buffer_map [group_start],
+ return 0);
+
+ mapped_length = ctx->buffer_map [i]
+ - ctx->buffer_map [group_start] + 1;
+ if (mapped_length <= ctx->buffer_frags) {
+ group_end = i;
+ } else {
+ /* ran out of room in the buffer, so write this group,
+ * and start a new one...
+ */
+ if (!group_write (ctx, group_start, group_end))
+ return 0;
+ group_start = group_end = i;
+ }
+ }
+
+ PED_ASSERT (group_start != -1, return 0);
+
+ if (!group_write (ctx, group_start, group_end))
+ return 0;
+ return 1;
+}
+
+/* default all fragments to unmoved
+ */
+static void
+init_remap (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatFragment i;
+
+ for (i = 0; i < old_fs_info->frag_count; i++)
+ ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i);
+}
+
+static FatFragment
+count_frags_to_dup (FatOpContext* ctx)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatFragment i;
+ FatFragment total;
+
+ total = 0;
+
+ for (i = 0; i < fs_info->frag_count; i++) {
+ if (needs_duplicating (ctx, i))
+ total++;
+ }
+
+ return total;
+}
+
+/* duplicates unreachable file clusters, and all directory clusters
+ */
+int
+fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer)
+{
+ FatFragment total_frags_to_dup;
+
+ init_remap (ctx);
+ total_frags_to_dup = count_frags_to_dup (ctx);
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, "moving data");
+
+ ctx->buffer_offset = 0;
+ ctx->frags_duped = 0;
+ while (search_next_fragment (ctx)) {
+ ped_timer_update (
+ timer, 1.0 * ctx->frags_duped / total_frags_to_dup);
+
+ if (!fetch_fragments (ctx))
+ return 0;
+ if (!write_fragments (ctx))
+ return 0;
+ ctx->buffer_offset += ctx->buffer_frags;
+ }
+
+ ped_timer_update (timer, 1.0);
+ return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.h b/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.h
new file mode 100644
index 0000000000..88316b7675
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/clstdup.h
@@ -0,0 +1,28 @@
+/*
+ libparted
+ Copyright (C) 1999, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_FAT_CLSTDUP_H_INCLUDED
+#define PED_FAT_CLSTDUP_H_INCLUDED
+
+#include "context.h"
+
+/* the big important one :-) */
+extern int fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer);
+
+#endif /* PED_FAT_CLSTDUP_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/context.c b/usr/src/lib/libparted/common/libparted/fs/fat/context.c
new file mode 100644
index 0000000000..8b04ad2536
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/context.c
@@ -0,0 +1,260 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <string.h>
+
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+/* Note: this deals with file system start and end sectors, even if the physical
+ * devices are different (eg for fat_copy()) Perhaps this is a hack, but it
+ * works ;-)
+ */
+static int
+calc_deltas (FatOpContext* ctx)
+{
+ PedFileSystem* old_fs = ctx->old_fs;
+ PedFileSystem* new_fs = ctx->new_fs;
+ FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
+ PedSector old_cluster_ofs;
+ PedSector new_cluster_ofs;
+ PedSector sector_delta;
+
+ old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset;
+ new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset;
+
+ if (new_cluster_ofs > old_cluster_ofs) {
+ ctx->start_move_dir = FAT_DIR_FORWARD;
+ sector_delta = new_cluster_ofs - old_cluster_ofs;
+ } else {
+ ctx->start_move_dir = FAT_DIR_BACKWARD;
+ sector_delta = old_cluster_ofs - new_cluster_ofs;
+ }
+
+ if (sector_delta % new_fs_info->cluster_sectors) {
+ ped_exception_throw (
+ PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+ _("Cluster start delta = %d, which is not a multiple "
+ "of the cluster size %d."),
+ (int) sector_delta,
+ (int) new_fs_info->cluster_sectors);
+ return 0;
+ }
+
+ ctx->start_move_delta = sector_delta / ctx->frag_sectors;
+
+#ifdef PED_VERBOSE
+ printf ("Start move delta is: %d %s.\n",
+ (int) ctx->start_move_delta,
+ (ctx->start_move_dir == FAT_DIR_FORWARD)?
+ "forwards" : "backwards");
+#endif
+
+ return 1;
+}
+
+FatOpContext*
+fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs);
+ FatOpContext* ctx;
+
+ ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext));
+ if (!ctx)
+ goto error;
+
+ ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors,
+ new_fs_info->cluster_sectors);
+ if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors))
+ goto error;
+ if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors))
+ goto error;
+
+ ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors;
+ ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment)
+ * ctx->buffer_frags);
+ if (!ctx->buffer_map)
+ goto error_free_ctx;
+
+ ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment)
+ * old_fs_info->frag_count);
+ if (!ctx->remap)
+ goto error_free_buffer_map;
+
+ ctx->new_fs = new_fs;
+ ctx->old_fs = old_fs;
+ if (!calc_deltas (ctx))
+ goto error_free_buffer_map;
+
+ return ctx;
+
+error_free_buffer_map:
+ ped_free (ctx->buffer_map);
+error_free_ctx:
+ ped_free (ctx);
+error:
+ return NULL;
+}
+
+void
+fat_op_context_destroy (FatOpContext* ctx)
+{
+ ped_free (ctx->buffer_map);
+ ped_free (ctx->remap);
+ ped_free (ctx);
+}
+
+FatFragment
+fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag)
+{
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatFragment result;
+
+ if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev)
+ return -1;
+
+ if (ctx->start_move_dir == FAT_DIR_FORWARD) {
+ if (frag < ctx->start_move_delta)
+ return -1;
+ result = frag - ctx->start_move_delta;
+ } else {
+ result = frag + ctx->start_move_delta;
+ }
+
+ if (result >= new_fs_info->frag_count)
+ return -1;
+
+ return result;
+}
+
+FatCluster
+fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst)
+{
+ FatFragment mapped_frag;
+
+ mapped_frag = fat_op_context_map_static_fragment (ctx,
+ fat_cluster_to_frag (ctx->old_fs, clst));
+ if (mapped_frag != -1)
+ return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
+ else
+ return 0;
+}
+
+FatFragment
+fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag)
+{
+ return ctx->remap [frag];
+}
+
+FatCluster
+fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst)
+{
+ FatFragment mapped_frag;
+
+ mapped_frag = fat_op_context_map_fragment (ctx,
+ fat_cluster_to_frag (ctx->old_fs, clst));
+ if (mapped_frag != -1)
+ return fat_frag_to_cluster (ctx->new_fs, mapped_frag);
+ else
+ return 0;
+}
+
+/* This function sets the initial fat for the new resized file system.
+ This is in *NO WAY* a proper FAT table - all it does is:
+ a) mark bad clusters as bad.
+ b) mark used clusters (that is, clusters from the original FS that are
+ reachable from the resized one). Marks as EOF (i.e. used, end of
+ file chain).
+ c) mark original file system metadata as EOF (i.e. used), to prevent
+ it from being clobbered. This will leave the original file system
+ intact, until the partition table is modified, if the start of
+ the partition is moved.
+
+ The FATs are rebuilt *properly* after cluster relocation. This here is
+ only to mark clusters as used, so when cluster relocation occurs, clusters
+ aren't relocated on top of ones marked in a, b or c.
+*/
+int
+fat_op_context_create_initial_fat (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatCluster clst;
+ FatCluster new_clst;
+ PedSector sect;
+ PedSector new_sect;
+ FatFragment frag;
+ FatFragment new_frag;
+ FatClusterFlag frag_flag;
+
+ new_fs_info->fat = fat_table_new (
+ new_fs_info->fat_type,
+ new_fs_info->fat_sectors * 512
+ / fat_table_entry_size (new_fs_info->fat_type));
+ if (!new_fs_info->fat)
+ return 0;
+
+ if (!fat_table_set_cluster_count (new_fs_info->fat,
+ new_fs_info->cluster_count))
+ return 0;
+
+/* mark bad and used clusters */
+ for (frag = 0; frag < old_fs_info->frag_count; frag++) {
+ frag_flag = fat_get_fragment_flag (ctx->old_fs, frag);
+ if (frag_flag == FAT_FLAG_FREE)
+ continue;
+
+ new_frag = fat_op_context_map_static_fragment (ctx, frag);
+ if (new_frag == -1)
+ continue;
+
+ new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag);
+ PED_ASSERT (new_clst != 0, return 0);
+
+ if (frag_flag == FAT_FLAG_BAD) {
+ if (!fat_table_set_bad (new_fs_info->fat, new_clst))
+ return 0;
+ } else {
+ if (!fat_table_set_eof (new_fs_info->fat, new_clst))
+ return 0;
+ }
+ }
+
+/* mark metadata regions that map to clusters on the new FS */
+ for (sect = 0; sect < old_fs_info->cluster_offset; sect++) {
+ new_sect = ped_geometry_map (ctx->new_fs->geom,
+ ctx->old_fs->geom, sect);
+ if (new_sect == -1
+ || !fat_is_sector_in_clusters (ctx->new_fs, new_sect))
+ continue;
+
+ clst = fat_sector_to_cluster (ctx->new_fs, new_sect);
+ PED_ASSERT (clst != 0, return 0);
+
+ if (!fat_table_set_eof (new_fs_info->fat, clst))
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/context.h b/usr/src/lib/libparted/common/libparted/fs/fat/context.h
new file mode 100644
index 0000000000..39119a18d0
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/context.h
@@ -0,0 +1,69 @@
+/*
+ libparted
+ Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_FAT_CONTEXT_H_INCLUDED
+#define PED_FAT_CONTEXT_H_INCLUDED
+
+#include "count.h"
+
+enum _FatDirection {
+ FAT_DIR_FORWARD,
+ FAT_DIR_BACKWARD
+};
+typedef enum _FatDirection FatDirection;
+
+struct _FatOpContext {
+ PedFileSystem* old_fs;
+ PedFileSystem* new_fs;
+
+ PedSector frag_sectors; /* should equal old_fs and
+ new_fs's frag_sectors */
+
+ FatDirection start_move_dir;
+ FatFragment start_move_delta;
+
+ FatFragment buffer_offset;
+ FatFragment buffer_frags;
+ FatFragment* buffer_map;
+
+ FatFragment frags_duped;
+
+ FatFragment* remap;
+
+ FatCluster new_root_dir [32];
+};
+typedef struct _FatOpContext FatOpContext;
+
+extern FatOpContext* fat_op_context_new (PedFileSystem* new_fs,
+ PedFileSystem* old_fs);
+
+extern void fat_op_context_destroy (FatOpContext* ctx);
+
+extern FatFragment fat_op_context_map_static_fragment (const FatOpContext* ctx,
+ FatFragment frag);
+extern FatCluster fat_op_context_map_static_cluster (const FatOpContext* ctx,
+ FatCluster clst);
+
+extern FatFragment fat_op_context_map_fragment (const FatOpContext* ctx,
+ FatFragment frag);
+extern FatCluster fat_op_context_map_cluster (const FatOpContext* ctx,
+ FatCluster clst);
+
+extern int fat_op_context_create_initial_fat (FatOpContext* ctx);
+
+#endif /* PED_FAT_CONTEXT_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/count.c b/usr/src/lib/libparted/common/libparted/fs/fat/count.c
new file mode 100644
index 0000000000..10328d1b3e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/count.c
@@ -0,0 +1,403 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+#if 0
+/* extremely ugly hack: stick everything that obviously isn't an unmovable file
+ * in here. Note: DAT is a bit dubious. Unfortunately, it's used by the
+ * registry, so it'll be all over the place :-(
+ */
+static char* movable_extensions[] = {
+ "",
+ "1ST",
+ "AVI",
+ "BAK", "BAT", "BMP",
+ "CFG", "COM", "CSS",
+ "DAT", "DLL", "DOC", "DRV",
+ "EXE",
+ "FAQ", "FLT", "FON",
+ "GID", "GIF",
+ "HLP", "HTT", "HTM",
+ "ICO", "INI",
+ "JPG",
+ "LNK", "LOG",
+ "KBD",
+ "ME", "MID", "MSG",
+ "OCX", "OLD",
+ "PIF", "PNG", "PRV",
+ "RTF",
+ "SCR", "SYS",
+ "TMP", "TTF", "TXT",
+ "URL",
+ "WAV",
+ "VBX", "VOC", "VXD",
+ NULL
+};
+
+static char*
+get_extension (char* file_name)
+{
+ char* ext;
+
+ ext = strrchr (file_name, '.');
+ if (!ext)
+ return "";
+ if (strchr (ext, '\\'))
+ return "";
+ return ext + 1;
+}
+
+static int
+is_movable_system_file (char* file_name)
+{
+ char* ext = get_extension (file_name);
+ int i;
+
+ for (i = 0; movable_extensions [i]; i++) {
+ if (strcasecmp (ext, movable_extensions [i]) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* 0 */
+
+/*
+ prints out the sequence of clusters for a given file chain, beginning
+ at start_cluster.
+*/
+#ifdef PED_VERBOSE
+static void
+print_chain (PedFileSystem* fs, FatCluster start)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster clst;
+ int this_row;
+
+ this_row = 0;
+ for (clst = start; !fat_table_is_eof (fs_info->fat, clst);
+ clst = fat_table_get (fs_info->fat, clst)) {
+ printf (" %d", (int) clst);
+ if (++this_row == 7) {
+ putchar ('\n');
+ this_row = 0;
+ }
+ }
+ putchar ('\n');
+}
+#endif /* PED_VERBOSE */
+
+static PedSector
+remainder_round_up (PedSector a, PedSector b)
+{
+ PedSector result;
+
+ result = a % b;
+ if (!result)
+ result = b;
+ return result;
+}
+
+/*
+ traverse the FAT for a file/directory, marking each entry's flag
+ to "flag".
+*/
+static int
+flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start,
+ FatClusterFlag flag, PedSector size)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster clst;
+ FatCluster prev_clst;
+ int last_cluster_usage;
+ FatCluster chain_length = 0;
+
+ if (fat_table_is_eof (fs_info->fat, start)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Bad directory entry for %s: first cluster is the "
+ "end of file marker."),
+ chain_name)
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+
+ for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst);
+ prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) {
+ chain_length++;
+ if (!clst) {
+ ped_exception_throw (PED_EXCEPTION_FATAL,
+ PED_EXCEPTION_CANCEL,
+ _("Bad FAT: unterminated chain for %s. You "
+ "should run dosfsck or scandisk."),
+ chain_name);
+ return 0;
+ }
+
+ if (clst >= fs_info->fat->cluster_count + 2) {
+ ped_exception_throw (PED_EXCEPTION_FATAL,
+ PED_EXCEPTION_CANCEL,
+ _("Bad FAT: cluster %d outside file system "
+ "in chain for %s. You should run dosfsck "
+ "or scandisk."),
+ (int) clst, chain_name);
+ return 0;
+ }
+
+ if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) {
+ ped_exception_throw (PED_EXCEPTION_FATAL,
+ PED_EXCEPTION_CANCEL,
+ _("Bad FAT: cluster %d is cross-linked for "
+ "%s. You should run dosfsck or scandisk."),
+ (int) clst, chain_name);
+ return 0;
+ }
+
+ if (flag == FAT_FLAG_DIRECTORY)
+ fs_info->total_dir_clusters++;
+
+ fs_info->cluster_info [clst].flag = flag;
+ fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */
+ }
+
+ if (size
+ && chain_length
+ != ped_div_round_up (size, fs_info->cluster_sectors)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("%s is %dk, but it has %d clusters (%dk)."),
+ chain_name,
+ (int) size / 2,
+ (int) chain_length,
+ (int) chain_length * fs_info->cluster_sectors / 2)
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+
+ last_cluster_usage
+ = ped_div_round_up (64 * remainder_round_up (size,
+ fs_info->cluster_sectors),
+ fs_info->cluster_sectors);
+
+ fs_info->cluster_info [prev_clst].units_used = last_cluster_usage;
+
+ return 1;
+}
+
+/*
+ recursively traverses a directory, flagging all clusters in the process.
+ It frees the traverse_info structure before returning.
+*/
+static int
+flag_traverse_dir (FatTraverseInfo* trav_info) {
+ PedFileSystem* fs = trav_info->fs;
+ FatDirEntry* this_entry;
+ FatTraverseInfo* subdir_trav_info;
+ char file_name [512];
+ char* file_name_start;
+ FatCluster first_cluster;
+ PedSector size;
+
+ PED_ASSERT (trav_info != NULL, return 0);
+
+ strcpy (file_name, trav_info->dir_name);
+ file_name_start = file_name + strlen (file_name);
+
+ while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) {
+ if (fat_dir_entry_is_null_term (this_entry))
+ break;
+ if (!fat_dir_entry_has_first_cluster (this_entry, fs))
+ continue;
+ if (this_entry->name [0] == '.')
+ continue; /* skip . and .. entries */
+
+ fat_dir_entry_get_name (this_entry, file_name_start);
+ first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs);
+ size = ped_div_round_up (fat_dir_entry_get_length (this_entry),
+ 512);
+
+#ifdef PED_VERBOSE
+ printf ("%s: ", file_name);
+ print_chain (fs, first_cluster);
+#endif
+
+#if 0
+ if (fat_dir_entry_is_system_file (this_entry)
+ && !is_movable_system_file (file_name)) {
+ PedExceptionOption ex_status;
+ ex_status = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The file %s is marked as a system file. "
+ "This means moving it could cause some "
+ "programs to stop working."),
+ file_name);
+
+ switch (ex_status) {
+ case PED_EXCEPTION_CANCEL:
+ return 0;
+
+ case PED_EXCEPTION_UNHANDLED:
+ ped_exception_catch ();
+ case PED_EXCEPTION_IGNORE:
+ }
+ }
+#endif /* 0 */
+
+ if (fat_dir_entry_is_directory (this_entry)) {
+ if (!flag_traverse_fat (fs, file_name, first_cluster,
+ FAT_FLAG_DIRECTORY, size))
+ return 0;
+
+ subdir_trav_info = fat_traverse_directory (trav_info,
+ this_entry);
+ if (!subdir_trav_info)
+ return 0;
+ if (!flag_traverse_dir (subdir_trav_info))
+ return 0;
+ } else if (fat_dir_entry_is_file (this_entry)) {
+ if (!flag_traverse_fat (fs, file_name, first_cluster,
+ FAT_FLAG_FILE, size))
+ return 0;
+ }
+ }
+
+ fat_traverse_complete (trav_info);
+ return 1;
+}
+
+static void
+_mark_bad_clusters (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster cluster;
+
+ for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) {
+ if (fat_table_is_bad (fs_info->fat, cluster))
+ fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD;
+ }
+}
+
+/*
+ fills in cluster_info. Each FAT entry (= cluster) is flagged as either
+ FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY.
+
+ Also, the fraction of each cluster (x/64) is recorded
+*/
+int
+fat_collect_cluster_info (PedFileSystem* fs) {
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatTraverseInfo* trav_info;
+
+ /* set all clusters to unused as a default */
+ memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2);
+ fs_info->total_dir_clusters = 0;
+
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ trav_info = fat_traverse_begin (fs, fs_info->root_cluster,
+ "\\");
+ if (!flag_traverse_dir (trav_info))
+ return 0;
+ if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster,
+ FAT_FLAG_DIRECTORY, 0))
+ return 0;
+ } else {
+ trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\");
+ if (!flag_traverse_dir (trav_info))
+ return 0;
+ }
+
+ _mark_bad_clusters (fs);
+ return 1;
+}
+
+FatClusterFlag
+fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ return fs_info->cluster_info [cluster].flag;
+}
+
+PedSector
+fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int fraction;
+
+ if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE)
+ return 0;
+
+ fraction = fs_info->cluster_info [cluster].units_used;
+ if (fraction == 0)
+ fraction = 64;
+
+ return fraction * fs_info->cluster_sectors / 64;
+}
+
+FatClusterFlag
+fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster cluster = fat_frag_to_cluster (fs, frag);
+ FatFragment offset = frag % fs_info->cluster_frags;
+ FatFragment last_frag_used;
+ FatClusterFlag flag;
+
+ PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
+ return 0);
+
+ flag = fat_get_cluster_flag (fs, cluster);
+ if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY)
+ return flag;
+ last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1)
+ / fs_info->frag_sectors;
+ if (offset > last_frag_used)
+ return FAT_FLAG_FREE;
+ else
+ return flag;
+}
+
+int
+fat_is_fragment_active (PedFileSystem* fs, FatFragment frag)
+{
+ switch (fat_get_fragment_flag (fs, frag)) {
+ case FAT_FLAG_FREE:
+ case FAT_FLAG_BAD:
+ return 0;
+
+ case FAT_FLAG_FILE:
+ case FAT_FLAG_DIRECTORY:
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/count.h b/usr/src/lib/libparted/common/libparted/fs/fat/count.h
new file mode 100644
index 0000000000..2b68de69c1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/count.h
@@ -0,0 +1,55 @@
+/*
+ libparted
+ Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef COUNT_H_INCLUDED
+#define COUNT_H_INCLUDED
+
+typedef enum _FatClusterFlag FatClusterFlag;
+typedef struct _FatClusterInfo FatClusterInfo;
+
+enum _FatClusterFlag {
+ FAT_FLAG_FREE=0,
+ FAT_FLAG_FILE=1,
+ FAT_FLAG_DIRECTORY=2,
+ FAT_FLAG_BAD=3
+};
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _FatClusterInfo {
+ unsigned int units_used:6; /* 1 unit = cluster_size / 64 */
+ FatClusterFlag flag:2;
+} fat16;
+#ifdef __sun
+#pragma pack()
+#endif
+
+extern int fat_collect_cluster_info (PedFileSystem *fs);
+extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs,
+ FatCluster cluster);
+extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster);
+extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs,
+ FatFragment frag);
+extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag);
+
+#endif /* COUNT_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/fat.c b/usr/src/lib/libparted/common/libparted/fs/fat/fat.c
new file mode 100644
index 0000000000..fa91a33a3d
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/fat.c
@@ -0,0 +1,887 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <string.h>
+#include <uuid/uuid.h>
+
+#include "fat.h"
+#include "calc.h"
+
+PedFileSystem*
+fat_alloc (const PedGeometry* geom)
+{
+ PedFileSystem* fs;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs)
+ goto error;
+
+ fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific));
+ if (!fs->type_specific)
+ goto error_free_fs;
+
+ fs->geom = ped_geometry_duplicate (geom);
+ if (!fs->geom)
+ goto error_free_type_specific;
+
+ fs->checked = 0;
+ return fs;
+
+error_free_type_specific:
+ ped_free (fs->type_specific);
+error_free_fs:
+ ped_free (fs);
+error:
+ return NULL;
+}
+
+/* Requires the boot sector to be analysed */
+int
+fat_alloc_buffers (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ fs_info->buffer_sectors = BUFFER_SIZE;
+ fs_info->buffer = ped_malloc (fs_info->buffer_sectors * 512);
+ if (!fs_info->buffer)
+ goto error;
+
+ fs_info->cluster_info = ped_malloc (fs_info->cluster_count + 2);
+ if (!fs_info->cluster_info)
+ goto error_free_buffer;
+
+ return 1;
+
+error_free_buffer:
+ ped_free (fs_info->buffer);
+error:
+ return 0;
+}
+
+void
+fat_free_buffers (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ ped_free (fs_info->cluster_info);
+ ped_free (fs_info->buffer);
+}
+
+void
+fat_free (PedFileSystem* fs)
+{
+ ped_geometry_destroy (fs->geom);
+ ped_free (fs->type_specific);
+ ped_free (fs);
+}
+
+int
+fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (fs_info->cluster_sectors % frag_sectors == 0
+ && frag_sectors <= fs_info->cluster_sectors,
+ return 0);
+
+ fs_info->frag_size = frag_sectors * 512;
+ fs_info->frag_sectors = frag_sectors;
+ fs_info->buffer_frags = fs_info->buffer_sectors / frag_sectors;
+ fs_info->cluster_frags = fs_info->cluster_sectors / frag_sectors;
+ fs_info->frag_count = fs_info->cluster_count * fs_info->cluster_frags;
+
+ return 1;
+}
+
+PedGeometry*
+fat_probe (PedGeometry* geom, FatType* fat_type)
+{
+ PedFileSystem* fs;
+ FatSpecific* fs_info;
+ PedGeometry* result;
+
+ fs = fat_alloc (geom);
+ if (!fs)
+ goto error;
+ fs_info = (FatSpecific*) fs->type_specific;
+
+ if (!fat_boot_sector_read (&fs_info->boot_sector, geom))
+ goto error_free_fs;
+ if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs))
+ goto error_free_fs;
+
+ *fat_type = fs_info->fat_type;
+ result = ped_geometry_new (geom->dev, geom->start,
+ fs_info->sector_count);
+
+ fat_free (fs);
+ return result;
+
+error_free_fs:
+ fat_free (fs);
+error:
+ return NULL;
+}
+
+PedGeometry*
+fat_probe_fat16 (PedGeometry* geom)
+{
+ FatType fat_type;
+ PedGeometry* probed_geom = fat_probe (geom, &fat_type);
+
+ if (probed_geom) {
+ if (fat_type == FAT_TYPE_FAT16)
+ return probed_geom;
+ ped_geometry_destroy (probed_geom);
+ }
+ return NULL;
+}
+
+PedGeometry*
+fat_probe_fat32 (PedGeometry* geom)
+{
+ FatType fat_type;
+ PedGeometry* probed_geom = fat_probe (geom, &fat_type);
+
+ if (probed_geom) {
+ if (fat_type == FAT_TYPE_FAT32)
+ return probed_geom;
+ ped_geometry_destroy (probed_geom);
+ }
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+int
+fat_clobber (PedGeometry* geom)
+{
+ FatBootSector boot_sector;
+
+ if (!fat_boot_sector_read (&boot_sector, geom))
+ return 1;
+
+ boot_sector.system_id[0] = 0;
+ boot_sector.boot_sign = 0;
+ if (boot_sector.u.fat16.fat_name[0] == 'F')
+ boot_sector.u.fat16.fat_name[0] = 0;
+ if (boot_sector.u.fat32.fat_name[0] == 'F')
+ boot_sector.u.fat32.fat_name[0] = 0;
+
+ return ped_geometry_write (geom, &boot_sector, 0, 1);
+}
+
+static int
+_init_fats (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster table_size;
+
+ table_size = fs_info->fat_sectors * 512
+ / fat_table_entry_size (fs_info->fat_type);
+ fs_info->fat = fat_table_new (fs_info->fat_type, table_size);
+ if (!fs_info->fat)
+ goto error;
+
+ if (!fat_table_read (fs_info->fat, fs, 0))
+ goto error_free_fat;
+
+ return 1;
+
+error_free_fat:
+ fat_table_destroy (fs_info->fat);
+error:
+ return 0;
+}
+
+PedFileSystem*
+fat_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ FatSpecific* fs_info;
+
+ fs = fat_alloc (geom);
+ if (!fs)
+ goto error;
+ fs_info = (FatSpecific*) fs->type_specific;
+
+ if (!fat_boot_sector_read (&fs_info->boot_sector, geom))
+ goto error_free_fs;
+ if (!fat_boot_sector_analyse (&fs_info->boot_sector, fs))
+ goto error_free_fs;
+ fs->type = (fs_info->fat_type == FAT_TYPE_FAT16)
+ ? &fat16_type
+ : &fat32_type;
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ if (!fat_info_sector_read (&fs_info->info_sector, fs))
+ goto error_free_fs;
+ }
+
+ if (!_init_fats (fs))
+ goto error_free_fs;
+ if (!fat_alloc_buffers (fs))
+ goto error_free_fat_table;
+ if (!fat_collect_cluster_info (fs))
+ goto error_free_buffers;
+
+ return fs;
+
+error_free_buffers:
+ fat_free_buffers (fs);
+error_free_fat_table:
+ fat_table_destroy (fs_info->fat);
+error_free_fs:
+ fat_free (fs);
+error:
+ return NULL;
+}
+
+static int
+fat_root_dir_clear (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ memset (fs_info->buffer, 0, 512 * fs_info->root_dir_sector_count);
+ return ped_geometry_write (fs->geom, fs_info->buffer,
+ fs_info->root_dir_offset,
+ fs_info->root_dir_sector_count);
+}
+
+/* hack: use the ext2 uuid library to generate a reasonably random (hopefully
+ * with /dev/random) number. Unfortunately, we can only use 4 bytes of it
+ */
+static uint32_t
+_gen_new_serial_number ()
+{
+ uuid_t uuid;
+
+ uuid_generate (uuid);
+ return * (uint32_t*) &uuid [0];
+}
+
+PedFileSystem*
+fat_create (PedGeometry* geom, FatType fat_type, PedTimer* timer)
+{
+ PedFileSystem* fs;
+ FatSpecific* fs_info;
+ FatCluster table_size;
+
+ fs = fat_alloc (geom);
+ if (!fs)
+ goto error;
+ fs_info = (FatSpecific*) fs->type_specific;
+
+ fs_info->logical_sector_size = 1;
+ fs_info->sectors_per_track = geom->dev->bios_geom.sectors;
+ fs_info->heads = geom->dev->bios_geom.heads;
+ fs_info->sector_count = fs->geom->length;
+ fs_info->fat_table_count = 2;
+/* some initial values, to be changed later */
+ fs_info->root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
+ / (512 / sizeof (FatDirEntry));
+ fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT;
+
+ fs_info->fat_type = fat_type;
+ if (!fat_calc_sizes (fs->geom->length, 0,
+ fs_info->fat_type,
+ fs_info->root_dir_sector_count,
+ &fs_info->cluster_sectors,
+ &fs_info->cluster_count,
+ &fs_info->fat_sectors)) {
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Partition too big/small for a %s file system."),
+ (fat_type == FAT_TYPE_FAT16)
+ ? fat16_type.name
+ : fat32_type.name);
+ goto error_free_fs;
+ }
+
+ fs_info->cluster_size = fs_info->cluster_sectors * 512;
+
+ fs_info->fat_offset = fat_min_reserved_sector_count (fs_info->fat_type);
+ fs_info->dir_entries_per_cluster
+ = fs_info->cluster_size / sizeof (FatDirEntry);
+
+ if (fs_info->fat_type == FAT_TYPE_FAT16) {
+ /* FAT16 */
+ fs->type = &fat16_type;
+
+ if (fs_info->cluster_count
+ > fat_max_cluster_count (fs_info->fat_type)) {
+ fs_info->cluster_count
+ = fat_max_cluster_count (fs_info->fat_type);
+ }
+
+ fs_info->root_dir_sector_count
+ = FAT_ROOT_DIR_ENTRY_COUNT
+ / (512 / sizeof (FatDirEntry));
+ fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT;
+ fs_info->root_dir_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ fs_info->cluster_offset
+ = fs_info->root_dir_offset
+ + fs_info->root_dir_sector_count;
+ } else {
+ /* FAT32 */
+ fs->type = &fat32_type;
+
+ fs_info->info_sector_offset = 1;
+ fs_info->boot_sector_backup_offset = 6;
+
+ fs_info->root_dir_sector_count = 0;
+ fs_info->root_dir_entry_count = 0;
+ fs_info->root_dir_offset = 0;
+
+ fs_info->cluster_offset
+ = fs_info->fat_offset
+ + fs_info->fat_sectors * fs_info->fat_table_count;
+ }
+
+ table_size = fs_info->fat_sectors * 512
+ / fat_table_entry_size (fs_info->fat_type);
+ fs_info->fat = fat_table_new (fs_info->fat_type, table_size);
+ if (!fs_info->fat)
+ goto error_free_fs;
+ fat_table_set_cluster_count (fs_info->fat, fs_info->cluster_count);
+ if (!fat_alloc_buffers (fs))
+ goto error_free_fat_table;
+
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ fs_info->root_cluster
+ = fat_table_alloc_cluster (fs_info->fat);
+ fat_table_set_eof (fs_info->fat, fs_info->root_cluster);
+ memset (fs_info->buffer, 0, fs_info->cluster_size);
+ if (!fat_write_cluster (fs, fs_info->buffer,
+ fs_info->root_cluster))
+ return 0;
+ }
+
+ fs_info->serial_number = _gen_new_serial_number ();
+
+ if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector))
+ goto error_free_buffers;
+ if (!fat_boot_sector_generate (&fs_info->boot_sector, fs))
+ goto error_free_buffers;
+ if (!fat_boot_sector_write (&fs_info->boot_sector, fs))
+ goto error_free_buffers;
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ if (!fat_info_sector_generate (&fs_info->info_sector, fs))
+ goto error_free_buffers;
+ if (!fat_info_sector_write (&fs_info->info_sector, fs))
+ goto error_free_buffers;
+ }
+
+ if (!fat_table_write_all (fs_info->fat, fs))
+ goto error_free_buffers;
+
+ if (fs_info->fat_type == FAT_TYPE_FAT16) {
+ if (!fat_root_dir_clear (fs))
+ goto error_free_buffers;
+ }
+
+ return fs;
+
+error_free_buffers:
+ fat_free_buffers (fs);
+error_free_fat_table:
+ fat_table_destroy (fs_info->fat);
+error_free_fs:
+ fat_free (fs);
+error:
+ return NULL;
+}
+
+PedFileSystem*
+fat_create_fat16 (PedGeometry* geom, PedTimer* timer)
+{
+ return fat_create (geom, FAT_TYPE_FAT16, timer);
+}
+
+PedFileSystem*
+fat_create_fat32 (PedGeometry* geom, PedTimer* timer)
+{
+ return fat_create (geom, FAT_TYPE_FAT32, timer);
+}
+
+int
+fat_close (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ fat_free_buffers (fs);
+ fat_table_destroy (fs_info->fat);
+ fat_free (fs);
+ return 1;
+}
+
+/* Hack: just resize the file system outside of its boundaries! */
+PedFileSystem*
+fat_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ PedFileSystem* new_fs;
+
+ new_fs = ped_file_system_open (fs->geom);
+ if (!new_fs)
+ goto error;
+ if (!ped_file_system_resize (new_fs, geom, timer))
+ goto error_close_new_fs;
+ return new_fs;
+
+error_close_new_fs:
+ ped_file_system_close (new_fs);
+error:
+ return 0;
+}
+
+static int
+_compare_fats (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatTable* table_copy;
+ FatCluster table_size;
+ int i;
+
+ table_size = fs_info->fat_sectors * 512
+ / fat_table_entry_size (fs_info->fat_type);
+
+ table_copy = fat_table_new (fs_info->fat_type, table_size);
+ if (!table_copy)
+ goto error;
+
+ for (i = 1; i < fs_info->fat_table_count; i++) {
+ if (!fat_table_read (table_copy, fs, i))
+ goto error_free_table_copy;
+ if (!fat_table_compare (fs_info->fat, table_copy)) {
+ if (ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The FATs don't match. If you don't know "
+ "what this means, then select cancel, run "
+ "scandisk on the file system, and then come "
+ "back."))
+ != PED_EXCEPTION_IGNORE)
+ goto error_free_table_copy;
+ }
+ }
+
+ fat_table_destroy (table_copy);
+ return 1;
+
+error_free_table_copy:
+ fat_table_destroy (table_copy);
+error:
+ return 0;
+}
+
+int
+fat_check (PedFileSystem* fs, PedTimer* timer)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector cluster_sectors;
+ FatCluster cluster_count;
+ PedSector fat_sectors;
+ PedSector align_sectors;
+ FatCluster info_free_clusters;
+
+ align_sectors = fs_info->fat_offset
+ - fat_min_reserved_sector_count (fs_info->fat_type);
+
+ if (!fat_calc_sizes (fs->geom->length,
+ align_sectors,
+ fs_info->fat_type,
+ fs_info->root_dir_sector_count,
+ &cluster_sectors,
+ &cluster_count,
+ &fat_sectors)) {
+ if (ped_exception_throw (PED_EXCEPTION_BUG,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("There are no possible configurations for this FAT "
+ "type."))
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ }
+
+ if (fs_info->fat_type == FAT_TYPE_FAT16) {
+ if (cluster_sectors != fs_info->cluster_sectors
+ || cluster_count != fs_info->cluster_count
+ || fat_sectors != fs_info->fat_sectors) {
+ if (ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("File system doesn't have expected sizes for "
+ "Windows to like it. "
+ "Cluster size is %dk (%dk expected); "
+ "number of clusters is %d (%d expected); "
+ "size of FATs is %d sectors (%d expected)."),
+ (int) fs_info->cluster_sectors / 2,
+ (int) cluster_sectors / 2,
+ (int) fs_info->cluster_count,
+ (int) cluster_count,
+ (int) fs_info->fat_sectors,
+ (int) fat_sectors)
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ }
+ }
+
+ if (fs_info->fat_type == FAT_TYPE_FAT32) {
+ info_free_clusters
+ = PED_LE32_TO_CPU (fs_info->info_sector.free_clusters);
+ if (info_free_clusters != (FatCluster) -1
+ && info_free_clusters != fs_info->fat->free_cluster_count) {
+ if (ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("File system is reporting the free space as "
+ "%d clusters, not %d clusters."),
+ info_free_clusters,
+ fs_info->fat->free_cluster_count)
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ }
+ }
+
+ if (!_compare_fats (fs))
+ goto error;
+
+ fs->checked = 1;
+ return 1; /* existence of fs implies consistency ;-) */
+
+error:
+ return 0;
+}
+
+/* Calculates how much space there will be in clusters in:
+ * old_fs intersect the-new-fs
+ */
+static PedSector
+_calc_resize_data_size (
+ const PedFileSystem* old_fs,
+ PedSector new_cluster_sectors,
+ FatCluster new_cluster_count,
+ PedSector new_fat_size)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs);
+ PedSector fat_size_delta;
+
+ fat_size_delta = old_fs_info->fat_sectors - new_fat_size;
+ return new_cluster_sectors * new_cluster_count - fat_size_delta * 2;
+}
+
+static int
+_test_resize_size (const PedFileSystem* fs,
+ PedSector length, PedSector min_data_size)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedGeometry geom;
+ PedSector _cluster_sectors;
+ FatCluster _cluster_count;
+ PedSector _fat_size;
+
+ ped_geometry_init (&geom, fs->geom->dev, fs->geom->start, length);
+
+ if (fat_calc_resize_sizes (
+ &geom,
+ fs_info->cluster_sectors,
+ FAT_TYPE_FAT16,
+ fs_info->root_dir_sector_count,
+ fs_info->cluster_sectors,
+ &_cluster_sectors,
+ &_cluster_count,
+ &_fat_size)
+ && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count,
+ _fat_size)
+ >= min_data_size)
+ return 1;
+
+ if (fat_calc_resize_sizes (
+ &geom,
+ fs_info->cluster_sectors,
+ FAT_TYPE_FAT32,
+ 0,
+ fs_info->cluster_sectors,
+ &_cluster_sectors,
+ &_cluster_count,
+ &_fat_size)
+ && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count,
+ _fat_size)
+ >= min_data_size)
+ return 1;
+
+ return 0;
+}
+
+/* does a binary search (!) for the mininum size. Too hard to compute directly
+ * (see calc_sizes() for why!)
+ */
+static PedSector
+_get_min_resize_size (const PedFileSystem* fs, PedSector min_data_size)
+{
+ PedSector min_length = 0;
+ PedSector max_length = fs->geom->length;
+ PedSector length;
+
+ while (min_length < max_length - 1) {
+ length = (min_length + max_length) / 2;
+ if (_test_resize_size (fs, length, min_data_size))
+ max_length = length;
+ else
+ min_length = length;
+ }
+
+/* adds a bit of leeway (64 sectors), for resolving extra issues, like root
+ * directory allocation, that aren't covered here.
+ */
+ return max_length + 64;
+}
+
+PedConstraint*
+fat_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedGeometry full_dev;
+ PedSector min_cluster_count;
+ FatCluster used_clusters;
+ PedSector min_data_size;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ used_clusters = fs_info->fat->cluster_count
+ - fs_info->fat->free_cluster_count;
+ min_cluster_count = used_clusters + fs_info->total_dir_clusters;
+ min_data_size = min_cluster_count * fs_info->cluster_sectors;
+
+ return ped_constraint_new (ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ _get_min_resize_size (fs, min_data_size),
+ dev->length);
+}
+
+PedConstraint*
+fat_get_resize_constraint (const PedFileSystem* fs)
+{
+ return fat_get_copy_constraint (fs, fs->geom->dev);
+}
+
+/* FIXME: fat_calc_sizes() needs to say "too big" or "too small", or
+ * something. This is a really difficult (maths) problem to do
+ * nicely...
+ * So, this algorithm works if dev->length / 2 is a valid fat_type
+ * size. (Which is how I got the magic numbers below)
+ */
+#if 0
+/* returns: -1 too small, 0 ok, 1 too big */
+static int
+_test_create_size (PedSector length, FatType fat_type,
+ PedSector cluster_sectors, PedSector cluster_count)
+{
+ PedSector rootdir_sectors;
+ PedSector _cluster_sectors;
+ FatCluster _cluster_count;
+ PedSector _fat_size;
+
+ rootdir_sectors = (fat_type == FAT_TYPE_FAT16) ? 16 : 0;
+
+ if (!fat_calc_sizes (length, 0, fat_type, rootdir_sectors,
+ &_cluster_sectors, &_cluster_count, &_fat_size))
+ return -1; // XXX: doesn't work... can't see a better way!
+
+ if (_cluster_sectors < cluster_sectors)
+ return -1;
+ if (_cluster_sectors > cluster_sectors)
+ return 1;
+
+ if (_cluster_count < cluster_count)
+ return -1;
+ if (_cluster_count > cluster_count)
+ return 1;
+
+ return 0;
+}
+
+static PedSector
+_get_create_size (PedSector upper_bound, FatType fat_type,
+ PedSector cluster_sectors, FatCluster cluster_count)
+{
+ PedSector min_length = 0;
+ PedSector max_length = upper_bound;
+ PedSector length;
+
+ while (1) {
+ length = (min_length + max_length) / 2;
+ switch (_test_create_size (length, fat_type, cluster_sectors,
+ cluster_count)) {
+ case -1: min_length = length; break;
+ case 0: return length;
+ case 1: max_length = length; break;
+ }
+ /* hack... won't always be able to get max cluster count
+ * with max cluster size, etc. */
+ if (max_length - min_length == 1)
+ return min_length;
+ }
+
+ return 0; /* shut gcc up */
+}
+#endif
+
+PedConstraint*
+fat_get_create_constraint_fat16 (const PedDevice* dev)
+{
+ PedGeometry full_dev;
+ PedSector min_size;
+ PedSector max_size;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+#if 0
+ min_size = _get_create_size (dev->length, FAT_TYPE_FAT16,
+ fat_min_cluster_size (FAT_TYPE_FAT16),
+ fat_min_cluster_count (FAT_TYPE_FAT16));
+ max_size = _get_create_size (dev->length, FAT_TYPE_FAT16,
+ fat_max_cluster_size (FAT_TYPE_FAT16),
+ fat_max_cluster_count (FAT_TYPE_FAT16));
+ if (!min_size)
+ return NULL;
+#else
+ min_size = 65794;
+ max_size = 2097153;
+#endif
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ min_size, max_size);
+}
+
+PedConstraint*
+fat_get_create_constraint_fat32 (const PedDevice* dev)
+{
+ PedGeometry full_dev;
+ PedSector min_size;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+#if 0
+ min_size = _get_create_size (dev->length, FAT_TYPE_FAT32,
+ fat_min_cluster_size (FAT_TYPE_FAT32),
+ fat_min_cluster_count (FAT_TYPE_FAT32));
+ if (!min_size)
+ return NULL;
+#else
+ min_size = 525224;
+#endif
+
+ return ped_constraint_new (
+ ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ min_size, dev->length);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps fat16_ops = {
+ .probe = fat_probe_fat16,
+#ifndef DISCOVER_ONLY
+ .clobber = fat_clobber,
+ .open = fat_open,
+ .create = fat_create_fat16,
+ .close = fat_close,
+ .check = fat_check,
+ .resize = fat_resize,
+ .copy = fat_copy,
+ .get_create_constraint = fat_get_create_constraint_fat16,
+ .get_resize_constraint = fat_get_resize_constraint,
+ .get_copy_constraint = fat_get_copy_constraint,
+#else /* !DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL,
+#endif /* !DISCOVER_ONLY */
+};
+
+static PedFileSystemOps fat32_ops = {
+ .probe = fat_probe_fat32,
+#ifndef DISCOVER_ONLY
+ .clobber = fat_clobber,
+ .open = fat_open,
+ .create = fat_create_fat32,
+ .close = fat_close,
+ .check = fat_check,
+ .resize = fat_resize,
+ .copy = fat_copy,
+ .get_create_constraint = fat_get_create_constraint_fat32,
+ .get_resize_constraint = fat_get_resize_constraint,
+ .get_copy_constraint = fat_get_copy_constraint,
+#else /* !DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .resize = NULL,
+ .copy = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL,
+#endif /* !DISCOVER_ONLY */
+};
+
+#define FAT_BLOCK_SIZES ((int[2]){512, 0})
+
+PedFileSystemType fat16_type = {
+ .next = NULL,
+ .ops = &fat16_ops,
+ .name = "fat16",
+ .block_sizes = FAT_BLOCK_SIZES
+};
+
+PedFileSystemType fat32_type = {
+ .next = NULL,
+ .ops = &fat32_ops,
+ .name = "fat32",
+ .block_sizes = FAT_BLOCK_SIZES
+};
+
+void
+ped_file_system_fat_init ()
+{
+ if (sizeof (FatBootSector) != 512) {
+ ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL,
+ _("GNU Parted was miscompiled: the FAT boot sector "
+ "should be 512 bytes. FAT support will be disabled."));
+ } else {
+ ped_file_system_type_register (&fat16_type);
+ ped_file_system_type_register (&fat32_type);
+ }
+}
+
+void
+ped_file_system_fat_done ()
+{
+ ped_file_system_type_unregister (&fat16_type);
+ ped_file_system_type_unregister (&fat32_type);
+}
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/fat.h b/usr/src/lib/libparted/common/libparted/fs/fat/fat.h
new file mode 100644
index 0000000000..82232fbfb5
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/fat.h
@@ -0,0 +1,168 @@
+/*
+ libparted
+ Copyright (C) 1998-2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FAT_H_INCLUDED
+#define FAT_H_INCLUDED
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define BUFFER_SIZE 1024 /* buffer size in sectors (512 bytes) */
+
+typedef uint32_t FatCluster;
+typedef int32_t FatFragment;
+
+enum _FatType {
+ FAT_TYPE_FAT12,
+ FAT_TYPE_FAT16,
+ FAT_TYPE_FAT32
+};
+typedef enum _FatType FatType;
+
+typedef struct _FatSpecific FatSpecific;
+typedef struct _FatDirEntry FatDirEntry;
+
+/* FIXME: YUCKY */
+#include "table.h"
+#include "bootsector.h"
+#include "context.h"
+#include "fatio.h"
+#include "traverse.h"
+#include "calc.h"
+#include "count.h"
+#include "clstdup.h"
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _FatDirEntry {
+ char name[8];
+ uint8_t extension[3];
+ uint8_t attributes;
+ uint8_t is_upper_case_name;
+ uint8_t creation_time_low; /* milliseconds */
+ uint16_t creation_time_high;
+ uint16_t creation_date;
+ uint16_t access_date;
+ uint16_t first_cluster_high; /* for FAT32 */
+ uint16_t time;
+ uint16_t date;
+ uint16_t first_cluster;
+ uint32_t length;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+struct _FatSpecific {
+ FatBootSector boot_sector; /* structure of boot sector */
+ FatInfoSector info_sector; /* fat32-only information sector */
+
+ int logical_sector_size; /* illogical sector size :-) */
+ PedSector sector_count;
+
+ int sectors_per_track; /* BIOS CHS stuff (S) */
+ int heads; /* BIOS CHS stuff (H) */
+
+ int cluster_size;
+ PedSector cluster_sectors;
+ FatCluster cluster_count;
+ int dir_entries_per_cluster;
+
+ FatType fat_type;
+ int fat_table_count;
+ PedSector fat_sectors;
+
+ uint32_t serial_number;
+
+ PedSector info_sector_offset; /* FAT32 only */
+ PedSector fat_offset;
+ PedSector root_dir_offset; /* non-FAT32 */
+ PedSector cluster_offset;
+ PedSector boot_sector_backup_offset;
+
+ FatCluster root_cluster; /* FAT32 only */
+ int root_dir_entry_count; /* non-FAT32 */
+ PedSector root_dir_sector_count; /* non-FAT32 */
+ FatCluster total_dir_clusters;
+
+ FatTable* fat;
+ FatClusterInfo* cluster_info;
+
+ PedSector buffer_sectors;
+ char* buffer;
+
+ int frag_size;
+ PedSector frag_sectors;
+ FatFragment frag_count;
+ FatFragment buffer_frags;
+ FatFragment cluster_frags;
+};
+
+#define FAT_SPECIFIC(fs) ((FatSpecific*) fs->type_specific)
+
+#define FAT_ROOT 0
+
+#define DELETED_FLAG 0xe5
+
+#define READONLY_ATTR 0x01
+#define HIDDEN_ATTR 0x02
+#define SYSTEM_ATTR 0x04
+#define VOLUME_LABEL_ATTR 0x08
+#define VFAT_ATTR 0x0f
+#define DIRECTORY_ATTR 0x10
+#define ARCH_ATTR 0x20
+
+#define MAX_FAT12_CLUSTERS 4086
+#define MAX_FAT16_CLUSTERS 65526
+#define MAX_FAT32_CLUSTERS 2000000
+
+#define FAT_ROOT_DIR_ENTRY_COUNT 512
+
+extern PedFileSystemType fat16_type;
+extern PedFileSystemType fat32_type;
+
+extern void fat_print (const PedFileSystem* fs);
+
+extern PedFileSystem* fat_alloc (const PedGeometry* geom);
+extern void fat_free (PedFileSystem* fs);
+extern int fat_alloc_buffers (PedFileSystem* fs);
+extern void fat_free_buffers (PedFileSystem* fs);
+
+extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer);
+
+extern int fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors);
+
+#endif /* FAT_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/fatio.c b/usr/src/lib/libparted/common/libparted/fs/fat/fatio.c
new file mode 100644
index 0000000000..c12ebc6e8c
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/fatio.c
@@ -0,0 +1,151 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "fatio.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifndef DISCOVER_ONLY
+
+int
+fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+ FatFragment count)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector sector = fat_frag_to_sector (fs, frag);
+ PedSector sector_count = count * fs_info->frag_sectors;
+
+ PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
+
+ return ped_geometry_read (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+ return fat_read_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+ FatFragment count)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector sector = fat_frag_to_sector (fs, frag);
+ PedSector sector_count = count * fs_info->frag_sectors;
+
+ PED_ASSERT (frag >= 0 && frag < fs_info->frag_count, return 0);
+
+ return ped_geometry_write (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+ return fat_write_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_write_sync_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+ FatFragment count)
+{
+ if (!fat_write_fragments (fs, buf, frag, count))
+ return 0;
+ if (!ped_geometry_sync (fs->geom))
+ return 0;
+ return 1;
+}
+
+int
+fat_write_sync_fragment (PedFileSystem* fs, char* buf, FatFragment frag)
+{
+ return fat_write_sync_fragments (fs, buf, frag, 1);
+}
+
+int
+fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+ FatCluster count)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector sector = fat_cluster_to_sector (fs, cluster);
+ PedSector sector_count = count * fs_info->cluster_sectors;
+
+ PED_ASSERT (cluster >= 2
+ && cluster + count - 1 < fs_info->cluster_count + 2,
+ return 0);
+
+ return ped_geometry_read (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+ return fat_read_clusters (fs, buf, cluster, 1);
+}
+
+int
+fat_write_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+ FatCluster count)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector sector = fat_cluster_to_sector (fs, cluster);
+ PedSector sector_count = count * fs_info->cluster_sectors;
+
+ PED_ASSERT (cluster >= 2
+ && cluster + count - 1 < fs_info->cluster_count + 2,
+ return 0);
+
+ return ped_geometry_write (fs->geom, buf, sector, sector_count);
+}
+
+int
+fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+ return fat_write_clusters (fs, buf, cluster, 1);
+}
+
+int
+fat_write_sync_clusters (PedFileSystem* fs, char *buf, FatCluster cluster,
+ FatCluster count)
+{
+ if (!fat_write_clusters (fs, buf, cluster, count))
+ return 0;
+ if (!ped_geometry_sync (fs->geom))
+ return 0;
+ return 1;
+}
+
+int
+fat_write_sync_cluster (PedFileSystem* fs, char *buf, FatCluster cluster)
+{
+ if (!fat_write_cluster (fs, buf, cluster))
+ return 0;
+ if (!ped_geometry_sync (fs->geom))
+ return 0;
+ return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/fatio.h b/usr/src/lib/libparted/common/libparted/fs/fat/fatio.h
new file mode 100644
index 0000000000..319b2262bd
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/fatio.h
@@ -0,0 +1,48 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FATIO_H_INCLUDED
+#define FATIO_H_INCLUDED
+
+#include "fat.h"
+
+extern int fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+ FatFragment count);
+extern int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag,
+ FatFragment count);
+extern int fat_write_sync_fragments (PedFileSystem* fs, char* buf,
+ FatFragment frag, FatFragment count);
+
+extern int fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag);
+extern int fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag);
+extern int fat_write_sync_fragment (PedFileSystem* fs, char* buf,
+ FatFragment frag);
+
+extern int fat_read_clusters (PedFileSystem* fs, char* buf, FatCluster cluster,
+ FatCluster count);
+extern int fat_write_clusters (PedFileSystem* fs, char* buf, FatCluster cluster,
+ FatCluster count);
+extern int fat_write_sync_clusters (PedFileSystem* fs, char* buf,
+ FatCluster cluster, FatCluster count);
+
+extern int fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster);
+extern int fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster);
+extern int fat_write_sync_cluster (PedFileSystem* fs, char *buf,
+ FatCluster cluster);
+
+#endif /* FATIO_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/resize.c b/usr/src/lib/libparted/common/libparted/fs/fat/resize.c
new file mode 100644
index 0000000000..7386948de1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/resize.c
@@ -0,0 +1,877 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+#include "count.h"
+#include "fatio.h"
+#include "calc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+/* Recursively builds (i.e. makes consistent) the duplicated directory tree
+ * (leaving the original directory tree in tact)
+ */
+static int
+fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info)
+{
+ FatTraverseInfo* sub_dir_info;
+ FatDirEntry* dir_entry;
+ FatCluster old_first_cluster;
+
+ while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) {
+ if (fat_dir_entry_is_null_term (dir_entry))
+ break;
+ if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs))
+ continue;
+
+ fat_traverse_mark_dirty (trav_info);
+
+ old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry,
+ ctx->old_fs);
+ fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs,
+ fat_op_context_map_cluster (ctx, old_first_cluster));
+
+ if (fat_dir_entry_is_directory (dir_entry)
+ && dir_entry->name [0] != '.') {
+ sub_dir_info
+ = fat_traverse_directory (trav_info, dir_entry);
+ if (!sub_dir_info)
+ return 0;
+ if (!fat_construct_directory (ctx, sub_dir_info))
+ return 0;
+ }
+ }
+ /* remove "stale" entries at the end */
+ while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) {
+ memset (dir_entry, 0, sizeof (FatDirEntry));
+ fat_traverse_mark_dirty (trav_info);
+ }
+ fat_traverse_complete (trav_info);
+ return 1;
+}
+
+static int
+duplicate_legacy_root_dir (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+ PED_ASSERT (old_fs_info->root_dir_sector_count
+ == new_fs_info->root_dir_sector_count, return 0);
+
+ if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
+ old_fs_info->root_dir_offset,
+ old_fs_info->root_dir_sector_count))
+ return 0;
+
+ if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
+ new_fs_info->root_dir_offset,
+ new_fs_info->root_dir_sector_count))
+ return 0;
+
+ return 1;
+}
+
+/*
+ Constructs the new directory tree for legacy (FAT16) file systems.
+*/
+static int
+fat_construct_legacy_root (FatOpContext* ctx)
+{
+ FatTraverseInfo* trav_info;
+
+ if (!duplicate_legacy_root_dir (ctx))
+ return 0;
+ trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\");
+ return fat_construct_directory (ctx, trav_info);
+}
+
+/*
+ Constructs the new directory tree for new (FAT32) file systems.
+*/
+static int
+fat_construct_root (FatOpContext* ctx)
+{
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatTraverseInfo* trav_info;
+
+ trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster,
+ "\\");
+ fat_construct_directory (ctx, trav_info);
+ return 1;
+}
+
+/* Converts the root directory between FAT16 and FAT32. NOTE: this code
+ * can also do no conversion. I'm leaving fat_construct_directory(), because
+ * it's really pretty :-) It also leaves a higher chance of deleted file
+ * recovery, because it doesn't remove redundant entries. (We do this here,
+ * because brain-damaged FAT16 has an arbitary limit on root directory entries,
+ * so we save room)
+ */
+static int
+fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav,
+ FatTraverseInfo* new_trav)
+{
+ FatTraverseInfo* sub_old_dir_trav;
+ FatTraverseInfo* sub_new_dir_trav;
+ FatDirEntry* new_dir_entry;
+ FatDirEntry* old_dir_entry;
+ FatCluster old_first_cluster;
+
+ while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) {
+ if (fat_dir_entry_is_null_term (old_dir_entry))
+ break;
+ if (!fat_dir_entry_is_active (old_dir_entry))
+ continue;
+
+ new_dir_entry = fat_traverse_next_dir_entry (new_trav);
+ if (!new_dir_entry) {
+ return ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("There's not enough room in the root "
+ "directory for all of the files. Either "
+ "cancel, or ignore to lose the files."))
+ == PED_EXCEPTION_IGNORE;
+ }
+
+ *new_dir_entry = *old_dir_entry;
+ fat_traverse_mark_dirty (new_trav);
+
+ if (!fat_dir_entry_has_first_cluster (old_dir_entry,
+ ctx->old_fs))
+ continue;
+
+ old_first_cluster = fat_dir_entry_get_first_cluster (
+ old_dir_entry, ctx->old_fs);
+ fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs,
+ fat_op_context_map_cluster (ctx, old_first_cluster));
+
+ if (fat_dir_entry_is_directory (old_dir_entry)
+ && old_dir_entry->name [0] != '.') {
+ sub_old_dir_trav
+ = fat_traverse_directory (old_trav, old_dir_entry);
+ sub_new_dir_trav
+ = fat_traverse_directory (new_trav, new_dir_entry);
+ if (!sub_old_dir_trav || !sub_new_dir_trav)
+ return 0;
+
+ if (!fat_convert_directory (ctx, sub_old_dir_trav,
+ sub_new_dir_trav))
+ return 0;
+ }
+ }
+
+ /* remove "stale" entries at the end, just in case there is some
+ * overlap
+ */
+ while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) {
+ memset (new_dir_entry, 0, sizeof (FatDirEntry));
+ fat_traverse_mark_dirty (new_trav);
+ }
+
+ fat_traverse_complete (old_trav);
+ fat_traverse_complete (new_trav);
+ return 1;
+}
+
+static void
+clear_cluster (PedFileSystem* fs, FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ memset (fs_info->buffer, 0, fs_info->cluster_size);
+ fat_write_cluster (fs, fs_info->buffer, cluster);
+}
+
+/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster
+ * allocation depend on the old FAT. The reason is, old clusters may
+ * still be needed during the resize, (particularly clusters in the directory
+ * tree) even if they will be discarded later.
+ */
+static int
+alloc_root_dir (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatCluster i;
+ FatCluster cluster;
+ FatCluster cluster_count;
+
+ PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32, return 0);
+
+ cluster_count = ped_div_round_up (
+ PED_MAX (16, old_fs_info->root_dir_sector_count),
+ new_fs_info->cluster_sectors);
+
+ for (i = 0; i < cluster_count; i++) {
+ cluster = fat_table_alloc_check_cluster (new_fs_info->fat,
+ ctx->new_fs);
+ if (!cluster)
+ return 0;
+ ctx->new_root_dir [i] = cluster;
+ clear_cluster (ctx->new_fs, cluster);
+ }
+ ctx->new_root_dir [i] = 0;
+ new_fs_info->root_cluster = ctx->new_root_dir [0];
+ return 1;
+}
+
+/* when converting FAT32 -> FAT16
+ * fat_duplicate clusters() duplicated the root directory unnecessarily.
+ * Let's free it.
+ *
+ * This must be called AFTER fat_construct_new_fat(). (otherwise, our
+ * changes just get overwritten)
+ */
+static int
+free_root_dir (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatCluster old_cluster;
+ FatFragment i;
+
+ PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32, return 0);
+ PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16, return 0);
+
+ for (old_cluster = old_fs_info->root_cluster;
+ !fat_table_is_eof (old_fs_info->fat, old_cluster);
+ old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) {
+ FatFragment old_frag;
+ old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster);
+ for (i = 0; i < new_fs_info->cluster_frags; i++) {
+ FatFragment new_frag;
+ FatCluster new_clst;
+ new_frag = fat_op_context_map_fragment (ctx,
+ old_frag + i);
+ new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag);
+ if (!fat_table_set_avail (new_fs_info->fat, new_clst))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+fat_clear_root_dir (PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int i;
+
+ PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16, return 0);
+ PED_ASSERT (fs_info->root_dir_sector_count, return 0);
+
+ memset (fs_info->buffer, 0, 512);
+
+ for (i = 0; i < fs_info->root_dir_sector_count; i++) {
+ if (!ped_geometry_write (fs->geom, fs_info->buffer,
+ fs_info->root_dir_offset + i, 1)) {
+ if (ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Error writing to the root directory."))
+ == PED_EXCEPTION_CANCEL)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+fat_construct_converted_tree (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatTraverseInfo* old_trav_info;
+ FatTraverseInfo* new_trav_info;
+
+ if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ new_trav_info = fat_traverse_begin (ctx->new_fs,
+ new_fs_info->root_cluster, "\\");
+ old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT,
+ "\\");
+ } else {
+ fat_clear_root_dir (ctx->new_fs);
+ new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT,
+ "\\");
+ old_trav_info = fat_traverse_begin (ctx->old_fs,
+ old_fs_info->root_cluster, "\\");
+ }
+ if (!new_trav_info || !old_trav_info)
+ return 0;
+ if (!fat_convert_directory (ctx, old_trav_info, new_trav_info))
+ return 0;
+ return 1;
+}
+
+/*
+ Constructs the new directory tree to match the new file locations.
+*/
+static int
+fat_construct_dir_tree (FatOpContext* ctx)
+{
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+
+ if (new_fs_info->fat_type == old_fs_info->fat_type) {
+ switch (old_fs_info->fat_type) {
+ case FAT_TYPE_FAT12:
+ PED_ASSERT (0, (void) 0);
+ break;
+
+ case FAT_TYPE_FAT16:
+ return fat_construct_legacy_root (ctx);
+
+ case FAT_TYPE_FAT32:
+ return fat_construct_root (ctx);
+ }
+ } else {
+ return fat_construct_converted_tree (ctx);
+ }
+
+ return 0;
+}
+
+static FatFragment
+_get_next_old_frag (FatOpContext* ctx, FatFragment frag)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatCluster cluster;
+ FatCluster next_cluster;
+
+ if ((frag + 1) % old_fs_info->cluster_frags != 0) {
+ if (fat_is_fragment_active (ctx->old_fs, frag + 1))
+ return frag + 1;
+ else
+ return -1;
+ } else {
+ cluster = fat_frag_to_cluster (ctx->old_fs, frag);
+ next_cluster = fat_table_get (old_fs_info->fat, cluster);
+
+ if (fat_table_is_eof (old_fs_info->fat, next_cluster))
+ return -1;
+ else
+ return fat_cluster_to_frag (ctx->old_fs, next_cluster);
+ }
+}
+
+/*
+ Constructs the new fat for the resized file system.
+*/
+static int
+fat_construct_new_fat (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ FatFragment old_frag;
+ FatCluster new_cluster;
+ FatFragment new_frag;
+ FatFragment old_next_frag;
+ FatFragment new_next_frag;
+ FatCluster new_next_cluster;
+ FatClusterFlag flag;
+ int i;
+
+ fat_table_clear (new_fs_info->fat);
+ if (!fat_table_set_cluster_count (new_fs_info->fat,
+ new_fs_info->cluster_count))
+ return 0;
+
+ for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) {
+ flag = fat_get_fragment_flag (ctx->old_fs, old_frag);
+ if (flag == FAT_FLAG_FREE)
+ continue;
+ if (flag == FAT_FLAG_BAD) {
+ new_frag = fat_op_context_map_static_fragment (
+ ctx, old_frag);
+ if (new_frag == -1)
+ continue;
+ new_cluster = fat_frag_to_cluster (ctx->new_fs,
+ new_frag);
+ fat_table_set_bad (new_fs_info->fat, new_cluster);
+ continue;
+ }
+
+ new_frag = fat_op_context_map_fragment (ctx, old_frag);
+ new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag);
+
+ old_next_frag = _get_next_old_frag (ctx, old_frag);
+ if (old_next_frag == -1) {
+ fat_table_set_eof (new_fs_info->fat, new_cluster);
+ continue;
+ }
+
+ new_next_frag = fat_op_context_map_fragment (ctx,
+ old_next_frag);
+ PED_ASSERT (new_next_frag != -1, return 0);
+
+ new_next_cluster = fat_frag_to_cluster (ctx->new_fs,
+ new_next_frag);
+ PED_ASSERT (new_next_cluster != new_cluster, return 0);
+
+ fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster);
+ }
+
+#if 0
+#ifdef PED_VERBOSE
+ for (old_cluster=2; old_cluster < old_fs_info->cluster_count+2;
+ old_cluster++) {
+ if (fat_table_is_available (old_fs_info->fat, old_cluster))
+ continue;
+
+ printf ("%d->%d\t(next: %d->%d)\n",
+ old_cluster,
+ ctx->remap [old_cluster],
+ fat_table_get (old_fs_info->fat, old_cluster),
+ fat_table_get (new_fs_info->fat,
+ ctx->remap [old_cluster]));
+ }
+#endif /* PED_VERBOSE */
+#endif
+
+ if (old_fs_info->fat_type == FAT_TYPE_FAT32
+ && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ new_fs_info->root_cluster
+ = fat_op_context_map_cluster (ctx,
+ old_fs_info->root_cluster);
+ }
+
+ if (old_fs_info->fat_type == FAT_TYPE_FAT16
+ && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ for (i=0; ctx->new_root_dir[i+1]; i++) {
+ fat_table_set (new_fs_info->fat,
+ ctx->new_root_dir[i],
+ ctx->new_root_dir[i+1]);
+ }
+ fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]);
+ }
+
+ return 1;
+}
+
+static int
+ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedExceptionOption status;
+ char* fat16_msg;
+ char* fat32_msg;
+
+ if (fs_info->fat_type == FAT_TYPE_FAT16)
+ fat16_msg = _("If you leave your file system as FAT16, "
+ "then you will have no problems.");
+ else
+ fat16_msg = _("If you convert to FAT16, and MS Windows "
+ "is installed on this partition, then "
+ "you must re-install the MS Windows boot "
+ "loader. If you want to do this, you "
+ "should consult the Parted manual (or "
+ "your distribution's manual).");
+
+ if (fs_info->fat_type == FAT_TYPE_FAT32)
+ fat32_msg = _("If you leave your file system as FAT32, "
+ "then you will not introduce any new "
+ "problems.");
+ else
+ fat32_msg = _("If you convert to FAT32, and MS Windows "
+ "is installed on this partition, then "
+ "you must re-install the MS Windows boot "
+ "loader. If you want to do this, you "
+ "should consult the Parted manual (or "
+ "your distribution's manual). Also, "
+ "converting to FAT32 will make the file "
+ "system unreadable by MS DOS, MS Windows "
+ "95a, and MS Windows NT.");
+
+ if (fat16_ok && fat32_ok) {
+ status = ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_YES_NO_CANCEL,
+ _("%s %s %s"),
+ _("Would you like to use FAT32?"),
+ fat16_msg,
+ fat32_msg);
+
+ switch (status) {
+ case PED_EXCEPTION_YES:
+ *out_fat_type = FAT_TYPE_FAT32;
+ return 1;
+
+ case PED_EXCEPTION_NO:
+ *out_fat_type = FAT_TYPE_FAT16;
+ return 1;
+
+ case PED_EXCEPTION_UNHANDLED:
+ *out_fat_type = fs_info->fat_type;
+ return 1;
+
+ case PED_EXCEPTION_CANCEL:
+ return 0;
+
+ default:
+ PED_ASSERT (0, (void) 0);
+ break;
+ }
+ }
+
+ if (fat16_ok) {
+ if (fs_info->fat_type != FAT_TYPE_FAT16) {
+ status = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK_CANCEL,
+ _("%s %s"),
+ _("The file system can only be resized to this "
+ "size by converting to FAT16."),
+ fat16_msg);
+ if (status == PED_EXCEPTION_CANCEL)
+ return 0;
+ }
+ *out_fat_type = FAT_TYPE_FAT16;
+ return 1;
+ }
+
+ if (fat32_ok) {
+ if (fs_info->fat_type != FAT_TYPE_FAT32) {
+ status = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK_CANCEL,
+ _("%s %s"),
+ _("The file system can only be resized to this "
+ "size by converting to FAT32."),
+ fat32_msg);
+ if (status == PED_EXCEPTION_CANCEL)
+ return 0;
+ }
+ *out_fat_type = FAT_TYPE_FAT32;
+ return 1;
+ }
+
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("GNU Parted cannot resize this partition to this size. "
+ "We're working on it!"));
+
+ return 0;
+}
+
+/* For resize operations: determine if the file system must be FAT16 or FAT32,
+ * or either. If the new file system must be FAT32, then query for
+ * confirmation. If either file system can be used, query for which one.
+ */
+static int
+get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom,
+ FatType* out_fat_type)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ PedSector fat16_cluster_sectors;
+ PedSector fat32_cluster_sectors;
+ FatCluster dummy_cluster_count;
+ PedSector dummy_fat_sectors;
+ int fat16_ok;
+ int fat32_ok;
+
+ fat16_ok = fat_calc_resize_sizes (
+ new_geom,
+ fs_info->cluster_sectors,
+ FAT_TYPE_FAT16,
+ fs_info->root_dir_sector_count,
+ fs_info->cluster_sectors,
+ &fat16_cluster_sectors,
+ &dummy_cluster_count,
+ &dummy_fat_sectors);
+
+ fat32_ok = fat_calc_resize_sizes (
+ new_geom,
+ fs_info->cluster_sectors,
+ FAT_TYPE_FAT32,
+ fs_info->root_dir_sector_count,
+ fs_info->cluster_sectors,
+ &fat32_cluster_sectors,
+ &dummy_cluster_count,
+ &dummy_fat_sectors);
+
+ return ask_type (fs, fat16_ok, fat32_ok, out_fat_type);
+}
+
+/* Creates the PedFileSystem struct for the new resized file system, and
+ sticks it in a FatOpContext. At the end of the process, the original
+ (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs).
+ */
+static FatOpContext*
+create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatSpecific* new_fs_info;
+ PedFileSystem* new_fs;
+ PedSector new_cluster_sectors;
+ FatCluster new_cluster_count;
+ PedSector new_fat_sectors;
+ FatType new_fat_type;
+ PedSector root_dir_sector_count;
+ FatOpContext* context;
+
+ /* hypothetical number of root dir sectors, if we end up using
+ * FAT16
+ */
+ if (fs_info->root_dir_sector_count)
+ root_dir_sector_count = fs_info->root_dir_sector_count;
+ else
+ root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT
+ * sizeof (FatDirEntry) / 512;
+
+ if (!get_fat_type (fs, new_geom, &new_fat_type))
+ return 0;
+
+ fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type,
+ root_dir_sector_count, fs_info->cluster_sectors,
+ &new_cluster_sectors, &new_cluster_count, &new_fat_sectors);
+
+ if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors,
+ new_cluster_count))
+ goto error;
+
+ new_fs = fat_alloc (new_geom);
+ if (!new_fs)
+ goto error;
+
+ new_fs_info = FAT_SPECIFIC (new_fs);
+ if (!new_fs_info)
+ goto error_free_new_fs;
+
+/* preserve boot code, etc. */
+ memcpy (&new_fs_info->boot_sector, &fs_info->boot_sector,
+ sizeof (FatBootSector));
+ memcpy (&new_fs_info->info_sector, &fs_info->info_sector,
+ sizeof (FatInfoSector));
+
+ new_fs_info->logical_sector_size = fs_info->logical_sector_size;
+ new_fs_info->sector_count = new_geom->length;
+
+ new_fs_info->sectors_per_track = fs_info->sectors_per_track;
+ new_fs_info->heads = fs_info->heads;
+
+ new_fs_info->cluster_size = new_cluster_sectors * 512;
+ new_fs_info->cluster_sectors = new_cluster_sectors;
+ new_fs_info->cluster_count = new_cluster_count;
+ new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster;
+
+ new_fs_info->fat_type = new_fat_type;
+ new_fs_info->fat_table_count = 2;
+ new_fs_info->fat_sectors = new_fat_sectors;
+
+ /* what about copying? */
+ new_fs_info->serial_number = fs_info->serial_number;
+
+ if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ new_fs_info->info_sector_offset = 1;
+ new_fs_info->boot_sector_backup_offset = 6;
+
+ new_fs_info->root_dir_offset = 0;
+ new_fs_info->root_dir_entry_count = 0;
+ new_fs_info->root_dir_sector_count = 0;
+
+ /* we add calc_align_sectors to push the cluster_offset
+ forward, to keep the clusters aligned between the new
+ and old file systems
+ */
+ new_fs_info->fat_offset
+ = fat_min_reserved_sector_count (FAT_TYPE_FAT32)
+ + fat_calc_align_sectors (new_fs, fs);
+
+ new_fs_info->cluster_offset
+ = new_fs_info->fat_offset
+ + 2 * new_fs_info->fat_sectors;
+ } else {
+ new_fs_info->root_dir_sector_count = root_dir_sector_count;
+ new_fs_info->root_dir_entry_count
+ = root_dir_sector_count * 512 / sizeof (FatDirEntry);
+
+ new_fs_info->fat_offset
+ = fat_min_reserved_sector_count (FAT_TYPE_FAT16)
+ + fat_calc_align_sectors (new_fs, fs);
+
+ new_fs_info->root_dir_offset = new_fs_info->fat_offset
+ + 2 * new_fs_info->fat_sectors;
+
+ new_fs_info->cluster_offset = new_fs_info->root_dir_offset
+ + new_fs_info->root_dir_sector_count;
+ }
+
+ new_fs_info->total_dir_clusters = fs_info->total_dir_clusters;
+
+ context = fat_op_context_new (new_fs, fs);
+ if (!context)
+ goto error_free_new_fs_info;
+
+ if (!fat_op_context_create_initial_fat (context))
+ goto error_free_context;
+
+ if (!fat_alloc_buffers (new_fs))
+ goto error_free_fat;
+
+ return context;
+
+error_free_fat:
+ fat_table_destroy (new_fs_info->fat);
+error_free_context:
+ ped_free (context);
+error_free_new_fs_info:
+ ped_free (new_fs_info);
+error_free_new_fs:
+ ped_free (new_fs);
+error:
+ return NULL;
+}
+
+static int
+resize_context_assimilate (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+ fat_free_buffers (ctx->old_fs);
+ fat_table_destroy (old_fs_info->fat);
+ ped_free (old_fs_info);
+ ped_geometry_destroy (ctx->old_fs->geom);
+
+ ctx->old_fs->type_specific = ctx->new_fs->type_specific;
+ ctx->old_fs->geom = ctx->new_fs->geom;
+ ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16)
+ ? &fat16_type
+ : &fat32_type;
+
+ ped_free (ctx->new_fs);
+
+ fat_op_context_destroy (ctx);
+
+ return 1;
+}
+
+static int
+resize_context_abort (FatOpContext* ctx)
+{
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+
+ fat_free_buffers (ctx->new_fs);
+ fat_table_destroy (new_fs_info->fat);
+ ped_free (new_fs_info);
+ ped_geometry_destroy (ctx->new_fs->geom);
+ ped_free (ctx->new_fs);
+
+ fat_op_context_destroy (ctx);
+
+ return 1;
+}
+
+/* copies the "hidden" sectors, between the boot sector and the FAT. Required,
+ * for the Windows 98 FAT32 boot loader
+ */
+int
+_copy_hidden_sectors (FatOpContext* ctx)
+{
+ FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs);
+ FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs);
+ PedSector first = 1;
+ PedSector last;
+ PedSector count;
+
+ /* nothing to copy for FAT16 */
+ if (old_fs_info->fat_type == FAT_TYPE_FAT16
+ || new_fs_info->fat_type == FAT_TYPE_FAT16)
+ return 1;
+
+ last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1;
+ count = last - first + 1;
+
+ PED_ASSERT (count < BUFFER_SIZE, return 0);
+
+ if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer,
+ first, count))
+ return 0;
+ if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer,
+ first, count))
+ return 0;
+ return 1;
+}
+
+int
+fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatSpecific* new_fs_info;
+ FatOpContext* ctx;
+ PedFileSystem* new_fs;
+
+ ctx = create_resize_context (fs, geom);
+ if (!ctx)
+ goto error;
+ new_fs = ctx->new_fs;
+ new_fs_info = FAT_SPECIFIC (new_fs);
+
+ if (!fat_duplicate_clusters (ctx, timer))
+ goto error_abort_ctx;
+ if (fs_info->fat_type == FAT_TYPE_FAT16
+ && new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ if (!alloc_root_dir (ctx))
+ goto error_abort_ctx;
+ }
+ if (!fat_construct_new_fat (ctx))
+ goto error_abort_ctx;
+ if (fs_info->fat_type == FAT_TYPE_FAT32
+ && new_fs_info->fat_type == FAT_TYPE_FAT16) {
+ if (!free_root_dir (ctx))
+ goto error_abort_ctx;
+ }
+ if (!fat_construct_dir_tree (ctx))
+ goto error_abort_ctx;
+ if (!fat_table_write_all (new_fs_info->fat, new_fs))
+ goto error_abort_ctx;
+
+ _copy_hidden_sectors (ctx);
+ fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs);
+ fat_boot_sector_write (&new_fs_info->boot_sector, new_fs);
+ if (new_fs_info->fat_type == FAT_TYPE_FAT32) {
+ fat_info_sector_generate (&new_fs_info->info_sector, new_fs);
+ fat_info_sector_write (&new_fs_info->info_sector, new_fs);
+ }
+
+ if (!resize_context_assimilate (ctx))
+ goto error;
+
+ return 1;
+
+error_abort_ctx:
+ resize_context_abort (ctx);
+error:
+ return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/table.c b/usr/src/lib/libparted/common/libparted/fs/fat/table.c
new file mode 100644
index 0000000000..e4a95944bc
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/table.c
@@ -0,0 +1,481 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <parted/endian.h>
+#include "fat.h"
+
+#ifndef DISCOVER_ONLY
+
+FatTable*
+fat_table_new (FatType fat_type, FatCluster size)
+{
+ FatTable* ft;
+ int entry_size = fat_table_entry_size (fat_type);
+
+ ft = (FatTable*) ped_malloc (sizeof (FatTable));
+ if (!ft) return NULL;
+
+ ft->cluster_count = ft->free_cluster_count = size - 2;
+
+/* ensure there's some free room on the end, to finish off the sector */
+ ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size;
+ ft->fat_type = fat_type;
+ ft->raw_size = ft->size * entry_size;
+
+ ft->table = ped_malloc (ft->raw_size);
+ if (!ft->table) {
+ ped_free (ft);
+ return NULL;
+ }
+
+ fat_table_clear (ft);
+ return ft;
+}
+
+void
+fat_table_destroy (FatTable* ft)
+{
+ ped_free (ft->table);
+ ped_free (ft);
+}
+
+FatTable*
+fat_table_duplicate (const FatTable* ft)
+{
+ FatTable* dup;
+
+ dup = fat_table_new (ft->fat_type, ft->size);
+ if (!dup) return NULL;
+
+ dup->cluster_count = ft->cluster_count;
+ dup->free_cluster_count = ft->free_cluster_count;
+ dup->bad_cluster_count = ft->bad_cluster_count;
+ dup->last_alloc = ft->last_alloc;
+
+ memcpy (dup->table, ft->table, ft->raw_size);
+
+ return dup;
+}
+
+void
+fat_table_clear (FatTable* ft)
+{
+ memset (ft->table, 0, ft->raw_size);
+
+ fat_table_set (ft, 0, 0x0ffffff8);
+ fat_table_set (ft, 1, 0x0fffffff);
+
+ ft->free_cluster_count = ft->cluster_count;
+ ft->bad_cluster_count = 0;
+ ft->last_alloc = 1;
+}
+
+int
+fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count)
+{
+ PED_ASSERT (new_cluster_count + 2 <= ft->size, return 0);
+
+ ft->cluster_count = new_cluster_count;
+ return fat_table_count_stats (ft);
+}
+
+int
+fat_table_count_stats (FatTable* ft)
+{
+ FatCluster i;
+
+ PED_ASSERT (ft->cluster_count + 2 <= ft->size, return 0);
+
+ ft->free_cluster_count = 0;
+ ft->bad_cluster_count = 0;
+
+ for (i=2; i < ft->cluster_count + 2; i++) {
+ if (fat_table_is_available (ft, i))
+ ft->free_cluster_count++;
+ if (fat_table_is_bad (ft, i))
+ ft->bad_cluster_count++;
+ }
+ return 1;
+}
+
+int
+fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
+
+ memset (ft->table, 0, ft->raw_size);
+
+ if (!ped_geometry_read (fs->geom, (void *) ft->table,
+ fs_info->fat_offset
+ + table_num * fs_info->fat_sectors,
+ fs_info->fat_sectors))
+ return 0;
+
+ if ( *((unsigned char*) ft->table) != fs_info->boot_sector.media) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("FAT %d media %x doesn't match the boot sector's "
+ "media %x. You should probably run scandisk."),
+ (int) table_num + 1,
+ (int) *((unsigned char*) ft->table),
+ (int) fs_info->boot_sector.media)
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+
+ ft->cluster_count = fs_info->cluster_count;
+
+ fat_table_count_stats (ft);
+
+ return 1;
+}
+
+int
+fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512, return 0);
+
+ if (!ped_geometry_write (fs->geom, (void *) ft->table,
+ fs_info->fat_offset
+ + table_num * fs_info->fat_sectors,
+ fs_info->fat_sectors))
+ return 0;
+ if (!ped_geometry_sync (fs->geom))
+ return 0;
+
+ return 1;
+}
+
+int
+fat_table_write_all (const FatTable* ft, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ int i;
+
+ for (i = 0; i < fs_info->fat_table_count; i++) {
+ if (!fat_table_write (ft, fs, i))
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+fat_table_compare (const FatTable* a, const FatTable* b)
+{
+ FatCluster i;
+
+ if (a->cluster_count != b->cluster_count)
+ return 0;
+
+ for (i = 0; i < a->cluster_count + 2; i++) {
+ if (fat_table_get (a, i) != fat_table_get (b, i))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+_test_code_available (const FatTable* ft, FatCluster code)
+{
+ return code == 0;
+}
+
+static int
+_test_code_bad (const FatTable* ft, FatCluster code)
+{
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ if (code == 0xff7) return 1;
+ break;
+
+ case FAT_TYPE_FAT16:
+ if (code == 0xfff7) return 1;
+ break;
+
+ case FAT_TYPE_FAT32:
+ if (code == 0x0ffffff7) return 1;
+ break;
+ }
+ return 0;
+}
+
+static int
+_test_code_eof (const FatTable* ft, FatCluster code)
+{
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ if (code >= 0xff7) return 1;
+ break;
+
+ case FAT_TYPE_FAT16:
+ if (code >= 0xfff7) return 1;
+ break;
+
+ case FAT_TYPE_FAT32:
+ if (code >= 0x0ffffff7) return 1;
+ break;
+ }
+ return 0;
+}
+
+void
+_update_stats (FatTable* ft, FatCluster cluster, FatCluster value)
+{
+ if (_test_code_available (ft, value)
+ && !fat_table_is_available (ft, cluster)) {
+ ft->free_cluster_count++;
+ if (fat_table_is_bad (ft, cluster))
+ ft->bad_cluster_count--;
+ }
+
+ if (!_test_code_available (ft, value)
+ && fat_table_is_available (ft, cluster)) {
+ ft->free_cluster_count--;
+ if (_test_code_bad (ft, cluster))
+ ft->bad_cluster_count--;
+ }
+}
+
+int
+fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value)
+{
+ if (cluster >= ft->cluster_count + 2) {
+ ped_exception_throw (PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("fat_table_set: cluster %ld outside "
+ "file system"),
+ (long) cluster);
+ return 0;
+ }
+
+ _update_stats (ft, cluster, value);
+
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ PED_ASSERT (0, (void) 0);
+ break;
+
+ case FAT_TYPE_FAT16:
+ ((unsigned short *) ft->table) [cluster]
+ = PED_CPU_TO_LE16 (value);
+ break;
+
+ case FAT_TYPE_FAT32:
+ ((unsigned int *) ft->table) [cluster]
+ = PED_CPU_TO_LE32 (value);
+ break;
+ }
+ return 1;
+}
+
+FatCluster
+fat_table_get (const FatTable* ft, FatCluster cluster)
+{
+ if (cluster >= ft->cluster_count + 2) {
+ ped_exception_throw (PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("fat_table_get: cluster %ld outside "
+ "file system"),
+ (long) cluster);
+ exit (1); /* FIXME */
+ }
+
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ PED_ASSERT (0, (void) 0);
+ break;
+
+ case FAT_TYPE_FAT16:
+ return PED_LE16_TO_CPU
+ (((unsigned short *) ft->table) [cluster]);
+
+ case FAT_TYPE_FAT32:
+ return PED_LE32_TO_CPU
+ (((unsigned int *) ft->table) [cluster]);
+ }
+
+ return 0;
+}
+
+FatCluster
+fat_table_alloc_cluster (FatTable* ft)
+{
+ FatCluster i;
+ FatCluster cluster;
+
+/* hack: assumes the first two FAT entries are marked as used (which they
+ * always should be)
+ */
+ for (i=1; i < ft->cluster_count + 1; i++) {
+ cluster = (i + ft->last_alloc) % ft->cluster_count;
+ if (fat_table_is_available (ft, cluster)) {
+ ft->last_alloc = cluster;
+ return cluster;
+ }
+ }
+
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("fat_table_alloc_cluster: no free clusters"));
+ return 0;
+}
+
+FatCluster
+fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster result;
+
+ while (1) {
+ result = fat_table_alloc_cluster (ft);
+ if (!result)
+ return 0;
+ if (fat_read_cluster (fs, fs_info->buffer, result))
+ return result;
+ fat_table_set_bad (ft, result);
+ }
+}
+
+/*
+ returns true if <cluster> is marked as bad
+*/
+int
+fat_table_is_bad (const FatTable* ft, FatCluster cluster)
+{
+ return _test_code_bad (ft, fat_table_get (ft, cluster));
+}
+
+/*
+ returns true if <cluster> represents an EOF marker
+*/
+int
+fat_table_is_eof (const FatTable* ft, FatCluster cluster)
+{
+ return _test_code_eof (ft, cluster);
+}
+
+/*
+ returns true if <cluster> is available.
+*/
+int
+fat_table_is_available (const FatTable* ft, FatCluster cluster)
+{
+ return _test_code_available (ft, fat_table_get (ft, cluster));
+}
+
+/*
+ returns true if <cluster> is empty. Note that this includes bad clusters.
+*/
+int
+fat_table_is_empty (const FatTable* ft, FatCluster cluster)
+{
+ return fat_table_is_available (ft, cluster)
+ || fat_table_is_bad (ft, cluster);
+}
+
+/*
+ returns true if <cluster> is being used for something constructive.
+*/
+int
+fat_table_is_active (const FatTable* ft, FatCluster cluster)
+{
+ return !fat_table_is_bad (ft, cluster)
+ && !fat_table_is_available (ft, cluster);
+}
+
+/*
+ marks <cluster> as the last cluster in the chain
+*/
+int
+fat_table_set_eof (FatTable* ft, FatCluster cluster)
+{
+
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ PED_ASSERT (0, (void) 0);
+ break;
+
+ case FAT_TYPE_FAT16:
+ return fat_table_set (ft, cluster, 0xfff8);
+
+ case FAT_TYPE_FAT32:
+ return fat_table_set (ft, cluster, 0x0fffffff);
+ }
+
+ return 0;
+}
+
+/*
+ Marks a clusters as unusable, due to physical disk damage.
+*/
+int
+fat_table_set_bad (FatTable* ft, FatCluster cluster)
+{
+ if (!fat_table_is_bad (ft, cluster))
+ ft->bad_cluster_count++;
+
+ switch (ft->fat_type) {
+ case FAT_TYPE_FAT12:
+ return fat_table_set (ft, cluster, 0xff7);
+
+ case FAT_TYPE_FAT16:
+ return fat_table_set (ft, cluster, 0xfff7);
+
+ case FAT_TYPE_FAT32:
+ return fat_table_set (ft, cluster, 0x0ffffff7);
+ }
+
+ return 0;
+}
+
+/*
+ marks <cluster> as unused/free/available
+*/
+int
+fat_table_set_avail (FatTable* ft, FatCluster cluster)
+{
+ return fat_table_set (ft, cluster, 0);
+}
+
+#endif /* !DISCOVER_ONLY */
+
+int
+fat_table_entry_size (FatType fat_type)
+{
+ switch (fat_type) {
+ case FAT_TYPE_FAT12:
+ return 2; /* FIXME: how? */
+
+ case FAT_TYPE_FAT16:
+ return 2;
+
+ case FAT_TYPE_FAT32:
+ return 4;
+ }
+
+ return 0;
+}
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/table.h b/usr/src/lib/libparted/common/libparted/fs/fat/table.h
new file mode 100644
index 0000000000..b30a4d54c3
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/table.h
@@ -0,0 +1,74 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PED_FAT_TABLE_H_INCLUDED
+#define PED_FAT_TABLE_H_INCLUDED
+
+typedef struct _FatTable FatTable;
+
+#include "fat.h"
+
+struct _FatTable {
+ void* table;
+ FatCluster size;
+ int raw_size;
+
+ FatType fat_type;
+ FatCluster cluster_count;
+ FatCluster free_cluster_count;
+ FatCluster bad_cluster_count;
+
+ FatCluster last_alloc;
+};
+
+extern FatTable* fat_table_new (FatType fat_type, FatCluster size);
+extern FatTable* fat_table_duplicate (const FatTable* ft);
+extern void fat_table_destroy (FatTable* ft);
+extern void fat_table_clear (FatTable* ft);
+extern int fat_table_set_cluster_count (FatTable* ft,
+ FatCluster new_cluster_count);
+
+extern int fat_table_read (FatTable* ft, const PedFileSystem* fs,
+ int table_num);
+extern int fat_table_write (const FatTable* ft, PedFileSystem* fs,
+ int table_num);
+extern int fat_table_write_all (const FatTable* ft, PedFileSystem* fs);
+extern int fat_table_compare (const FatTable* a, const FatTable* b);
+extern int fat_table_count_stats (FatTable* ft);
+
+extern FatCluster fat_table_get (const FatTable* ft, FatCluster cluster);
+extern int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value);
+
+extern FatCluster fat_table_alloc_cluster (FatTable* ft);
+extern FatCluster fat_table_alloc_check_cluster (FatTable* ft,
+ PedFileSystem* fs);
+
+extern int fat_table_is_bad (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_eof (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_empty (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_available (const FatTable* ft, FatCluster cluster);
+extern int fat_table_is_active (const FatTable* ft, FatCluster cluster);
+
+extern int fat_table_set_eof (FatTable* ft, FatCluster cluster);
+extern int fat_table_set_avail (FatTable* ft, FatCluster cluster);
+extern int fat_table_set_bad (FatTable* ft, FatCluster cluster);
+
+extern int fat_table_entry_size (FatType fat_type);
+
+#endif /* PED_FAT_TABLE_H_INCLUDED */
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/traverse.c b/usr/src/lib/libparted/common/libparted/fs/fat/traverse.c
new file mode 100644
index 0000000000..3d2e2b5e80
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/traverse.c
@@ -0,0 +1,365 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "fat.h"
+#include "traverse.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DISCOVER_ONLY
+
+#define NO_CLUSTER -1
+
+static char tmp_buffer [4096];
+
+int
+fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info)
+{
+ return trav_info->buffer_size / sizeof (FatDirEntry);
+}
+
+/* returns 1 if there are no more directory entries in the directory being
+ * traversed, 0 otherwise.
+ */
+static int
+is_last_buffer (FatTraverseInfo* trav_info) {
+ FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
+
+ if (trav_info->is_legacy_root_dir)
+ return 1;
+ else
+ return fat_table_is_eof (fs_info->fat, trav_info->next_buffer);
+}
+
+static int
+write_root_dir (FatTraverseInfo* trav_info)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
+
+ if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries,
+ fs_info->root_dir_offset,
+ fs_info->root_dir_sector_count))
+ return 0;
+ if (!ped_geometry_sync (trav_info->fs->geom))
+ return 0;
+ trav_info->dirty = 0;
+ return 1;
+}
+
+static int
+write_dir_cluster (FatTraverseInfo* trav_info)
+{
+ if (!fat_write_sync_cluster (trav_info->fs,
+ (void*) trav_info->dir_entries,
+ trav_info->this_buffer))
+ return 0;
+ trav_info->dirty = 0;
+ return 1;
+}
+
+static int
+write_dir_buffer (FatTraverseInfo* trav_info)
+{
+ if (trav_info->is_legacy_root_dir)
+ return write_root_dir (trav_info);
+ else
+ return write_dir_cluster (trav_info);
+}
+
+static int
+read_next_dir_buffer (FatTraverseInfo* trav_info)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs);
+
+ PED_ASSERT (!trav_info->is_legacy_root_dir, return 0);
+
+ trav_info->this_buffer = trav_info->next_buffer;
+
+ if (trav_info->this_buffer < 2
+ || trav_info->this_buffer >= fs_info->cluster_count + 2) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ "Cluster %ld in directory %s is outside file system!",
+ (long) trav_info->this_buffer,
+ trav_info->dir_name);
+ return 0;
+ }
+
+ trav_info->next_buffer
+ = fat_table_get (fs_info->fat, trav_info->this_buffer);
+
+ return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries,
+ trav_info->this_buffer);
+}
+
+/* FIXME: put into fat_dir_entry_* operations */
+void
+fat_traverse_mark_dirty (FatTraverseInfo* trav_info)
+{
+ trav_info->dirty = 1;
+}
+
+FatTraverseInfo*
+fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster,
+ char* dir_name)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatTraverseInfo* trav_info;
+
+ trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo));
+ if (!trav_info)
+ goto error;
+
+ trav_info->dir_name = strdup (dir_name);
+ if (!trav_info->dir_name)
+ goto error_free_trav_info;
+
+ trav_info->fs = fs;
+ trav_info->is_legacy_root_dir
+ = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0);
+ trav_info->dirty = 0;
+ trav_info->eof = 0;
+ trav_info->current_entry = -1;
+
+ if (trav_info->is_legacy_root_dir) {
+ trav_info->buffer_size = 512 * fs_info->root_dir_sector_count;
+ } else {
+ trav_info->next_buffer = start_cluster;
+ trav_info->buffer_size = fs_info->cluster_size;
+ }
+
+ trav_info->dir_entries
+ = (FatDirEntry*) ped_malloc (trav_info->buffer_size);
+ if (!trav_info->dir_entries)
+ goto error_free_dir_name;
+
+ if (trav_info->is_legacy_root_dir) {
+ if (!ped_geometry_read (fs->geom, trav_info->dir_entries,
+ fs_info->root_dir_offset,
+ fs_info->root_dir_sector_count))
+ goto error_free_dir_entries;
+ } else {
+ if (!read_next_dir_buffer (trav_info))
+ goto error_free_dir_entries;
+ }
+
+ return trav_info;
+
+error_free_dir_entries:
+ ped_free (trav_info->dir_entries);
+error_free_dir_name:
+ ped_free (trav_info->dir_name);
+error_free_trav_info:
+ ped_free (trav_info);
+error:
+ return NULL;
+}
+
+int
+fat_traverse_complete (FatTraverseInfo* trav_info)
+{
+ if (trav_info->dirty) {
+ if (!write_dir_buffer (trav_info))
+ return 0;
+ }
+ ped_free (trav_info->dir_entries);
+ ped_free (trav_info->dir_name);
+ ped_free (trav_info);
+ return 1;
+}
+
+FatTraverseInfo*
+fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent)
+{
+ strcpy (tmp_buffer, trav_info->dir_name);
+ fat_dir_entry_get_name (parent,
+ tmp_buffer + strlen (trav_info->dir_name));
+ strcat (tmp_buffer, "\\");
+
+ return fat_traverse_begin (trav_info->fs,
+ fat_dir_entry_get_first_cluster (parent, trav_info->fs),
+ tmp_buffer);
+}
+
+FatDirEntry*
+fat_traverse_next_dir_entry (FatTraverseInfo *trav_info)
+{
+ if (trav_info->eof)
+ return NULL;
+
+ trav_info->current_entry++;
+ if (trav_info->current_entry
+ >= fat_traverse_entries_per_buffer (trav_info)) {
+ if (trav_info->dirty) {
+ if (!write_dir_buffer (trav_info))
+ return NULL;
+ }
+
+ trav_info->current_entry = 0;
+ if (is_last_buffer (trav_info)) {
+ trav_info->eof = 1;
+ return NULL;
+ }
+ if (!read_next_dir_buffer (trav_info))
+ return NULL;
+ }
+ return trav_info->dir_entries + trav_info->current_entry;
+}
+
+FatCluster
+fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ switch (fs_info->fat_type) {
+ case FAT_TYPE_FAT12:
+ case FAT_TYPE_FAT16:
+ return PED_LE16_TO_CPU (dir_entry->first_cluster);
+
+ case FAT_TYPE_FAT32:
+ return PED_LE16_TO_CPU (dir_entry->first_cluster_high)
+ * 65536L
+ + PED_LE16_TO_CPU (dir_entry->first_cluster);
+ }
+
+ return 0;
+}
+
+void
+fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs,
+ FatCluster cluster)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+
+ switch (fs_info->fat_type) {
+ case FAT_TYPE_FAT12:
+ PED_ASSERT (0, (void) 0);
+ break;
+
+ case FAT_TYPE_FAT16:
+ dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster);
+ break;
+
+ case FAT_TYPE_FAT32:
+ dir_entry->first_cluster
+ = PED_CPU_TO_LE16 (cluster & 0xffff);
+ dir_entry->first_cluster_high
+ = PED_CPU_TO_LE16 (cluster / 0x10000);
+ break;
+ }
+}
+
+uint32_t
+fat_dir_entry_get_length (FatDirEntry* dir_entry)
+{
+ return PED_LE32_TO_CPU (dir_entry->length);
+}
+
+int
+fat_dir_entry_is_null_term (const FatDirEntry* dir_entry)
+{
+ FatDirEntry null_entry;
+
+ memset (&null_entry, 0, sizeof (null_entry));
+ return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0;
+}
+
+int
+fat_dir_entry_is_active (FatDirEntry* dir_entry)
+{
+ if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0;
+ if ((unsigned char) dir_entry->name[0] == 0) return 0;
+ if ((unsigned char) dir_entry->name[0] == 0xF6) return 0;
+ return 1;
+}
+
+int
+fat_dir_entry_is_file (FatDirEntry* dir_entry) {
+ if (dir_entry->attributes == VFAT_ATTR) return 0;
+ if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
+ if (!fat_dir_entry_is_active (dir_entry)) return 0;
+ if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0;
+ return 1;
+}
+
+int
+fat_dir_entry_is_system_file (FatDirEntry* dir_entry)
+{
+ if (!fat_dir_entry_is_file (dir_entry)) return 0;
+ return (dir_entry->attributes & SYSTEM_ATTR)
+ || (dir_entry->attributes & HIDDEN_ATTR);
+}
+
+int
+fat_dir_entry_is_directory (FatDirEntry* dir_entry)
+{
+ if (dir_entry->attributes == VFAT_ATTR) return 0;
+ if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
+ if (!fat_dir_entry_is_active (dir_entry)) return 0;
+ return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR;
+}
+
+int
+fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs)
+{
+ FatSpecific* fs_info = FAT_SPECIFIC (fs);
+ FatCluster first_cluster;
+
+ if (!fat_dir_entry_is_file (dir_entry)
+ && !fat_dir_entry_is_directory (dir_entry))
+ return 0;
+
+ first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs);
+ if (first_cluster == 0
+ || fat_table_is_eof (fs_info->fat, first_cluster))
+ return 0;
+
+ return 1;
+}
+
+/*
+ decrypts silly DOS names to FILENAME.EXT
+*/
+void
+fat_dir_entry_get_name (FatDirEntry*dir_entry, char *result) {
+ int i;
+ char *src;
+
+ src = dir_entry->name;
+
+ for (i=0; i<8; i++) {
+ if (src[i] == ' ' || src[i] == 0) break;
+ *result++ = src[i];
+ }
+
+ if (src[8] != ' ' && src[8] != 0) {
+ *result++ = '.';
+ for (i=8; i<11; i++) {
+ if (src[i] == ' ' || src[i] == 0) break;
+ *result++ = src[i];
+ }
+ }
+
+ *result = 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/fat/traverse.h b/usr/src/lib/libparted/common/libparted/fs/fat/traverse.h
new file mode 100644
index 0000000000..21e4c27d6e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/fat/traverse.h
@@ -0,0 +1,73 @@
+/*
+ libparted
+ Copyright (C) 1998, 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef TRAVERSE_H_INCLUDED
+#define TRAVERSE_H_INCLUDED
+
+#include "fatio.h"
+
+typedef struct _FatTraverseInfo FatTraverseInfo;
+
+struct _FatTraverseInfo {
+ PedFileSystem* fs;
+ char* dir_name;
+
+ int is_legacy_root_dir;
+ int dirty;
+ int eof;
+
+ FatDirEntry* dir_entries;
+ int current_entry;
+ FatCluster this_buffer, next_buffer;
+ int buffer_size;
+};
+
+extern int fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info);
+
+/* starts traversal at an arbitary cluster. if start_cluster==0, then uses
+ root directory */
+extern FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs,
+ FatCluster start_cluster, char* dir_name);
+
+extern int fat_traverse_complete (FatTraverseInfo* trav_info);
+
+extern FatTraverseInfo* fat_traverse_directory (FatTraverseInfo* trav_info,
+ FatDirEntry* parent);
+
+extern void fat_traverse_mark_dirty (FatTraverseInfo* trav_info);
+
+extern FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo* trav_info);
+
+extern FatCluster fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry,
+ PedFileSystem* fs);
+
+extern void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry,
+ PedFileSystem* fs, FatCluster cluster);
+
+extern uint32_t fat_dir_entry_get_length (FatDirEntry* dir_entry);
+
+extern int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_file (FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_system_file (FatDirEntry* dir_entry);
+extern int fat_dir_entry_is_directory (FatDirEntry* dir_entry);
+extern void fat_dir_entry_get_name (FatDirEntry* dir_entry, char* result);
+extern int fat_dir_entry_is_active (FatDirEntry* dir_entry);
+extern int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry,
+ PedFileSystem* fs);
+
+#endif /* TRAVERSE_H_INCLUDED */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.c b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.c
new file mode 100644
index 0000000000..b3438bd56e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.c
@@ -0,0 +1,328 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file.h"
+
+#include "advfs.h"
+
+/* - if a < b, 0 if a == b, + if a > b */
+/* Comparaison is done in the following order : */
+/* CNID, then fork type, then start block */
+/* Note that HFS implementation in linux has a bug */
+/* in this function */
+static int
+hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b)
+{
+ HfsExtentKey* key1 = (HfsExtentKey*) a;
+ HfsExtentKey* key2 = (HfsExtentKey*) b;
+
+ /* do NOT use a substraction, because */
+ /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */
+ /* would return -2, despite the fact */
+ /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */
+ if (key1->file_ID != key2->file_ID)
+ return PED_BE32_TO_CPU(key1->file_ID) <
+ PED_BE32_TO_CPU(key2->file_ID) ?
+ -1 : +1;
+
+ if (key1->type != key2->type)
+ return (int)(key1->type - key2->type);
+
+ if (key1->start == key2->start)
+ return 0;
+ /* the whole thing wont work with 16 bits ints */
+ /* anyway */
+ return (int)( PED_BE16_TO_CPU(key1->start) -
+ PED_BE16_TO_CPU(key2->start) );
+}
+
+/* do a B-Tree lookup */
+/* read the first record immediatly inferior or egal to the given key */
+/* return 0 on error */
+/* record_out _must_ be large enough to receive record_size bytes */
+/* WARNING : the compare function called only handle Extents BTree */
+/* so modify this function if you want to do lookup in */
+/* other BTrees has well */
+int
+hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
+ void *record_out, unsigned int record_size,
+ HfsCPrivateLeafRec* record_ref)
+{
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+ HfsHeaderRecord* header;
+ HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
+ HfsPrivateGenericKey* record_key = NULL;
+ unsigned int node_number, record_number;
+ int i;
+
+ /* Read the header node */
+ if (!hfs_file_read_sector(b_tree_file, node, 0))
+ return 0;
+ header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(PED_SECTOR_SIZE_DEFAULT-2))))));
+
+ /* Get the node number of the root */
+ node_number = PED_BE32_TO_CPU(header->root_node);
+ if (!node_number)
+ return 0;
+
+ /* Read the root node */
+ if (!hfs_file_read_sector(b_tree_file, node, node_number))
+ return 0;
+
+ /* Follow the white rabbit */
+ while (1) {
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = record_number; i; i--) {
+ record_key = (HfsPrivateGenericKey*)
+ (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
+ /* check for obvious error in FS */
+ if (((uint8_t*)record_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)record_key - node
+ >= PED_SECTOR_SIZE_DEFAULT
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ return 0;
+ }
+ if (hfs_extent_key_cmp(record_key, key) <= 0)
+ break;
+ }
+ if (!i) return 0;
+ if (desc->type == HFS_IDX_NODE) {
+ unsigned int skip;
+
+ skip = (1 + record_key->key_length + 1) & ~1;
+ node_number = PED_BE32_TO_CPU (*((uint32_t *)
+ (((uint8_t *) record_key) + skip)));
+ if (!hfs_file_read_sector(b_tree_file, node,
+ node_number))
+ return 0;
+ } else
+ break;
+ }
+
+ /* copy the result if needed */
+ if (record_size)
+ memcpy (record_out, record_key, record_size);
+
+ /* send record reference if needed */
+ if (record_ref) {
+ record_ref->node_size = 1; /* in sectors */
+ record_ref->node_number = node_number;
+ record_ref->record_pos = (uint8_t*)record_key - node;
+ record_ref->record_number = i;
+ }
+
+ /* success */
+ return 1;
+}
+
+/* free the bad blocks linked list */
+void
+hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first)
+{
+ HfsPrivateLinkExtent* next;
+
+ while (first) {
+ next = first->next;
+ ped_free (first);
+ first = next;
+ }
+}
+
+/* This function reads bad blocks extents in the extents file
+ and store it in f.s. specific data of fs */
+int
+hfs_read_bad_blocks (const PedFileSystem *fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+
+ if (priv_data->bad_blocks_loaded)
+ return 1;
+
+ {
+ uint8_t record[sizeof (HfsExtentKey)
+ + sizeof (HfsExtDataRec)];
+ HfsExtentKey search;
+ HfsExtentKey* ret_key = (HfsExtentKey*) record;
+ HfsExtDescriptor* ret_cache = (HfsExtDescriptor*)
+ (record + sizeof (HfsExtentKey));
+ unsigned int block, last_start, first_pass = 1;
+
+ search.key_length = sizeof (HfsExtentKey) - 1;
+ search.type = HFS_DATA_FORK;
+ search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+
+ last_start = -1; block = 0;
+ while (1) {
+ int i;
+
+ search.start = PED_CPU_TO_BE16 (block);
+ if (!hfs_btree_search (priv_data->extent_file,
+ (HfsPrivateGenericKey*) &search,
+ record, sizeof (record), NULL)
+ || ret_key->file_ID != search.file_ID
+ || ret_key->type != search.type) {
+ if (first_pass)
+ break;
+ else
+ goto errbb;
+ }
+ if (PED_BE16_TO_CPU (ret_key->start) == last_start)
+ break;
+
+ last_start = PED_BE16_TO_CPU (ret_key->start);
+ for (i = 0; i < HFS_EXT_NB; i++) {
+ if (ret_cache[i].block_count) {
+ HfsPrivateLinkExtent* new_xt =
+ (HfsPrivateLinkExtent*) ped_malloc (
+ sizeof (HfsPrivateLinkExtent));
+ if (!new_xt)
+ goto errbb;
+ new_xt->next = priv_data->bad_blocks_xtent_list;
+ memcpy(&(new_xt->extent), ret_cache+i,
+ sizeof (HfsExtDescriptor));
+ priv_data->bad_blocks_xtent_list = new_xt;
+ priv_data->bad_blocks_xtent_nb++;
+ block += PED_BE16_TO_CPU (
+ ret_cache[i].block_count);
+ }
+ }
+ first_pass = 0;
+ }
+
+ priv_data->bad_blocks_loaded = 1;
+ return 1;}
+
+errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+ priv_data->bad_blocks_xtent_list=NULL;
+ priv_data->bad_blocks_xtent_nb=0;
+ return 0;
+}
+
+/* This function check if fblock is a bad block */
+int
+hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsPrivateLinkExtent* walk;
+
+ for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
+ /* Won't compile without the strange cast ! gcc bug ? */
+ /* or maybe C subtilties... */
+ if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) &&
+ (fblock < (unsigned int) (PED_BE16_TO_CPU (
+ walk->extent.start_block)
+ + PED_BE16_TO_CPU (
+ walk->extent.block_count))))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This function returns the first sector of the last free block of an
+ HFS volume we can get after a hfs_pack_free_space_from_block call */
+/* On error this function returns 0 */
+PedSector
+hfs_get_empty_end (const PedFileSystem *fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsMasterDirectoryBlock* mdb = priv_data->mdb;
+ HfsPrivateLinkExtent* link;
+ unsigned int block, last_bad, end_free_blocks;
+
+ /* find the next block to the last bad block of the volume */
+ if (!hfs_read_bad_blocks (fs))
+ return 0;
+
+ last_bad = 0;
+ for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) {
+ if ((unsigned int) PED_BE16_TO_CPU (link->extent.start_block)
+ + PED_BE16_TO_CPU (link->extent.block_count) > last_bad)
+ last_bad = PED_BE16_TO_CPU (link->extent.start_block)
+ + PED_BE16_TO_CPU (link->extent.block_count);
+ }
+
+ /* Count the free blocks from last_bad to the end of the volume */
+ end_free_blocks = 0;
+ for (block = last_bad;
+ block < PED_BE16_TO_CPU (mdb->total_blocks);
+ block++) {
+ if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ end_free_blocks++;
+ }
+
+ /* Calculate the block that will by the first free at the
+ end of the volume */
+ block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks;
+
+ return (PedSector) PED_BE16_TO_CPU (mdb->start_block)
+ + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+}
+
+/* return the block which should be used to pack data to have at
+ least free fblock blocks at the end of the volume */
+unsigned int
+hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ unsigned int block;
+
+ for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1;
+ block && fblock;
+ block--) {
+ if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ fblock--;
+ }
+
+ while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ block--;
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ block++;
+
+ return block;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.h b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.h
new file mode 100644
index 0000000000..18a2661e35
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs.h
@@ -0,0 +1,48 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _ADVFS_H
+#define _ADVFS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
+ void *record_out, unsigned int record_size,
+ HfsCPrivateLeafRec* record_ref);
+
+void
+hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first);
+
+int
+hfs_read_bad_blocks (const PedFileSystem *fs);
+
+int
+hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
+
+PedSector
+hfs_get_empty_end (const PedFileSystem *fs);
+
+unsigned int
+hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
+
+#endif /* _ADVFS_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.c b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.c
new file mode 100644
index 0000000000..b810c8b089
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.c
@@ -0,0 +1,383 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs.h"
+#include "file_plus.h"
+
+#include "advfs_plus.h"
+
+/* - if a < b, 0 if a == b, + if a > b */
+/* Comparaison is done in the following order : */
+/* CNID, then fork type, then start block */
+static int
+hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b)
+{
+ HfsPExtentKey* key1 = (HfsPExtentKey*) a;
+ HfsPExtentKey* key2 = (HfsPExtentKey*) b;
+
+ if (key1->file_ID != key2->file_ID)
+ return PED_BE32_TO_CPU(key1->file_ID) <
+ PED_BE32_TO_CPU(key2->file_ID) ?
+ -1 : +1;
+
+ if (key1->type != key2->type)
+ return (int)(key1->type - key2->type);
+
+ if (key1->start == key2->start)
+ return 0;
+ return PED_BE32_TO_CPU(key1->start) <
+ PED_BE32_TO_CPU(key2->start) ?
+ -1 : +1;
+}
+
+/* do a B-Tree lookup */
+/* read the first record immediatly inferior or egal to the given key */
+/* return 0 on error */
+/* record_out _must_ be large enough to receive the whole record (key + data) */
+/* WARNING : the search function called only handle Extents BTree */
+/* so modify this function if you want to do lookup in */
+/* other BTrees has well */
+int
+hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
+ void *record_out, unsigned int record_size,
+ HfsCPrivateLeafRec* record_ref)
+{
+ uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
+ uint8_t* node;
+ HfsPHeaderRecord* header;
+ HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1;
+ HfsPPrivateGenericKey* record_key = NULL;
+ unsigned int node_number, record_number, size, bsize;
+ int i;
+
+ /* Read the header node */
+ if (!hfsplus_file_read_sector(b_tree_file, node_1, 0))
+ return 0;
+ header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
+
+ /* Get the node number of the root */
+ node_number = PED_BE32_TO_CPU (header->root_node);
+ if (!node_number)
+ return 0;
+
+ /* Get the size of a node in sectors and allocate buffer */
+ size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT;
+ node = (uint8_t*) ped_malloc (bsize);
+ if (!node)
+ return 0;
+ desc = (HfsPNodeDescriptor*) node;
+
+ /* Read the root node */
+ if (!hfsplus_file_read (b_tree_file, node,
+ (PedSector) node_number * size, size))
+ return 0;
+
+ /* Follow the white rabbit */
+ while (1) {
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = record_number; i; i--) {
+ record_key = (HfsPPrivateGenericKey*)
+ (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(bsize - 2*i)))));
+ /* check for obvious error in FS */
+ if (((uint8_t*)record_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)record_key - node
+ >= (signed)bsize
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ ped_free (node);
+ return 0;
+ }
+ if (hfsplus_extent_key_cmp(record_key, key) <= 0)
+ break;
+ }
+ if (!i) { ped_free (node); return 0; }
+ if (desc->type == HFS_IDX_NODE) {
+ unsigned int skip;
+
+ skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length)
+ + 1 ) & ~1;
+ node_number = PED_BE32_TO_CPU (*((uint32_t *)
+ (((uint8_t *) record_key) + skip)));
+ if (!hfsplus_file_read(b_tree_file, node,
+ (PedSector) node_number * size,
+ size)) {
+ ped_free (node);
+ return 0;
+ }
+ } else
+ break;
+ }
+
+ /* copy the result if needed */
+ if (record_size)
+ memcpy (record_out, record_key, record_size);
+
+ /* send record reference if needed */
+ if (record_ref) {
+ record_ref->node_size = size; /* in sectors */
+ record_ref->node_number = node_number;
+ record_ref->record_pos = (uint8_t*)record_key - node;
+ record_ref->record_number = i;
+ }
+
+ /* success */
+ ped_free (node);
+ return 1;
+}
+
+/* free the bad blocks linked list */
+void
+hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first)
+{
+ HfsPPrivateLinkExtent* next;
+
+ while (first) {
+ next = first->next;
+ ped_free (first);
+ first = next;
+ }
+}
+
+/* This function reads bad blocks extents in the extents file
+ and store it in f.s. specific data of fs */
+int
+hfsplus_read_bad_blocks (const PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+
+ if (priv_data->bad_blocks_loaded)
+ return 1;
+
+ {
+ uint8_t record[sizeof (HfsPExtentKey)
+ + sizeof (HfsPExtDataRec)];
+ HfsPExtentKey search;
+ HfsPExtentKey* ret_key = (HfsPExtentKey*) record;
+ HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*)
+ (record + sizeof (HfsPExtentKey));
+ int block, first_pass = 1;
+ unsigned int last_start;
+
+ search.key_length = sizeof (HfsExtentKey) - 2;
+ search.type = HFS_DATA_FORK;
+ search.pad = 0;
+ search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+
+ last_start = -1; block = 0;
+ while (1) {
+ int i;
+
+ search.start = PED_CPU_TO_BE32 (block);
+ if (!hfsplus_btree_search (priv_data->extents_file,
+ (HfsPPrivateGenericKey*) &search,
+ record, sizeof (record), NULL)
+ || ret_key->file_ID != search.file_ID
+ || ret_key->type != search.type) {
+ if (first_pass)
+ break;
+ else
+ goto errbbp;
+ }
+ if (PED_BE32_TO_CPU (ret_key->start) == last_start)
+ break;
+
+ last_start = PED_BE32_TO_CPU (ret_key->start);
+ for (i = 0; i < HFSP_EXT_NB; i++) {
+ if (ret_cache[i].block_count) {
+ HfsPPrivateLinkExtent* new_xt =
+ (HfsPPrivateLinkExtent*) ped_malloc (
+ sizeof (HfsPPrivateLinkExtent));
+ if (!new_xt)
+ goto errbbp;
+ new_xt->next = priv_data->bad_blocks_xtent_list;
+ memcpy (&(new_xt->extent), ret_cache+i,
+ sizeof (HfsPExtDescriptor));
+ priv_data->bad_blocks_xtent_list = new_xt;
+ priv_data->bad_blocks_xtent_nb++;
+ block += PED_BE32_TO_CPU (
+ ret_cache[i].block_count);
+ }
+ }
+ first_pass = 0;
+ }
+
+ priv_data->bad_blocks_loaded = 1;
+ return 1;}
+
+errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+ priv_data->bad_blocks_xtent_list=NULL;
+ priv_data->bad_blocks_xtent_nb=0;
+ return 0;
+}
+
+/* This function check if fblock is a bad block */
+int
+hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPPrivateLinkExtent* walk;
+
+ for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
+ /* Won't compile without the strange cast ! gcc bug ? */
+ /* or maybe C subtilties... */
+ if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) &&
+ (fblock < (unsigned int)(PED_BE32_TO_CPU (
+ walk->extent.start_block)
+ + PED_BE32_TO_CPU (walk->extent.block_count))))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This function returns the first sector of the last free block of
+ an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */
+PedSector
+hfsplus_get_empty_end (const PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPVolumeHeader* vh = priv_data->vh;
+ HfsPPrivateLinkExtent* link;
+ unsigned int block, last_bad, end_free_blocks;
+
+ /* find the next block to the last bad block of the volume */
+ if (!hfsplus_read_bad_blocks (fs)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Bad blocks could not be read."));
+ return 0;
+ }
+
+ last_bad = 0;
+ for (link = priv_data->bad_blocks_xtent_list; link; link = link->next) {
+ if ((unsigned int) PED_BE32_TO_CPU (link->extent.start_block)
+ + PED_BE32_TO_CPU (link->extent.block_count) > last_bad)
+ last_bad = PED_BE32_TO_CPU (link->extent.start_block)
+ + PED_BE32_TO_CPU (link->extent.block_count);
+ }
+
+ /* Count the free blocks from last_bad to the end of the volume */
+ end_free_blocks = 0;
+ for (block = last_bad;
+ block < PED_BE32_TO_CPU (vh->total_blocks);
+ block++) {
+ if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ end_free_blocks++;
+ }
+
+ /* Calculate the block that will by the first free at
+ the end of the volume */
+ block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks;
+
+ return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT );
+}
+
+/* On error, returns 0 */
+PedSector
+hfsplus_get_min_size (const PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ PedSector min_size;
+
+ /* don't need to add anything because every sector
+ can be part of allocation blocks in HFS+, and
+ the last block _must_ be reserved */
+ min_size = hfsplus_get_empty_end(fs);
+ if (!min_size) return 0;
+
+ if (priv_data->wrapper) {
+ HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*)
+ priv_data->wrapper->type_specific;
+ unsigned int hfs_sect_block;
+ PedSector hgee;
+ hfs_sect_block =
+ PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+ /*
+ * if hfs+ is embedded in an hfs wrapper then the new size is :
+ * the new size of the hfs+ volume rounded up to the size
+ * of hfs blocks
+ * + the minimum size of the hfs wrapper without any hfs+
+ * modification
+ * - the current size of the hfs+ volume in the hfs wrapper
+ */
+ hgee = hfs_get_empty_end(priv_data->wrapper);
+ if (!hgee) return 0;
+ min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block)
+ * hfs_sect_block
+ + hgee + 2
+ - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb
+ ->old_new.embedded
+ .location.block_count )
+ * hfs_sect_block;
+ }
+
+ return min_size;
+}
+
+/* return the block which should be used to pack data to have
+ at least free fblock blocks at the end of the volume */
+unsigned int
+hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ unsigned int block;
+
+ for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1;
+ block && fblock;
+ block--) {
+ if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ fblock--;
+ }
+
+ while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ block--;
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
+ block++;
+
+ return block;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.h b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.h
new file mode 100644
index 0000000000..75bd3b7bcb
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/advfs_plus.h
@@ -0,0 +1,51 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _ADVFS_PLUS_H
+#define _ADVFS_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key,
+ void *record_out, unsigned int record_size,
+ HfsCPrivateLeafRec* record_ref);
+
+void
+hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first);
+
+int
+hfsplus_read_bad_blocks (const PedFileSystem *fs);
+
+int
+hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock);
+
+PedSector
+hfsplus_get_empty_end (const PedFileSystem *fs);
+
+PedSector
+hfsplus_get_min_size (const PedFileSystem *fs);
+
+unsigned int
+hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock);
+
+#endif /* _ADVFS_PLUS_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/cache.c b/usr/src/lib/libparted/common/libparted/fs/hfs/cache.c
new file mode 100644
index 0000000000..de34e72a45
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/cache.c
@@ -0,0 +1,238 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+
+#include "cache.h"
+
+static HfsCPrivateCacheTable*
+hfsc_new_cachetable(unsigned int size)
+{
+ HfsCPrivateCacheTable* ret;
+
+ ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret));
+ if (!ret) return NULL;
+
+ ret->next_cache = NULL;
+ ret->table_size = size;
+ ret->table_first_free = 0;
+
+ ret->table = ped_malloc(sizeof(*ret->table)*size);
+ if (!ret->table) { ped_free(ret); return NULL; }
+ memset(ret->table, 0, sizeof(*ret->table)*size);
+
+ return ret;
+}
+
+HfsCPrivateCache*
+hfsc_new_cache(unsigned int block_number, unsigned int file_number)
+{
+ unsigned int cachetable_size, i;
+ HfsCPrivateCache* ret;
+
+ ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret));
+ if (!ret) return NULL;
+ ret->block_number = block_number;
+ /* following code avoid integer overflow */
+ ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ?
+ ( block_number >> CR_SHIFT ) + 1 :
+ ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT
+ ;
+
+ ret->linked_ref = (HfsCPrivateExtent**)
+ ped_malloc( sizeof(*ret->linked_ref)
+ * ret->linked_ref_size );
+ if (!ret->linked_ref) { ped_free(ret); return NULL; }
+
+ cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST;
+ if (cachetable_size < file_number) cachetable_size = (unsigned) -1;
+ ret->first_cachetable_size = cachetable_size;
+ ret->table_list = hfsc_new_cachetable(cachetable_size);
+ if (!ret->table_list) {
+ ped_free(ret->linked_ref);
+ ped_free(ret);
+ return NULL;
+ }
+ ret->last_table = ret->table_list;
+
+ for (i = 0; i < ret->linked_ref_size; ++i)
+ ret->linked_ref[i] = NULL;
+
+ ret->needed_alloc_size = 0;
+
+ return ret;
+}
+
+static void
+hfsc_delete_cachetable(HfsCPrivateCacheTable* list)
+{
+ HfsCPrivateCacheTable* next;
+
+ while (list) {
+ ped_free (list->table);
+ next = list->next_cache;
+ ped_free (list);
+ list = next;
+ }
+}
+
+void
+hfsc_delete_cache(HfsCPrivateCache* cache)
+{
+ hfsc_delete_cachetable(cache->table_list);
+ ped_free(cache->linked_ref);
+ ped_free(cache);
+}
+
+HfsCPrivateExtent*
+hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
+ uint32_t block, uint16_t offset, uint8_t sbb,
+ uint8_t where, uint8_t ref_index)
+{
+ HfsCPrivateExtent* ext;
+ unsigned int idx = start >> CR_SHIFT;
+
+ PED_ASSERT(idx < cache->linked_ref_size, return NULL);
+
+ for (ext = cache->linked_ref[idx];
+ ext && start != ext->ext_start;
+ ext = ext->next);
+
+ if (ext) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to register an extent starting at block "
+ "0x%X, but another one already exists at this "
+ "position. You should check the file system!"),
+ start);
+ return NULL;
+ }
+
+ if ( cache->last_table->table_first_free
+ == cache->last_table->table_size ) {
+ cache->last_table->next_cache =
+ hfsc_new_cachetable( ( cache->first_cachetable_size
+ / CR_NEW_ALLOC_DIV )
+ + CR_ADD_CST );
+ if (!cache->last_table->next_cache)
+ return NULL;
+ cache->last_table = cache->last_table->next_cache;
+ }
+
+ ext = cache->last_table->table+(cache->last_table->table_first_free++);
+
+ ext->ext_start = start;
+ ext->ext_length = length;
+ ext->ref_block = block;
+ ext->ref_offset = offset;
+ ext->sect_by_block = sbb;
+ ext->where = where;
+ ext->ref_index = ref_index;
+
+ ext->next = cache->linked_ref[idx];
+ cache->linked_ref[idx] = ext;
+
+ cache->needed_alloc_size = cache->needed_alloc_size >
+ (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ?
+ cache->needed_alloc_size :
+ (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb;
+
+ return ext;
+}
+
+HfsCPrivateExtent*
+hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start)
+{
+ HfsCPrivateExtent* ret;
+ unsigned int idx = start >> CR_SHIFT;
+
+ PED_ASSERT(idx < cache->linked_ref_size, return NULL);
+
+ for (ret = cache->linked_ref[idx];
+ ret && start != ret->ext_start;
+ ret = ret->next);
+
+ return ret;
+}
+
+/* Can't fail if extent begining at old_start exists */
+/* Returns 0 if no such extent, or on error */
+HfsCPrivateExtent*
+hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
+ uint32_t new_start)
+{
+ HfsCPrivateExtent** ppext;
+ HfsCPrivateExtent* pext;
+
+ unsigned int idx1 = old_start >> CR_SHIFT;
+ unsigned int idx2 = new_start >> CR_SHIFT;
+
+ PED_ASSERT(idx1 < cache->linked_ref_size, return NULL);
+ PED_ASSERT(idx2 < cache->linked_ref_size, return NULL);
+
+ for (pext = cache->linked_ref[idx2];
+ pext && new_start != pext->ext_start;
+ pext = pext->next);
+
+ if (pext) {
+ ped_exception_throw (
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to move an extent from block Ox%X to block "
+ "Ox%X, but another one already exists at this "
+ "position. This should not happen!"),
+ old_start, new_start);
+ return NULL;
+ }
+
+ for (ppext = &(cache->linked_ref[idx1]);
+ (*ppext) && old_start != (*ppext)->ext_start;
+ ppext = &((*ppext)->next));
+
+ if (!(*ppext)) return NULL;
+
+ /* removing the extent from the cache */
+ pext = *ppext;
+ (*ppext) = pext->next;
+
+ /* change ext_start and insert the extent again */
+ pext->ext_start = new_start;
+ pext->next = cache->linked_ref[idx2];
+ cache->linked_ref[idx2] = pext;
+
+ return pext;
+}
+
+#endif /* DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/cache.h b/usr/src/lib/libparted/common/libparted/fs/hfs/cache.h
new file mode 100644
index 0000000000..8e1b921e0e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/cache.h
@@ -0,0 +1,117 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_H
+#define _CACHE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+/* CR => CACHE REF */
+#define CR_NULL 0 /* reserved */
+#define CR_PRIM_CAT 1
+#define CR_PRIM_EXT 2
+#define CR_PRIM_ATTR 3
+#define CR_PRIM_ALLOC 4
+#define CR_PRIM_START 5
+#define CR_BTREE_CAT 6
+#define CR_BTREE_ATTR 7
+#define CR_BTREE_EXT_0 8
+#define CR_BTREE_EXT_CAT 9
+#define CR_BTREE_EXT_EXT 10 /* should not happen ! */
+#define CR_BTREE_EXT_ATTR 11
+#define CR_BTREE_EXT_ALLOC 12
+#define CR_BTREE_EXT_START 13 /* unneeded in current code */
+#define CR_BTREE_CAT_JIB 14 /* journal info block */
+#define CR_BTREE_CAT_JL 15 /* journal */
+/* 16 -> 31 || high order bit */ /* reserved */
+
+/* tuning */
+#define CR_SHIFT 8 /* number of bits to shift start_block by */
+ /* to get the index of the linked list */
+#define CR_OVER_DIV 16 /* alloc a table for (1+1/CR_OVER_DIV) *
+ file_number + CR_ADD_CST */
+#define CR_ADD_CST 16
+#define CR_NEW_ALLOC_DIV 4 /* divide the size of the first alloc table
+ by this value to allocate next tables */
+
+/* See DOC for an explaination of this structure */
+/* Access read only from outside cache.c */
+struct _HfsCPrivateExtent {
+ struct _HfsCPrivateExtent* next;
+ uint32_t ext_start;
+ uint32_t ext_length;
+ uint32_t ref_block;
+ uint16_t ref_offset;
+ uint8_t sect_by_block;
+ unsigned where : 5;
+ unsigned ref_index : 3; /* 0 -> 7 */
+};
+typedef struct _HfsCPrivateExtent HfsCPrivateExtent;
+
+/* Internaly used by cache.c for custom memory managment only */
+struct _HfsCPrivateCacheTable {
+ struct _HfsCPrivateCacheTable* next_cache;
+ HfsCPrivateExtent* table;
+ unsigned int table_size;
+ unsigned int table_first_free;
+ /* first_elemt ? */
+};
+typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable;
+
+/* Internaly used by cache.c for custom memory managment
+ and cache handling only */
+struct _HfsCPrivateCache {
+ HfsCPrivateCacheTable* table_list;
+ HfsCPrivateCacheTable* last_table;
+ HfsCPrivateExtent** linked_ref;
+ unsigned int linked_ref_size;
+ unsigned int block_number;
+ unsigned int first_cachetable_size;
+ unsigned int needed_alloc_size;
+};
+typedef struct _HfsCPrivateCache HfsCPrivateCache;
+
+HfsCPrivateCache*
+hfsc_new_cache(unsigned int block_number, unsigned int file_number);
+
+void
+hfsc_delete_cache(HfsCPrivateCache* cache);
+
+HfsCPrivateExtent*
+hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length,
+ uint32_t block, uint16_t offset, uint8_t sbb,
+ uint8_t where, uint8_t index);
+
+HfsCPrivateExtent*
+hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start);
+
+HfsCPrivateExtent*
+hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start,
+ uint32_t new_start);
+
+static __inline__ unsigned int
+hfsc_cache_needed_buffer(HfsCPrivateCache* cache)
+{
+ return cache->needed_alloc_size;
+}
+
+#endif /* _CACHE_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/file.c b/usr/src/lib/libparted/common/libparted/fs/hfs/file.c
new file mode 100644
index 0000000000..f440749359
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/file.c
@@ -0,0 +1,228 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs.h"
+
+#include "file.h"
+
+/* Open the data fork of a file with its first three extents and its CNID */
+HfsPrivateFile*
+hfs_file_open (PedFileSystem *fs, uint32_t CNID,
+ HfsExtDataRec ext_desc, PedSector sect_nb)
+{
+ HfsPrivateFile* file;
+
+ file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile));
+ if (!file) return NULL;
+
+ file->fs = fs;
+ file->sect_nb = sect_nb;
+ file->CNID = CNID;
+ memcpy(file->first, ext_desc, sizeof (HfsExtDataRec));
+ file->start_cache = 0;
+
+ return file;
+}
+
+/* Close an HFS file */
+void
+hfs_file_close (HfsPrivateFile* file)
+{
+ ped_free (file);
+}
+
+/* warning : only works on data forks */
+static int
+hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block,
+ HfsExtDataRec cache, uint16_t* ptr_start_cache)
+{
+ uint8_t record[sizeof (HfsExtentKey)
+ + sizeof (HfsExtDataRec)];
+ HfsExtentKey search;
+ HfsExtentKey* ret_key = (HfsExtentKey*) record;
+ HfsExtDescriptor* ret_cache = (HfsExtDescriptor*)
+ (record + sizeof (HfsExtentKey));
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ file->fs->type_specific;
+
+ search.key_length = sizeof (HfsExtentKey) - 1;
+ search.type = HFS_DATA_FORK;
+ search.file_ID = file->CNID;
+ search.start = PED_CPU_TO_BE16 (block);
+
+ if (!hfs_btree_search (priv_data->extent_file,
+ (HfsPrivateGenericKey*) &search,
+ record, sizeof (record), NULL))
+ return 0;
+
+ if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
+ return 0;
+
+ memcpy (cache, ret_cache, sizeof(HfsExtDataRec));
+ *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start);
+
+ return 1;
+}
+
+/* find and return the nth sector of a file */
+/* return 0 on error */
+static PedSector
+hfs_file_find_sector (HfsPrivateFile* file, PedSector sector)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ file->fs->type_specific;
+ unsigned int sect_by_block = PED_BE32_TO_CPU (
+ priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+ unsigned int i, s, vol_block;
+ unsigned int block = sector / sect_by_block;
+ unsigned int offset = sector % sect_by_block;
+
+ /* in the three first extent */
+ for (s = 0, i = 0; i < HFS_EXT_NB; i++) {
+ if ((block >= s) && ( block < s + PED_BE16_TO_CPU (
+ file->first[i].block_count))) {
+ vol_block = (block - s) + PED_BE16_TO_CPU (
+ file->first[i].start_block);
+ goto sector_found;
+ }
+ s += PED_BE16_TO_CPU (file->first[i].block_count);
+ }
+
+ /* in the three cached extent */
+ if (file->start_cache && block >= file->start_cache)
+ for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
+ if ((block >= s) && (block < s + PED_BE16_TO_CPU (
+ file->cache[i].block_count))) {
+ vol_block = (block - s) + PED_BE16_TO_CPU (
+ file->cache[i].start_block);
+ goto sector_found;
+ }
+ s += PED_BE16_TO_CPU (file->cache[i].block_count);
+ }
+
+ /* update cache */
+ if (!hfs_get_extent_containing (file, block, file->cache,
+ &(file->start_cache))) {
+ ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_CANCEL,
+ _("Could not update the extent cache for HFS file with "
+ "CNID %X."),
+ PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ /* in the three cached extent */
+ PED_ASSERT(file->start_cache && block >= file->start_cache, return 0);
+ for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) {
+ if ((block >= s) && (block < s + PED_BE16_TO_CPU (
+ file->cache[i].block_count))) {
+ vol_block = (block - s) + PED_BE16_TO_CPU (
+ file->cache[i].start_block);
+ goto sector_found;
+ }
+ s += PED_BE16_TO_CPU (file->cache[i].block_count);
+ }
+
+ return 0;
+
+ sector_found:
+ return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block)
+ + (PedSector) vol_block * sect_by_block
+ + offset;
+}
+
+/* Read the nth sector of a file */
+/* return 0 on error */
+int
+hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector)
+{
+ PedSector abs_sector;
+
+ if (sector >= file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to read HFS file with CNID %X behind EOF."),
+ PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ abs_sector = hfs_file_find_sector (file, sector);
+ if (!abs_sector) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not find sector %lli of HFS file with "
+ "CNID %X."),
+ sector, PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ return ped_geometry_read (file->fs->geom, buf, abs_sector, 1);
+}
+
+/* Write the nth sector of a file */
+/* return 0 on error */
+int
+hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector)
+{
+ PedSector abs_sector;
+
+ if (sector >= file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to write HFS file with CNID %X behind EOF."),
+ PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ abs_sector = hfs_file_find_sector (file, sector);
+ if (!abs_sector) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not find sector %lli of HFS file with "
+ "CNID %X."),
+ sector, PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ return ped_geometry_write (file->fs->geom, buf, abs_sector, 1);
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/file.h b/usr/src/lib/libparted/common/libparted/fs/hfs/file.h
new file mode 100644
index 0000000000..de21c1f698
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/file.h
@@ -0,0 +1,41 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _FILE_H
+#define _FILE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+HfsPrivateFile*
+hfs_file_open (PedFileSystem *fs, uint32_t CNID,
+ HfsExtDataRec ext_desc, PedSector sect_nb);
+
+void
+hfs_file_close (HfsPrivateFile* file);
+
+int
+hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector);
+
+int
+hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector);
+
+#endif /* _FILE_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.c b/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.c
new file mode 100644
index 0000000000..753997f203
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.c
@@ -0,0 +1,273 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "advfs_plus.h"
+
+#include "file_plus.h"
+
+/* Open the data fork of a file with its first eight extents and its CNID */
+/* CNID and ext_desc must be in disc order, sect_nb in CPU order */
+/* return null on failure */
+HfsPPrivateFile*
+hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
+ HfsPExtDataRec ext_desc, PedSector sect_nb)
+{
+ HfsPPrivateFile* file;
+
+ file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile));
+ if (!file) return NULL;
+
+ file->fs = fs;
+ file->sect_nb = sect_nb;
+ file->CNID = CNID;
+ memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec));
+ file->start_cache = 0;
+
+ return file;
+}
+
+/* Close an HFS+ file */
+void
+hfsplus_file_close (HfsPPrivateFile* file)
+{
+ ped_free (file);
+}
+
+/* warning : only works on data forks */
+static int
+hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block,
+ HfsPExtDataRec cache, uint32_t* ptr_start_cache)
+{
+ uint8_t record[sizeof (HfsPExtentKey)
+ + sizeof (HfsPExtDataRec)];
+ HfsPExtentKey search;
+ HfsPExtentKey* ret_key = (HfsPExtentKey*) record;
+ HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*)
+ (record + sizeof (HfsPExtentKey));
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ file->fs->type_specific;
+
+ search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2);
+ search.type = HFS_DATA_FORK;
+ search.pad = 0;
+ search.file_ID = file->CNID;
+ search.start = PED_CPU_TO_BE32 (block);
+
+ if (!hfsplus_btree_search (priv_data->extents_file,
+ (HfsPPrivateGenericKey*) &search,
+ record, sizeof (record), NULL))
+ return 0;
+
+ if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
+ return 0;
+
+ memcpy (cache, ret_cache, sizeof(HfsPExtDataRec));
+ *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start);
+
+ return 1;
+}
+
+/* find a sub extent contained in the desired area */
+/* and with the same starting point */
+/* return 0 in sector_count on error, or the physical area */
+/* on the volume corresponding to the logical area in the file */
+static HfsPPrivateExtent
+hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector,
+ unsigned int nb)
+{
+ HfsPPrivateExtent ret = {0,0};
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ file->fs->type_specific;
+ unsigned int sect_by_block = PED_BE32_TO_CPU (
+ priv_data->vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+ unsigned int i, s, vol_block, size;
+ PedSector sect_size;
+ unsigned int block = sector / sect_by_block;
+ unsigned int offset = sector % sect_by_block;
+
+ /* in the 8 first extent */
+ for (s = 0, i = 0; i < HFSP_EXT_NB; i++) {
+ if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+ file->first[i].block_count))) {
+ vol_block = (block - s)
+ + PED_BE32_TO_CPU (file->first[i]
+ .start_block);
+ size = PED_BE32_TO_CPU (file->first[i].block_count)
+ + s - block;
+ goto plus_sector_found;
+ }
+ s += PED_BE32_TO_CPU (file->first[i].block_count);
+ }
+
+ /* in the 8 cached extent */
+ if (file->start_cache && block >= file->start_cache)
+ for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
+ if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+ file->cache[i].block_count))) {
+ vol_block = (block - s)
+ + PED_BE32_TO_CPU (file->cache[i]
+ .start_block);
+ size = PED_BE32_TO_CPU (file->cache[i].block_count)
+ + s - block;
+ goto plus_sector_found;
+ }
+ s += PED_BE32_TO_CPU (file->cache[i].block_count);
+ }
+
+ /* update cache */
+ if (!hfsplus_get_extent_containing (file, block, file->cache,
+ &(file->start_cache))) {
+ ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_CANCEL,
+ _("Could not update the extent cache for HFS+ file "
+ "with CNID %X."),
+ PED_BE32_TO_CPU(file->CNID));
+ return ret; /* ret == {0,0} */
+ }
+
+ /* ret == {0,0} */
+ PED_ASSERT(file->start_cache && block >= file->start_cache, return ret);
+
+ for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
+ if ((block >= s) && (block < s + PED_BE32_TO_CPU (
+ file->cache[i].block_count))) {
+ vol_block = (block - s)
+ + PED_BE32_TO_CPU (file->cache[i]
+ .start_block);
+ size = PED_BE32_TO_CPU (file->cache[i].block_count)
+ + s - block;
+ goto plus_sector_found;
+ }
+ s += PED_BE32_TO_CPU (file->cache[i].block_count);
+ }
+
+ return ret;
+
+plus_sector_found:
+ sect_size = (PedSector) size * sect_by_block - offset;
+ ret.start_sector = vol_block * sect_by_block + offset;
+ ret.sector_count = (sect_size < nb) ? sect_size : nb;
+ return ret;
+}
+
+int
+hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector,
+ unsigned int nb)
+{
+ HfsPPrivateExtent phy_area;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ file->fs->type_specific;
+ char *b = buf;
+
+ if (sector+nb < sector /* detect overflow */
+ || sector+nb > file->sect_nb) /* out of file */ {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to read HFS+ file with CNID %X behind EOF."),
+ PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ while (nb) {
+ phy_area = hfsplus_file_find_extent(file, sector, nb);
+ if (phy_area.sector_count == 0) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not find sector %lli of HFS+ file "
+ "with CNID %X."),
+ sector, PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+ if (!ped_geometry_read(priv_data->plus_geom, b,
+ phy_area.start_sector,
+ phy_area.sector_count))
+ return 0;
+
+ nb -= phy_area.sector_count; /* < nb anyway ... */
+ sector += phy_area.sector_count;
+ b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
+ }
+
+ return 1;
+}
+
+int
+hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector,
+ unsigned int nb)
+{
+ HfsPPrivateExtent phy_area;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ file->fs->type_specific;
+ char *b = buf;
+
+ if (sector+nb < sector /* detect overflow */
+ || sector+nb > file->sect_nb) /* out of file */ {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Trying to write HFS+ file with CNID %X behind EOF."),
+ PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+
+ while (nb) {
+ phy_area = hfsplus_file_find_extent(file, sector, nb);
+ if (phy_area.sector_count == 0) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not find sector %lli of HFS+ file "
+ "with CNID %X."),
+ sector, PED_BE32_TO_CPU(file->CNID));
+ return 0;
+ }
+ if (!ped_geometry_write(priv_data->plus_geom, b,
+ phy_area.start_sector,
+ phy_area.sector_count))
+ return 0;
+
+ nb -= phy_area.sector_count; /* < nb anyway ... */
+ sector += phy_area.sector_count;
+ b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
+ }
+
+ return 1;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.h b/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.h
new file mode 100644
index 0000000000..0c875d13ae
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/file_plus.h
@@ -0,0 +1,60 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _FILE_PLUS_H
+#define _FILE_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+HfsPPrivateFile*
+hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
+ HfsPExtDataRec ext_desc, PedSector sect_nb);
+
+void
+hfsplus_file_close (HfsPPrivateFile* file);
+
+int
+hfsplus_file_read(HfsPPrivateFile* file, void *buf,
+ PedSector sector, unsigned int nb);
+
+int
+hfsplus_file_write(HfsPPrivateFile* file, void *buf,
+ PedSector sector, unsigned int nb);
+
+/* Read the nth sector of a file */
+/* return 0 on error */
+static __inline__ int
+hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
+{
+ return hfsplus_file_read(file, buf, sector, 1);
+}
+
+/* Write the nth sector of a file */
+/* return 0 on error */
+static __inline__ int
+hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector)
+{
+ return hfsplus_file_write(file, buf, sector, 1);
+}
+
+
+#endif /* _FILE_PLUS_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.c b/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.c
new file mode 100644
index 0000000000..2a3936d6f1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.c
@@ -0,0 +1,1353 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ Author : Guillaume Knispel <k_guillaume@libertysurf.fr>
+ Report bug to <bug-parted@gnu.org>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "probe.h"
+
+uint8_t* hfs_block = NULL;
+uint8_t* hfsp_block = NULL;
+unsigned hfs_block_count;
+unsigned hfsp_block_count;
+
+#define HFS_BLOCK_SIZES ((int[2]){512, 0})
+#define HFSP_BLOCK_SIZES ((int[2]){512, 0})
+#define HFSX_BLOCK_SIZES ((int[2]){512, 0})
+
+#ifndef DISCOVER_ONLY
+#include "file.h"
+#include "reloc.h"
+#include "advfs.h"
+
+static PedFileSystemType hfs_type;
+static PedFileSystemType hfsplus_type;
+
+
+/* ----- HFS ----- */
+
+/* This is a very unundoable operation */
+/* Maybe I shouldn't touch the alternate MDB ? */
+/* Anyway clobber is call before other fs creation */
+/* So this is a non-issue */
+static int
+hfs_clobber (PedGeometry* geom)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+
+ memset (buf, 0, PED_SECTOR_SIZE_DEFAULT);
+
+ /* destroy boot blocks, mdb, alternate mdb ... */
+ return (!!ped_geometry_write (geom, buf, 0, 1)) &
+ (!!ped_geometry_write (geom, buf, 1, 1)) &
+ (!!ped_geometry_write (geom, buf, 2, 1)) &
+ (!!ped_geometry_write (geom, buf, geom->length - 2, 1)) &
+ (!!ped_geometry_write (geom, buf, geom->length - 1, 1)) &
+ (!!ped_geometry_sync (geom));
+}
+
+static PedFileSystem*
+hfs_open (PedGeometry* geom)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ PedFileSystem* fs;
+ HfsMasterDirectoryBlock* mdb;
+ HfsPrivateFSData* priv_data;
+
+ if (!hfsc_can_use_geom (geom))
+ return NULL;
+
+ /* Read MDB */
+ if (!ped_geometry_read (geom, buf, 2, 1))
+ return NULL;
+
+ /* Allocate memory */
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs) goto ho;
+ mdb = (HfsMasterDirectoryBlock*)
+ ped_malloc (sizeof (HfsMasterDirectoryBlock));
+ if (!mdb) goto ho_fs;
+ priv_data = (HfsPrivateFSData*)
+ ped_malloc (sizeof (HfsPrivateFSData));
+ if (!priv_data) goto ho_mdb;
+
+ memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock));
+
+ /* init structures */
+ priv_data->mdb = mdb;
+ priv_data->bad_blocks_loaded = 0;
+ priv_data->bad_blocks_xtent_nb = 0;
+ priv_data->bad_blocks_xtent_list = NULL;
+ priv_data->extent_file =
+ hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
+ mdb->extents_file_rec,
+ PED_CPU_TO_BE32 (mdb->extents_file_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->extent_file) goto ho_pd;
+ priv_data->catalog_file =
+ hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
+ mdb->catalog_file_rec,
+ PED_CPU_TO_BE32 (mdb->catalog_file_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->catalog_file) goto ho_ce;
+ /* Read allocation blocks */
+ if (!ped_geometry_read(geom, priv_data->alloc_map,
+ PED_BE16_TO_CPU (mdb->volume_bitmap_block),
+ ( PED_BE16_TO_CPU (mdb->total_blocks)
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8) ) )
+ goto ho_cf;
+
+ fs->type = &hfs_type;
+ fs->geom = ped_geometry_duplicate (geom);
+ if (!fs->geom) goto ho_cf;
+ fs->type_specific = (void*) priv_data;
+ fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes)
+ >> HFS_UNMOUNTED ) & 1;
+
+ return fs;
+
+/*--- clean error handling ---*/
+ho_cf: hfs_file_close(priv_data->catalog_file);
+ho_ce: hfs_file_close(priv_data->extent_file);
+ho_pd: ped_free(priv_data);
+ho_mdb: ped_free(mdb);
+ho_fs: ped_free(fs);
+ho: return NULL;
+}
+
+static int
+hfs_close (PedFileSystem *fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific;
+
+ hfs_file_close (priv_data->extent_file);
+ hfs_file_close (priv_data->catalog_file);
+ if (priv_data->bad_blocks_loaded)
+ hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list);
+ ped_free (priv_data->mdb);
+ ped_free (priv_data);
+ ped_geometry_destroy (fs->geom);
+ ped_free (fs);
+
+ return 1;
+}
+
+static PedConstraint*
+hfs_get_resize_constraint (const PedFileSystem *fs)
+{
+ PedDevice* dev = fs->geom->dev;
+ PedAlignment start_align;
+ PedGeometry start_sector;
+ PedGeometry full_dev;
+ PedSector min_size;
+
+ if (!ped_alignment_init (&start_align, fs->geom->start, 0))
+ return NULL;
+ if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
+ return NULL;
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+ /* 2 = last two sectors (alternate MDB and unused sector) */
+ min_size = hfs_get_empty_end(fs) + 2;
+ if (min_size == 2) return NULL;
+
+ return ped_constraint_new (&start_align, ped_alignment_any,
+ &start_sector, &full_dev, min_size,
+ fs->geom->length);
+}
+
+static int
+hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ unsigned int nblock, nfree;
+ unsigned int block, to_free;
+ HfsPrivateFSData* priv_data;
+ HfsMasterDirectoryBlock* mdb;
+ int resize = 1;
+ unsigned int hfs_sect_block;
+ PedSector hgee;
+
+ /* check preconditions */
+ PED_ASSERT (fs != NULL, return 0);
+ PED_ASSERT (fs->geom != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+#ifdef DEBUG
+ PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
+#else
+ if ((hgee = hfs_get_empty_end(fs)) == 0)
+ return 0;
+#endif
+
+ PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0, return 0);
+
+ if (ped_geometry_test_equal(fs->geom, geom))
+ return 1;
+
+ priv_data = (HfsPrivateFSData*) fs->type_specific;
+ mdb = priv_data->mdb;
+ hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+
+ if (fs->geom->start != geom->start
+ || geom->length > fs->geom->length
+ || geom->length < hgee + 2) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Sorry, HFS cannot be resized that way yet."));
+ return 0;
+ }
+
+ /* Flush caches */
+ if (!ped_geometry_sync(fs->geom))
+ return 0;
+
+ /* Clear the unmounted bit */
+ mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED ));
+ if (!ped_geometry_read (fs->geom, buf, 2, 1))
+ return 0;
+ memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock));
+ if ( !ped_geometry_write (fs->geom, buf, 2, 1)
+ || !ped_geometry_sync (fs->geom))
+ return 0;
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name(timer, _("shrinking"));
+ ped_timer_update(timer, 0.0);
+ /* relocate data */
+ to_free = ( fs->geom->length - geom->length
+ + hfs_sect_block - 1 )
+ / hfs_sect_block ;
+ block = hfs_find_start_pack (fs, to_free);
+ if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) {
+ resize = 0;
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Data relocation has failed."));
+ goto write_MDB;
+ }
+
+ /* Calculate new block number and other MDB field */
+ nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) )
+ / hfs_sect_block;
+ nfree = PED_BE16_TO_CPU (mdb->free_blocks)
+ - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock );
+
+ /* Check that all block after future end are really free */
+ for (block = nblock;
+ block < PED_BE16_TO_CPU (mdb->total_blocks);
+ block++) {
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
+ resize = 0;
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Data relocation left some data in the end "
+ "of the volume."));
+ goto write_MDB;
+ }
+ }
+
+ /* Mark out of volume blocks as used
+ (broken implementations compatibility) */
+ for ( block = nblock; block < (1 << 16); ++block)
+ SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
+
+ /* save the allocation map
+ I do not write until start of allocation blocks
+ but only until pre-resize end of bitmap blocks
+ because the specifications do _not_ assert that everything
+ until allocation blocks is boot, mdb and alloc */
+ ped_geometry_write(fs->geom, priv_data->alloc_map,
+ PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
+ ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1)
+ / (PED_SECTOR_SIZE_DEFAULT * 8));
+
+ /* Update geometry */
+ if (resize) {
+ /* update in fs structure */
+ if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock)
+ mdb->next_allocation = PED_CPU_TO_BE16 (0);
+ mdb->total_blocks = PED_CPU_TO_BE16 (nblock);
+ mdb->free_blocks = PED_CPU_TO_BE16 (nfree);
+ /* update parted structure */
+ fs->geom->length = geom->length;
+ fs->geom->end = fs->geom->start + geom->length - 1;
+ }
+
+ /* Set the unmounted bit */
+ mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED );
+
+ /* Effective write */
+ write_MDB:
+ ped_timer_set_state_name(timer,_("writing HFS Master Directory Block"));
+
+ if (!hfs_update_mdb(fs)) {
+ ped_geometry_sync(geom);
+ return 0;
+ }
+
+ if (!ped_geometry_sync(geom))
+ return 0;
+
+ ped_timer_update(timer, 1.0);
+
+ return (resize);
+}
+
+/* ----- HFS+ ----- */
+
+#include "file_plus.h"
+#include "advfs_plus.h"
+#include "reloc_plus.h"
+#include "journal.h"
+
+static int
+hfsplus_clobber (PedGeometry* geom)
+{
+ unsigned int i = 1;
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ HfsMasterDirectoryBlock *mdb;
+
+ mdb = (HfsMasterDirectoryBlock *) buf;
+
+ if (!ped_geometry_read (geom, buf, 2, 1))
+ return 0;
+
+ if (PED_BE16_TO_CPU (mdb->signature) == HFS_SIGNATURE) {
+ /* embedded hfs+ */
+ PedGeometry *embedded;
+
+ i = PED_BE32_TO_CPU(mdb->block_size) / PED_SECTOR_SIZE_DEFAULT;
+ embedded = ped_geometry_new (
+ geom->dev,
+ (PedSector) geom->start
+ + PED_BE16_TO_CPU (mdb->start_block)
+ + (PedSector) PED_BE16_TO_CPU (
+ mdb->old_new.embedded.location.start_block ) * i,
+ (PedSector) PED_BE16_TO_CPU (
+ mdb->old_new.embedded.location.block_count ) * i );
+ if (!embedded) i = 0;
+ else {
+ i = hfs_clobber (embedded);
+ ped_geometry_destroy (embedded);
+ }
+ }
+
+ /* non-embedded or envelop destroy as hfs */
+ return ( hfs_clobber (geom) && i );
+}
+
+static int
+hfsplus_close (PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+
+ if (priv_data->bad_blocks_loaded)
+ hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
+ ped_free(priv_data->alloc_map);
+ ped_free(priv_data->dirty_alloc_map);
+ hfsplus_file_close (priv_data->allocation_file);
+ hfsplus_file_close (priv_data->attributes_file);
+ hfsplus_file_close (priv_data->catalog_file);
+ hfsplus_file_close (priv_data->extents_file);
+ if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
+ if (priv_data->wrapper) hfs_close(priv_data->wrapper);
+ ped_geometry_destroy (fs->geom);
+ ped_free(priv_data->vh);
+ ped_free(priv_data);
+ ped_free(fs);
+
+ return 1;
+}
+
+static PedFileSystem*
+hfsplus_open (PedGeometry* geom)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ PedFileSystem* fs;
+ HfsPVolumeHeader* vh;
+ HfsPPrivateFSData* priv_data;
+ PedGeometry* wrapper_geom;
+ unsigned int map_sectors;
+
+ if (!hfsc_can_use_geom (geom))
+ return NULL;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs) goto hpo;
+ vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader));
+ if (!vh) goto hpo_fs;
+ priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData));
+ if (!priv_data) goto hpo_vh;
+
+ fs->geom = ped_geometry_duplicate (geom);
+ if (!fs->geom) goto hpo_pd;
+ fs->type_specific = (void*) priv_data;
+
+ if ((wrapper_geom = hfs_and_wrapper_probe (geom))) {
+ HfsPrivateFSData* hfs_priv_data;
+ PedSector abs_sect, length;
+ unsigned int bs;
+
+ ped_geometry_destroy (wrapper_geom);
+ priv_data->wrapper = hfs_open(geom);
+ if (!priv_data->wrapper) goto hpo_gm;
+ hfs_priv_data = (HfsPrivateFSData*)
+ priv_data->wrapper->type_specific;
+ bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+ abs_sect = (PedSector) geom->start
+ + (PedSector) PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->start_block)
+ + (PedSector) PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->old_new
+ .embedded.location.start_block )
+ * bs;
+ length = (PedSector) PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->old_new
+ .embedded.location.block_count)
+ * bs;
+ priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect,
+ length);
+ if (!priv_data->plus_geom) goto hpo_wr;
+ priv_data->free_geom = 1;
+ } else {
+ priv_data->wrapper = NULL;
+ priv_data->plus_geom = fs->geom;
+ priv_data->free_geom = 0;
+ }
+
+ if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg;
+ memcpy (vh, buf, sizeof (HfsPVolumeHeader));
+ priv_data->vh = vh;
+
+ if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE)
+ && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) {
+ ped_exception_throw (
+ PED_EXCEPTION_BUG,
+ PED_EXCEPTION_CANCEL,
+ _("No valid HFS[+X] signature has been found while "
+ "opening."));
+ goto hpo_pg;
+ }
+
+ if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE)
+ && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Version %d of HFS+ isn't supported."),
+ PED_BE16_TO_CPU(vh->version))
+ != PED_EXCEPTION_IGNORE)
+ goto hpo_pg;
+ }
+
+ if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE)
+ && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Version %d of HFSX isn't supported."),
+ PED_BE16_TO_CPU(vh->version))
+ != PED_EXCEPTION_IGNORE)
+ goto hpo_pg;
+ }
+
+ priv_data->jib_start_block = 0;
+ priv_data->jl_start_block = 0;
+ if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) {
+ if (!hfsj_replay_journal(fs))
+ goto hpo_pg;
+ }
+
+ priv_data->bad_blocks_loaded = 0;
+ priv_data->bad_blocks_xtent_nb = 0;
+ priv_data->bad_blocks_xtent_list = NULL;
+ priv_data->extents_file =
+ hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID),
+ vh->extents_file.extents,
+ PED_BE64_TO_CPU (
+ vh->extents_file.logical_size )
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->extents_file) goto hpo_pg;
+ priv_data->catalog_file =
+ hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID),
+ vh->catalog_file.extents,
+ PED_BE64_TO_CPU (
+ vh->catalog_file.logical_size )
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->catalog_file) goto hpo_ce;
+ priv_data->attributes_file =
+ hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID),
+ vh->attributes_file.extents,
+ PED_BE64_TO_CPU (
+ vh->attributes_file.logical_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->attributes_file) goto hpo_cc;
+
+ map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks)
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8);
+ priv_data->dirty_alloc_map = (uint8_t*)
+ ped_malloc ((map_sectors + 7) / 8);
+ if (!priv_data->dirty_alloc_map) goto hpo_cl;
+ memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8);
+ priv_data->alloc_map = (uint8_t*)
+ ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->alloc_map) goto hpo_dm;
+
+ priv_data->allocation_file =
+ hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID),
+ vh->allocation_file.extents,
+ PED_BE64_TO_CPU (
+ vh->allocation_file.logical_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (!priv_data->allocation_file) goto hpo_am;
+ if (!hfsplus_file_read (priv_data->allocation_file,
+ priv_data->alloc_map, 0, map_sectors)) {
+ hfsplus_close(fs);
+ return NULL;
+ }
+
+ fs->type = &hfsplus_type;
+ fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1)
+ && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1);
+
+ return fs;
+
+/*--- clean error handling ---*/
+hpo_am: ped_free(priv_data->alloc_map);
+hpo_dm: ped_free(priv_data->dirty_alloc_map);
+hpo_cl: hfsplus_file_close (priv_data->attributes_file);
+hpo_cc: hfsplus_file_close (priv_data->catalog_file);
+hpo_ce: hfsplus_file_close (priv_data->extents_file);
+hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom);
+hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper);
+hpo_gm: ped_geometry_destroy (fs->geom);
+hpo_pd: ped_free(priv_data);
+hpo_vh: ped_free(vh);
+hpo_fs: ped_free(fs);
+hpo: return NULL;
+}
+
+static PedConstraint*
+hfsplus_get_resize_constraint (const PedFileSystem *fs)
+{
+ PedDevice* dev = fs->geom->dev;
+ PedAlignment start_align;
+ PedGeometry start_sector;
+ PedGeometry full_dev;
+ PedSector min_size;
+
+ if (!ped_alignment_init (&start_align, fs->geom->start, 0))
+ return NULL;
+ if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1))
+ return NULL;
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ min_size = hfsplus_get_min_size (fs);
+ if (!min_size) return NULL;
+
+ return ped_constraint_new (&start_align, ped_alignment_any,
+ &start_sector, &full_dev, min_size,
+ fs->geom->length);
+}
+
+static int
+hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ unsigned int nblock, nfree, mblock;
+ unsigned int block, to_free, old_blocks;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPVolumeHeader* vh = priv_data->vh;
+ int resize = 1;
+ unsigned int hfsp_sect_block =
+ ( PED_BE32_TO_CPU (vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT );
+ unsigned int map_sectors;
+
+ old_blocks = PED_BE32_TO_CPU (vh->total_blocks);
+
+ /* Flush caches */
+ if (!ped_geometry_sync(priv_data->plus_geom))
+ return 0;
+
+ /* Clear the unmounted bit */
+ /* and set the implementation code (Apple Creator Code) */
+ vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED ));
+ vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk);
+ if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1))
+ return 0;
+ memcpy (buf, vh, sizeof (HfsPVolumeHeader));
+ if ( !ped_geometry_write (priv_data->plus_geom, buf, 2, 1)
+ || !ped_geometry_sync (priv_data->plus_geom))
+ return 0;
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name(timer, _("shrinking"));
+ ped_timer_update(timer, 0.0);
+ /* relocate data */
+ to_free = ( priv_data->plus_geom->length
+ - geom->length + hfsp_sect_block
+ - 1 ) / hfsp_sect_block;
+ block = hfsplus_find_start_pack (fs, to_free);
+ if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) {
+ resize = 0;
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Data relocation has failed."));
+ goto write_VH;
+ }
+
+ /* Calculate new block number and other VH field */
+ /* nblock must be rounded _down_ */
+ nblock = geom->length / hfsp_sect_block;
+ nfree = PED_BE32_TO_CPU (vh->free_blocks)
+ - (old_blocks - nblock);
+ /* free block readjustement is only needed when incorrect nblock
+ was used by my previous implementation, so detect the case */
+ if (priv_data->plus_geom->length < old_blocks
+ * ( PED_BE32_TO_CPU (vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT) ) {
+ if (priv_data->plus_geom->length % hfsp_sect_block == 1)
+ nfree++;
+ }
+
+ /* Check that all block after future end are really free */
+ mblock = ( priv_data->plus_geom->length - 2 )
+ / hfsp_sect_block;
+ if (mblock > old_blocks - 1)
+ mblock = old_blocks - 1;
+ for ( block = nblock;
+ block < mblock;
+ block++ ) {
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) {
+ resize = 0;
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Data relocation left some data at the end "
+ "of the volume."));
+ goto write_VH;
+ }
+ }
+
+ /* Mark out of volume blocks as used */
+ map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8) )
+ * (PED_SECTOR_SIZE_DEFAULT * 8);
+ for ( block = nblock; block < map_sectors; ++block)
+ SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+
+ /* Update geometry */
+ if (resize) {
+ /* update in fs structure */
+ if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock)
+ vh->next_allocation = PED_CPU_TO_BE32 (0);
+ vh->total_blocks = PED_CPU_TO_BE32 (nblock);
+ vh->free_blocks = PED_CPU_TO_BE32 (nfree);
+ /* update parted structure */
+ priv_data->plus_geom->length = geom->length;
+ priv_data->plus_geom->end = priv_data->plus_geom->start
+ + geom->length - 1;
+ }
+
+ /* Effective write */
+ write_VH:
+ /* lasts two sectors are allocated by the alternate VH
+ and a reserved sector, and last block is always reserved */
+ block = (priv_data->plus_geom->length - 1) / hfsp_sect_block;
+ if (block < PED_BE32_TO_CPU (vh->total_blocks))
+ SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+ block = (priv_data->plus_geom->length - 2) / hfsp_sect_block;
+ if (block < PED_BE32_TO_CPU (vh->total_blocks))
+ SET_BLOC_OCCUPATION(priv_data->alloc_map, block);
+ SET_BLOC_OCCUPATION(priv_data->alloc_map,
+ PED_BE32_TO_CPU (vh->total_blocks) - 1);
+
+ /* Write the _old_ area to set out of volume blocks as used */
+ map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8);
+ if (!hfsplus_file_write (priv_data->allocation_file,
+ priv_data->alloc_map, 0, map_sectors)) {
+ resize = 0;
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Error while writing the allocation file."));
+ } else {
+ /* Write remaining part of allocation bitmap */
+ /* This is necessary to handle pre patch-11 and third party */
+ /* implementations */
+ memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT);
+ for (block = map_sectors;
+ block < priv_data->allocation_file->sect_nb;
+ ++block) {
+ if (!hfsplus_file_write_sector (
+ priv_data->allocation_file,
+ buf, block)) {
+ ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("Error while writing the "
+ "compatibility part of the "
+ "allocation file."));
+ break;
+ }
+ }
+ }
+ ped_geometry_sync (priv_data->plus_geom);
+
+ if (resize) {
+ /* Set the unmounted bit and clear the inconsistent bit */
+ vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED );
+ vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT );
+ }
+
+ ped_timer_set_state_name(timer, _("writing HFS+ Volume Header"));
+ if (!hfsplus_update_vh(fs)) {
+ ped_geometry_sync(priv_data->plus_geom);
+ return 0;
+ }
+
+ if (!ped_geometry_sync(priv_data->plus_geom))
+ return 0;
+
+ ped_timer_update(timer, 1.0);
+
+ return (resize);
+}
+
+/* Update the HFS wrapper mdb and bad blocks file to reflect
+ the new geometry of the embedded HFS+ volume */
+static int
+hfsplus_wrapper_update (PedFileSystem* fs)
+{
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+ HfsCPrivateLeafRec ref;
+ HfsExtentKey key;
+ HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node;
+ HfsExtentKey* ret_key;
+ HfsExtDescriptor* ret_data;
+ unsigned int i;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*)
+ priv_data->wrapper->type_specific;
+ unsigned int hfs_sect_block =
+ PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT ;
+ PedSector hfsplus_sect = (PedSector)
+ PED_BE32_TO_CPU (priv_data->vh->total_blocks)
+ * ( PED_BE32_TO_CPU (priv_data->vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT );
+ unsigned int hfs_blocks_embedded =
+ (hfsplus_sect + hfs_sect_block - 1)
+ / hfs_sect_block;
+ unsigned int hfs_blocks_embedded_old;
+
+ /* update HFS wrapper MDB */
+ hfs_blocks_embedded_old = PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->old_new
+ .embedded.location.block_count );
+ hfs_priv_data->mdb->old_new.embedded.location.block_count =
+ PED_CPU_TO_BE16 (hfs_blocks_embedded);
+ /* maybe macOS will boot with this */
+ /* update : yes it does \o/ :) */
+ hfs_priv_data->mdb->free_blocks =
+ PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks)
+ + hfs_blocks_embedded_old
+ - hfs_blocks_embedded );
+
+ if (!hfs_update_mdb(priv_data->wrapper))
+ return 0;
+
+ /* force reload bad block list */
+ if (hfs_priv_data->bad_blocks_loaded) {
+ hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list);
+ hfs_priv_data->bad_blocks_xtent_list = NULL;
+ hfs_priv_data->bad_blocks_xtent_nb = 0;
+ hfs_priv_data->bad_blocks_loaded = 0;
+ }
+
+ /* clean HFS wrapper allocation map */
+ for (i = PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->old_new.embedded
+ .location.start_block )
+ + hfs_blocks_embedded;
+ i < PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->old_new.embedded
+ .location.start_block )
+ + hfs_blocks_embedded_old;
+ i++ ) {
+ CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i);
+ }
+ /* and save it */
+ if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map,
+ PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->volume_bitmap_block ),
+ ( PED_BE16_TO_CPU (
+ hfs_priv_data->mdb->total_blocks )
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8)))
+ return 0;
+ if (!ped_geometry_sync (fs->geom))
+ return 0;
+
+ /* search and update the bad blocks file */
+ key.key_length = sizeof(key) - 1;
+ key.type = HFS_DATA_FORK;
+ key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
+ key.start = 0;
+ if (!hfs_btree_search (hfs_priv_data->extent_file,
+ (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("An error occurred while looking for the mandatory "
+ "bad blocks file."));
+ return 0;
+ }
+ if (!hfs_file_read_sector (hfs_priv_data->extent_file, node,
+ ref.node_number))
+ return 0;
+ ret_key = (HfsExtentKey*) (node + ref.record_pos);
+ ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
+ + sizeof (HfsExtentKey) );
+
+ while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) {
+ for (i = 0; i < HFS_EXT_NB; i++) {
+ if ( ret_data[i].start_block
+ == hfs_priv_data->mdb->old_new
+ .embedded.location.start_block) {
+ ret_data[i].block_count =
+ hfs_priv_data->mdb->old_new
+ .embedded.location.block_count;
+ /* found ! : update */
+ if (!hfs_file_write_sector (
+ hfs_priv_data->extent_file,
+ node, ref.node_number)
+ || !ped_geometry_sync(fs->geom))
+ return 0;
+ return 1;
+ }
+ }
+
+ if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) {
+ ref.record_number++;
+ } else {
+ ref.node_number = PED_BE32_TO_CPU (node_desc->next);
+ if (!ref.node_number
+ || !hfs_file_read_sector(hfs_priv_data->extent_file,
+ node, ref.node_number))
+ goto bb_not_found;
+ ref.record_number = 1;
+ }
+
+ ref.record_pos =
+ PED_BE16_TO_CPU (*((uint16_t *)
+ (node + (PED_SECTOR_SIZE_DEFAULT
+ - 2*ref.record_number))));
+ ret_key = (HfsExtentKey*) (node + ref.record_pos);
+ ret_data = (HfsExtDescriptor*) ( node + ref.record_pos
+ + sizeof (HfsExtentKey) );
+ }
+
+bb_not_found:
+ /* not found : not a valid hfs+ wrapper : failure */
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("It seems there is an error in the HFS wrapper: the bad "
+ "blocks file doesn't contain the embedded HFS+ volume."));
+ return 0;
+}
+
+static int
+hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data;
+ PedTimer* timer_plus;
+ PedGeometry* embedded_geom;
+ PedSector hgms;
+
+ /* check preconditions */
+ PED_ASSERT (fs != NULL, return 0);
+ PED_ASSERT (fs->geom != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (fs->geom->dev == geom->dev, return 0);
+#ifdef DEBUG
+ PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0, return 0);
+#else
+ if ((hgms = hfsplus_get_min_size (fs)) == 0)
+ return 0;
+#endif
+
+ if (ped_geometry_test_equal(fs->geom, geom))
+ return 1;
+
+ priv_data = (HfsPPrivateFSData*) fs->type_specific;
+
+ if (fs->geom->start != geom->start
+ || geom->length > fs->geom->length
+ || geom->length < hgms) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Sorry, HFS+ cannot be resized that way yet."));
+ return 0;
+ }
+
+ if (priv_data->wrapper) {
+ PedSector red, hgee;
+ HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*)
+ priv_data->wrapper->type_specific;
+ unsigned int hfs_sect_block =
+ PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT;
+
+ /* There is a wrapper so we must calculate the new geometry
+ of the embedded HFS+ volume */
+ red = ( (fs->geom->length - geom->length + hfs_sect_block - 1)
+ / hfs_sect_block ) * hfs_sect_block;
+ /* Can't we shrink the hfs+ volume by the desired size ? */
+ hgee = hfsplus_get_empty_end (fs);
+ if (!hgee) return 0;
+ if (red > priv_data->plus_geom->length - hgee) {
+ /* No, shrink hfs+ by the greatest possible value */
+ hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block)
+ * hfs_sect_block;
+ red = priv_data->plus_geom->length - hgee;
+ }
+ embedded_geom = ped_geometry_new (geom->dev,
+ priv_data->plus_geom->start,
+ priv_data->plus_geom->length
+ - red);
+
+ /* There is a wrapper so the resize process is a two stages
+ process (embedded resizing then wrapper resizing) :
+ we create a sub timer */
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer,
+ _("shrinking embedded HFS+ volume"));
+ ped_timer_update(timer, 0.0);
+ timer_plus = ped_timer_new_nested (timer, 0.98);
+ } else {
+ /* No wrapper : the desired geometry is the desired
+ HFS+ volume geometry */
+ embedded_geom = geom;
+ timer_plus = timer;
+ }
+
+ /* Resize the HFS+ volume */
+ if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) {
+ if (timer_plus != timer) ped_timer_destroy_nested (timer_plus);
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Resizing the HFS+ volume has failed."));
+ return 0;
+ }
+
+ if (priv_data->wrapper) {
+ ped_geometry_destroy (embedded_geom);
+ ped_timer_destroy_nested (timer_plus);
+ ped_timer_set_state_name(timer, _("shrinking HFS wrapper"));
+ timer_plus = ped_timer_new_nested (timer, 0.02);
+ /* There's a wrapper : second stage = resizing it */
+ if (!hfsplus_wrapper_update (fs)
+ || !hfs_resize (priv_data->wrapper, geom, timer_plus)) {
+ ped_timer_destroy_nested (timer_plus);
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Updating the HFS wrapper has failed."));
+ return 0;
+ }
+ ped_timer_destroy_nested (timer_plus);
+ }
+ ped_timer_update(timer, 1.0);
+
+ return 1;
+}
+
+#ifdef HFS_EXTRACT_FS
+/* The following is for debugging purpose only, NOT for packaging */
+
+#include <stdio.h>
+
+uint8_t* extract_buffer = NULL;
+
+static int
+hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file)
+{
+ FILE* fout;
+ PedSector sect;
+
+ fout = fopen(filename, "w");
+ if (!fout) return 0;
+
+ for (sect = 0; sect < hfs_file->sect_nb; ++sect) {
+ if (!hfs_file_read_sector(hfs_file, extract_buffer, sect))
+ goto err_close;
+ if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+ goto err_close;
+ }
+
+ return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+ fclose(fout);
+ return 0;
+}
+
+static int
+hfs_extract_bitmap(const char* filename, PedFileSystem* fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsMasterDirectoryBlock* mdb = priv_data->mdb;
+ unsigned int count;
+ FILE* fout;
+ PedSector sect;
+
+ fout = fopen(filename, "w");
+ if (!fout) return 0;
+
+ for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block);
+ sect < PED_BE16_TO_CPU(mdb->start_block);
+ sect += count) {
+ uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block);
+ count = (st_block-sect) < BLOCK_MAX_BUFF ?
+ (st_block-sect) : BLOCK_MAX_BUFF;
+ if (!ped_geometry_read(fs->geom, extract_buffer, sect, count))
+ goto err_close;
+ if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT,
+ 1, fout))
+ goto err_close;
+ }
+
+ return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+ fclose(fout);
+ return 0;
+}
+
+static int
+hfs_extract_mdb (const char* filename, PedFileSystem* fs)
+{
+ FILE* fout;
+
+ fout = fopen(filename, "w");
+ if (!fout) return 0;
+
+ if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1))
+ goto err_close;
+ if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+ goto err_close;
+
+ return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+ fclose(fout);
+ return 0;
+}
+
+static int
+hfs_extract (PedFileSystem* fs, PedTimer* timer)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This is not a real %s check. This is going to extract "
+ "special low level files for debugging purposes."),
+ "HFS");
+
+ extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
+ if (!extract_buffer) return 0;
+
+ hfs_extract_mdb(HFS_MDB_FILENAME, fs);
+ hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file);
+ hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file);
+ hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs);
+
+ ped_free(extract_buffer); extract_buffer = NULL;
+ return 0; /* nothing has been fixed by us ! */
+}
+
+static int
+hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file)
+{
+ FILE* fout;
+ unsigned int cp_sect;
+ PedSector rem_sect;
+
+ fout = fopen(filename, "w");
+ if (!fout) return 0;
+
+ for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) {
+ cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF;
+ if (!hfsplus_file_read(hfsp_file, extract_buffer,
+ hfsp_file->sect_nb - rem_sect, cp_sect))
+ goto err_close;
+ if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT,
+ 1, fout))
+ goto err_close;
+ }
+
+ return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+ fclose(fout);
+ return 0;
+}
+
+static int
+hfsplus_extract_vh (const char* filename, PedFileSystem* fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ FILE* fout;
+ PedGeometry* geom = priv_data->plus_geom;
+
+
+ fout = fopen(filename, "w");
+ if (!fout) return 0;
+
+ if (!ped_geometry_read(geom, extract_buffer, 2, 1))
+ goto err_close;
+ if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout))
+ goto err_close;
+
+ return (fclose(fout) == 0 ? 1 : 0);
+
+err_close:
+ fclose(fout);
+ return 0;
+}
+
+/* TODO : use the timer to report what is happening */
+/* TODO : use exceptions to report errors */
+static int
+hfsplus_extract (PedFileSystem* fs, PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPVolumeHeader* vh = priv_data->vh;
+ HfsPPrivateFile* startup_file;
+
+ if (priv_data->wrapper) {
+ /* TODO : create nested timer */
+ hfs_extract (priv_data->wrapper, timer);
+ }
+
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This is not a real %s check. This is going to extract "
+ "special low level files for debugging purposes."),
+ "HFS+");
+
+ extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT);
+ if (!extract_buffer) return 0;
+
+ hfsplus_extract_vh(HFSP_VH_FILENAME, fs);
+ hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file);
+ hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file);
+ hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file);
+ hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file);
+
+ startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID),
+ vh->startup_file.extents,
+ PED_BE64_TO_CPU (
+ vh->startup_file.logical_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+ if (startup_file) {
+ hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file);
+ hfsplus_file_close(startup_file); startup_file = NULL;
+ }
+
+ ped_free(extract_buffer); extract_buffer = NULL;
+ return 0; /* nothing has been fixed by us ! */
+}
+#endif /* HFS_EXTRACT_FS */
+
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps hfs_ops = {
+ .probe = hfs_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = hfs_clobber,
+ .open = hfs_open,
+ .create = NULL,
+ .close = hfs_close,
+#ifndef HFS_EXTRACT_FS
+ .check = NULL,
+#else
+ .check = hfs_extract,
+#endif
+ .copy = NULL,
+ .resize = hfs_resize,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = hfs_get_resize_constraint,
+ .get_copy_constraint = NULL,
+#else /* DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+static PedFileSystemOps hfsplus_ops = {
+ .probe = hfsplus_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = hfsplus_clobber,
+ .open = hfsplus_open,
+ .create = NULL,
+ .close = hfsplus_close,
+#ifndef HFS_EXTRACT_FS
+ .check = NULL,
+#else
+ .check = hfsplus_extract,
+#endif
+ .copy = NULL,
+ .resize = hfsplus_resize,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = hfsplus_get_resize_constraint,
+ .get_copy_constraint = NULL,
+#else /* DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+static PedFileSystemOps hfsx_ops = {
+ .probe = hfsx_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = hfs_clobber, /* NOT hfsplus_clobber !
+ HFSX can't be embedded */
+ .open = hfsplus_open,
+ .create = NULL,
+ .close = hfsplus_close,
+#ifndef HFS_EXTRACT_FS
+ .check = NULL,
+#else
+ .check = hfsplus_extract,
+#endif
+ .copy = NULL,
+ .resize = hfsplus_resize,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = hfsplus_get_resize_constraint,
+ .get_copy_constraint = NULL,
+#else /* DISCOVER_ONLY */
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL,
+#endif /* DISCOVER_ONLY */
+};
+
+
+static PedFileSystemType hfs_type = {
+ .next = NULL,
+ .ops = &hfs_ops,
+ .name = "hfs",
+ .block_sizes = HFS_BLOCK_SIZES
+};
+
+static PedFileSystemType hfsplus_type = {
+ .next = NULL,
+ .ops = &hfsplus_ops,
+ .name = "hfs+",
+ .block_sizes = HFSP_BLOCK_SIZES
+};
+
+static PedFileSystemType hfsx_type = {
+ .next = NULL,
+ .ops = &hfsx_ops,
+ .name = "hfsx",
+ .block_sizes = HFSX_BLOCK_SIZES
+};
+
+void
+ped_file_system_hfs_init ()
+{
+ ped_file_system_type_register (&hfs_type);
+ ped_file_system_type_register (&hfsplus_type);
+ ped_file_system_type_register (&hfsx_type);
+}
+
+void
+ped_file_system_hfs_done ()
+{
+ ped_file_system_type_unregister (&hfs_type);
+ ped_file_system_type_unregister (&hfsplus_type);
+ ped_file_system_type_unregister (&hfsx_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.h b/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.h
new file mode 100644
index 0000000000..076d355f96
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/hfs.h
@@ -0,0 +1,830 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _HFS_H
+#define _HFS_H
+
+/* WARNING : bn is used 2 times in theses macro */
+/* so _never_ use side effect operators when using them */
+#define TST_BLOC_OCCUPATION(tab,bn) \
+ (((tab)[(bn)/8]) & (1<<(7-((bn)&7))))
+#define SET_BLOC_OCCUPATION(tab,bn) \
+ (((tab)[(bn)/8]) |= (1<<(7-((bn)&7))))
+#define CLR_BLOC_OCCUPATION(tab,bn) \
+ (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7))))
+
+/* Maximum number of blocks for the copy buffers */
+#define BLOCK_MAX_BUFF 256
+/* Maximum size of the copy buffers, in bytes */
+#define BYTES_MAX_BUFF 8388608
+
+/* Apple Creator Codes follow */
+#define HFSP_IMPL_Shnk 0x53686e6b /* in use */
+#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */
+#define HFSP_IMPL_Resz 0x5265737a /* reserved */
+#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */
+#define HFSP_IMPL_traP 0x74726150 /* reserved */
+#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */
+
+#define HFS_SIGNATURE 0x4244 /* 'BD' */
+#define HFSP_SIGNATURE 0x482B /* 'H+' */
+#define HFSX_SIGNATURE 0x4858 /* 'HX' */
+
+#define HFSP_VERSION 4
+#define HFSX_VERSION 5
+
+#define HFS_HARD_LOCK 7
+#define HFS_UNMOUNTED 8
+#define HFS_BAD_SPARED 9
+#define HFS_SOFT_LOCK 15
+#define HFSP_NO_CACHE 10
+#define HFSP_INCONSISTENT 11
+#define HFSP_REUSE_CNID 12
+#define HFSP_JOURNALED 13
+
+#define HFS_IDX_NODE 0x00
+#define HFS_HDR_NODE 0x01
+#define HFS_MAP_NODE 0x02
+#define HFS_LEAF_NODE 0xFF
+
+#define HFS_FIRST_REC 0x0E
+#define HFS_NSD_HD_REC 0x78
+#define HFS_MAP_REC 0xF8
+
+#define HFS_DATA_FORK 0x00
+#define HFS_RES_FORK 0xFF
+
+#define HFS_CAT_DIR 0x01
+#define HFS_CAT_FILE 0x02
+#define HFS_CAT_DIR_TH 0x03
+#define HFS_CAT_FILE_TH 0x04
+
+#define HFSP_ATTR_INLINE 0x10
+#define HFSP_ATTR_FORK 0x20
+#define HFSP_ATTR_EXTENTS 0x30
+
+#define HFS_ROOT_PAR_ID 0x01
+#define HFS_ROOT_DIR_ID 0x02
+#define HFS_XTENT_ID 0x03
+#define HFS_CATALOG_ID 0x04
+#define HFS_BAD_BLOCK_ID 0x05
+#define HFSP_ALLOC_ID 0x06
+#define HFSP_STARTUP_ID 0x07
+#define HFSP_ATTRIB_ID 0x08
+#define HFSP_BOGUS_ID 0x0F
+#define HFSP_FIRST_AV_ID 0x10
+
+#define HFSJ_JOURN_IN_FS 0x00
+#define HFSJ_JOURN_OTHER_DEV 0x01
+#define HFSJ_JOURN_NEED_INIT 0x02
+
+#define HFSJ_HEADER_MAGIC 0x4a4e4c78
+#define HFSJ_ENDIAN_MAGIC 0x12345678
+
+#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */
+#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */
+
+#define HFS_EXT_NB 3
+#define HFSP_EXT_NB 8
+
+/* Define the filenames used by the FS extractor */
+#ifdef HFS_EXTRACT_FS
+
+#define HFS_MDB_FILENAME "mdb.hfs"
+#define HFS_CATALOG_FILENAME "catalog.hfs"
+#define HFS_EXTENTS_FILENAME "extents.hfs"
+#define HFS_BITMAP_FILENAME "bitmap.hfs"
+
+#define HFSP_VH_FILENAME "vh.hfsplus"
+#define HFSP_CATALOG_FILENAME "catalog.hfsplus"
+#define HFSP_EXTENTS_FILENAME "extents.hfsplus"
+#define HFSP_BITMAP_FILENAME "bitmap.hfsplus"
+#define HFSP_ATTRIB_FILENAME "attributes.hfsplus"
+#define HFSP_STARTUP_FILENAME "startup.hfsplus"
+
+#endif /* HFS_EXTRACT_FS */
+
+
+
+/* ----------------------------------- */
+/* -- HFS DATA STRUCTURES -- */
+/* ----------------------------------- */
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+/* Extent descriptor */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsExtDescriptor {
+ uint16_t start_block;
+ uint16_t block_count;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsExtDescriptor HfsExtDescriptor;
+typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB];
+
+/* Volume header */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsMasterDirectoryBlock {
+ uint16_t signature;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint16_t volume_attributes;
+ uint16_t files_in_root;
+ uint16_t volume_bitmap_block; /* in sectors */
+ uint16_t next_allocation;
+ uint16_t total_blocks;
+ uint32_t block_size; /* in bytes */
+ uint32_t def_clump_size; /* in bytes */
+ uint16_t start_block; /* in sectors */
+ uint32_t next_free_node;
+ uint16_t free_blocks;
+ uint8_t name_length;
+ char name[27];
+ uint32_t backup_date;
+ uint16_t backup_number;
+ uint32_t write_count;
+ uint32_t extents_clump;
+ uint32_t catalog_clump;
+ uint16_t dirs_in_root;
+ uint32_t file_count;
+ uint32_t dir_count;
+ uint32_t finder_info[8];
+ union __attribute__ ((packed)) {
+ struct __attribute__ ((packed)) {
+ uint16_t volume_cache_size; /* in blocks */
+ uint16_t bitmap_cache_size; /* in blocks */
+ uint16_t common_cache_size; /* in blocks */
+ } legacy;
+ struct __attribute__ ((packed)) {
+ uint16_t signature;
+ HfsExtDescriptor location;
+ } embedded;
+ } old_new;
+ uint32_t extents_file_size; /* in bytes, block size multiple */
+ HfsExtDataRec extents_file_rec;
+ uint32_t catalog_file_size; /* in bytes, block size multiple */
+ HfsExtDataRec catalog_file_rec;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock;
+
+/* B*-Tree Node Descriptor */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsNodeDescriptor {
+ uint32_t next;
+ uint32_t previous;
+ int8_t type;
+ uint8_t height;
+ uint16_t rec_nb;
+ uint16_t reserved;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsNodeDescriptor HfsNodeDescriptor;
+
+/* Header record of a whole B*-Tree */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsHeaderRecord {
+ uint16_t depth;
+ uint32_t root_node;
+ uint32_t leaf_records;
+ uint32_t first_leaf_node;
+ uint32_t last_leaf_node;
+ uint16_t node_size;
+ uint16_t max_key_len;
+ uint32_t total_nodes;
+ uint32_t free_nodes;
+ int8_t reserved[76];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsHeaderRecord HfsHeaderRecord;
+
+/* Catalog key for B*-Tree lookup in the catalog file */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsCatalogKey {
+ uint8_t key_length; /* length of the key without key_length */
+ uint8_t reserved;
+ uint32_t parent_ID;
+ uint8_t name_length;
+ char name[31]; /* in fact physicaly 1 upto 31 */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsCatalogKey HfsCatalogKey;
+
+/* Extents overflow key for B*-Tree lookup */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsExtentKey {
+ uint8_t key_length; /* length of the key without key_length */
+ uint8_t type; /* data or ressource fork */
+ uint32_t file_ID;
+ uint16_t start;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsExtentKey HfsExtentKey;
+
+/* Catalog subdata case directory */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsDir {
+ uint16_t flags;
+ uint16_t valence; /* number of files in this directory */
+ uint32_t dir_ID;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t backup_date;
+ int8_t DInfo[16]; /* used by Finder, handle as reserved */
+ int8_t DXInfo[16]; /* used by Finder, handle as reserved */
+ uint32_t reserved[4];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsDir HfsDir;
+
+/* Catalog subdata case file */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsFile {
+ int8_t flags;
+ int8_t type; /* should be 0 */
+ int8_t FInfo[16]; /* used by Finder, handle as reserved */
+ uint32_t file_ID;
+ uint16_t data_start_block;
+ uint32_t data_sz_byte;
+ uint32_t data_sz_block;
+ uint16_t res_start_block;
+ uint32_t res_sz_byte;
+ uint32_t res_sz_block;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t backup_date;
+ int8_t FXInfo[16]; /* used by Finder, handle as reserved */
+ uint16_t clump_size;
+ HfsExtDataRec extents_data;
+ HfsExtDataRec extents_res;
+ uint32_t reserved;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsFile HfsFile;
+
+/* Catalog subdata case directory thread */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsDirTh {
+ uint32_t reserved[2];
+ uint32_t parent_ID;
+ int8_t name_length;
+ char name[31];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsDirTh HfsDirTh;
+
+/* Catalog subdata case file thread */
+typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */
+
+/* Catalog data */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsCatalog {
+ int8_t type;
+ int8_t reserved;
+ union {
+ HfsDir dir;
+ HfsFile file;
+ HfsDirTh dir_th;
+ HfsFileTh file_th;
+ } sel;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsCatalog HfsCatalog;
+
+
+
+/* ------------------------------------ */
+/* -- HFS+ DATA STRUCTURES -- */
+/* ------------------------------------ */
+
+/* documented since 2004 in tn1150 */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPPerms {
+ uint32_t owner_ID;
+ uint32_t group_ID;
+ uint32_t permissions;
+ uint32_t special_devices;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPPerms HfsPPerms;
+
+/* HFS+ extent descriptor*/
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPExtDescriptor {
+ uint32_t start_block;
+ uint32_t block_count;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPExtDescriptor HfsPExtDescriptor;
+typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB];
+
+/* HFS+ fork data structure */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPForkData {
+ uint64_t logical_size;
+ uint32_t clump_size;
+ uint32_t total_blocks;
+ HfsPExtDataRec extents;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPForkData HfsPForkData;
+
+/* HFS+ catalog node ID */
+typedef uint32_t HfsPNodeID;
+
+/* HFS+ file names */
+typedef uint16_t unichar;
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPUniStr255 {
+ uint16_t length;
+ unichar unicode[255]; /* 1 upto 255 */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPUniStr255 HfsPUniStr255;
+
+/* HFS+ volume header */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPVolumeHeader {
+ uint16_t signature;
+ uint16_t version;
+ uint32_t attributes;
+ uint32_t last_mounted_version;
+ uint32_t journal_info_block;
+
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t backup_date;
+ uint32_t checked_date;
+
+ uint32_t file_count;
+ uint32_t dir_count;
+
+ uint32_t block_size;
+ uint32_t total_blocks;
+ uint32_t free_blocks;
+
+ uint32_t next_allocation;
+ uint32_t res_clump_size;
+ uint32_t data_clump_size;
+ HfsPNodeID next_catalog_ID;
+
+ uint32_t write_count;
+ uint64_t encodings_bitmap;
+
+ uint8_t finder_info[32];
+
+ HfsPForkData allocation_file;
+ HfsPForkData extents_file;
+ HfsPForkData catalog_file;
+ HfsPForkData attributes_file;
+ HfsPForkData startup_file;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPVolumeHeader HfsPVolumeHeader;
+
+/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPNodeDescriptor {
+ uint32_t next;
+ uint32_t previous;
+ int8_t type;
+ uint8_t height;
+ uint16_t rec_nb;
+ uint16_t reserved;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor;
+
+/* Header record of a whole HFS+ B-Tree. */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPHeaderRecord {
+ uint16_t depth;
+ uint32_t root_node;
+ uint32_t leaf_records;
+ uint32_t first_leaf_node;
+ uint32_t last_leaf_node;
+ uint16_t node_size;
+ uint16_t max_key_len;
+ uint32_t total_nodes;
+ uint32_t free_nodes; /* same as hfs btree until here */
+ uint16_t reserved1;
+
+ uint32_t clump_size;
+ uint8_t btree_type; /* must be 0 for HFS+ B-Tree */
+ uint8_t key_compare_type; /* hfsx => 0xCF = case folding */
+ /* 0xBC = binary compare */
+ /* otherwise, reserved */
+ uint32_t attributes;
+ uint32_t reserved3[16];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPHeaderRecord HfsPHeaderRecord;
+
+/* Catalog key for B-Tree lookup in the HFS+ catalog file */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPCatalogKey {
+ uint16_t key_length;
+ HfsPNodeID parent_ID;
+ HfsPUniStr255 node_name;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPCatalogKey HfsPCatalogKey;
+
+/* HFS+ catalog subdata case dir */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPDir {
+ uint16_t flags;
+ uint32_t valence;
+ HfsPNodeID dir_ID;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t attrib_mod_date;
+ uint32_t access_date;
+ uint32_t backup_date;
+ HfsPPerms permissions;
+ int8_t DInfo[16]; /* used by Finder, handle as reserved */
+ int8_t DXInfo[16]; /* used by Finder, handle as reserved */
+ uint32_t text_encoding;
+ uint32_t reserved;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPDir HfsPDir;
+
+/* HFS+ catalog subdata case file */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPFile {
+ uint16_t flags;
+ uint32_t reserved1;
+ HfsPNodeID file_ID;
+ uint32_t create_date;
+ uint32_t modify_date;
+ uint32_t attrib_mod_date;
+ uint32_t access_date;
+ uint32_t backup_date;
+ HfsPPerms permissions;
+ int8_t FInfo[16]; /* used by Finder, handle as reserved */
+ int8_t FXInfo[16]; /* used by Finder, handle as reserved */
+ uint32_t text_encoding;
+ uint32_t reserved2;
+
+ HfsPForkData data_fork;
+ HfsPForkData res_fork;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPFile HfsPFile;
+
+/* HFS+ catalog subdata case thread */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPThread {
+ int16_t reserved;
+ HfsPNodeID parent_ID;
+ HfsPUniStr255 node_name;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPThread HfsPDirTh;
+typedef struct _HfsPThread HfsPFileTh;
+
+/* HFS+ Catalog leaf data */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPCatalog {
+ int16_t type;
+ union {
+ HfsPDir dir;
+ HfsPFile file;
+ HfsPDirTh dir_th;
+ HfsPFileTh file_th;
+ } sel;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPCatalog HfsPCatalog;
+
+/* HFS+ extents file key */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPExtentKey {
+ uint16_t key_length;
+ uint8_t type;
+ uint8_t pad;
+ HfsPNodeID file_ID;
+ uint32_t start;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPExtentKey HfsPExtentKey;
+
+/* extent file data is HfsPExtDataRec */
+
+/* Fork data attribute file */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPForkDataAttr {
+ uint32_t record_type;
+ uint32_t reserved;
+ union __attribute__ ((packed)) {
+ HfsPForkData fork;
+ HfsPExtDataRec extents;
+ } fork_res;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPForkDataAttr HfsPForkDataAttr;
+
+
+/* ----------- Journal data structures ----------- */
+
+/* Info block : stored in a block # defined in the VH */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsJJournalInfoBlock {
+ uint32_t flags;
+ uint32_t device_signature[8];
+ uint64_t offset;
+ uint64_t size;
+ uint32_t reserved[32];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock;
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsJJournalHeader {
+ uint32_t magic;
+ uint32_t endian;
+ uint64_t start;
+ uint64_t end;
+ uint64_t size;
+ uint32_t blhdr_size;
+ uint32_t checksum;
+ uint32_t jhdr_size;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsJJournalHeader HfsJJournalHeader;
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsJBlockInfo {
+ uint64_t bnum; /* sector number */
+ uint32_t bsize; /* size in bytes */
+ uint32_t next;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsJBlockInfo HfsJBlockInfo;
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsJBlockListHeader {
+ uint16_t max_blocks; /* reserved */
+ uint16_t num_blocks;
+ uint32_t bytes_used;
+ uint32_t checksum;
+ uint32_t pad;
+ HfsJBlockInfo binfo[1];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsJBlockListHeader HfsJBlockListHeader;
+
+
+/* ---------------------------------------- */
+/* -- INTERNAL DATA STRUCTURES -- */
+/* ---------------------------------------- */
+
+/* Data of an opened HFS file */
+struct _HfsPrivateFile {
+ PedSector sect_nb;
+ PedFileSystem* fs;
+ uint32_t CNID; /* disk order (BE) */
+ HfsExtDataRec first; /* disk order (BE) */
+ HfsExtDataRec cache; /* disk order (BE) */
+ uint16_t start_cache; /* CPU order */
+};
+typedef struct _HfsPrivateFile HfsPrivateFile;
+
+/* To store bad block list */
+struct _HfsPrivateLinkExtent {
+ HfsExtDescriptor extent;
+ struct _HfsPrivateLinkExtent* next;
+};
+typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent;
+
+/* HFS Filesystem specific data */
+struct _HfsPrivateFSData {
+ uint8_t alloc_map[(1<<16) / 8];
+ HfsMasterDirectoryBlock* mdb;
+ HfsPrivateFile* extent_file;
+ HfsPrivateFile* catalog_file;
+ HfsPrivateLinkExtent* bad_blocks_xtent_list;
+ unsigned int bad_blocks_xtent_nb;
+ char bad_blocks_loaded;
+};
+typedef struct _HfsPrivateFSData HfsPrivateFSData;
+
+/* Generic btree key */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPrivateGenericKey {
+ uint8_t key_length;
+ uint8_t key_content[1]; /* we use 1 as a minimum size */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey;
+
+/* ----- HFS+ ----- */
+
+/* Data of an opened HFS file */
+struct _HfsPPrivateFile {
+ PedSector sect_nb;
+ PedFileSystem* fs;
+ HfsPNodeID CNID; /* disk order (BE) */
+ HfsPExtDataRec first; /* disk order (BE) */
+ HfsPExtDataRec cache; /* disk order (BE) */
+ uint32_t start_cache; /* CPU order */
+};
+typedef struct _HfsPPrivateFile HfsPPrivateFile;
+
+struct _HfsPPrivateExtent {
+ PedSector start_sector;
+ PedSector sector_count;
+};
+typedef struct _HfsPPrivateExtent HfsPPrivateExtent;
+
+/* To store bad block list */
+struct _HfsPPrivateLinkExtent {
+ HfsPExtDescriptor extent;
+ struct _HfsPPrivateLinkExtent* next;
+};
+typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent;
+
+/* HFS+ file system specific data */
+struct _HfsPPrivateFSData {
+ PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */
+ PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */
+ uint8_t* alloc_map;
+ uint8_t* dirty_alloc_map;
+ HfsPVolumeHeader* vh;
+ HfsPPrivateFile* extents_file;
+ HfsPPrivateFile* catalog_file;
+ HfsPPrivateFile* attributes_file;
+ HfsPPrivateFile* allocation_file;
+ HfsPPrivateLinkExtent* bad_blocks_xtent_list;
+ uint32_t jib_start_block;
+ uint32_t jl_start_block;
+ unsigned int bad_blocks_xtent_nb;
+ char bad_blocks_loaded;
+ char free_geom; /* 1 = plus_geom must be freed */
+};
+typedef struct _HfsPPrivateFSData HfsPPrivateFSData;
+
+/* Generic + btree key */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _HfsPPrivateGenericKey {
+ uint16_t key_length;
+ uint8_t key_content[1]; /* we use 1 as a minimum size */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey;
+
+/* ---- common ---- */
+
+/* node and lead record reference for a BTree search */
+struct _HfsCPrivateLeafRec {
+ unsigned int node_size; /* in sectors */
+ unsigned int node_number;
+ unsigned int record_pos;
+ unsigned int record_number;
+};
+typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec;
+
+extern uint8_t* hfs_block;
+extern uint8_t* hfsp_block;
+extern unsigned hfs_block_count;
+extern unsigned hfsp_block_count;
+
+#endif /* _HFS_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/journal.c b/usr/src/lib/libparted/common/libparted/fs/hfs/journal.c
new file mode 100644
index 0000000000..083cb598e5
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/journal.c
@@ -0,0 +1,389 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "reloc_plus.h"
+
+#include "journal.h"
+
+static int hfsj_vh_replayed = 0;
+static int is_le = 0;
+
+static uint32_t
+hfsj_calc_checksum(uint8_t *ptr, int len)
+{
+ int i;
+ uint32_t cksum=0;
+
+ for (i=0; i < len; i++, ptr++) {
+ cksum = (cksum << 8) ^ (cksum + *ptr);
+ }
+
+ return (~cksum);
+}
+
+int
+hfsj_update_jib(PedFileSystem* fs, uint32_t block)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+
+ priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block);
+
+ if (!hfsplus_update_vh (fs))
+ return 0;
+
+ priv_data->jib_start_block = block;
+ return 1;
+}
+
+int
+hfsj_update_jl(PedFileSystem* fs, uint32_t block)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ PedSector sector;
+ uint64_t offset;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsJJournalInfoBlock* jib;
+ int binsect;
+
+ binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
+ sector = (PedSector) priv_data->jib_start_block * binsect;
+ if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+ return 0;
+ jib = (HfsJJournalInfoBlock*) buf;
+
+ offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect;
+ jib->offset = HFS_CPU_TO_64(offset, is_le);
+
+ if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
+ || !ped_geometry_sync(priv_data->plus_geom))
+ return 0;
+
+ priv_data->jl_start_block = block;
+ return 1;
+}
+
+/* Return the sector in the journal that is after the area read */
+/* or 0 on error */
+static PedSector
+hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh,
+ PedSector journ_sect, PedSector journ_length,
+ PedSector read_sect, unsigned int nb_sect,
+ void* buf)
+{
+ int r;
+
+ while (nb_sect--) {
+ r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1);
+ if (!r) return 0;
+
+ buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT;
+ read_sect++;
+ if (read_sect == journ_length)
+ read_sect = 1; /* skip journal header
+ which is asserted to be
+ 1 sector long */
+ }
+
+ return read_sect;
+}
+
+static int
+hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh,
+ PedSector jsector, PedSector jlength)
+{
+ PedSector start, sector;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsJBlockListHeader* blhdr;
+ uint8_t* block;
+ unsigned int blhdr_nbsect;
+ int i, r;
+ uint32_t cksum, size;
+
+ blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT;
+ blhdr = (HfsJBlockListHeader*)
+ ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT);
+ if (!blhdr) return 0;
+
+ start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT;
+ do {
+ start = hfsj_journal_read(priv_data->plus_geom, jh, jsector,
+ jlength, start, blhdr_nbsect, blhdr);
+ if (!start) goto err_replay;
+
+ cksum = HFS_32_TO_CPU(blhdr->checksum, is_le);
+ blhdr->checksum = 0;
+ if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Bad block list header checksum."));
+ goto err_replay;
+ }
+ blhdr->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+ for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) {
+ size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le);
+ sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le);
+ if (!size) continue;
+ if (size % PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Invalid size of a transaction "
+ "block while replaying the journal "
+ "(%i bytes)."),
+ size);
+ goto err_replay;
+ }
+ block = (uint8_t*) ped_malloc(size);
+ if (!block) goto err_replay;
+ start = hfsj_journal_read(priv_data->plus_geom, jh,
+ jsector, jlength, start,
+ size / PED_SECTOR_SIZE_DEFAULT,
+ block);
+ if (!start) {
+ ped_free (block);
+ goto err_replay;
+ }
+ /* the sector stored in the journal seems to be
+ relative to the begin of the block device which
+ contains the hfs+ journaled volume */
+ if (sector != ~0LL)
+ r = ped_geometry_write (fs->geom, block, sector,
+ size / PED_SECTOR_SIZE_DEFAULT);
+ else
+ r = 1;
+ ped_free (block);
+ /* check if wrapper mdb or vh with no wrapper has
+ changed */
+ if ( (sector != ~0LL)
+ && (2 >= sector)
+ && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) )
+ hfsj_vh_replayed = 1;
+ /* check if vh of embedded hfs+ has changed */
+ if ( (sector != ~0LL)
+ && (priv_data->plus_geom != fs->geom)
+ && (sector
+ + fs->geom->start
+ - priv_data->plus_geom->start <= 2)
+ && (sector
+ + size / PED_SECTOR_SIZE_DEFAULT
+ + fs->geom->start
+ - priv_data->plus_geom->start > 2) )
+ hfsj_vh_replayed = 1;
+ if (!r) goto err_replay;
+ }
+ } while (blhdr->binfo[0].next);
+
+ jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le);
+
+ ped_free (blhdr);
+ return (ped_geometry_sync (fs->geom));
+
+err_replay:
+ ped_free (blhdr);
+ return 0;
+}
+
+/* 0 => Failure, don't continue to open ! */
+/* 1 => Success, the journal has been completly replayed, or don't need to */
+int
+hfsj_replay_journal(PedFileSystem* fs)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ PedSector sector, length;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsJJournalInfoBlock* jib;
+ HfsJJournalHeader* jh;
+ int binsect;
+ uint32_t cksum;
+
+ binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT;
+ priv_data->jib_start_block =
+ PED_BE32_TO_CPU(priv_data->vh->journal_info_block);
+ sector = (PedSector) priv_data->jib_start_block * binsect;
+ if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+ return 0;
+ jib = (HfsJJournalInfoBlock*) buf;
+
+ if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
+ && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
+ priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le)
+ / ( PED_SECTOR_SIZE_DEFAULT * binsect );
+ }
+
+ if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT))
+ return 1;
+
+ if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS))
+ || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Journal stored outside of the volume are "
+ "not supported. Try to desactivate the "
+ "journal and run Parted again."));
+ return 0;
+ }
+
+ if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT)
+ || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Journal offset or size is not multiple of "
+ "the sector size."));
+ return 0;
+ }
+
+ sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT;
+ length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT;
+
+ jib = NULL;
+ if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1))
+ return 0;
+ jh = (HfsJJournalHeader*) buf;
+
+ if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC))
+ is_le = 1;
+
+ if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le))
+ || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Incorrect magic values in the journal header."));
+ return 0;
+ }
+
+ if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT)
+ || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT
+ != (uint64_t)length) ) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Journal size mismatch between journal info block "
+ "and journal header."));
+ return 0;
+ }
+
+ if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT)
+ || (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT)
+ || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT)
+ || (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Some header fields are not multiple of the sector "
+ "size."));
+ return 0;
+ }
+
+ if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The sector size stored in the journal is not 512 "
+ "bytes. Parted only supports 512 bytes length "
+ "sectors."));
+ return 0;
+ }
+
+ cksum = HFS_32_TO_CPU(jh->checksum, is_le);
+ jh->checksum = 0;
+ if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Bad journal checksum."));
+ return 0;
+ }
+ jh->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+ /* The 2 following test are in the XNU Darwin source code */
+ /* so I assume they're needed */
+ if (jh->start == jh->size)
+ jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
+ if (jh->end == jh->size)
+ jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le);
+
+ if (jh->start == jh->end)
+ return 1;
+
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
+ _("The journal is not empty. Parted must replay the "
+ "transactions before opening the file system. This will "
+ "modify the file system."))
+ != PED_EXCEPTION_FIX)
+ return 0;
+
+ while (jh->start != jh->end) {
+ /* Replay one complete transaction */
+ if (!hfsj_replay_transaction(fs, jh, sector, length))
+ return 0;
+
+ /* Recalculate cksum of the journal header */
+ jh->checksum = 0; /* need to be 0 while calculating the cksum */
+ cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh));
+ jh->checksum = HFS_CPU_TO_32(cksum, is_le);
+
+ /* Update the Journal Header */
+ if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1)
+ || !ped_geometry_sync(priv_data->plus_geom))
+ return 0;
+ }
+
+ if (hfsj_vh_replayed) {
+ /* probe could have reported incorrect info ! */
+ /* is there a way to ask parted to quit ? */
+ ped_exception_throw(
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_OK,
+ _("The volume header or the master directory block has "
+ "changed while replaying the journal. You should "
+ "restart Parted."));
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/journal.h b/usr/src/lib/libparted/common/libparted/fs/hfs/journal.h
new file mode 100644
index 0000000000..6bab396a2b
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/journal.h
@@ -0,0 +1,44 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _JOURNAL_H
+#define _JOURNAL_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsj_replay_journal(PedFileSystem* fs);
+
+int
+hfsj_update_jib(PedFileSystem* fs, uint32_t block);
+
+int
+hfsj_update_jl(PedFileSystem* fs, uint32_t block);
+
+#define HFS_16_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_LE16_TO_CPU(x) : (uint16_t)PED_BE16_TO_CPU(x))
+#define HFS_32_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_LE32_TO_CPU(x) : (uint32_t)PED_BE32_TO_CPU(x))
+#define HFS_64_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_LE64_TO_CPU(x) : (uint64_t)PED_BE64_TO_CPU(x))
+#define HFS_CPU_TO_16(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_CPU_TO_LE16(x) : (uint16_t)PED_CPU_TO_BE16(x))
+#define HFS_CPU_TO_32(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_CPU_TO_LE32(x) : (uint32_t)PED_CPU_TO_BE32(x))
+#define HFS_CPU_TO_64(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_CPU_TO_LE64(x) : (uint64_t)PED_CPU_TO_BE64(x))
+
+#endif /* _JOURNAL_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/probe.c b/usr/src/lib/libparted/common/libparted/fs/hfs/probe.c
new file mode 100644
index 0000000000..4da4594f69
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/probe.c
@@ -0,0 +1,230 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+
+#include "probe.h"
+
+int
+hfsc_can_use_geom (PedGeometry* geom)
+{
+ PedDevice* dev;
+
+ dev = geom->dev;
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Parted can't use HFS file systems on disks "
+ "with a sector size not equal to %d bytes."),
+ (int)PED_SECTOR_SIZE_DEFAULT );
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Probe an HFS volume, detecting it even if
+it is in fact a wrapper to an HFS+ volume */
+/* Used by hfsplus_probe and hfs_probe */
+PedGeometry*
+hfs_and_wrapper_probe (PedGeometry* geom)
+{
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ HfsMasterDirectoryBlock *mdb;
+ PedGeometry* geom_ret;
+ PedSector search, max;
+
+ PED_ASSERT (geom != NULL, return NULL);
+ PED_ASSERT (hfsc_can_use_geom (geom), return NULL);
+
+ mdb = (HfsMasterDirectoryBlock *) buf;
+
+ /* is 5 an intelligent value ? */
+ if ((geom->length < 5)
+ || (!ped_geometry_read (geom, buf, 2, 1))
+ || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) )
+ return NULL;
+
+ search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block)
+ + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks)
+ * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT )));
+ max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT);
+ if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2)))
+ return NULL;
+
+ for (; search < max; search++) {
+ if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2)
+ || !ped_geometry_read (geom_ret, buf, search, 1))
+ break;
+ if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE))
+ return geom_ret;
+ }
+
+ ped_geometry_destroy (geom_ret);
+ return NULL;
+}
+
+PedGeometry*
+hfsplus_probe (PedGeometry* geom)
+{
+ PedGeometry* geom_ret;
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!hfsc_can_use_geom (geom))
+ return NULL;
+
+ if ((geom_ret = hfs_and_wrapper_probe(geom))) {
+ /* HFS+ is embedded in an HFS volume ? */
+ HfsMasterDirectoryBlock *mdb;
+ mdb = (HfsMasterDirectoryBlock *) buf;
+
+ if (!ped_geometry_read (geom, buf, 2, 1)
+ || (mdb->old_new.embedded.signature
+ != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) {
+ ped_geometry_destroy (geom_ret);
+ return NULL;
+ } else
+ return geom_ret;
+ } else {
+ /* This is a standalone HFS+ volume ? */
+ PedSector search, max;
+ HfsPVolumeHeader *vh;
+ vh = (HfsPVolumeHeader *) buf;
+
+ if ((geom->length < 5)
+ || !ped_geometry_read (geom, buf, 2, 1)
+ || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)))
+ return NULL;
+
+ /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */
+ /* But previous versions of my implementation used to */
+ /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */
+ /* (blocks-1)*sz-1 has to be scanned last, because */
+ /* it can belong to a regular file */
+ max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
+ * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
+ - 2;
+ search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT ) + 2;
+ if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
+ search + 2)))
+ return NULL;
+
+ for (; search < max; search++) {
+ if (!ped_geometry_set (geom_ret, geom_ret->start,
+ search + 2)
+ || !ped_geometry_read (geom_ret, buf, search, 1))
+ break;
+ if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE))
+ return geom_ret;
+ }
+ search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1)
+ * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
+ - 1;
+ if (!ped_geometry_set (geom_ret, geom_ret->start,
+ search + 2)
+ || !ped_geometry_read (geom_ret, buf, search, 1)
+ || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) {
+ ped_geometry_destroy (geom_ret);
+ return NULL;
+ } else
+ return geom_ret;
+ }
+}
+
+PedGeometry*
+hfs_probe (PedGeometry* geom)
+{
+ PedGeometry* geom_base;
+ PedGeometry* geom_plus = NULL;
+
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!hfsc_can_use_geom (geom))
+ return NULL;
+
+ if ((geom_base = hfs_and_wrapper_probe(geom))
+ && (!(geom_plus = hfsplus_probe(geom_base))))
+ return geom_base;
+ else {
+ if (geom_base) ped_geometry_destroy (geom_base);
+ if (geom_plus) ped_geometry_destroy (geom_plus);
+ return NULL;
+ }
+}
+
+PedGeometry*
+hfsx_probe (PedGeometry* geom)
+{
+ PedGeometry* geom_ret;
+ uint8_t buf[PED_SECTOR_SIZE_DEFAULT];
+ PedSector search, max;
+ HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf;
+
+ PED_ASSERT (geom != NULL, return NULL);
+
+ if (!hfsc_can_use_geom (geom))
+ return NULL;
+
+ if ((geom->length < 5)
+ || !ped_geometry_read (geom, buf, 2, 1)
+ || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE)))
+ return NULL;
+
+ /* unlike the hfs+ code, which should be kept compatible
+ with my old previous implementations, we only care here
+ about legal alternate VH positions, like TN1150 describes them */
+ max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1)
+ * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT )
+ - 2;
+ search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT );
+ if (!(geom_ret = ped_geometry_new (geom->dev, geom->start,
+ search + 2)))
+ return NULL;
+ for (; search < max; search++) {
+ if (!ped_geometry_set (geom_ret, geom_ret->start,
+ search + 2)
+ || !ped_geometry_read (geom_ret, buf, search, 1))
+ break;
+ if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE))
+ return geom_ret;
+ }
+
+ ped_geometry_destroy (geom_ret);
+ return NULL;
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/probe.h b/usr/src/lib/libparted/common/libparted/fs/hfs/probe.h
new file mode 100644
index 0000000000..f508de356e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/probe.h
@@ -0,0 +1,43 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _PROBE_H
+#define _PROBE_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsc_can_use_geom (PedGeometry* geom);
+
+PedGeometry*
+hfs_and_wrapper_probe (PedGeometry* geom);
+
+PedGeometry*
+hfsplus_probe (PedGeometry* geom);
+
+PedGeometry*
+hfs_probe (PedGeometry* geom);
+
+PedGeometry*
+hfsx_probe (PedGeometry* geom);
+
+#endif /* _PROBE_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.c b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.c
new file mode 100644
index 0000000000..3c8911563f
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.c
@@ -0,0 +1,670 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file.h"
+#include "advfs.h"
+#include "cache.h"
+
+#include "reloc.h"
+
+/* This function moves data of size blocks starting
+ at block *ptr_fblock to block *ptr_to_fblock */
+/* return new start or -1 on failure */
+static int
+hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
+ unsigned int *ptr_to_fblock, unsigned int size)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ unsigned int i, ok = 0;
+ unsigned int next_to_fblock;
+ unsigned int start, stop;
+
+ PED_ASSERT (hfs_block != NULL, return -1);
+ PED_ASSERT (*ptr_to_fblock <= *ptr_fblock, return -1);
+ /* quiet gcc */
+ next_to_fblock = start = stop = 0;
+
+/*
+ Try to fit the extent AT or _BEFORE_ the wanted place,
+ or then in the gap between dest and source.
+ If failed try to fit the extent after source, for 2 pass relocation
+ The extent is always copied in a non overlapping way
+*/
+
+ /* Backward search */
+ /* 1 pass relocation AT or BEFORE *ptr_to_fblock */
+ if (*ptr_to_fblock != *ptr_fblock) {
+ start = stop = *ptr_fblock < *ptr_to_fblock+size ?
+ *ptr_fblock : *ptr_to_fblock+size;
+ while (start && stop-start != size) {
+ --start;
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
+ stop = start;
+ }
+ ok = (stop-start == size);
+ }
+
+ /* Forward search */
+ /* 1 pass relocation in the gap merged with 2 pass reloc after source */
+ if (!ok && *ptr_to_fblock != *ptr_fblock) {
+ start = stop = *ptr_to_fblock+1;
+ while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks)
+ && stop-start != size) {
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
+ start = stop + 1;
+ ++stop;
+ }
+ ok = (stop-start == size);
+ }
+
+ /* new non overlapping room has been found ? */
+ if (ok) {
+ /* enough room */
+ unsigned int j;
+ unsigned int start_block =
+ PED_BE16_TO_CPU (priv_data->mdb->start_block );
+ unsigned int block_sz =
+ (PED_BE32_TO_CPU (priv_data->mdb->block_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+
+ if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
+ /* Fit in the gap */
+ next_to_fblock = stop;
+ else
+ /* Before or after the gap */
+ next_to_fblock = *ptr_to_fblock;
+
+ /* move blocks */
+ for (i = 0; i < size; /*i+=j*/) {
+ PedSector abs_sector;
+ unsigned int ai;
+
+ j = size - i; j = (j < hfs_block_count) ?
+ j : hfs_block_count ;
+
+ abs_sector = start_block
+ + (PedSector) (*ptr_fblock + i) * block_sz;
+ if (!ped_geometry_read (fs->geom, hfs_block, abs_sector,
+ block_sz * j))
+ return -1;
+
+ abs_sector = start_block
+ + (PedSector) (start + i) * block_sz;
+ if (!ped_geometry_write (fs->geom,hfs_block,abs_sector,
+ block_sz * j))
+ return -1;
+
+ for (ai = i+j; i < ai; i++) {
+ /* free source block */
+ CLR_BLOC_OCCUPATION(priv_data->alloc_map,
+ *ptr_fblock + i);
+
+ /* set dest block */
+ SET_BLOC_OCCUPATION(priv_data->alloc_map,
+ start + i);
+ }
+ }
+ if (!ped_geometry_sync_fast (fs->geom))
+ return -1;
+
+ *ptr_fblock += size;
+ *ptr_to_fblock = next_to_fblock;
+ } else {
+ if (*ptr_fblock != *ptr_to_fblock)
+ /* not enough room, but try to continue */
+ ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("An extent has not been relocated."));
+ start = *ptr_fblock;
+ *ptr_fblock = *ptr_to_fblock = start + size;
+ }
+
+ return start;
+}
+
+/* Update MDB */
+/* Return 0 if an error occurred */
+/* Return 1 if everything ok */
+int
+hfs_update_mdb (PedFileSystem *fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+
+ if (!ped_geometry_read (fs->geom, node, 2, 1))
+ return 0;
+ memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock));
+ if ( !ped_geometry_write (fs->geom, node, 2, 1)
+ || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1)
+ || !ped_geometry_sync_fast (fs->geom))
+ return 0;
+ return 1;
+}
+
+/* Generic relocator */
+/* replace previous hfs_do_move_* */
+static int
+hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src,
+ unsigned int *ptr_dest, HfsCPrivateCache* cache,
+ HfsCPrivateExtent* ref)
+{
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsPrivateFile* file;
+ HfsExtDescriptor* extent;
+ HfsCPrivateExtent* move;
+ int new_start;
+
+ new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest,
+ ref->ext_length);
+ if (new_start == -1) return -1;
+
+ if (ref->ext_start != (unsigned) new_start) {
+ /* Load, modify & save */
+ switch (ref->where) {
+ /******** MDB *********/
+ case CR_PRIM_CAT :
+ priv_data->catalog_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE16(new_start);
+ goto CR_PRIM;
+ case CR_PRIM_EXT :
+ priv_data->extent_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE16(new_start);
+ CR_PRIM :
+ extent = ( HfsExtDescriptor* )
+ ( (uint8_t*)priv_data->mdb + ref->ref_offset );
+ extent[ref->ref_index].start_block =
+ PED_CPU_TO_BE16(new_start);
+ if (!hfs_update_mdb(fs)) return -1;
+ break;
+
+ /********* BTREE *******/
+ case CR_BTREE_EXT_CAT :
+ if (priv_data->catalog_file
+ ->cache[ref->ref_index].start_block
+ == PED_CPU_TO_BE16(ref->ext_start))
+ priv_data->catalog_file
+ ->cache[ref->ref_index].start_block =
+ PED_CPU_TO_BE16(new_start);
+ case CR_BTREE_EXT_0 :
+ file = priv_data->extent_file;
+ goto CR_BTREE;
+ case CR_BTREE_CAT :
+ file = priv_data->catalog_file;
+ CR_BTREE:
+ PED_ASSERT(ref->sect_by_block == 1
+ && ref->ref_offset < PED_SECTOR_SIZE_DEFAULT,
+ return -1);
+ if (!hfs_file_read_sector(file, node, ref->ref_block))
+ return -1;
+ extent = ( HfsExtDescriptor* ) (node + ref->ref_offset);
+ extent[ref->ref_index].start_block =
+ PED_CPU_TO_BE16(new_start);
+ if (!hfs_file_write_sector(file, node, ref->ref_block)
+ || !ped_geometry_sync_fast (fs->geom))
+ return -1;
+ break;
+
+ /********** BUG ********/
+ default :
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("A reference to an extent comes from a place "
+ "it should not. You should check the file "
+ "system!"));
+ return -1;
+ break;
+ }
+
+ /* Update the cache */
+ move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
+ if (!move) return -1; /* "cleanly" fail */
+ PED_ASSERT(move == ref, return -1); /* generate a bug */
+ }
+
+ return new_start;
+}
+
+/* 0 error, 1 ok */
+static int
+hfs_save_allocation(PedFileSystem* fs)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ unsigned int map_sectors;
+
+ map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks)
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1 )
+ / (PED_SECTOR_SIZE_DEFAULT * 8);
+ return ( ped_geometry_write (fs->geom, priv_data->alloc_map,
+ PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block),
+ map_sectors) );
+}
+
+/* This function moves an extent starting at block fblock to block to_fblock
+ if there's enough room */
+/* Return 1 if everything was fine */
+/* Return -1 if an error occurred */
+/* Return 0 if no extent was found */
+/* Generic search thanks to the file system cache */
+static int
+hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
+ unsigned int *ptr_to_fblock,
+ HfsCPrivateCache* cache)
+{
+ HfsCPrivateExtent* ref;
+ unsigned int old_start, new_start;
+
+ /* Reference search powered by the cache... */
+ /* This is the optimisation secret :) */
+ ref = hfsc_cache_search_extent(cache, *ptr_fblock);
+ if (!ref) return 0; /* not found */
+
+ old_start = *ptr_fblock;
+ new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
+ if (new_start == (unsigned int) -1) return -1;
+ if (new_start > old_start) { /* detect 2 pass reloc */
+ new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref);
+ if (new_start == (unsigned int) -1 || new_start > old_start)
+ return -1;
+ }
+
+ /* allocation bitmap save is not atomic with data relocation */
+ /* so we only do it a few times, and without syncing */
+ /* The unmounted bit protect us anyway */
+ hfs_save_allocation(fs);
+ return 1;
+}
+
+static int
+hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsExtDescriptor* extent;
+ unsigned int j;
+
+ extent = priv_data->mdb->extents_file_rec;
+ for (j = 0; j < HFS_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE16_TO_CPU(extent[j].start_block),
+ PED_BE16_TO_CPU(extent[j].block_count),
+ 0, /* unused for mdb */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
+ 1, /* load/save only 1 sector */
+ CR_PRIM_EXT,
+ j )
+ )
+ return 0;
+ }
+
+ extent = priv_data->mdb->catalog_file_rec;
+ for (j = 0; j < HFS_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE16_TO_CPU(extent[j].start_block),
+ PED_BE16_TO_CPU(extent[j].block_count),
+ 0,
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb),
+ 1,
+ CR_PRIM_CAT,
+ j )
+ )
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+ HfsHeaderRecord* header;
+ HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
+ HfsCatalogKey* catalog_key;
+ HfsCatalog* catalog_data;
+ HfsExtDescriptor* extent;
+ unsigned int leaf_node, record_number;
+ unsigned int i, j;
+
+ if (!priv_data->catalog_file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This HFS volume has no catalog file. "
+ "This is very unusual!"));
+ return 1;
+ }
+
+ if (!hfs_file_read_sector (priv_data->catalog_file, node, 0))
+ return 0;
+ header = (HfsHeaderRecord*)(node + PED_BE16_TO_CPU(*((uint16_t*)
+ (node+(PED_SECTOR_SIZE_DEFAULT-2)))));
+
+ for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+ leaf_node;
+ leaf_node = PED_BE32_TO_CPU (desc->next)) {
+ if (!hfs_file_read_sector (priv_data->catalog_file,
+ node, leaf_node))
+ return 0;
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = 1; i <= record_number; ++i) {
+ /* undocumented alignement */
+ unsigned int skip;
+ catalog_key = (HfsCatalogKey*) (node + PED_BE16_TO_CPU(
+ *((uint16_t*)(node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
+ skip = (1 + catalog_key->key_length + 1) & ~1;
+ catalog_data = (HfsCatalog*)( ((uint8_t*)catalog_key)
+ + skip );
+ /* check for obvious error in FS */
+ if (((uint8_t*)catalog_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)catalog_data - node
+ >= PED_SECTOR_SIZE_DEFAULT
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ return 0;
+ }
+
+ if (catalog_data->type != HFS_CAT_FILE) continue;
+
+ extent = catalog_data->sel.file.extents_data;
+ for (j = 0; j < HFS_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE16_TO_CPU(extent[j].start_block),
+ PED_BE16_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ 1, /* hfs => btree block = 512 b */
+ CR_BTREE_CAT,
+ j )
+ )
+ return 0;
+ }
+
+ extent = catalog_data->sel.file.extents_res;
+ for (j = 0; j < HFS_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE16_TO_CPU(extent[j].start_block),
+ PED_BE16_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ 1, /* hfs => btree block = 512 b */
+ CR_BTREE_CAT,
+ j )
+ )
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+ HfsHeaderRecord* header;
+ HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node;
+ HfsExtentKey* extent_key;
+ HfsExtDescriptor* extent;
+ unsigned int leaf_node, record_number;
+ unsigned int i, j;
+
+ if (!priv_data->extent_file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This HFS volume has no extents overflow "
+ "file. This is quite unusual!"));
+ return 1;
+ }
+
+ if (!hfs_file_read_sector (priv_data->extent_file, node, 0))
+ return 0;
+ header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(PED_SECTOR_SIZE_DEFAULT-2))))));
+
+ for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+ leaf_node;
+ leaf_node = PED_BE32_TO_CPU (desc->next)) {
+ if (!hfs_file_read_sector (priv_data->extent_file, node,
+ leaf_node))
+ return 0;
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = 1; i <= record_number; i++) {
+ uint8_t where;
+ extent_key = (HfsExtentKey*)
+ (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
+ /* size is cst */
+ extent = (HfsExtDescriptor*)(((uint8_t*)extent_key)
+ + sizeof (HfsExtentKey));
+ /* check for obvious error in FS */
+ if (((uint8_t*)extent_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)extent - node
+ >= PED_SECTOR_SIZE_DEFAULT
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ return 0;
+ }
+
+ switch (extent_key->file_ID) {
+ case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The extents overflow file should not"
+ " contain its own extents! You "
+ "should check the file system."))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ where = CR_BTREE_EXT_EXT;
+ break;
+ case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
+ where = CR_BTREE_EXT_CAT;
+ break;
+ default :
+ where = CR_BTREE_EXT_0;
+ break;
+ }
+
+ for (j = 0; j < HFS_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE16_TO_CPU(extent[j].start_block),
+ PED_BE16_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ 1, /* hfs => btree block = 512 b */
+ where,
+ j )
+ )
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* This function cache every extents start and length stored in any
+ fs structure into the adt defined in cache.[ch]
+ Returns NULL on failure */
+static HfsCPrivateCache*
+hfs_cache_extents(PedFileSystem *fs, PedTimer* timer)
+{
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsCPrivateCache* ret;
+ unsigned int file_number, block_number;
+
+ file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count);
+ block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks);
+ ret = hfsc_new_cache(block_number, file_number);
+ if (!ret) return NULL;
+
+ if (!hfs_cache_from_mdb(ret, fs, timer) ||
+ !hfs_cache_from_catalog(ret, fs, timer) ||
+ !hfs_cache_from_extent(ret, fs, timer)) {
+ ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not cache the file system in memory."));
+ hfsc_delete_cache(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* This function moves file's data to compact used and free space,
+ starting at fblock block */
+/* return 0 on error */
+int
+hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+ PedTimer* timer, unsigned int to_free)
+{
+ PedSector bytes_buff;
+ HfsPrivateFSData* priv_data = (HfsPrivateFSData*)
+ fs->type_specific;
+ HfsMasterDirectoryBlock* mdb = priv_data->mdb;
+ HfsCPrivateCache* cache;
+ unsigned int to_fblock = fblock;
+ unsigned int start = fblock;
+ unsigned int divisor = PED_BE16_TO_CPU (mdb->total_blocks)
+ + 1 - start - to_free;
+ int ret;
+
+ PED_ASSERT (!hfs_block, return 0);
+
+ cache = hfs_cache_extents (fs, timer);
+ if (!cache)
+ return 0;
+
+ /* Calculate the size of the copy buffer :
+ * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
+ * takes the maximum number of HFS blocks so that the buffer
+ * will remain smaller than or equal to BYTES_MAX_BUFF, with
+ * a minimum of 1 HFS block */
+ bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size)
+ * (PedSector) BLOCK_MAX_BUFF;
+ if (bytes_buff > BYTES_MAX_BUFF) {
+ hfs_block_count = BYTES_MAX_BUFF
+ / PED_BE32_TO_CPU (priv_data->mdb->block_size);
+ if (!hfs_block_count)
+ hfs_block_count = 1;
+ bytes_buff = (PedSector) hfs_block_count
+ * PED_BE32_TO_CPU (priv_data->mdb->block_size);
+ } else
+ hfs_block_count = BLOCK_MAX_BUFF;
+
+ /* If the cache code requests more space, give it to him */
+ if (bytes_buff < hfsc_cache_needed_buffer (cache))
+ bytes_buff = hfsc_cache_needed_buffer (cache);
+
+ hfs_block = (uint8_t*) ped_malloc (bytes_buff);
+ if (!hfs_block)
+ goto error_cache;
+
+ if (!hfs_read_bad_blocks (fs)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Bad blocks list could not be loaded."));
+ goto error_alloc;
+ }
+
+ while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) {
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock)
+ && (!hfs_is_bad_block (fs, fblock))) {
+ if (!(ret = hfs_move_extent_starting_at (fs, &fblock,
+ &to_fblock, cache)))
+ to_fblock = ++fblock;
+ else if (ret == -1) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("An error occurred during extent "
+ "relocation."));
+ goto error_alloc;
+ }
+ } else {
+ fblock++;
+ }
+
+ ped_timer_update(timer, (float)(to_fblock - start)/divisor);
+ }
+
+ ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
+ hfsc_delete_cache (cache);
+ return 1;
+
+error_alloc:
+ ped_free (hfs_block); hfs_block = NULL; hfs_block_count = 0;
+error_cache:
+ hfsc_delete_cache (cache);
+ return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.h b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.h
new file mode 100644
index 0000000000..1d7add2a05
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc.h
@@ -0,0 +1,35 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RELOC_H
+#define _RELOC_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfs_update_mdb (PedFileSystem *fs);
+
+int
+hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+ PedTimer* timer, unsigned int to_free);
+
+#endif /* _RELOC_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.c b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.c
new file mode 100644
index 0000000000..ca2a59a1e1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.c
@@ -0,0 +1,945 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DISCOVER_ONLY
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+#include <stdint.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "hfs.h"
+#include "file_plus.h"
+#include "advfs_plus.h"
+#include "cache.h"
+#include "journal.h"
+
+#include "reloc_plus.h"
+
+/* This function moves data of size blocks starting at block *ptr_fblock
+ to block *ptr_to_fblock */
+/* return new start or -1 on failure */
+/* -1 is ok because there can only be 2^32-1 blocks, so the max possible
+ last one is 2^32-2 (and anyway it contains Alternate VH), so
+ -1 (== 2^32-1[2^32]) never represent a valid block */
+static int
+hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock,
+ unsigned int *ptr_to_fblock, unsigned int size)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ unsigned int i, ok = 0;
+ unsigned int next_to_fblock;
+ unsigned int start, stop;
+
+ PED_ASSERT (hfsp_block != NULL, return -1);
+ PED_ASSERT (*ptr_to_fblock <= *ptr_fblock, return -1);
+ /* quiet GCC */
+ next_to_fblock = start = stop = 0;
+
+/*
+ Try to fit the extent AT or _BEFORE_ the wanted place,
+ or then in the gap between dest and source.
+ If failed try to fit the extent after source, for 2 pass relocation
+ The extent is always copied in a non overlapping way
+*/
+
+ /* Backward search */
+ /* 1 pass relocation AT or BEFORE *ptr_to_fblock */
+ if (*ptr_to_fblock != *ptr_fblock) {
+ start = stop = *ptr_fblock < *ptr_to_fblock+size ?
+ *ptr_fblock : *ptr_to_fblock+size;
+ while (start && stop-start != size) {
+ --start;
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start))
+ stop = start;
+ }
+ ok = (stop-start == size);
+ }
+
+ /* Forward search */
+ /* 1 pass relocation in the gap merged with 2 pass reloc after source */
+ if (!ok && *ptr_to_fblock != *ptr_fblock) {
+ start = stop = *ptr_to_fblock+1;
+ while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks)
+ && stop-start != size) {
+ if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop))
+ start = stop + 1;
+ ++stop;
+ }
+ ok = (stop-start == size);
+ }
+
+ /* new non overlapping room has been found ? */
+ if (ok) {
+ /* enough room */
+ PedSector abs_sector;
+ unsigned int ai, j, block;
+ unsigned int block_sz = (PED_BE32_TO_CPU (
+ priv_data->vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT);
+
+ if (stop > *ptr_to_fblock && stop <= *ptr_fblock)
+ /* Fit in the gap */
+ next_to_fblock = stop;
+ else
+ /* Before or after the gap */
+ next_to_fblock = *ptr_to_fblock;
+
+ /* move blocks */
+ for (i = 0; i < size; /*i++*/) {
+ j = size - i; j = (j < hfsp_block_count) ?
+ j : hfsp_block_count ;
+
+ abs_sector = (PedSector) (*ptr_fblock + i) * block_sz;
+ if (!ped_geometry_read (priv_data->plus_geom,
+ hfsp_block, abs_sector,
+ block_sz * j))
+ return -1;
+
+ abs_sector = (PedSector) (start + i) * block_sz;
+ if (!ped_geometry_write (priv_data->plus_geom,
+ hfsp_block, abs_sector,
+ block_sz * j))
+ return -1;
+
+ for (ai = i+j; i < ai; i++) {
+ /* free source block */
+ block = *ptr_fblock + i;
+ CLR_BLOC_OCCUPATION(priv_data->alloc_map,block);
+ SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
+ block/(PED_SECTOR_SIZE_DEFAULT*8));
+
+ /* set dest block */
+ block = start + i;
+ SET_BLOC_OCCUPATION(priv_data->alloc_map,block);
+ SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map,
+ block/(PED_SECTOR_SIZE_DEFAULT*8));
+ }
+ }
+ if (!ped_geometry_sync_fast (priv_data->plus_geom))
+ return -1;
+
+ *ptr_fblock += size;
+ *ptr_to_fblock = next_to_fblock;
+ } else {
+ if (*ptr_fblock != *ptr_to_fblock)
+ /* not enough room */
+ ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("An extent has not been relocated."));
+ start = *ptr_fblock;
+ *ptr_fblock = *ptr_to_fblock = start + size;
+ }
+
+ return start;
+}
+
+/* Returns 0 on error */
+/* 1 on succes */
+int
+hfsplus_update_vh (PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ uint8_t node[PED_SECTOR_SIZE_DEFAULT];
+
+ if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1))
+ return 0;
+ memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader));
+ if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1)
+ || !ped_geometry_write (priv_data->plus_geom, node,
+ priv_data->plus_geom->length - 2, 1)
+ || !ped_geometry_sync_fast (priv_data->plus_geom))
+ return 0;
+ return 1;
+}
+
+static int
+hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src,
+ unsigned int *ptr_dest, HfsCPrivateCache* cache,
+ HfsCPrivateExtent* ref)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPPrivateFile* file;
+ HfsPExtDescriptor* extent;
+ HfsCPrivateExtent* move;
+ int new_start;
+
+ new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest,
+ ref->ext_length);
+
+ if (new_start == -1) return -1;
+
+ if (ref->ext_start != (unsigned) new_start) {
+ switch (ref->where) {
+ /************ VH ************/
+ case CR_PRIM_CAT :
+ priv_data->catalog_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_PRIM;
+ case CR_PRIM_EXT :
+ priv_data->extents_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_PRIM;
+ case CR_PRIM_ATTR :
+ priv_data->attributes_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_PRIM;
+ case CR_PRIM_ALLOC :
+ priv_data->allocation_file
+ ->first[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_PRIM;
+ case CR_PRIM_START :
+ /* No startup file opened */
+ CR_PRIM :
+ extent = ( HfsPExtDescriptor* )
+ ( (uint8_t*)priv_data->vh + ref->ref_offset );
+ extent[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ if (!hfsplus_update_vh(fs))
+ return -1;
+ break;
+
+ /************** BTREE *************/
+ case CR_BTREE_CAT_JIB :
+ if (!hfsj_update_jib(fs, new_start))
+ return -1;
+ goto BTREE_CAT;
+
+ case CR_BTREE_CAT_JL :
+ if (!hfsj_update_jl(fs, new_start))
+ return -1;
+ goto BTREE_CAT;
+
+ BTREE_CAT:
+ case CR_BTREE_CAT :
+ file = priv_data->catalog_file;
+ goto CR_BTREE;
+
+ case CR_BTREE_ATTR :
+ file = priv_data->attributes_file;
+ goto CR_BTREE;
+
+ case CR_BTREE_EXT_ATTR :
+ if (priv_data->attributes_file
+ ->cache[ref->ref_index].start_block
+ == PED_CPU_TO_BE32(ref->ext_start))
+ priv_data->attributes_file
+ ->cache[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_BTREE_EXT;
+ case CR_BTREE_EXT_CAT :
+ if (priv_data->catalog_file
+ ->cache[ref->ref_index].start_block
+ == PED_CPU_TO_BE32(ref->ext_start))
+ priv_data->catalog_file
+ ->cache[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_BTREE_EXT;
+ case CR_BTREE_EXT_ALLOC :
+ if (priv_data->allocation_file
+ ->cache[ref->ref_index].start_block
+ == PED_CPU_TO_BE32(ref->ext_start))
+ priv_data->allocation_file
+ ->cache[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ goto CR_BTREE_EXT;
+ case CR_BTREE_EXT_START :
+ /* No startup file opened */
+ CR_BTREE_EXT :
+ case CR_BTREE_EXT_0 :
+ file = priv_data->extents_file;
+
+ CR_BTREE :
+ PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block
+ > ref->ref_offset, return -1 );
+ if (!hfsplus_file_read(file, hfsp_block,
+ (PedSector)ref->ref_block * ref->sect_by_block,
+ ref->sect_by_block))
+ return -1;
+ extent = ( HfsPExtDescriptor* )
+ ( hfsp_block + ref->ref_offset );
+ extent[ref->ref_index].start_block =
+ PED_CPU_TO_BE32(new_start);
+ if (!hfsplus_file_write(file, hfsp_block,
+ (PedSector)ref->ref_block * ref->sect_by_block,
+ ref->sect_by_block)
+ || !ped_geometry_sync_fast (priv_data->plus_geom))
+ return -1;
+ break;
+
+ /********** BUG *********/
+ default :
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("A reference to an extent comes from a place "
+ "it should not. You should check the file "
+ "system!"));
+ return -1;
+ break;
+ }
+
+ move = hfsc_cache_move_extent(cache, ref->ext_start, new_start);
+ if (!move) return -1;
+ PED_ASSERT(move == ref, return -1);
+ }
+
+ return new_start;
+}
+
+/* save any dirty sector of the allocation bitmap file */
+static int
+hfsplus_save_allocation(PedFileSystem *fs)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ unsigned int map_sectors, i, j;
+ int ret = 1;
+
+ map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks)
+ + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8);
+
+ for (i = 0; i < map_sectors;) {
+ for (j = i;
+ (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j));
+ ++j)
+ CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j);
+ if (j-i) {
+ ret = hfsplus_file_write(priv_data->allocation_file,
+ priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT,
+ i, j-i) && ret;
+ i = j;
+ } else
+ ++i;
+ }
+
+ return ret;
+}
+
+/* This function moves an extent starting at block fblock
+ to block to_fblock if there's enough room */
+/* Return 1 if everything was fine */
+/* Return -1 if an error occurred */
+/* Return 0 if no extent was found */
+static int
+hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock,
+ unsigned int *ptr_to_fblock,
+ HfsCPrivateCache* cache)
+{
+ HfsCPrivateExtent* ref;
+ unsigned int old_start, new_start;
+
+ ref = hfsc_cache_search_extent(cache, *ptr_fblock);
+ if (!ref) return 0;
+
+ old_start = *ptr_fblock;
+ new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref);
+ if (new_start == (unsigned)-1) return -1;
+ if (new_start > old_start) {
+ new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock,
+ cache, ref);
+ if (new_start == (unsigned)-1 || new_start > old_start)
+ return -1;
+ }
+
+ hfsplus_save_allocation(fs);
+ return 1;
+}
+
+static int
+hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPExtDescriptor* extent;
+ unsigned int j;
+
+ extent = priv_data->vh->allocation_file.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ 0, /* unused for vh */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+ 1, /* load / save 1 sector */
+ CR_PRIM_ALLOC,
+ j )
+ )
+ return 0;
+ }
+
+ extent = priv_data->vh->extents_file.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ 0, /* unused for vh */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+ 1, /* load / save 1 sector */
+ CR_PRIM_EXT,
+ j )
+ )
+ return 0;
+ }
+
+ extent = priv_data->vh->catalog_file.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ 0, /* unused for vh */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+ 1, /* load / save 1 sector */
+ CR_PRIM_CAT,
+ j )
+ )
+ return 0;
+ }
+
+ extent = priv_data->vh->attributes_file.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ 0, /* unused for vh */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+ 1, /* load / save 1 sector */
+ CR_PRIM_ATTR,
+ j )
+ )
+ return 0;
+ }
+
+ extent = priv_data->vh->startup_file.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ 0, /* unused for vh */
+ ((uint8_t*)extent) - ((uint8_t*)priv_data->vh),
+ 1, /* load / save 1 sector */
+ CR_PRIM_START,
+ j )
+ )
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
+ uint8_t* node;
+ HfsPHeaderRecord* header;
+ HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1;
+ HfsPCatalogKey* catalog_key;
+ HfsPCatalog* catalog_data;
+ HfsPExtDescriptor* extent;
+ unsigned int leaf_node, record_number;
+ unsigned int i, j, size, bsize;
+ uint32_t jib = priv_data->jib_start_block,
+ jl = priv_data->jl_start_block;
+
+ if (!priv_data->catalog_file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This HFS+ volume has no catalog file. "
+ "This is very unusual!"));
+ return 1;
+ }
+
+ /* Search the extent starting at *ptr_block in the catalog file */
+ if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0))
+ return 0;
+ header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC);
+ leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+ bsize = PED_BE16_TO_CPU (header->node_size);
+ size = bsize / PED_SECTOR_SIZE_DEFAULT;
+ PED_ASSERT(size < 256, return 0);
+
+ node = (uint8_t*) ped_malloc(bsize);
+ if (!node) return 0;
+ desc = (HfsPNodeDescriptor*) node;
+
+ for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+ if (!hfsplus_file_read (priv_data->catalog_file, node,
+ (PedSector) leaf_node * size, size)) {
+ ped_free (node);
+ return 0;
+ }
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = 1; i <= record_number; i++) {
+ unsigned int skip;
+ uint8_t where;
+
+ catalog_key = (HfsPCatalogKey*)
+ ( node + PED_BE16_TO_CPU (*((uint16_t *)
+ (node+(bsize - 2*i)))) );
+ skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length)
+ + 1) & ~1;
+ catalog_data = (HfsPCatalog*)
+ (((uint8_t*)catalog_key) + skip);
+ /* check for obvious error in FS */
+ if (((uint8_t*)catalog_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)catalog_data - node
+ >= (signed) bsize
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ ped_free (node);
+ return 0;
+ }
+
+ if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE)
+ continue;
+
+ extent = catalog_data->sel.file.data_fork.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ where = CR_BTREE_CAT;
+ if ( PED_BE32_TO_CPU(extent[j].start_block)
+ == jib ) {
+ jib = 0;
+ where = CR_BTREE_CAT_JIB;
+ } else
+ if ( PED_BE32_TO_CPU(extent[j].start_block)
+ == jl ) {
+ jl = 0;
+ where = CR_BTREE_CAT_JL;
+ }
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ size,
+ where,
+ j )
+ ) {
+ ped_free (node);
+ return 0;
+ }
+ }
+
+ extent = catalog_data->sel.file.res_fork.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ size,
+ CR_BTREE_CAT,
+ j )
+ ) {
+ ped_free (node);
+ return 0;
+ }
+ }
+ }
+ }
+
+ ped_free (node);
+ return 1;
+}
+
+static int
+hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
+ uint8_t* node;
+ HfsPHeaderRecord* header;
+ HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1;
+ HfsPExtentKey* extent_key;
+ HfsPExtDescriptor* extent;
+ unsigned int leaf_node, record_number;
+ unsigned int i, j, size, bsize;
+
+ if (!priv_data->extents_file->sect_nb) {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ _("This HFS+ volume has no extents overflow "
+ "file. This is quite unusual!"));
+ return 1;
+ }
+
+ if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0))
+ return 0;
+ header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
+ leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+ bsize = PED_BE16_TO_CPU (header->node_size);
+ size = bsize / PED_SECTOR_SIZE_DEFAULT;
+ PED_ASSERT(size < 256, return 0);
+
+ node = (uint8_t*) ped_malloc (bsize);
+ if (!node) return -1;
+ desc = (HfsPNodeDescriptor*) node;
+
+ for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+ if (!hfsplus_file_read (priv_data->extents_file, node,
+ (PedSector) leaf_node * size, size)) {
+ ped_free (node);
+ return 0;
+ }
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = 1; i <= record_number; i++) {
+ uint8_t where;
+ extent_key = (HfsPExtentKey*)
+ (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(bsize - 2*i)))));
+ extent = (HfsPExtDescriptor*)
+ (((uint8_t*)extent_key) + sizeof (HfsPExtentKey));
+ /* check for obvious error in FS */
+ if (((uint8_t*)extent_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)extent - node
+ >= (signed)bsize
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ ped_free (node);
+ return -1;
+ }
+
+ switch (extent_key->file_ID) {
+ case PED_CPU_TO_BE32 (HFS_XTENT_ID) :
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The extents overflow file should not"
+ " contain its own extents! You should "
+ "check the file system."))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ where = CR_BTREE_EXT_EXT;
+ break;
+ case PED_CPU_TO_BE32 (HFS_CATALOG_ID) :
+ where = CR_BTREE_EXT_CAT;
+ break;
+ case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) :
+ where = CR_BTREE_EXT_ALLOC;
+ break;
+ case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) :
+ where = CR_BTREE_EXT_START;
+ break;
+ case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) :
+ where = CR_BTREE_EXT_ATTR;
+ break;
+ default :
+ where = CR_BTREE_EXT_0;
+ break;
+ }
+
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU(extent[j].start_block),
+ PED_BE32_TO_CPU(extent[j].block_count),
+ leaf_node,
+ (uint8_t*)extent - node,
+ size,
+ where,
+ j )
+ ) {
+ ped_free (node);
+ return 0;
+ }
+ }
+ }
+ }
+
+ ped_free (node);
+ return 1;
+}
+
+static int
+hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs,
+ PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ uint8_t node_1[PED_SECTOR_SIZE_DEFAULT];
+ uint8_t* node;
+ HfsPHeaderRecord* header;
+ HfsPNodeDescriptor* desc = (HfsPNodeDescriptor*) node_1;
+ HfsPPrivateGenericKey* generic_key;
+ HfsPForkDataAttr* fork_ext_data;
+ HfsPExtDescriptor* extent;
+ unsigned int leaf_node, record_number;
+ unsigned int i, j, size, bsize;
+
+ /* attributes file is facultative */
+ if (!priv_data->attributes_file->sect_nb)
+ return 1;
+
+ /* Search the extent starting at *ptr_block in the catalog file */
+ if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0))
+ return 0;
+ header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC));
+ leaf_node = PED_BE32_TO_CPU (header->first_leaf_node);
+ bsize = PED_BE16_TO_CPU (header->node_size);
+ size = bsize / PED_SECTOR_SIZE_DEFAULT;
+ PED_ASSERT(size < 256, return 0);
+
+ node = (uint8_t*) ped_malloc(bsize);
+ if (!node) return 0;
+ desc = (HfsPNodeDescriptor*) node;
+
+ for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) {
+ if (!hfsplus_file_read (priv_data->attributes_file, node,
+ (PedSector) leaf_node * size, size)) {
+ ped_free (node);
+ return 0;
+ }
+ record_number = PED_BE16_TO_CPU (desc->rec_nb);
+ for (i = 1; i <= record_number; i++) {
+ unsigned int skip;
+ generic_key = (HfsPPrivateGenericKey*)
+ (node + PED_BE16_TO_CPU(*((uint16_t *)
+ (node+(bsize - 2*i)))));
+ skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length)
+ + 1 ) & ~1;
+ fork_ext_data = (HfsPForkDataAttr*)
+ (((uint8_t*)generic_key) + skip);
+ /* check for obvious error in FS */
+ if (((uint8_t*)generic_key - node < HFS_FIRST_REC)
+ || ((uint8_t*)fork_ext_data - node
+ >= (signed) bsize
+ - 2 * (signed)(record_number+1))) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system contains errors."));
+ ped_free (node);
+ return 0;
+ }
+
+ if (fork_ext_data->record_type
+ == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) {
+ extent = fork_ext_data->fork_res.fork.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU (
+ extent[j].start_block ),
+ PED_BE32_TO_CPU (
+ extent[j].block_count ),
+ leaf_node,
+ (uint8_t*)extent-node,
+ size,
+ CR_BTREE_ATTR,
+ j )
+ ) {
+ ped_free(node);
+ return 0;
+ }
+ }
+ } else if (fork_ext_data->record_type
+ == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) {
+ extent = fork_ext_data->fork_res.extents;
+ for (j = 0; j < HFSP_EXT_NB; ++j) {
+ if (!extent[j].block_count) break;
+ if (!hfsc_cache_add_extent(
+ cache,
+ PED_BE32_TO_CPU (
+ extent[j].start_block ),
+ PED_BE32_TO_CPU (
+ extent[j].block_count ),
+ leaf_node,
+ (uint8_t*)extent-node,
+ size,
+ CR_BTREE_ATTR,
+ j )
+ ) {
+ ped_free(node);
+ return 0;
+ }
+ }
+ } else continue;
+ }
+ }
+
+ ped_free (node);
+ return 1;
+}
+
+static HfsCPrivateCache*
+hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer)
+{
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsCPrivateCache* ret;
+ unsigned int file_number, block_number;
+
+ file_number = PED_BE32_TO_CPU(priv_data->vh->file_count);
+ block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks);
+ ret = hfsc_new_cache(block_number, file_number);
+ if (!ret) return NULL;
+
+ if (!hfsplus_cache_from_vh(ret, fs, timer) ||
+ !hfsplus_cache_from_catalog(ret, fs, timer) ||
+ !hfsplus_cache_from_extent(ret, fs, timer) ||
+ !hfsplus_cache_from_attributes(ret, fs, timer)) {
+ ped_exception_throw(
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Could not cache the file system in memory."));
+ hfsc_delete_cache(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* This function moves file's data to compact used and free space,
+ starting at fblock block */
+/* return 0 on error */
+int
+hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+ PedTimer* timer, unsigned int to_free)
+{
+ PedSector bytes_buff;
+ HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
+ fs->type_specific;
+ HfsPVolumeHeader* vh = priv_data->vh;
+ HfsCPrivateCache* cache;
+ unsigned int to_fblock = fblock;
+ unsigned int start = fblock;
+ unsigned int divisor = PED_BE32_TO_CPU (vh->total_blocks)
+ + 1 - start - to_free;
+ int ret;
+
+ PED_ASSERT (!hfsp_block, return 0);
+
+ cache = hfsplus_cache_extents (fs, timer);
+ if (!cache)
+ return 0;
+
+ /* Calculate the size of the copy buffer :
+ * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF
+ * takes the maximum number of HFS blocks so that the buffer
+ * will remain smaller than or equal to BYTES_MAX_BUFF, with
+ * a minimum of 1 HFS block */
+ bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size)
+ * (PedSector) BLOCK_MAX_BUFF;
+ if (bytes_buff > BYTES_MAX_BUFF) {
+ hfsp_block_count = BYTES_MAX_BUFF
+ / PED_BE32_TO_CPU (priv_data->vh->block_size);
+ if (!hfsp_block_count)
+ hfsp_block_count = 1;
+ bytes_buff = (PedSector) hfsp_block_count
+ * PED_BE32_TO_CPU (priv_data->vh->block_size);
+ } else
+ hfsp_block_count = BLOCK_MAX_BUFF;
+
+ /* If the cache code requests more space, give it to him */
+ if (bytes_buff < hfsc_cache_needed_buffer (cache))
+ bytes_buff = hfsc_cache_needed_buffer (cache);
+
+ hfsp_block = (uint8_t*) ped_malloc (bytes_buff);
+ if (!hfsp_block)
+ goto error_cache;
+
+ if (!hfsplus_read_bad_blocks (fs)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Bad blocks list could not be loaded."));
+ goto error_alloc;
+ }
+
+ while ( fblock < ( priv_data->plus_geom->length - 2 )
+ / ( PED_BE32_TO_CPU (vh->block_size)
+ / PED_SECTOR_SIZE_DEFAULT ) ) {
+ if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock)
+ && (!hfsplus_is_bad_block (fs, fblock))) {
+ if (!(ret = hfsplus_move_extent_starting_at (fs,
+ &fblock, &to_fblock, cache)))
+ to_fblock = ++fblock;
+ else if (ret == -1) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("An error occurred during extent "
+ "relocation."));
+ goto error_alloc;
+ }
+ } else {
+ fblock++;
+ }
+
+ ped_timer_update(timer, (float)(to_fblock - start) / divisor);
+ }
+
+ ped_free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
+ hfsc_delete_cache (cache);
+ return 1;
+
+error_alloc:
+ ped_free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0;
+error_cache:
+ hfsc_delete_cache (cache);
+ return 0;
+}
+
+#endif /* !DISCOVER_ONLY */
diff --git a/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.h b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.h
new file mode 100644
index 0000000000..b764bab943
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/hfs/reloc_plus.h
@@ -0,0 +1,36 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RELOC_PLUS_H
+#define _RELOC_PLUS_H
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#include "hfs.h"
+
+int
+hfsplus_update_vh (PedFileSystem *fs);
+
+int
+hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock,
+ PedTimer* timer, unsigned int to_free);
+
+
+#endif /* _RELOC_PLUS_H */
diff --git a/usr/src/lib/libparted/common/libparted/fs/jfs/jfs.c b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs.c
new file mode 100644
index 0000000000..b01d18a6d3
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs.c
@@ -0,0 +1,109 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+
+#define _JFS_UTILITY
+#include "jfs_types.h"
+#include "jfs_superblock.h"
+
+#define JFS_SUPER_SECTOR 64
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define JFS_BLOCK_SIZES ((int[2]){512, 0})
+
+static PedGeometry*
+jfs_probe (PedGeometry* geom)
+{
+ union {
+ struct superblock sb;
+ char bytes[512];
+ } buf;
+
+ if (geom->length < JFS_SUPER_SECTOR + 1)
+ return NULL;
+ if (!ped_geometry_read (geom, &buf, JFS_SUPER_SECTOR, 1))
+ return NULL;
+
+ if (strncmp (buf.sb.s_magic, JFS_MAGIC, 4) == 0) {
+ PedSector block_size = PED_LE32_TO_CPU (buf.sb.s_pbsize) / 512;
+ PedSector block_count = PED_LE64_TO_CPU (buf.sb.s_size);
+
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ } else {
+ return NULL;
+ }
+}
+
+#ifndef DISCOVER_ONLY
+static int
+jfs_clobber (PedGeometry* geom)
+{
+ char buf[512];
+
+ memset (buf, 0, 512);
+ return ped_geometry_write (geom, buf, JFS_SUPER_SECTOR, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps jfs_ops = {
+ .probe = jfs_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = jfs_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemType jfs_type = {
+ .next = NULL,
+ .ops = &jfs_ops,
+ .name = "jfs",
+ .block_sizes = JFS_BLOCK_SIZES
+};
+
+void
+ped_file_system_jfs_init ()
+{
+ ped_file_system_type_register (&jfs_type);
+}
+
+void
+ped_file_system_jfs_done ()
+{
+ ped_file_system_type_unregister (&jfs_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_superblock.h b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_superblock.h
new file mode 100644
index 0000000000..71fe3f10ac
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_superblock.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef _H_JFS_SUPERBLOCK
+#define _H_JFS_SUPERBLOCK
+/*
+ * jfs_superblock.h
+ */
+
+/*
+ * make the magic number something a human could read
+ */
+#define JFS_MAGIC "JFS1" /* Magic word: Version 1 */
+
+#define JFS_VERSION 1 /* Version number: Version 1 */
+
+#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.
+ */
+#ifdef _JFS_UTILITY
+struct superblock
+#else
+struct jfs_superblock
+#endif
+{
+ 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 file system 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
+ */
+
+ /* 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 - */
+
+ /*
+ * DFS VFS support (preliminary)
+ */
+ char s_attach; /* 1: VFS: flag: set when aggregate is attached
+ */
+ u8 rsrvd4[7]; /* 7: reserved - set to 0 */
+
+ u64 totalUsable; /* 8: VFS: total of 1K blocks which are
+ * available to "normal" (non-root) users.
+ */
+ u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for
+ * exclusive use of root. This value can be 0,
+ * and if it is then totalUsable will be equal
+ * to # of blocks in aggregate. I believe this
+ * means that minFree + totalUsable = # blocks.
+ * In that case, we don't need to store both
+ * totalUsable and minFree since we can compute
+ * one from the other. I would guess minFree
+ * would be the one we should store, and
+ * totalUsable would be the one we should
+ * compute. (Just a guess...)
+ */
+
+ u64 realFree; /* 8: VFS: # of free 1K blocks can be used by
+ * "normal" users. It may be this is something
+ * we should compute when asked for instead of
+ * storing in the superblock. I don't know how
+ * often this information is needed.
+ */
+ /*
+ * graffiti area
+ */
+};
+
+#endif /*_H_JFS_SUPERBLOCK */
diff --git a/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_types.h b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_types.h
new file mode 100644
index 0000000000..ca865bb3e8
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/jfs/jfs_types.h
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) International Business Machines Corp., 2000
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#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.
+ */
+
+#ifdef _JFS_UTILITY
+/* this is defined in asm/byteorder.h for i386, but
+ * is NOT defined in asm/byteorder.h for ppc (non-kernel).
+ * Until that is changed, we'll define it here. */
+#define __BYTEORDER_HAS_U64__
+
+#include <sys/types.h>
+//#include <asm/byteorder.h>
+typedef unsigned short UniChar;
+#else
+#include <linux/types.h>
+#include <linux/jfs_fs.h>
+#include <linux/nls.h>
+
+#ifndef _ULS_UNICHAR_DEFINED
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0))
+typedef wchar_t UniChar;
+#else
+typedef unsigned short UniChar;
+#endif
+#define _ULS_UNICHAR_DEFINED
+#endif
+#endif
+/* #include "endian24.h" */
+
+/*
+ * primitive types
+ */
+#ifdef _JFS_UTILITY
+typedef int8_t s8;
+typedef uint8_t u8;
+typedef int16_t s16;
+typedef uint16_t u16;
+typedef int32_t s32;
+typedef uint32_t u32;
+typedef int64_t s64;
+typedef uint64_t u64;
+
+#ifndef _UINT_TYPES
+ /* unicode includes also define these */
+typedef u16 uint16;
+typedef u32 uint32;
+#define _UINT_TYPES
+#endif
+
+typedef s8 int8;
+typedef u8 uint8;
+typedef s16 int16;
+typedef s32 int32;
+typedef s64 int64;
+typedef u64 uint64;
+
+#endif /* _JFS_UTILITY */
+/*
+ * Holdovers from OS/2. Try to get away from using these altogether.
+ */
+typedef unsigned long ULONG;
+typedef unsigned short USHORT;
+typedef unsigned char UCHAR;
+typedef void *PVOID;
+#define MAXPATHLEN 255
+
+
+/*
+ * Almost identical to Linux's timespec, but not quite
+ */
+struct timestruc_t {
+ u32 tv_sec;
+ u32 tv_nsec;
+};
+
+/*
+ * handy
+ */
+#undef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#undef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#undef ROUNDUP
+#define ROUNDUP(x, y) ( ((x) + ((y) - 1)) & ~((y) - 1) )
+
+#define LEFTMOSTONE 0x80000000
+#define HIGHORDER 0x80000000u /* high order bit on */
+#define ONES 0xffffffffu /* all bit on */
+
+#if !defined(__sun)
+typedef int boolean_t;
+#endif
+
+#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 */
+typedef struct {
+ s16 maxnlxd;
+ s16 nlxd;
+ lxd_t *lxd;
+} lxdlist_t;
+
+/*
+ * 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))
+
+/* pxd list */
+typedef struct {
+ s16 maxnpxd;
+ s16 npxd;
+ pxd_t pxd[8];
+} pxdlist_t;
+
+
+/*
+ * 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
+
+/*
+ * directory entry argument
+ */
+typedef struct component_name {
+ int namlen;
+ UniChar *name;
+} component_t;
+
+
+/*
+ * DASD limit information - stored in directory inode
+ */
+typedef 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) */
+} dasd_t;
+
+#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);\
+}
+
+/*
+ * circular doubly-linked list (cdll)
+ *
+ * A circular doubly-linked list (cdll) is anchored by a pair of pointers,
+ * one to the head of the list and the other to the tail of the list.
+ * The elements are doubly linked so that an arbitrary element can be
+ * removed without a need to traverse the list.
+ * New elements can be added to the list before or after an existing element,
+ * at the head of the list, or at the tail of the list.
+ * A circle queue may be traversed in either direction.
+ *
+ * +----------+ +-------------------------------------+
+ * | | | |
+ * +->+-----+ | +->+-----+ +->+-----+ +->+-----+ |
+ * | | h +-+ | | h +--+ | n +----+ | n +--+
+ * | +-----+ | +-----+ | +-----+ | +-----+
+ * | | t +-+ +-----+ t | | | p +--+ | | p +--+
+ * | +-----+ | | | +-----+ | +-----+ | | +-----+ |
+ * +----------+ | +-----------------------+ | |
+ * | | | |
+ * | +-------------------------+
+ * | |
+ * +----------------------------+
+ */
+/*
+ * define header
+ *
+ * list header field definition in header element:
+ *
+ * type - type of list element struct embedding the link field
+ */
+#define CDLL_HEADER(type)\
+struct {\
+ struct type *head;\
+ struct type *tail;\
+}
+
+struct cdll_header {
+ struct cdll_header *head;
+ struct cdll_header *tail;
+};
+
+/*
+ * define link
+ *
+ * list link field definition in list element:
+ *
+ * type - type of parent list element struct embedding the link field
+ */
+#define CDLL_ENTRY(type)\
+struct {\
+ struct type *next;\
+ struct type *prev;\
+}
+
+struct cdll_entry {
+ struct cdll_entry *next;
+ struct cdll_entry *prev;
+};
+
+/*
+ * initialize header
+ *
+ * header - ptr to the header field in the header element
+ */
+#define CDLL_INIT(header) {\
+ (header)->head = (void *)(header);\
+ (header)->tail = (void *)(header);\
+}
+
+/*
+ * scan list
+ *
+ * header - ptr to the header field in the header element
+ * elm - ptr to the element to be inserted
+ * field - name of the link field in the list element
+ *
+ * struct header_container *container;
+ * struct header_type *header;
+ * struct element_type *elm;
+ *
+ * header = &container->header_field;
+ * for (elm = header->head; elm != (void *)header; elm = elm->field.next)
+ */
+
+/*
+ * insert <elm> at head of list anchored at <header>
+ *
+ * header - ptr to the header field in the header element
+ * elm - ptr to the list element to be inserted
+ * field - name of the link field in the list element
+ */
+#define CDLL_INSERT_HEAD(header, elm, field) {\
+ (elm)->field.next = (header)->head;\
+ (elm)->field.prev = (void *)(header);\
+ if ((header)->tail == (void *)(header))\
+ (header)->tail = (elm);\
+ else\
+ (header)->head->field.prev = (elm);\
+ (header)->head = (elm);\
+}
+
+/*
+ * insert <elm> at tail of list anchored at <header>
+ *
+ * header - ptr to the header field in the header element
+ * elm - ptr to the list element to be inserted
+ * field - name of the link field in the list element
+ */
+#define CDLL_INSERT_TAIL(header, elm, field) {\
+ (elm)->field.next = (void *)(header);\
+ (elm)->field.prev = (header)->tail;\
+ if ((header)->head == (void *)(header))\
+ (header)->head = (elm);\
+ else\
+ (header)->tail->field.next = (elm);\
+ (header)->tail = (elm);\
+}
+
+/*
+ * insert <elm> after <listelm> of list anchored at <header>
+ *
+ * header - ptr to the header field in the header element
+ * listelm - ptr to the list element at insertion point
+ * elm - ptr to the list element to be inserted
+ * field - name of the link field in the list element
+ */
+#define CDLL_INSERT_AFTER(header, listelm, elm, field) {\
+ (elm)->field.next = (listelm)->field.next;\
+ (elm)->field.prev = (listelm);\
+ if ((listelm)->field.next == (void *)(header))\
+ (header)->tail = (elm);\
+ else\
+ (listelm)->field.next->field.prev = (elm);\
+ (listelm)->field.next = (elm);\
+}
+
+/*
+ * insert <elm> before <listelm> of list anchored at <header>
+ *
+ * header - ptr to the header field in the header element
+ * listelm - ptr to list element at insertion point
+ * elm - ptr to the element to be inserted
+ * field - name of the link field in the list element
+ */
+#define CDLL_INSERT_BEFORE(header, listelm, elm, field) {\
+ (elm)->field.next = (listelm);\
+ (elm)->field.prev = (listelm)->field.prev;\
+ if ((listelm)->field.prev == (void *)(header))\
+ (header)->head = (elm);\
+ else\
+ (listelm)->field.prev->field.next = (elm);\
+ (listelm)->field.prev = (elm);\
+}
+
+/*
+ * remove <elm> from list anchored at <header>
+ *
+ * header - ptr to the header field in the header element
+ * elm - ptr to the list element to be removed
+ * field - name of the link field in the list element
+ */
+#define CDLL_REMOVE(header, elm, field) {\
+ if ((elm)->field.next == (void *)(header))\
+ (header)->tail = (elm)->field.prev;\
+ else\
+ (elm)->field.next->field.prev = (elm)->field.prev;\
+ if ((elm)->field.prev == (void *)(header))\
+ (header)->head = (elm)->field.next;\
+ else\
+ (elm)->field.prev->field.next = (elm)->field.next;\
+}
+
+#define CDLL_MOVE_TO_HEAD(header, elm, field) {\
+ if ((elm)->field.prev != (void *)(header))\
+ {\
+ if ((elm)->field.next == (void *)(header))\
+ (header)->tail = (elm)->field.prev;\
+ else\
+ (elm)->field.next->field.prev = (elm)->field.prev;\
+ (elm)->field.prev->field.next = (elm)->field.next;\
+ (elm)->field.next = (header)->head;\
+ (elm)->field.prev = (void *)(header);\
+ (header)->head->field.prev = (elm);\
+ (header)->head = (elm);\
+ }\
+}
+
+#define CDLL_MOVE_TO_TAIL(header, elm, field) {\
+ if ((elm)->field.next != (void *)(header))\
+ {\
+ (elm)->field.next->field.prev = (elm)->field.prev;\
+ if ((elm)->field.prev == (void *)(header))\
+ (header)->head = (elm)->field.next;\
+ else\
+ (elm)->field.prev->field.next = (elm)->field.next;\
+ (elm)->field.next = (void *)(header);\
+ (elm)->field.prev = (header)->tail;\
+ (header)->tail->field.next = (elm);\
+ (header)->tail = (elm);\
+ }\
+}
+
+/*
+ * orphan list element
+ */
+#define CDLL_SELF(elm, field)\
+ (elm)->field.next = (elm)->field.prev = (elm);
+
+
+/*
+ * single head doubly-linked list
+ *
+ * A list is headed by a single head pointer.
+ * The elements are doubly linked so that an arbitrary element can be
+ * removed without a need to traverse the list.
+ * New elements can be added to the list at the head of the list, or
+ * after an existing element (NO insert at tail).
+ * A list may only be traversed in the forward direction.
+ * (note: the list is NULL terminated in next field.)
+ *
+ * +-----+ +->+-----+ +->+-----+ +->+-----+
+ * | NULL| | | h +--+ | n +----+ | NULL|
+ * +-----+ | +-----+ | +-----+ +-----+
+ * | | | p +--+ | p +--+
+ * | | +-----+ | +-----+ |
+ * +-----------------------+ |
+ * | |
+ * +-------------------------+
+ */
+#define LIST_HEADER(type)\
+struct {\
+ struct type *head;\
+}
+
+#define LIST_ENTRY(type)\
+struct {\
+ struct type *next;\
+ struct type **prev;\
+}
+
+#define LIST_INIT(header) { (header)->head = NULL; }
+
+/*
+ * scan list
+ *
+ * header - ptr to the header (field in header element)
+ * elm - ptr to the element to be inserted
+ * field - name of the link field in list element
+ *
+ * struct header_container *container;
+ * struct header_type *header;
+ * struct element_type *elm;
+ *
+ * header = &container->header_field;
+ * for (elm = header->head; elm; elm = elm->field.next)
+ */
+
+#define LIST_INSERT_HEAD(header, elm, field) {\
+ if (((elm)->field.next = (header)->head) != NULL)\
+ (header)->head->field.prev = &(elm)->field.next;\
+ (header)->head = (elm);\
+ (elm)->field.prev = &(header)->head;\
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) {\
+ if (((elm)->field.next = (listelm)->field.next) != NULL)\
+ (listelm)->field.next->field.prev = &(elm)->field.next;\
+ (listelm)->field.next = (elm);\
+ (elm)->field.prev = &(listelm)->field.next;\
+}
+
+#define LIST_REMOVE(elm, field) {\
+ if ((elm)->field.next != NULL)\
+ (elm)->field.next->field.prev = (elm)->field.prev;\
+ *(elm)->field.prev = (elm)->field.next;\
+}
+
+#define LIST_SELF(elm, field) {\
+ (elm)->field.next = NULL;\
+ (elm)->field.prev = &(elm)->field.next;\
+}
+
+#endif /* !_H_JFS_TYPES */
diff --git a/usr/src/lib/libparted/common/libparted/fs/linux_swap/linux_swap.c b/usr/src/lib/libparted/common/libparted/fs/linux_swap/linux_swap.c
new file mode 100644
index 0000000000..5aa81e6c47
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/linux_swap/linux_swap.c
@@ -0,0 +1,522 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* It's a bit silly calling a swap partition a file system. Oh well... */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <unistd.h>
+
+#define SWAP_SPECIFIC(fs) ((SwapSpecific*) (fs->type_specific))
+#define BUFFER_SIZE 128
+
+#define LINUXSWAP_BLOCK_SIZES ((int[2]){512, 0})
+
+typedef struct {
+ char page_map[1];
+} SwapOldHeader;
+
+/* ripped from mkswap */
+typedef struct {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ uint32_t version;
+ uint32_t last_page;
+ uint32_t nr_badpages;
+ unsigned char sws_uuid[16];
+ unsigned char sws_volume[16];
+ uint32_t padding[117];
+ uint32_t badpages[1];
+} SwapNewHeader;
+
+typedef struct {
+ union {
+ SwapNewHeader new;
+ SwapOldHeader old;
+ }* header;
+
+ void* buffer;
+ int buffer_size;
+
+ PedSector page_sectors;
+ unsigned int page_count;
+ unsigned int version;
+ unsigned int max_bad_pages;
+} SwapSpecific;
+
+static PedFileSystemType swap_type;
+
+static PedFileSystem* swap_open (PedGeometry* geom);
+static int swap_close (PedFileSystem* fs);
+
+static PedGeometry*
+swap_probe (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ PedGeometry* probed_geom;
+ PedSector length;
+
+ fs = swap_open (geom);
+ if (!fs)
+ goto error;
+ fs_info = SWAP_SPECIFIC (fs);
+
+ if (fs_info->version)
+ length = fs_info->page_sectors * fs_info->page_count;
+ else
+ length = geom->length;
+ probed_geom = ped_geometry_new (geom->dev, geom->start, length);
+ if (!probed_geom)
+ goto error_close_fs;
+ swap_close (fs);
+ return probed_geom;
+
+error_close_fs:
+ swap_close (fs);
+error:
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+swap_clobber (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ char buf[512];
+
+ fs = swap_open (geom);
+ if (!fs)
+ return 1;
+
+ memset (buf, 0, 512);
+ if (!ped_geometry_write (geom, buf, getpagesize() / 512 - 1, 1))
+ goto error_close_fs;
+
+ swap_close (fs);
+ return 1;
+
+error_close_fs:
+ swap_close (fs);
+
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static void
+swap_init (PedFileSystem* fs, int fresh)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+
+ fs_info->page_sectors = getpagesize () / 512;
+ fs_info->page_count = fs->geom->length / fs_info->page_sectors;
+ fs_info->version = 1;
+ fs_info->max_bad_pages = (getpagesize()
+ - sizeof (SwapNewHeader)) / 4;
+
+ if (fresh)
+ memset (fs_info->header, 0, getpagesize());
+ else
+ ped_geometry_read (fs->geom, fs_info->header,
+ 0, fs_info->page_sectors);
+}
+
+static PedFileSystem*
+swap_alloc (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs)
+ goto error;
+
+ fs->type_specific = (SwapSpecific*) ped_malloc (sizeof (SwapSpecific));
+ if (!fs->type_specific)
+ goto error_free_fs;
+
+ fs_info = SWAP_SPECIFIC (fs);
+ fs_info->header = ped_malloc (getpagesize());
+ if (!fs_info->header)
+ goto error_free_type_specific;
+
+ fs_info = SWAP_SPECIFIC (fs);
+ fs_info->buffer_size = getpagesize() * BUFFER_SIZE;
+ fs_info->buffer = ped_malloc (fs_info->buffer_size);
+ if (!fs_info->buffer)
+ goto error_free_header;
+
+ fs->geom = ped_geometry_duplicate (geom);
+ if (!fs->geom)
+ goto error_free_buffer;
+ fs->type = &swap_type;
+ return fs;
+
+error_free_buffer:
+ ped_free (fs_info->buffer);
+error_free_header:
+ ped_free (fs_info->header);
+error_free_type_specific:
+ ped_free (fs->type_specific);
+error_free_fs:
+ ped_free (fs);
+error:
+ return NULL;
+}
+
+static void
+swap_free (PedFileSystem* fs)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+
+ ped_free (fs_info->buffer);
+ ped_free (fs_info->header);
+ ped_free (fs->type_specific);
+
+ ped_geometry_destroy (fs->geom);
+ ped_free (fs);
+}
+
+static PedFileSystem*
+swap_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ const char* sig;
+
+ fs = swap_alloc (geom);
+ if (!fs)
+ goto error;
+ swap_init (fs, 0);
+
+ fs_info = SWAP_SPECIFIC (fs);
+ if (!ped_geometry_read (fs->geom, fs_info->header, 0,
+ fs_info->page_sectors))
+ goto error_free_fs;
+
+ sig = ((char*) fs_info->header) + getpagesize() - 10;
+ if (strncmp (sig, "SWAP-SPACE", 10) == 0) {
+ fs_info->version = 0;
+ fs_info->page_count
+ = PED_MIN (fs->geom->length / fs_info->page_sectors,
+ 8 * (getpagesize() - 10));
+ } else if (strncmp (sig, "SWAPSPACE2", 10) == 0) {
+ fs_info->version = 1;
+ fs_info->page_count = fs_info->header->new.last_page;
+ } else if (strncmp (sig, "S1SUSPEND", 9) == 0) {
+ fs_info->version = -1;
+ } else {
+ char _sig [11];
+
+ memcpy (_sig, sig, 10);
+ _sig [10] = 0;
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unrecognised linux swap signature '%10s'."), _sig);
+ goto error_free_fs;
+ }
+
+ fs->checked = 1;
+ return fs;
+
+error_free_fs:
+ swap_free (fs);
+error:
+ return NULL;
+}
+
+static int
+swap_close (PedFileSystem* fs)
+{
+ swap_free (fs);
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+swap_new_find_bad_page (PedFileSystem* fs, unsigned int page)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ unsigned int i;
+
+ for (i=0; i < fs_info->header->new.nr_badpages; i++) {
+ if (fs_info->header->new.badpages [i] == page)
+ return i;
+ }
+
+ return 0;
+}
+
+static int
+swap_new_remove_bad_page (PedFileSystem* fs, unsigned int page)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ unsigned int pos;
+
+ pos = swap_new_find_bad_page (fs, page);
+ if (!pos)
+ return 0;
+
+ for (; pos < fs_info->header->new.nr_badpages; pos++) {
+ fs_info->header->new.badpages [pos - 1]
+ = fs_info->header->new.badpages [pos];
+ }
+
+ return 1;
+}
+
+static int
+swap_mark_page (PedFileSystem* fs, unsigned int page, int ok)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ char* ptr;
+ unsigned int mask;
+
+ if (fs_info->version == 0) {
+ ptr = &fs_info->header->old.page_map [page/8];
+ mask = 1 << (page%8);
+ *ptr = (*ptr & ~mask) + ok * mask;
+ } else {
+ if (ok) {
+ if (swap_new_remove_bad_page (fs, page))
+ fs_info->header->new.nr_badpages--;
+ } else {
+ if (swap_new_find_bad_page (fs, page))
+ return 1;
+
+ if (fs_info->header->new.nr_badpages
+ > fs_info->max_bad_pages) {
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Too many bad pages."));
+ return 0;
+ }
+
+ fs_info->header->new.badpages
+ [fs_info->header->new.nr_badpages] = page;
+ fs_info->header->new.nr_badpages++;
+ }
+ }
+
+ return 1;
+}
+
+static void
+swap_clear_pages (PedFileSystem* fs)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ unsigned int i;
+
+ for (i = 1; i < fs_info->page_count; i++) {
+ swap_mark_page (fs, i, 1);
+ }
+
+ if (fs_info->version == 0) {
+ for (; i < 1024; i++) {
+ swap_mark_page (fs, i, 0);
+ }
+ }
+}
+
+static int
+swap_check_pages (PedFileSystem* fs, PedTimer* timer)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ PedSector result;
+ int first_page = 1;
+ int stop_page = 0;
+ int last_page = fs_info->page_count - 1;
+ PedTimer* nested_timer;
+
+ ped_timer_reset (timer);
+ ped_timer_set_state_name (timer, _("checking for bad blocks"));
+
+ swap_clear_pages (fs);
+ while (first_page <= last_page) {
+ nested_timer = ped_timer_new_nested (
+ timer,
+ 1.0 * (last_page - first_page) / last_page);
+ result = ped_geometry_check (
+ fs->geom,
+ fs_info->buffer,
+ fs_info->buffer_size / 512,
+ first_page * fs_info->page_sectors,
+ fs_info->page_sectors,
+ (last_page - first_page + 1)
+ * fs_info->page_sectors,
+ nested_timer);
+ ped_timer_destroy_nested (nested_timer);
+ if (!result)
+ return 1;
+ stop_page = result / fs_info->page_sectors;
+ if (!swap_mark_page (fs, stop_page, 0))
+ return 0;
+ first_page = stop_page + 1;
+ }
+ return 1;
+}
+
+static int
+swap_write (PedFileSystem* fs)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+ char* sig = ((char*) fs_info->header) + getpagesize() - 10;
+
+ if (fs_info->version == 0) {
+ memcpy (sig, "SWAP-SPACE", 10);
+ } else {
+ fs_info->header->new.version = 1;
+ fs_info->header->new.last_page = fs_info->page_count - 1;
+ fs_info->header->new.nr_badpages = 0;
+ memcpy (sig, "SWAPSPACE2", 10);
+ }
+
+ return ped_geometry_write (fs->geom, fs_info->header, 0,
+ fs_info->page_sectors);
+}
+
+static PedFileSystem*
+swap_create (PedGeometry* geom, PedTimer* timer)
+{
+ PedFileSystem* fs;
+
+ fs = swap_alloc (geom);
+ if (!fs)
+ goto error;
+ swap_init (fs, 1);
+ if (!swap_write (fs))
+ goto error_free_fs;
+ return fs;
+
+error_free_fs:
+ swap_free (fs);
+error:
+ return NULL;
+}
+
+static int
+swap_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ PedGeometry* old_geom = fs->geom;
+
+ fs->geom = ped_geometry_duplicate (geom);
+ swap_init (fs, old_geom->start != geom->start);
+ if (!swap_write (fs))
+ goto error;
+ ped_geometry_destroy (old_geom);
+ return 1;
+
+error:
+ ped_geometry_destroy (fs->geom);
+ fs->geom = old_geom;
+ return 0;
+}
+
+static PedFileSystem*
+swap_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ return ped_file_system_create (geom, &swap_type, timer);
+}
+
+static int
+swap_check (PedFileSystem* fs, PedTimer* timer)
+{
+ return swap_check_pages (fs, timer)
+ && swap_write (fs);
+}
+
+static PedConstraint*
+swap_get_create_constraint (const PedDevice* dev)
+{
+ PedGeometry full_dev;
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new (ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ getpagesize() / 512, dev->length);
+}
+
+static PedConstraint*
+swap_get_resize_constraint (const PedFileSystem* fs)
+{
+ return swap_get_create_constraint (fs->geom->dev);
+}
+
+static PedConstraint*
+swap_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
+{
+ return swap_get_create_constraint (dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps swap_ops = {
+ .probe = swap_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = swap_clobber,
+ .open = swap_open,
+ .create = swap_create,
+ .close = swap_close,
+ .check = swap_check,
+ .copy = swap_copy,
+ .resize = swap_resize,
+ .get_create_constraint = swap_get_create_constraint,
+ .get_resize_constraint = swap_get_resize_constraint,
+ .get_copy_constraint = swap_get_copy_constraint
+#else
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+#endif /* !DISCOVER_ONLY */
+};
+
+static PedFileSystemType swap_type = {
+ .next = NULL,
+ .ops = &swap_ops,
+ .name = "linux-swap",
+ .block_sizes = LINUXSWAP_BLOCK_SIZES
+};
+
+void
+ped_file_system_linux_swap_init ()
+{
+ ped_file_system_type_register (&swap_type);
+}
+
+void
+ped_file_system_linux_swap_done ()
+{
+ ped_file_system_type_unregister (&swap_type);
+}
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/ntfs/ntfs.c b/usr/src/lib/libparted/common/libparted/fs/ntfs/ntfs.c
new file mode 100644
index 0000000000..963be7c9c8
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ntfs/ntfs.c
@@ -0,0 +1,599 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <unistd.h>
+#include <string.h>
+#include <limits.h> /* for PATH_MAX */
+
+#define NTFS_BLOCK_SIZES ((int[2]){512, 0})
+
+#define NTFS_SIGNATURE "NTFS"
+
+#define NTFSRESIZE_CMD_PATH "ntfsresize"
+#define NTFSCREATE_CMD_PATH "mkntfs"
+#define NTFSFIX_CMD_PATH "ntfsfix"
+#define NTFSCLONE_CMD_PATH "ntfsclone"
+
+static PedFileSystemType ntfs_type;
+
+static char bigbuf[128*1024]; /* for command output storage */
+
+static PedGeometry*
+ntfs_probe (PedGeometry* geom)
+{
+ char buf[512];
+
+ PED_ASSERT(geom != NULL, return 0);
+
+ if (!ped_geometry_read (geom, buf, 0, 1))
+ return 0;
+
+ if (strncmp (NTFS_SIGNATURE, buf + 3, strlen (NTFS_SIGNATURE)) == 0)
+ return ped_geometry_new (geom->dev, geom->start,
+ PED_LE64_TO_CPU (*(uint64_t*)
+ (buf + 0x28)));
+ else
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+ntfs_clobber (PedGeometry* geom)
+{
+ char buf[512];
+
+ PED_ASSERT(geom != NULL, return 0);
+
+ memset (buf, 0, sizeof(buf));
+ return ped_geometry_write (geom, buf, 0, 1);
+}
+
+static PedFileSystem*
+ntfs_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+
+ PED_ASSERT(geom != NULL, return 0);
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs)
+ return NULL;
+
+ fs->type = &ntfs_type;
+ fs->geom = ped_geometry_duplicate (geom);
+ fs->checked = 1; /* XXX */
+ fs->type_specific = NULL;
+
+ return fs;
+}
+
+/*
+ * Returns partition number (1..4) that contains geom, 0 otherwise.
+ */
+static int
+_get_partition_num_by_geom(const PedGeometry* geom)
+{
+ PedDisk *disk;
+ PedPartition *part;
+ int partnum = 0;
+
+ PED_ASSERT(geom != NULL, return 0);
+
+ disk = ped_disk_new (geom->dev);
+ if (!disk) {
+ printf("_get_partition_num_by_geom: ped_disk_new failed!\n");
+ }
+ else {
+ part = ped_disk_get_partition_by_sector (disk, geom->start);
+ if (part == NULL) {
+ printf("_get_partition_num_by_geom: "
+ "ped_disk_get_partition_by_sector failed!\n");
+ }
+ else {
+ if (part->num > 0)
+ partnum = part->num;
+ }
+ ped_disk_destroy (disk);
+ }
+ return partnum;
+}
+
+/*
+ * return the partition device name for geom in partpath.
+ * return 1 on success, 0 on failure.
+ */
+static int
+_get_part_device_path(const PedGeometry* geom, char *partpath, const int len)
+{
+ int partnum;
+
+ PED_ASSERT(geom != NULL, return 0);
+ PED_ASSERT(partpath != NULL, return 0);
+
+ partnum = _get_partition_num_by_geom(geom);
+ if (!partnum)
+ return 0;
+
+ strncpy(partpath, geom->dev->path, len);
+ /*
+ * XXX Solaris specific
+ * Create the path name to the *pn device, where n is the partition #
+ * geom->dev->path looks like this: "/devices/.../cmdk@0,0:q"
+ * or like this: "/dev/dsk/...p0"
+ * ":q" is the "/dev/dsk/...p0" device
+ * :r is p1, :s is p2, :t is p3, :u is p4
+ * 'q' + 1 == 'r'
+ * '0' + 1 == '1'
+ */
+ partpath[strlen(partpath) -1] += partnum;
+
+ return 1;
+}
+
+/*
+ * Executes cmd in a pipe.
+ * Returns -1 on popen failure or the return value from pclose.
+ * Saves the output from cmd in bigbuf for later display.
+ */
+static int
+_execute(const char *cmd)
+{
+ FILE *fp;
+ char buf[512];
+ int szbigbuf;
+
+ PED_ASSERT(cmd != NULL, return 0);
+
+ fp = popen(cmd, "r");
+ if (fp == NULL)
+ return -1;
+
+ strcpy(bigbuf, "");
+ szbigbuf = sizeof(bigbuf) -1;
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (szbigbuf > 0) {
+ strncat(bigbuf, buf, szbigbuf);
+ szbigbuf -= strlen(buf);
+ }
+ }
+
+ return pclose(fp);
+}
+
+/*
+ * ./mkntfs -f -s 512 -S 63 -H 255 -p 0 /dev/dsk/c0d0p1
+ * Returns new fs on success, NULL on failure.
+ */
+PedFileSystem*
+ntfs_create (PedGeometry* geom, PedTimer* timer)
+{
+ int x;
+ PedFileSystem* fs = NULL;
+ char partpath[PATH_MAX];
+ char cmd[PATH_MAX];
+
+ PED_ASSERT(geom != NULL, return 0);
+ PED_ASSERT(timer != NULL, return 0);
+
+ ped_timer_reset (timer);
+ ped_timer_update (timer, 0.0);
+ ped_timer_set_state_name(timer, _("creating"));
+
+ if (_get_part_device_path(geom, partpath, sizeof(partpath)) == 0)
+ goto error;
+
+ snprintf(cmd, sizeof(cmd), "%s -f -s %lld -S %d -H %d -p %lld %s",
+ NTFSCREATE_CMD_PATH,
+ geom->dev->sector_size,
+ geom->dev->hw_geom.sectors,
+ geom->dev->hw_geom.heads,
+ (PedSector) 0, /* partition start sector */
+ partpath);
+ printf("%s\n", cmd);
+
+ /*
+ * Use system() so the output that shows progress is displayed.
+ */
+ ped_device_begin_external_access(geom->dev);
+ x = system(cmd);
+ ped_device_end_external_access(geom->dev);
+
+ if (x != 0) {
+ goto error;
+ }
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs)
+ goto error;
+ fs->type = &ntfs_type;
+ fs->geom = ped_geometry_duplicate (geom);
+ fs->checked = 1; /* XXX */
+ fs->type_specific = NULL;
+
+error:
+ ped_timer_update (timer, 1.0);
+ return fs;
+}
+
+/*
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+ntfs_close (PedFileSystem *fs)
+{
+ PED_ASSERT(fs != NULL, return 0);
+
+ ped_geometry_destroy (fs->geom);
+ ped_free (fs);
+
+ return 1;
+}
+
+/*
+ * ntfsfix /dev/dsk/c0d0p1
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+ntfs_check(PedFileSystem *fs, PedTimer *timer)
+{
+ int x;
+ int ret = 0;
+ char partpath[PATH_MAX];
+ char cmd[PATH_MAX];
+
+ PED_ASSERT(fs != NULL, return 0);
+ PED_ASSERT(timer != NULL, return 0);
+
+ ped_timer_reset(timer);
+ ped_timer_set_state_name(timer, _("checking"));
+ ped_timer_update(timer, 0.0);
+
+ if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
+ goto error;
+
+ snprintf(cmd, sizeof(cmd), "%s %s",
+ NTFSFIX_CMD_PATH, partpath);
+ printf("%s\n", cmd);
+
+ /*
+ * Use system() so the output that shows progress is displayed.
+ */
+ ped_device_begin_external_access(fs->geom->dev);
+ x = system(cmd);
+ ped_device_end_external_access(fs->geom->dev);
+
+ if (x == 0) {
+ ret = 1; /* return success to the upper layer */
+ }
+ else {
+ goto error;
+ }
+
+error:
+ ped_timer_update(timer, 1.0);
+ return ret;
+}
+
+/*
+ * Copy from source fs to destination geom.
+ * The destination partition must alreay exist.
+ * ntfsclone --overwrite destination-device source-device
+ * Returns new fs on success, NULL on failure.
+ */
+static PedFileSystem*
+ntfs_copy(const PedFileSystem *fs, PedGeometry *geom, PedTimer *timer)
+{
+ int x;
+ char spartpath[PATH_MAX];
+ char dpartpath[PATH_MAX];
+ char cmd[PATH_MAX];
+ PedFileSystem *new_fs = NULL;
+
+ PED_ASSERT(fs != NULL, return 0);
+ PED_ASSERT(geom != NULL, return 0);
+ PED_ASSERT(timer != NULL, return 0);
+
+ ped_timer_reset(timer);
+ ped_timer_set_state_name(timer, _("copying"));
+ ped_timer_update(timer, 0.0);
+
+ if (_get_part_device_path(fs->geom, spartpath, sizeof(spartpath)) == 0)
+ goto error;
+
+ if (_get_part_device_path(geom, dpartpath, sizeof(dpartpath)) == 0)
+ goto error;
+
+ snprintf(cmd, sizeof(cmd), "%s --overwrite %s %s",
+ NTFSCLONE_CMD_PATH, dpartpath, spartpath);
+ printf("%s\n", cmd);
+
+ /*
+ * Use system() so the output that shows progress is displayed.
+ */
+ ped_device_begin_external_access(geom->dev);
+ x = system(cmd);
+ ped_device_end_external_access(geom->dev);
+
+ if (x != 0) {
+ goto error;
+ }
+
+ if (!(new_fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem))))
+ goto error;
+
+ new_fs->type = &ntfs_type;
+ new_fs->geom = ped_geometry_duplicate(geom);
+ new_fs->checked = 0;
+ new_fs->type_specific = NULL;
+
+error:
+ ped_timer_update(timer, 1.0);
+ return new_fs;
+}
+
+/*
+ * fs->geom has the current filesystem size in sectors.
+ * geom has the new, requested filesystem size in sectors.
+ *
+ * fs->geom->dev is the same object as geom->dev.
+ * geom->dev->path looks like this:
+ * /dev/dsk/...p0
+ * or this:
+ * /devices/.../cmdk@0,0:q
+ *
+ * The ntfsresize cmd wants the block disk device, not the raw one.
+ * It also wants the partition device, not the whole disk.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+static int
+ntfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer)
+{
+ int x;
+ int ret = 0; /* this tells the upper layer NOT to resize partition */
+ char partpath[PATH_MAX];
+ char cmd[PATH_MAX];
+
+ PED_ASSERT(fs != NULL, return 0);
+ PED_ASSERT(geom != NULL, return 0);
+ PED_ASSERT(timer != NULL, return 0);
+
+ if (fs->geom->start != geom->start) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Sorry, can't move the start of "
+ "ntfs partitions yet."));
+ return 0;
+ }
+
+ ped_timer_reset (timer);
+ ped_timer_update (timer, 0.0);
+
+ if (fs->geom->length > geom->length) {
+ ped_timer_set_state_name(timer, _("shrinking"));
+ }
+ else if (fs->geom->length < geom->length) {
+ ped_timer_set_state_name(timer, _("enlarging"));
+ }
+ else {
+ ped_timer_set_state_name(timer, _("no change"));
+ }
+
+ if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
+ goto error1;
+
+ ped_device_begin_external_access(geom->dev);
+
+ /*
+ * ntfsresize -f says don't worry about consistency flag
+ */
+ snprintf(cmd, sizeof(cmd), "%s -f -i %s",
+ NTFSRESIZE_CMD_PATH, partpath);
+ printf("%s\n", cmd);
+ x = _execute(cmd);
+ if (x != 0) {
+ printf("ntfsresize had this message:\n%s\n", bigbuf);
+ goto error2;
+ }
+
+ snprintf(cmd, sizeof(cmd), "%s -f -n -s %lld %s",
+ NTFSRESIZE_CMD_PATH,
+ geom->length * geom->dev->sector_size, partpath);
+ printf("%s\n", cmd);
+ x = _execute(cmd);
+ if (x != 0) {
+ printf("ntfsresize had this message:\n%s\n", bigbuf);
+ goto error2;
+ }
+
+ /*
+ * ntfsresize -f -f means don't ask "Are you sure?"
+ * Use system() so the output that shows progress is displayed.
+ */
+ snprintf(cmd, sizeof(cmd), "%s -f -f -s %lld %s",
+ NTFSRESIZE_CMD_PATH,
+ geom->length * geom->dev->sector_size, partpath);
+ printf("%s\n", cmd);
+ x = system(cmd);
+ if (x == 0) {
+ ret = 1; /* this tells upper layer to resize the partition */
+ }
+ else {
+ goto error2;
+ }
+
+error2:
+ ped_device_end_external_access(geom->dev);
+error1:
+ ped_timer_update (timer, 1.0);
+ return ret;
+}
+
+/*
+ * return the minimum resize size from the ntfsresize external cmd
+ * in blocks, 0 on error.
+ * Saves the output from cmd in bigbuf for later display.
+ */
+static PedSector
+_get_min_from_ntfsresize(const char *cmd)
+{
+ FILE *fp;
+ char buf[512];
+ PedSector size = 0;
+ int x;
+ int szbigbuf;
+
+ PED_ASSERT(cmd != NULL, return 0);
+
+ fp = popen(cmd, "r");
+ if (fp == NULL)
+ return 0;
+
+ strcpy(bigbuf, "");
+ szbigbuf = sizeof(bigbuf) -1;
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (szbigbuf > 0) {
+ strncat(bigbuf, buf, szbigbuf);
+ szbigbuf -= strlen(buf);
+ }
+ x = sscanf(buf, "You might resize at %lld", &size);
+ if (x > 0)
+ break;
+ }
+
+ pclose(fp);
+ return size;
+}
+
+/*
+ * return the minimum resize size in blocks, fs->geom->length on error.
+ */
+static PedSector
+_get_min_resize_size (const PedFileSystem* fs)
+{
+ PedSector max_length = fs->geom->length;
+ PedSector length;
+ char partpath[PATH_MAX];
+ char cmd[PATH_MAX];
+
+ PED_ASSERT(fs != NULL, return 0);
+
+ if (_get_part_device_path(fs->geom, partpath, sizeof(partpath)) == 0)
+ return max_length;
+
+ snprintf(cmd, sizeof(cmd), "%s -f -i %s",
+ NTFSRESIZE_CMD_PATH, partpath);
+
+ length = _get_min_from_ntfsresize(cmd);
+ if (length == 0) {
+ printf("ntfsresize had this message:\n%s\n", bigbuf);
+ return max_length;
+ }
+
+ return (length / fs->geom->dev->sector_size);
+}
+
+PedConstraint*
+ntfs_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev)
+{
+ PedGeometry full_dev;
+
+ PED_ASSERT(fs != NULL, return 0);
+ PED_ASSERT(dev != NULL, return 0);
+
+ if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new (ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ _get_min_resize_size (fs),
+ dev->length);
+}
+
+PedConstraint*
+ntfs_get_resize_constraint (const PedFileSystem* fs)
+{
+ PED_ASSERT(fs != NULL, return 0);
+
+ return ntfs_get_copy_constraint (fs, fs->geom->dev);
+}
+
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps ntfs_ops = {
+ .probe = ntfs_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = ntfs_clobber,
+ .open = ntfs_open,
+ .create = ntfs_create,
+ .close = ntfs_close,
+ .check = ntfs_check,
+ .copy = ntfs_copy,
+ .resize = ntfs_resize,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = ntfs_get_resize_constraint,
+ .get_copy_constraint = ntfs_get_copy_constraint
+#else
+ .clobber = NULL,
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+#endif
+};
+
+static PedFileSystemType ntfs_type = {
+ .next = NULL,
+ .ops = &ntfs_ops,
+ .name = "ntfs",
+ .block_sizes = NTFS_BLOCK_SIZES
+};
+
+void
+ped_file_system_ntfs_init ()
+{
+ ped_file_system_type_register (&ntfs_type);
+}
+
+void
+ped_file_system_ntfs_done ()
+{
+ ped_file_system_type_unregister (&ntfs_type);
+}
+
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.c b/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.c
new file mode 100644
index 0000000000..0e52e6a3c5
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.c
@@ -0,0 +1,138 @@
+/*
+ geom_dal.c -- parted device abstraction layer
+ Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#if (DYNAMIC_LOADING || HAVE_LIBREISERFS) && !DISCOVER_ONLY
+
+#include "geom_dal.h"
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+static blk_t __len(dal_t *dal) {
+ PED_ASSERT(dal != NULL, return 0);
+
+ return ((PedGeometry *)dal->dev)->length /
+ (dal->block_size / PED_SECTOR_SIZE_DEFAULT);
+}
+
+static int __read(dal_t *dal, void *buff, blk_t block, blk_t count) {
+ blk_t k;
+ PedSector block_pos;
+ PedSector block_count;
+
+ PED_ASSERT(dal != NULL, return 0);
+
+ k = dal->block_size / PED_SECTOR_SIZE_DEFAULT;
+ block_pos = (PedSector)(block * k);
+ block_count = (PedSector)(count * k);
+
+ return ped_geometry_read((PedGeometry *)dal->dev, buff, block_pos, block_count);
+}
+
+static int __write(dal_t *dal, void *buff, blk_t block, blk_t count) {
+ blk_t k;
+ PedSector block_pos;
+ PedSector block_count;
+
+ PED_ASSERT(dal != NULL, return 0);
+
+ k = dal->block_size / PED_SECTOR_SIZE_DEFAULT;
+ block_pos = (PedSector)(block * k);
+ block_count = (PedSector)(count * k);
+
+ return ped_geometry_write((PedGeometry *)dal->dev, buff, block_pos,
+ block_count);
+}
+
+static int __sync(dal_t *dal) {
+ PED_ASSERT(dal != NULL, return 0);
+ return ped_geometry_sync((PedGeometry *)dal->dev);
+}
+
+static int __flags(dal_t *dal) {
+ PED_ASSERT(dal != NULL, return 0);
+ return dal->flags;
+}
+
+static int __equals(dal_t *dal1, dal_t *dal2) {
+ PED_ASSERT(dal1 != NULL, return 0);
+ PED_ASSERT(dal2 != NULL, return 0);
+
+ return ped_geometry_test_equal((PedGeometry *)dal1->dev,
+ (PedGeometry *)dal2->dev);
+}
+
+static int __stat(dal_t *dal, struct stat *st) {
+
+ PED_ASSERT(dal != NULL, return 0);
+ PED_ASSERT(st != NULL, return 0);
+
+ if (stat(((PedGeometry *)dal->dev)->dev->path, st))
+ return 0;
+
+ return 1;
+}
+
+static dev_t __dev(dal_t *dal) {
+ struct stat st;
+
+ if (!__stat(dal, &st))
+ return (dev_t)0;
+
+ return st.st_dev;
+}
+
+static struct dal_ops ops = {
+ __len, __read, __write, __sync,
+ __flags, __equals, __stat, __dev
+};
+
+dal_t *geom_dal_create(PedGeometry *geom, size_t block_size, int flags) {
+ dal_t *dal;
+
+ if (!geom)
+ return NULL;
+
+ if (!(dal = ped_malloc(sizeof(dal_t))))
+ return NULL;
+
+ dal->ops = &ops;
+ dal->dev = geom;
+ dal->block_size = block_size;
+ dal->flags = flags;
+ dal->len = 0;
+
+ return dal;
+}
+
+int geom_dal_reopen(dal_t *dal, int flags) {
+
+ if (!dal) return 0;
+ dal->flags = flags;
+
+ return 1;
+}
+
+void geom_dal_free(dal_t *dal) {
+ PED_ASSERT(dal != NULL, return);
+ ped_free(dal);
+}
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.h b/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.h
new file mode 100644
index 0000000000..f79cb8c01d
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/reiserfs/geom_dal.h
@@ -0,0 +1,60 @@
+/*
+ geom_dal.h -- parted device abstraction layer
+ Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GEOM_DAL_H
+#define GEOM_DAL_H
+
+#include <parted/parted.h>
+
+#if DYNAMIC_LOADING || !DISCOVER_ONLY
+
+#include <sys/stat.h>
+
+typedef unsigned long blk_t;
+
+struct dal_ops;
+
+struct _dal {
+ struct dal_ops *ops;
+ const void *dev;
+ size_t block_size;
+ int flags;
+ void *data;
+ blk_t len;
+};
+
+typedef struct _dal dal_t;
+
+struct dal_ops {
+ blk_t (*len)(dal_t *);
+ int (*read)(dal_t *, void *, blk_t, blk_t);
+ int (*write)(dal_t *, void *, blk_t, blk_t);
+ int (*sync)(dal_t *);
+ int (*flags)(dal_t *);
+ int (*equals)(dal_t *, dal_t *);
+ int (*stat)(dal_t *, struct stat *);
+ dev_t (*dev)(dal_t *);
+};
+
+extern dal_t *geom_dal_create(PedGeometry *geom, size_t block_size, int flags);
+extern int geom_dal_reopen(dal_t *dal, int flags);
+extern void geom_dal_free(dal_t *dal);
+
+#endif
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.c b/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.c
new file mode 100644
index 0000000000..e624193372
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.c
@@ -0,0 +1,866 @@
+/*
+ reiserfs.c -- libparted / libreiserfs glue
+ Copyright (C) 2001, 2002, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ This is all rather complicated. There are a few combinations:
+ * shared libraries full support
+ * dynamic libraries present full support (via dlopen)
+ * dynamic libraries absent (full support disabled) (via dlopen)
+ * discover only
+
+ We'd love to hear comments...
+
+ So far, we've opted for maximum flexibility for the user. Is it
+ all worth it?
+*/
+
+#include <config.h>
+
+#if (HAVE_LIBREISERFS || DYNAMIC_LOADING) && !DISCOVER_ONLY
+# define REISER_FULL_SUPPORT
+#endif
+
+#include <uuid/uuid.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef DYNAMIC_LOADING
+# include <dlfcn.h>
+#endif
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif
+
+#include "reiserfs.h"
+#include "geom_dal.h"
+
+#define REISERFS_BLOCK_SIZES ((int[2]){512, 0})
+
+static PedSector reiserfs_super_offset[] = { 128, 16, -1 };
+static PedFileSystemType* reiserfs_type;
+
+#ifdef DYNAMIC_LOADING
+# define FPTR *
+# define FCLASS static
+#else
+# define FPTR
+# define FCLASS extern
+#endif
+
+#ifdef DYNAMIC_LOADING
+
+static int libreiserfs_present;
+
+static void *libdal_handle;
+static void *libreiserfs_handle;
+
+#endif /* DYNAMIC_LOADING */
+
+#ifdef REISER_FULL_SUPPORT
+
+FCLASS blk_t (FPTR reiserfs_fs_probe) (dal_t *);
+
+FCLASS int (FPTR libreiserfs_exception_type) (reiserfs_exception_t *);
+FCLASS int (FPTR libreiserfs_exception_option) (reiserfs_exception_t *);
+FCLASS char *(FPTR libreiserfs_exception_message) (reiserfs_exception_t *);
+FCLASS void (FPTR libreiserfs_exception_set_handler)
+ (int(FPTR)(reiserfs_exception_t *));
+
+FCLASS void (FPTR dal_realize) (dal_t *);
+FCLASS size_t (FPTR dal_block_size) (dal_t *);
+FCLASS blk_t (FPTR dal_len) (dal_t *);
+FCLASS int (FPTR dal_flags) (dal_t *);
+
+FCLASS reiserfs_fs_t* (FPTR reiserfs_fs_open) (dal_t *, dal_t *);
+FCLASS reiserfs_fs_t* (FPTR reiserfs_fs_create) (dal_t *, dal_t *,
+ blk_t, blk_t, blk_t, size_t,
+ int, int, const char *,
+ const char *, blk_t,
+ reiserfs_gauge_t *);
+
+FCLASS int (FPTR reiserfs_fs_resize) (reiserfs_fs_t *, blk_t, reiserfs_gauge_t *);
+#ifdef HAVE_REISERFS_FS_CHECK
+FCLASS int (FPTR reiserfs_fs_check) (reiserfs_fs_t *, reiserfs_gauge_t *);
+#endif
+
+FCLASS reiserfs_fs_t *(FPTR reiserfs_fs_copy) (reiserfs_fs_t *, dal_t *,
+ reiserfs_gauge_t *);
+
+FCLASS int (FPTR reiserfs_fs_clobber) (dal_t *);
+FCLASS void (FPTR reiserfs_fs_close) (reiserfs_fs_t *);
+
+FCLASS int (FPTR reiserfs_fs_is_resizeable) (reiserfs_fs_t *);
+FCLASS int (FPTR reiserfs_fs_is_consistent) (reiserfs_fs_t *);
+
+FCLASS blk_t (FPTR reiserfs_fs_min_size) (reiserfs_fs_t *);
+FCLASS blk_t (FPTR reiserfs_fs_block_size) (reiserfs_fs_t *);
+FCLASS dal_t* (FPTR reiserfs_fs_host_dal) (reiserfs_fs_t *);
+
+FCLASS blk_t (FPTR reiserfs_fs_bitmap_used) (reiserfs_fs_t *);
+FCLASS int (FPTR reiserfs_fs_bitmap_check) (reiserfs_fs_t *);
+
+FCLASS reiserfs_gauge_t *(FPTR libreiserfs_gauge_create) (
+ char *, reiserfs_gauge_handler_t, void *);
+
+FCLASS void (FPTR libreiserfs_gauge_free) (reiserfs_gauge_t *);
+
+static void gauge_handler(const char *name, unsigned int value, void *data,
+ int determined, int update_header,
+ int update_footer)
+{
+ PedTimer *timer = (PedTimer *) data;
+ ped_timer_set_state_name(timer, name);
+ ped_timer_update(timer, 1.0 * value / 100);
+}
+
+static PedExceptionOption
+exopt_libreiserfs_to_parted(reiserfs_exception_option_t option)
+{
+ switch (option) {
+ case EXCEPTION_UNHANDLED:
+ return PED_EXCEPTION_UNHANDLED;
+ case EXCEPTION_FIX:
+ return PED_EXCEPTION_FIX;
+ case EXCEPTION_YES:
+ return PED_EXCEPTION_YES;
+ case EXCEPTION_NO:
+ return PED_EXCEPTION_NO;
+ case EXCEPTION_OK:
+ return PED_EXCEPTION_OK;
+ case EXCEPTION_RETRY:
+ return PED_EXCEPTION_RETRY;
+ case EXCEPTION_IGNORE:
+ return PED_EXCEPTION_IGNORE;
+ case EXCEPTION_CANCEL:
+ return PED_EXCEPTION_CANCEL;
+
+ default:
+ return PED_EXCEPTION_UNHANDLED;
+ }
+}
+
+static PedExceptionType
+extype_libreiserfs_to_parted(reiserfs_exception_type_t type)
+{
+ switch (type) {
+ case EXCEPTION_INFORMATION:
+ return PED_EXCEPTION_INFORMATION;
+ case EXCEPTION_WARNING:
+ return PED_EXCEPTION_WARNING;
+ case EXCEPTION_ERROR:
+ return PED_EXCEPTION_ERROR;
+ case EXCEPTION_FATAL:
+ return PED_EXCEPTION_FATAL;
+ case EXCEPTION_BUG:
+ return PED_EXCEPTION_BUG;
+ case EXCEPTION_NO_FEATURE:
+ return PED_EXCEPTION_NO_FEATURE;
+
+ default:
+ return PED_EXCEPTION_NO_FEATURE;
+ }
+}
+
+static int exception_handler(reiserfs_exception_t *exception)
+{
+ int ex_type = libreiserfs_exception_type(exception);
+ int ex_option = libreiserfs_exception_option(exception);
+ char *ex_message = libreiserfs_exception_message(exception);
+
+ return ped_exception_throw (extype_libreiserfs_to_parted (ex_type),
+ exopt_libreiserfs_to_parted (ex_option),
+ ex_message);
+}
+#endif /* REISER_FULL_SUPPORT */
+
+static PedGeometry *reiserfs_probe(PedGeometry *geom)
+{
+ int i;
+ reiserfs_super_block_t sb;
+
+ PED_ASSERT(geom != NULL, return NULL);
+
+ for (i = 0; reiserfs_super_offset[i] != -1; i++) {
+ if (reiserfs_super_offset[i] >= geom->length)
+ continue;
+ if (!ped_geometry_read (geom, &sb, reiserfs_super_offset[i], 1))
+ continue;
+
+ if (strncmp(REISERFS_SIGNATURE, sb.s_magic,
+ strlen(REISERFS_SIGNATURE)) == 0
+ || strncmp(REISER2FS_SIGNATURE, sb.s_magic,
+ strlen(REISER2FS_SIGNATURE)) == 0
+ || strncmp(REISER3FS_SIGNATURE, sb.s_magic,
+ strlen(REISER3FS_SIGNATURE)) == 0) {
+ PedSector block_size;
+ PedSector block_count;
+
+ block_size = PED_LE16_TO_CPU(sb.s_blocksize)
+ / PED_SECTOR_SIZE_DEFAULT;
+ block_count = PED_LE32_TO_CPU(sb.s_block_count);
+
+ return ped_geometry_new(geom->dev, geom->start,
+ block_size * block_count);
+ }
+ }
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+static int reiserfs_clobber(PedGeometry *geom)
+{
+ int i;
+ char buf[512];
+
+ PED_ASSERT(geom != NULL, return 0);
+
+ memset(buf, 0, 512);
+ for (i = 0; reiserfs_super_offset[i] != -1; i++) {
+ if (reiserfs_super_offset[i] >= geom->length)
+ continue;
+ if (!ped_geometry_write
+ (geom, buf, reiserfs_super_offset[i], 1))
+ return 0;
+ }
+ return 1;
+}
+#endif /* !DISCOVER_ONLY */
+
+#ifdef REISER_FULL_SUPPORT
+
+static PedFileSystem *reiserfs_open(PedGeometry *geom)
+{
+ PedFileSystem *fs;
+ PedGeometry *fs_geom;
+ dal_t *dal;
+ reiserfs_fs_t *fs_info;
+
+ PED_ASSERT(geom != NULL, return NULL);
+
+ if (!(fs_geom = ped_geometry_duplicate(geom)))
+ goto error;
+
+ if (! (dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDONLY)))
+ goto error_fs_geom_free;
+
+ /*
+ We are passing NULL as DAL for journal. Therefore we let libreiserfs know,
+ that journal not available and parted will be working fine for reiserfs
+ with relocated journal too.
+ */
+ if (!(fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem))))
+ goto error_free_dal;
+
+ if (!(fs_info = reiserfs_fs_open(dal, NULL)))
+ goto error_free_fs;
+
+ fs->type = reiserfs_type;
+ fs->geom = fs_geom;
+ fs->type_specific = (void *) fs_info;
+
+ return fs;
+
+error_free_fs:
+ ped_free(fs);
+error_free_dal:
+ geom_dal_free(dal);
+error_fs_geom_free:
+ ped_geometry_destroy(fs_geom);
+error:
+ return NULL;
+}
+
+static PedFileSystem *reiserfs_create(PedGeometry *geom, PedTimer *timer)
+{
+ dal_t *dal;
+ uuid_t uuid;
+ PedFileSystem *fs;
+ PedGeometry *fs_geom;
+ reiserfs_fs_t *fs_info;
+ reiserfs_gauge_t *gauge = NULL;
+
+ PED_ASSERT(geom != NULL, return NULL);
+
+ fs_geom = ped_geometry_duplicate(geom);
+
+ if (!(dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDWR)))
+ goto error_fs_geom_free;
+
+ memset(uuid, 0, sizeof(uuid));
+ uuid_generate(uuid);
+
+ ped_timer_reset(timer);
+ ped_timer_set_state_name(timer, _("creating"));
+
+ if (libreiserfs_gauge_create && libreiserfs_gauge_free) {
+ if (! (gauge =
+ libreiserfs_gauge_create(NULL, gauge_handler, timer)))
+ goto error_free_dal;
+ }
+
+ if (!(fs_info = reiserfs_fs_create(dal, dal, 0, JOURNAL_MAX_TRANS,
+ DEFAULT_JOURNAL_SIZE,
+ DEFAULT_BLOCK_SIZE,
+ FS_FORMAT_3_6, R5_HASH, NULL,
+ (char *) uuid, dal_len(dal),
+ gauge)))
+ goto error_free_gauge;
+
+ ped_timer_update(timer, 1.0);
+
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+
+ if (!(fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem))))
+ goto error_free_fs_info;
+
+ fs->type = reiserfs_type;
+ fs->geom = fs_geom;
+ fs->type_specific = (void *) fs_info;
+
+ return fs;
+
+error_free_fs_info:
+ ped_free(fs_info);
+error_free_gauge:
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+error_free_dal:
+ geom_dal_free(dal);
+error_fs_geom_free:
+ ped_geometry_destroy(fs_geom);
+ return NULL;
+}
+
+static int reiserfs_close(PedFileSystem *fs)
+{
+ dal_t *dal;
+
+ PED_ASSERT(fs != NULL, return 0);
+
+ dal = reiserfs_fs_host_dal(fs->type_specific);
+ reiserfs_fs_close(fs->type_specific);
+
+ geom_dal_free(dal);
+ ped_geometry_sync(fs->geom);
+
+ ped_free(fs);
+ return 1;
+}
+
+static PedConstraint *reiserfs_get_create_constraint(const PedDevice *dev)
+{
+ PedGeometry full_dev;
+ PedSector min_blks = (SUPER_OFFSET_IN_BYTES / DEFAULT_BLOCK_SIZE)
+ + 2 + DEFAULT_JOURNAL_SIZE + 1 + 100 + 1;
+
+ if (!ped_geometry_init(&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new(ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ min_blks * (DEFAULT_BLOCK_SIZE / 512),
+ dev->length);
+}
+
+static int reiserfs_check(PedFileSystem *fs, PedTimer *timer)
+{
+ reiserfs_fs_t *fs_info;
+#ifdef HAVE_REISERFS_FS_CHECK
+ reiserfs_gauge_t *gauge = NULL;
+#endif
+
+ PED_ASSERT(fs != NULL, return 0);
+
+ fs_info = fs->type_specific;
+
+ if (!reiserfs_fs_is_consistent(fs_info)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The file system is in an invalid "
+ "state. Perhaps it is mounted?"));
+ return 0;
+ }
+
+ if (!reiserfs_fs_is_resizeable(fs_info))
+ ped_exception_throw(PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("The file system is in old "
+ "(unresizeable) format."));
+
+ if (!reiserfs_fs_bitmap_check(fs_info)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Invalid free blocks count. Run "
+ "reiserfsck --check first."));
+ return 0;
+ }
+
+#ifdef HAVE_REISERFS_FS_CHECK
+ ped_timer_reset(timer);
+
+ if (libreiserfs_gauge_create && libreiserfs_gauge_free) {
+ if (!
+ (gauge =
+ libreiserfs_gauge_create(NULL, gauge_handler, timer)))
+ return 0;
+ }
+
+ ped_timer_set_state_name(timer, _("checking"));
+ ped_timer_update(timer, 0.0);
+
+ if (!reiserfs_fs_check(fs_info, gauge)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Reiserfs tree seems to be corrupted. "
+ "Run reiserfsck --check first."));
+ return 0;
+ }
+
+ ped_timer_update(timer, 1.0);
+
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+#endif
+
+ ped_exception_throw(PED_EXCEPTION_INFORMATION, PED_EXCEPTION_OK,
+ _("The reiserfs file system passed a basic check. "
+ "For a more comprehensive check, run "
+ "reiserfsck --check."));
+
+ return 1;
+}
+
+static int reiserfs_resize(PedFileSystem *fs, PedGeometry *geom,
+ PedTimer *timer)
+{
+ dal_t *dal;
+ blk_t fs_len;
+ PedSector old_length;
+ reiserfs_fs_t *fs_info;
+ reiserfs_gauge_t *gauge = NULL;
+
+ PED_ASSERT(fs != NULL, return 0);
+
+ old_length = fs->geom->length;
+
+ PED_ASSERT (fs->geom->dev == geom->dev, return 0);
+
+ if (fs->geom->start != geom->start) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Sorry, can't move the start of "
+ "reiserfs partitions yet."));
+ return 0;
+ }
+
+ fs_info = fs->type_specific;
+
+ fs_len = (blk_t) (geom->length / (reiserfs_fs_block_size(fs_info) /
+ PED_SECTOR_SIZE_DEFAULT));
+
+ dal = reiserfs_fs_host_dal(fs_info);
+
+ if (dal_flags(dal) && O_RDONLY) {
+ if (!geom_dal_reopen(dal, O_RDWR)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Couldn't reopen device "
+ "abstraction layer for "
+ "read/write."));
+ return 0;
+ }
+ }
+
+ ped_timer_reset(timer);
+
+ if (libreiserfs_gauge_create && libreiserfs_gauge_free) {
+ if (!
+ (gauge =
+ libreiserfs_gauge_create(NULL, gauge_handler, timer)))
+ return 0;
+ }
+
+ if (old_length > geom->length) {
+
+ ped_timer_set_state_name(timer, _("shrinking"));
+ ped_timer_update(timer, 0.0);
+
+ if (!reiserfs_fs_resize(fs_info, fs_len, gauge))
+ goto error_free_gauge;
+
+ ped_geometry_set_end (fs->geom, geom->end);
+ dal_realize(dal);
+ } else {
+ ped_geometry_set_end (fs->geom, geom->end);
+ dal_realize(dal);
+
+ ped_timer_set_state_name(timer, _("expanding"));
+ ped_timer_update(timer, 0.0);
+
+ if (!reiserfs_fs_resize(fs_info, fs_len, gauge))
+ goto error_free_gauge;
+ }
+
+ ped_timer_update(timer, 1.0);
+
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+
+ return 1;
+
+error_free_gauge:
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+ ped_geometry_set_end (fs->geom, fs->geom->start + old_length - 1);
+ return 0;
+}
+
+static PedConstraint *reiserfs_get_resize_constraint(const PedFileSystem *
+ fs)
+{
+ PedDevice *dev;
+ PedSector min_size;
+ PedGeometry full_disk;
+ reiserfs_fs_t *fs_info;
+ PedAlignment start_align;
+ PedGeometry start_sector;
+
+ PED_ASSERT(fs != NULL, return NULL);
+
+ fs_info = fs->type_specific;
+ dev = fs->geom->dev;
+
+ if (!ped_alignment_init(&start_align, fs->geom->start, 0))
+ return NULL;
+ if (!ped_geometry_init(&full_disk, dev, 0, dev->length - 1))
+ return NULL;
+ if (!ped_geometry_init(&start_sector, dev, fs->geom->start, 1))
+ return NULL;
+
+ /*
+ Minsize for reiserfs is area occupied by data blocks and
+ metadata blocks minus free space blocks and minus bitmap
+ blocks which describes free space blocks.
+ */
+ min_size = reiserfs_fs_min_size(fs_info) *
+ (reiserfs_fs_block_size(fs_info) / PED_SECTOR_SIZE_DEFAULT);
+
+ return ped_constraint_new(&start_align, ped_alignment_any,
+ &start_sector, &full_disk, min_size,
+ dev->length);
+}
+
+static PedFileSystem *reiserfs_copy(const PedFileSystem *fs,
+ PedGeometry *geom, PedTimer *timer)
+{
+ dal_t *dal;
+ PedGeometry *fs_geom;
+ PedFileSystem *new_fs;
+ blk_t fs_len, min_needed_blk;
+
+ reiserfs_fs_t *dest_fs, *src_fs;
+ reiserfs_gauge_t *gauge = NULL;
+
+ fs_geom = ped_geometry_duplicate(geom);
+
+ if (!(dal = geom_dal_create(fs_geom, DEFAULT_BLOCK_SIZE, O_RDWR))) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Couldn't create reiserfs device "
+ "abstraction handler."));
+ goto error_free_fs_geom;
+ }
+
+ src_fs = fs->type_specific;
+
+ fs_len =
+ (geom->length / (reiserfs_fs_block_size(src_fs) / PED_SECTOR_SIZE_DEFAULT));
+ min_needed_blk = reiserfs_fs_bitmap_used(src_fs);
+
+ if (fs_len <= min_needed_blk) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Device is too small for %lu blocks."),
+ min_needed_blk);
+ goto error_free_dal;
+ }
+
+ if (! (new_fs = (PedFileSystem *) ped_malloc(sizeof(PedFileSystem))))
+ goto error_free_dal;
+
+ ped_timer_reset(timer);
+ ped_timer_set_state_name(timer, _("copying"));
+ ped_timer_update(timer, 0.0);
+
+ if (libreiserfs_gauge_create && libreiserfs_gauge_free) {
+ if (! (gauge =
+ libreiserfs_gauge_create(NULL, gauge_handler, timer)))
+ goto error_free_new_fs;
+ }
+
+ if (!(dest_fs = reiserfs_fs_copy(src_fs, dal, gauge)))
+ goto error_free_gauge;
+
+ ped_timer_update(timer, 1.0);
+
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+
+ new_fs->type = reiserfs_type;
+ new_fs->geom = fs_geom;
+ new_fs->type_specific = (void *) dest_fs;
+
+ return new_fs;
+
+error_free_gauge:
+ if (gauge)
+ libreiserfs_gauge_free(gauge);
+error_free_new_fs:
+ ped_free(new_fs);
+error_free_dal:
+ geom_dal_free(dal);
+error_free_fs_geom:
+ ped_geometry_destroy(fs_geom);
+ return NULL;
+}
+
+static PedConstraint *reiserfs_get_copy_constraint(const PedFileSystem *fs,
+ const PedDevice *dev)
+{
+ PedGeometry full_dev;
+
+ PED_ASSERT(fs != NULL, return NULL);
+ PED_ASSERT(dev != NULL, return NULL);
+
+ if (!ped_geometry_init(&full_dev, dev, 0, dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new(ped_alignment_any, ped_alignment_any,
+ &full_dev, &full_dev,
+ reiserfs_fs_bitmap_used(fs->type_specific),
+ dev->length);
+}
+
+#endif /* !REISER_FULL_SUPPORT */
+
+#ifdef DYNAMIC_LOADING
+
+#define INIT_SYM(SYM) SYM = getsym (libreiserfs_handle, #SYM)
+
+static void *getsym(void *handle, const char *symbol)
+{
+ void *entry;
+ char *error;
+
+ entry = dlsym(handle, symbol);
+ if ((error = dlerror()) != NULL) {
+ ped_exception_throw(PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("Couldn't resolve symbol %s. "
+ "Error: %s."),
+ symbol, error);
+ return NULL;
+ }
+
+ return entry;
+}
+
+static int reiserfs_ops_interface_version_check(void)
+{
+ int min_interface_version, max_interface_version;
+ int (*libreiserfs_get_max_interface_version) (void);
+ int (*libreiserfs_get_min_interface_version) (void);
+
+ INIT_SYM(libreiserfs_get_max_interface_version);
+ INIT_SYM(libreiserfs_get_min_interface_version);
+
+ if (!libreiserfs_get_min_interface_version ||
+ !libreiserfs_get_max_interface_version) {
+ ped_exception_throw(
+ PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL,
+ _("GNU Parted found an invalid libreiserfs library."));
+ return 0;
+ }
+
+ min_interface_version = libreiserfs_get_min_interface_version();
+ max_interface_version = libreiserfs_get_max_interface_version();
+
+ if (REISERFS_API_VERSION < min_interface_version ||
+ REISERFS_API_VERSION > max_interface_version) {
+ ped_exception_throw(
+ PED_EXCEPTION_WARNING, PED_EXCEPTION_CANCEL,
+ _("GNU Parted has detected libreiserfs interface "
+ "version mismatch. Found %d-%d, required %d. "
+ "ReiserFS support will be disabled."),
+ min_interface_version,
+ max_interface_version,
+ REISERFS_API_VERSION);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int reiserfs_ops_init(void)
+{
+ if (!(libreiserfs_handle = dlopen("libreiserfs.so", RTLD_NOW)))
+ goto error;
+
+ if (!reiserfs_ops_interface_version_check())
+ goto error_free_libreiserfs_handle;
+
+ if (!(libdal_handle = dlopen("libdal.so", RTLD_NOW)))
+ goto error_free_libreiserfs_handle;
+
+ INIT_SYM(reiserfs_fs_probe);
+ INIT_SYM(libreiserfs_exception_type);
+
+ INIT_SYM(libreiserfs_exception_option);
+ INIT_SYM(libreiserfs_exception_message);
+ INIT_SYM(libreiserfs_exception_set_handler);
+
+ INIT_SYM(reiserfs_fs_clobber);
+ INIT_SYM(reiserfs_fs_open);
+ INIT_SYM(reiserfs_fs_create);
+ INIT_SYM(reiserfs_fs_resize);
+ INIT_SYM(reiserfs_fs_copy);
+
+ INIT_SYM(reiserfs_fs_is_resizeable);
+ INIT_SYM(reiserfs_fs_is_consistent);
+
+ INIT_SYM(reiserfs_fs_bitmap_check);
+ INIT_SYM(reiserfs_fs_bitmap_used);
+
+ INIT_SYM(reiserfs_fs_min_size);
+ INIT_SYM(reiserfs_fs_block_size);
+
+ INIT_SYM(reiserfs_fs_host_dal);
+ INIT_SYM(reiserfs_fs_close);
+
+ INIT_SYM(libreiserfs_gauge_create);
+ INIT_SYM(libreiserfs_gauge_free);
+
+ INIT_SYM(dal_realize);
+ INIT_SYM(dal_flags);
+
+ INIT_SYM(dal_block_size);
+ INIT_SYM(dal_len);
+
+ return 1;
+
+error_free_libreiserfs_handle:
+ dlclose(libreiserfs_handle);
+ libreiserfs_handle = NULL;
+error:
+ return 0;
+}
+
+static void reiserfs_ops_done()
+{
+ if (libdal_handle)
+ dlclose(libdal_handle);
+ if (libreiserfs_handle)
+ dlclose(libreiserfs_handle);
+}
+#endif /* DYNAMIC_LOADING */
+
+#define REISER_BLOCK_SIZES ((int[]){512, 1024, 2048, 4096, 8192, 0})
+
+#ifdef REISER_FULL_SUPPORT
+static PedFileSystemOps reiserfs_full_ops = {
+ .probe = reiserfs_probe,
+ .clobber = reiserfs_clobber,
+ .open = reiserfs_open,
+ .create = reiserfs_create,
+ .close = reiserfs_close,
+ .check = reiserfs_check,
+ .copy = reiserfs_copy,
+ .resize = reiserfs_resize,
+ .get_create_constraint = reiserfs_get_create_constraint,
+ .get_resize_constraint = reiserfs_get_resize_constraint,
+ .get_copy_constraint = reiserfs_get_copy_constraint
+};
+
+static PedFileSystemType reiserfs_full_type = {
+ .next = NULL,
+ .ops = &reiserfs_full_ops,
+ .name = "reiserfs",
+ .block_sizes = REISER_BLOCK_SIZES
+};
+#endif /* REISER_FULL_SUPPORT */
+
+static PedFileSystemOps reiserfs_simple_ops = {
+ .probe = reiserfs_probe,
+#ifdef DISCOVER_ONLY
+ .clobber = NULL,
+#else
+ .clobber = reiserfs_clobber,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemType reiserfs_simple_type = {
+ .next = NULL,
+ .ops = &reiserfs_simple_ops,
+ .name = "reiserfs",
+ .block_sizes = REISER_BLOCK_SIZES
+};
+
+void ped_file_system_reiserfs_init()
+{
+#ifdef DYNAMIC_LOADING
+ libreiserfs_present = reiserfs_ops_init();
+ if (libreiserfs_present) {
+ reiserfs_type = &reiserfs_full_type;
+ libreiserfs_exception_set_handler(exception_handler);
+ } else {
+ reiserfs_type = &reiserfs_simple_type;
+ }
+#else /* !DYNAMIC_LOADING */
+#ifdef REISER_FULL_SUPPORT
+ libreiserfs_exception_set_handler(exception_handler);
+ reiserfs_type = &reiserfs_full_type;
+#else
+ reiserfs_type = &reiserfs_simple_type;
+#endif
+#endif /* !DYNAMIC_LOADING */
+ ped_file_system_type_register(reiserfs_type);
+}
+
+void ped_file_system_reiserfs_done()
+{
+ ped_file_system_type_unregister(reiserfs_type);
+#ifdef DYNAMIC_LOADING
+ reiserfs_ops_done();
+#endif /* DYNAMIC_LOADING */
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.h b/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.h
new file mode 100644
index 0000000000..3cd68c0398
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/reiserfs/reiserfs.h
@@ -0,0 +1,108 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef REISERFS_H
+#define REISERFS_H
+
+#define REISERFS_API_VERSION 0
+
+#define REISERFS_SIGNATURE "ReIsErFs"
+#define REISER2FS_SIGNATURE "ReIsEr2Fs"
+#define REISER3FS_SIGNATURE "ReIsEr3Fs"
+
+#define DEFAULT_BLOCK_SIZE 4096
+
+struct reiserfs_super_block {
+ uint32_t s_block_count;
+ uint32_t s_free_blocks;
+ uint32_t s_root_block;
+ uint32_t s_journal_block;
+ uint32_t s_journal_dev;
+ uint32_t s_orig_journal_size;
+ uint32_t s_journal_trans_max;
+ uint32_t s_journal_block_count;
+ uint32_t s_journal_max_batch;
+ uint32_t s_journal_max_commit_age;
+ uint32_t s_journal_max_trans_age;
+ uint16_t s_blocksize;
+ uint16_t s_oid_maxsize;
+ uint16_t s_oid_cursize;
+ uint16_t s_state;
+ char s_magic[10];
+ uint16_t s_fsck_state;
+ uint32_t s_hash_function_code;
+ uint16_t s_tree_height;
+ uint16_t s_bmap_nr;
+ uint16_t s_version;
+ char padding[438];
+};
+
+typedef struct reiserfs_super_block reiserfs_super_block_t;
+
+enum reiserfs_exception_type {
+ EXCEPTION_INFORMATION = 1,
+ EXCEPTION_WARNING = 2,
+ EXCEPTION_ERROR = 3,
+ EXCEPTION_FATAL = 4,
+ EXCEPTION_BUG = 5,
+ EXCEPTION_NO_FEATURE = 6
+};
+
+typedef enum reiserfs_exception_type reiserfs_exception_type_t;
+
+enum reiserfs_exception_option {
+ EXCEPTION_UNHANDLED = 1 << 0,
+ EXCEPTION_FIX = 1 << 1,
+ EXCEPTION_YES = 1 << 2,
+ EXCEPTION_NO = 1 << 3,
+ EXCEPTION_OK = 1 << 4,
+ EXCEPTION_RETRY = 1 << 5,
+ EXCEPTION_IGNORE = 1 << 6,
+ EXCEPTION_CANCEL = 1 << 7
+};
+
+typedef enum reiserfs_exception_option reiserfs_exception_option_t;
+
+typedef void (reiserfs_gauge_handler_t)(const char *, unsigned int, void *, int, int, int);
+
+typedef void * reiserfs_exception_t;
+typedef void * reiserfs_gauge_t;
+typedef void * reiserfs_fs_t;
+
+#define FS_FORMAT_3_5 0
+#define FS_FORMAT_3_6 2
+
+#define SUPER_OFFSET_IN_BYTES 64*1024
+
+#define DEFAULT_JOURNAL_SIZE 8192
+
+#define JOURNAL_MIN_SIZE 512
+#define JOURNAL_MIN_TRANS 256
+#define JOURNAL_MAX_TRANS 1024
+
+#define JOURNAL_DEF_RATIO 8
+#define JOURNAL_MIN_RATIO 2
+#define JOURNAL_MAX_BATCH 900
+#define JOURNAL_MAX_COMMIT_AGE 30
+#define JOURNAL_MAX_TRANS_AGE 30
+
+#define TEA_HASH 1
+#define YURA_HASH 2
+#define R5_HASH 3
+
+#endif
diff --git a/usr/src/lib/libparted/common/libparted/fs/solaris_x86/solaris_x86.c b/usr/src/lib/libparted/common/libparted/fs/solaris_x86/solaris_x86.c
new file mode 100644
index 0000000000..29d5e935d6
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/solaris_x86/solaris_x86.c
@@ -0,0 +1,171 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * libparted - a library for manipulating disk partitions
+ *
+ * This module recognizes the Solaris x86 VTOC so that the
+ * partition can be identified as "solaris".
+ *
+ * Mark Logan <mark.logan@sun.com>
+ */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+#include <libintl.h>
+#define _(String) dgettext(PACKAGE, String)
+#else
+#define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <unistd.h>
+#include <string.h>
+
+#define BLOCK_SIZES ((int[2]) {512, 0})
+
+#define VTOC_SANE 0x600DDEEE
+#define LEN_DKL_VVOL 8
+#define V_NUMPAR 16 /* # of logical partitions */
+#define LEN_DKL_ASCII 128 /* length of dkl_asciilabel */
+
+#define LEN_DKL_PAD \
+ (512 - \
+ ((5 * sizeof (uint32_t)) + \
+ LEN_DKL_VVOL + \
+ (2 * sizeof (uint16_t)) + \
+ (10 * sizeof (uint32_t)) + \
+ (V_NUMPAR * sizeof (struct partition)) + \
+ (V_NUMPAR * sizeof (uint32_t)) + \
+ LEN_DKL_ASCII + \
+ (2 * (sizeof (uint16_t)))))
+
+#define DKL_MAGIC 0xDABE /* magic number */
+
+struct partition {
+ unsigned short p_tag; /* ID tag of partition */
+ unsigned short p_flag; /* permission flags */
+ long p_start; /* start sector no of partition */
+ long p_size; /* # of blocks in partition */
+};
+
+struct vtoc {
+ unsigned long v_bootinfo[3]; /* info for mboot (unsupported) */
+ unsigned long v_sanity; /* to verify vtoc sanity */
+ unsigned long v_version; /* layout version */
+ char v_volume[LEN_DKL_VVOL]; /* volume name */
+ unsigned short v_sectorsz; /* sector size in bytes */
+ unsigned short v_nparts; /* number of partitions */
+ unsigned long v_reserved[10]; /* free space */
+ struct partition v_part[V_NUMPAR]; /* partition headers */
+ int32_t timestamp[V_NUMPAR]; /* partition timestamp (unsupported) */
+ char v_asciilabel[LEN_DKL_ASCII]; /* for compatibility */
+ char dkl_pad[LEN_DKL_PAD]; /* unused part of 512 bytes */
+ uint16_t dkl_magic; /* identifies this label format */
+ uint16_t dkl_cksum; /* xor checksum of sector */
+};
+
+static PedGeometry*
+solaris_x86_probe(PedGeometry* geom)
+{
+ int8_t buf[512 * 3];
+ struct vtoc *pvtoc;
+ uint16_t *dkl_magic;
+
+ if (geom->length < 5)
+ return (0);
+ if (!ped_geometry_read(geom, buf, 1, 1))
+ return (0);
+
+ pvtoc = (struct vtoc *)buf;
+
+ if (pvtoc->v_sanity == VTOC_SANE && pvtoc->dkl_magic == DKL_MAGIC) {
+ PedSector block_size = pvtoc->v_sectorsz / 512;
+ /*
+ * Use the size of the backup slice:
+ */
+ PedSector block_count = pvtoc->v_part[2].p_size;
+ return ped_geometry_new(geom->dev, geom->start,
+ block_size * block_count);
+ }
+
+ return (NULL);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+solaris_x86_clobber(PedGeometry* geom)
+{
+ char buf[512*3];
+
+ if (!ped_geometry_read(geom, buf, 1, 1))
+ return (0);
+
+ memset(buf, 0, sizeof (struct vtoc));
+
+ return (ped_geometry_write(geom, buf, 1, 1));
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps solaris_x86_ops = {
+ .probe = solaris_x86_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = solaris_x86_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemType solaris_x86_type = {
+ .next = NULL,
+ .ops = &solaris_x86_ops,
+ .name = "solaris",
+ .block_sizes = BLOCK_SIZES
+};
+
+void
+ped_file_system_solaris_x86_init()
+{
+ ped_file_system_type_register(&solaris_x86_type);
+}
+
+void
+ped_file_system_solaris_x86_done()
+{
+ ped_file_system_type_unregister(&solaris_x86_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/ufs/ufs.c b/usr/src/lib/libparted/common/libparted/fs/ufs/ufs.c
new file mode 100644
index 0000000000..060c6bebc4
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/ufs/ufs.c
@@ -0,0 +1,324 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Ben Collins <bcollins@debian.org>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+#include <parted/debug.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <unistd.h>
+#include <string.h>
+
+#define SUN_UFS_BLOCK_SIZES ((int[2]){512, 0})
+#define HP_UFS_BLOCK_SIZES ((int[2]){512, 0})
+
+
+/* taken from ufs_fs.h in Linux */
+#define UFS_MAXNAMLEN 255
+#define UFS_MAXMNTLEN 512
+#define UFS_MAXCSBUFS 31
+#define UFS_LINK_MAX 32000
+
+#define UFS_MAGIC 0x00011954
+#define UFS_MAGIC_LFN 0x00095014
+#define UFS_MAGIC_FEA 0x00195612
+#define UFS_MAGIC_4GB 0x05231994
+
+struct ufs_csum {
+ uint32_t cs_ndir; /* number of directories */
+ uint32_t cs_nbfree; /* number of free blocks */
+ uint32_t cs_nifree; /* number of free inodes */
+ uint32_t cs_nffree; /* number of free frags */
+};
+
+struct ufs_super_block {
+ uint32_t fs_link; /* UNUSED */
+ uint32_t fs_rlink; /* UNUSED */
+ uint32_t fs_sblkno; /* addr of super-block in filesys */
+ uint32_t fs_cblkno; /* offset of cyl-block in filesys */
+ uint32_t fs_iblkno; /* offset of inode-blocks in filesys */
+ uint32_t fs_dblkno; /* offset of first data after cg */
+ uint32_t fs_cgoffset; /* cylinder group offset in cylinder */
+ uint32_t fs_cgmask; /* used to calc mod fs_ntrak */
+ uint32_t fs_time; /* last time written -- time_t */
+ uint32_t fs_size; /* number of blocks in fs */
+ uint32_t fs_dsize; /* number of data blocks in fs */
+ uint32_t fs_ncg; /* number of cylinder groups */
+ uint32_t fs_bsize; /* size of basic blocks in fs */
+ uint32_t fs_fsize; /* size of frag blocks in fs */
+ uint32_t fs_frag; /* number of frags in a block in fs */
+/* these are configuration parameters */
+ uint32_t fs_minfree; /* minimum percentage of free blocks */
+ uint32_t fs_rotdelay; /* num of ms for optimal next block */
+ uint32_t fs_rps; /* disk revolutions per second */
+/* these fields can be computed from the others */
+ uint32_t fs_bmask; /* ``blkoff'' calc of blk offsets */
+ uint32_t fs_fmask; /* ``fragoff'' calc of frag offsets */
+ uint32_t fs_bshift; /* ``lblkno'' calc of logical blkno */
+ uint32_t fs_fshift; /* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+ uint32_t fs_maxcontig; /* max number of contiguous blks */
+ uint32_t fs_maxbpg; /* max number of blks per cyl group */
+/* these fields can be computed from the others */
+ uint32_t fs_fragshift; /* block to frag shift */
+ uint32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ uint32_t fs_sbsize; /* actual size of super block */
+ uint32_t fs_csmask; /* csum block offset */
+ uint32_t fs_csshift; /* csum block number */
+ uint32_t fs_nindir; /* value of NINDIR */
+ uint32_t fs_inopb; /* value of INOPB */
+ uint32_t fs_nspf; /* value of NSPF */
+/* yet another configuration parameter */
+ uint32_t fs_optim; /* optimization preference, see below */
+/* these fields are derived from the hardware */
+ union {
+ struct {
+ uint32_t fs_npsect; /* # sectors/track including spares */
+ } fs_sun;
+ struct {
+ int32_t fs_state; /* file system state time stamp */
+ } fs_sunx86;
+ } fs_u1;
+ uint32_t fs_interleave; /* hardware sector interleave */
+ uint32_t fs_trackskew; /* sector 0 skew, per track */
+/* a unique id for this file system (currently unused and unmaintained) */
+/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */
+/* Neither of those fields is used in the Tahoe code right now but */
+/* there could be problems if they are. */
+ uint32_t fs_id[2]; /* file system id */
+/* sizes determined by number of cylinder groups and their sizes */
+ uint32_t fs_csaddr; /* blk addr of cyl grp summary area */
+ uint32_t fs_cssize; /* size of cyl grp summary area */
+ uint32_t fs_cgsize; /* cylinder group size */
+/* these fields are derived from the hardware */
+ uint32_t fs_ntrak; /* tracks per cylinder */
+ uint32_t fs_nsect; /* sectors per track */
+ uint32_t fs_spc; /* sectors per cylinder */
+/* this comes from the disk driver partitioning */
+ uint32_t fs_ncyl; /* cylinders in file system */
+/* these fields can be computed from the others */
+ uint32_t fs_cpg; /* cylinders per group */
+ uint32_t fs_ipg; /* inodes per group */
+ uint32_t fs_fpg; /* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+ struct ufs_csum fs_cstotal; /* cylinder summary information */
+/* these fields are cleared at mount time */
+ int8_t fs_fmod; /* super block modified flag */
+ int8_t fs_clean; /* file system is clean flag */
+ int8_t fs_ronly; /* mounted read-only flag */
+ int8_t fs_flags; /* currently unused flag */
+ int8_t fs_fsmnt[UFS_MAXMNTLEN]; /* name mounted on */
+/* these fields retain the current block allocation info */
+ uint32_t fs_cgrotor; /* last cg searched */
+ uint32_t fs_csp[UFS_MAXCSBUFS]; /* list of fs_cs info buffers */
+ uint32_t fs_maxcluster;
+ uint32_t fs_cpc; /* cyl per cycle in postbl */
+ uint16_t fs_opostbl[16][8]; /* old rotation block list head */
+ union {
+ struct {
+ int32_t fs_sparecon[53];/* reserved for future constants */
+ int32_t fs_reclaim;
+ int32_t fs_sparecon2[1];
+ int32_t fs_state; /* file system state time stamp */
+ uint32_t fs_qbmask[2]; /* ~usb_bmask */
+ uint32_t fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sun;
+ struct {
+ int32_t fs_sparecon[53];/* reserved for future constants */
+ int32_t fs_reclaim;
+ int32_t fs_sparecon2[1];
+ uint32_t fs_npsect; /* # sectors/track including spares */
+ uint32_t fs_qbmask[2]; /* ~usb_bmask */
+ uint32_t fs_qfmask[2]; /* ~usb_fmask */
+ } fs_sunx86;
+ struct {
+ int32_t fs_sparecon[50];/* reserved for future constants */
+ int32_t fs_contigsumsize;/* size of cluster summary array */
+ int32_t fs_maxsymlinklen;/* max length of an internal symlink */
+ int32_t fs_inodefmt; /* format of on-disk inodes */
+ uint32_t fs_maxfilesize[2]; /* max representable file size */
+ uint32_t fs_qbmask[2]; /* ~usb_bmask */
+ uint32_t fs_qfmask[2]; /* ~usb_fmask */
+ int32_t fs_state; /* file system state time stamp */
+ } fs_44;
+ } fs_u2;
+ int32_t fs_postblformat; /* format of positional layout tables */
+ int32_t fs_nrpos; /* number of rotational positions */
+ int32_t fs_postbloff; /* (__s16) rotation block list head */
+ int32_t fs_rotbloff; /* (uint8_t) blocks for each rotation */
+ int32_t fs_magic; /* magic number */
+ uint8_t fs_space[4]; /* list of blocks for each rotation */
+};
+
+static PedGeometry*
+ufs_probe_sun (PedGeometry* geom)
+{
+ int8_t buf[512 * 3];
+ struct ufs_super_block *sb;
+
+ if (geom->length < 5)
+ return 0;
+ if (!ped_geometry_read (geom, buf, 16, 3))
+ return 0;
+
+ sb = (struct ufs_super_block *)buf;
+
+ if (PED_BE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) {
+ PedSector block_size = PED_BE32_TO_CPU(sb->fs_bsize) / 512;
+ PedSector block_count = PED_BE32_TO_CPU(sb->fs_size);
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+ if (PED_LE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) {
+ PedSector block_size = PED_LE32_TO_CPU(sb->fs_bsize) / 512;
+ PedSector block_count = PED_LE32_TO_CPU(sb->fs_size);
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+ return NULL;
+}
+
+static PedGeometry*
+ufs_probe_hp (PedGeometry* geom)
+{
+ int8_t buf[1536];
+ struct ufs_super_block *sb;
+ PedSector block_size;
+ PedSector block_count;
+
+ if (geom->length < 5)
+ return 0;
+ if (!ped_geometry_read (geom, buf, 16, 3))
+ return 0;
+
+ sb = (struct ufs_super_block *)buf;
+
+ /* Try sane bytesex */
+ switch (PED_BE32_TO_CPU(sb->fs_magic)) {
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ block_size = PED_BE32_TO_CPU(sb->fs_bsize) / 512;
+ block_count = PED_BE32_TO_CPU(sb->fs_size);
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+
+ /* Try perverted bytesex */
+ switch (PED_LE32_TO_CPU(sb->fs_magic)) {
+ case UFS_MAGIC_LFN:
+ case UFS_MAGIC_FEA:
+ case UFS_MAGIC_4GB:
+ block_size = PED_LE32_TO_CPU(sb->fs_bsize) / 512;
+ block_count = PED_LE32_TO_CPU(sb->fs_size);
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+ufs_clobber (PedGeometry* geom)
+{
+ char buf[1536];
+
+ if (!ped_geometry_read (geom, buf, 16, 3))
+ return 0;
+
+ memset (buf, 0, sizeof(struct ufs_super_block));
+
+ return ped_geometry_write (geom, buf, 16, 3);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps ufs_ops_sun = {
+ .probe = ufs_probe_sun,
+#ifndef DISCOVER_ONLY
+ .clobber = ufs_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemOps ufs_ops_hp = {
+ .probe = ufs_probe_hp,
+#ifndef DISCOVER_ONLY
+ .clobber = ufs_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemType ufs_type_sun = {
+ .next = NULL,
+ .ops = &ufs_ops_sun,
+ .name = "sun-ufs",
+ .block_sizes = SUN_UFS_BLOCK_SIZES
+};
+
+static PedFileSystemType ufs_type_hp = {
+ .next = NULL,
+ .ops = &ufs_ops_hp,
+ .name = "hp-ufs",
+ .block_sizes = HP_UFS_BLOCK_SIZES
+};
+
+void
+ped_file_system_ufs_init ()
+{
+ PED_ASSERT (sizeof (struct ufs_super_block) == 1380, return);
+
+ ped_file_system_type_register (&ufs_type_sun);
+ ped_file_system_type_register (&ufs_type_hp);
+}
+
+void
+ped_file_system_ufs_done ()
+{
+ ped_file_system_type_unregister (&ufs_type_hp);
+ ped_file_system_type_unregister (&ufs_type_sun);
+}
diff --git a/usr/src/lib/libparted/common/libparted/fs/xfs/platform_defs.h b/usr/src/lib/libparted/common/libparted/fs/xfs/platform_defs.h
new file mode 100644
index 0000000000..8696273b11
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/xfs/platform_defs.h
@@ -0,0 +1,117 @@
+/* include/platform_defs.h. Generated automatically by configure. */
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * 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/
+ *
+ * @configure_input@
+ */
+#ifndef __XFS_PLATFORM_DEFS_H__
+#define __XFS_PLATFORM_DEFS_H__
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 1)
+# define constpp const char * const *
+#else
+# define constpp char * const *
+#endif
+
+#ifdef __sparc__
+# ifndef O_DIRECT
+# define O_DIRECT 0x100000
+# endif
+#endif
+
+#if defined(__sun)
+typedef off_t loff_t;
+#endif
+
+typedef loff_t xfs_off_t;
+typedef uint64_t xfs_ino_t;
+typedef uint32_t xfs_dev_t;
+typedef int64_t xfs_daddr_t;
+typedef char* xfs_caddr_t;
+
+/* long and pointer must be either 32 bit or 64 bit */
+/* #undef HAVE_64BIT_LONG */
+#define HAVE_32BIT_LONG 1
+#define HAVE_32BIT_PTR 1
+/* #undef HAVE_64BIT_PTR */
+
+/* Check if __psint_t is set to something meaningful */
+/* #undef HAVE___PSINT_T */
+#ifndef HAVE___PSINT_T
+# ifdef HAVE_32BIT_PTR
+typedef int __psint_t;
+# elif defined HAVE_64BIT_PTR
+# ifdef HAVE_64BIT_LONG
+typedef long __psint_t;
+# else
+/* This is a very strange architecture, which has 64 bit pointers but
+ * not 64 bit longs. So, I'd just punt here and assume long long is Ok */
+typedef long long __psint_t;
+# endif
+# else
+# error Unknown pointer size
+# endif
+#endif
+
+/* Check if __psunsigned_t is set to something meaningful */
+/* #undef HAVE___PSUNSIGNED_T */
+#ifndef HAVE___PSUNSIGNED_T
+# ifdef HAVE_32BIT_PTR
+typedef unsigned int __psunsigned_t;
+# elif defined HAVE_64BIT_PTR
+# ifdef HAVE_64BIT_LONG
+typedef long __psunsigned_t;
+# else
+/* This is a very strange architecture, which has 64 bit pointers but
+ * not 64 bit longs. So, I'd just punt here and assume long long is Ok */
+typedef unsigned long long __psunsigned_t;
+# endif
+# else
+# error Unknown pointer size
+# endif
+#endif
+
+#ifdef DEBUG
+# define ASSERT assert
+#else
+# define ASSERT(EX) ((void) 0)
+#endif
+
+#endif /* __XFS_PLATFORM_DEFS_H__ */
diff --git a/usr/src/lib/libparted/common/libparted/fs/xfs/xfs.c b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs.c
new file mode 100644
index 0000000000..2ca51a88a9
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs.c
@@ -0,0 +1,119 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <uuid/uuid.h>
+#include "platform_defs.h"
+#include "xfs_types.h"
+#include "xfs_sb.h"
+
+#define XFS_BLOCK_SIZES ((int[2]){512, 0})
+
+static PedGeometry*
+xfs_probe (PedGeometry* geom)
+{
+ PedSector block_size;
+ PedSector block_count;
+ union {
+ struct xfs_sb sb;
+ char bytes [512];
+ } buf;
+
+ if (geom->length < XFS_SB_DADDR + 1)
+ return NULL;
+ if (!ped_geometry_read (geom, &buf, XFS_SB_DADDR, 1))
+ return NULL;
+
+ if (PED_LE32_TO_CPU (buf.sb.sb_magicnum) == XFS_SB_MAGIC) {
+ block_size = PED_LE32_TO_CPU (buf.sb.sb_blocksize) / 512;
+ block_count = PED_LE64_TO_CPU (buf.sb.sb_dblocks);
+
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+
+ if (PED_BE32_TO_CPU (buf.sb.sb_magicnum) == XFS_SB_MAGIC) {
+ block_size = PED_BE32_TO_CPU (buf.sb.sb_blocksize) / 512;
+ block_count = PED_BE64_TO_CPU (buf.sb.sb_dblocks);
+
+ return ped_geometry_new (geom->dev, geom->start,
+ block_size * block_count);
+ }
+
+ return NULL;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+xfs_clobber (PedGeometry* geom)
+{
+ char buf[512];
+
+ memset (buf, 0, 512);
+ return ped_geometry_write (geom, buf, XFS_SB_DADDR, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedFileSystemOps xfs_ops = {
+ .probe = xfs_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = xfs_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .open = NULL,
+ .create = NULL,
+ .close = NULL,
+ .check = NULL,
+ .copy = NULL,
+ .resize = NULL,
+ .get_create_constraint = NULL,
+ .get_resize_constraint = NULL,
+ .get_copy_constraint = NULL
+};
+
+static PedFileSystemType xfs_type = {
+ .next = NULL,
+ .ops = &xfs_ops,
+ .name = "xfs",
+ .block_sizes = XFS_BLOCK_SIZES
+};
+
+void
+ped_file_system_xfs_init ()
+{
+ ped_file_system_type_register (&xfs_type);
+}
+
+void
+ped_file_system_xfs_done ()
+{
+ ped_file_system_type_unregister (&xfs_type);
+}
+
diff --git a/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_sb.h b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_sb.h
new file mode 100644
index 0000000000..139cd07c0f
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_sb.h
@@ -0,0 +1,489 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * 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 512-byte buffer at daddr_t 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_EXTFLGBIT 0x1000
+#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#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)
+#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) \
+ (((ia) || (dia) || (extflag) || (dirv2)) ? \
+ (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)) : \
+ XFS_SB_VERSION_1)
+
+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_dummy[7]; /* padding */
+} 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_DUMMY,
+ 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)
+#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_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
+
+/*
+ * end of superblock version macros
+ */
+
+#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in file system/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
+
+/*
+ * 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)
+
+#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
+
+#endif /* __XFS_SB_H__ */
diff --git a/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_types.h b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_types.h
new file mode 100644
index 0000000000..643a19b95e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/fs/xfs/xfs_types.h
@@ -0,0 +1,302 @@
+/*
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * 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__
+
+/*
+ * Some types are conditional based on the selected configuration.
+ * Set XFS_BIG_FILES=1 or 0 and XFS_BIG_FILESYSTEMS=1 or 0 depending
+ * on the desired configuration.
+ * XFS_BIG_FILES needs pgno_t to be 64 bits (64-bit kernels).
+ * XFS_BIG_FILESYSTEMS needs daddr_t to be 64 bits (N32 and 64-bit kernels).
+ *
+ * Expect these to be set from klocaldefs, or from the machine-type
+ * defs files for the normal case.
+ */
+
+#define XFS_BIG_FILES 1
+#define XFS_BIG_FILESYSTEMS 1
+
+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_FILESYSTEMS
+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
+#if XFS_BIG_FILES
+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 */
+#else
+typedef uint32_t xfs_fileoff_t; /* block number in a file */
+typedef int32_t xfs_sfiloff_t; /* signed block number in a file */
+typedef uint32_t xfs_filblks_t; /* number of blocks in a file */
+#endif
+
+typedef uint8_t xfs_arch_t; /* architecutre 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 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;
+
+
+#ifdef CONFIG_PROC_FS
+/*
+ * 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_QUOTA_OPS (XFSSTAT_END_ATTRIBUTE_OPS+8)
+ 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;
+# define XFSSTAT_END_INODE_CLUSTER (XFSSTAT_END_QUOTA_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 */
+ struct xfsstats_xpc {
+ uint64_t xs_xstrat_bytes;
+ uint64_t xs_write_bytes;
+ uint64_t xs_read_bytes;
+ } xpc;
+} xfsstats;
+
+# define XFS_STATS_INC(count) ( xfsstats.##count ++ )
+# define XFS_STATS_DEC(count) ( xfsstats.##count -- )
+# define XFS_STATS_ADD(count, inc) ( xfsstats.##count += (inc) )
+# define XFS_STATS64_INC(count) ( xfsstats.xpc.##count ++ )
+# define XFS_STATS64_ADD(count, inc) ( xfsstats.xpc.##count += (inc) )
+#else /* !CONFIG_PROC_FS */
+# define XFS_STATS_INC(count)
+# define XFS_STATS_DEC(count)
+# define XFS_STATS_ADD(count, inc)
+# define XFS_STATS64_INC(count)
+# define XFS_STATS64_ADD(count, inc)
+#endif /* !CONFIG_PROC_FS */
+
+
+#ifdef __KERNEL__
+
+/* juggle IRIX device numbers - still used in ondisk structures */
+
+#define IRIX_DEV_BITSMAJOR 14
+#define IRIX_DEV_BITSMINOR 18
+#define IRIX_DEV_MAXMAJ 0x1ff
+#define IRIX_DEV_MAXMIN 0x3ffff
+#define IRIX_DEV_MAJOR(dev) ((int)(((unsigned)(dev)>>IRIX_DEV_BITSMINOR) \
+ & IRIX_DEV_MAXMAJ))
+#define IRIX_DEV_MINOR(dev) ((int)((dev)&IRIX_DEV_MAXMIN))
+#define IRIX_MKDEV(major,minor) ((xfs_dev_t)(((major)<<IRIX_DEV_BITSMINOR) \
+ | (minor&IRIX_DEV_MAXMIN)))
+
+#define IRIX_DEV_TO_KDEVT(dev) MKDEV(IRIX_DEV_MAJOR(dev),IRIX_DEV_MINOR(dev))
+#define IRIX_DEV_TO_DEVT(dev) ((IRIX_DEV_MAJOR(dev)<<8)|IRIX_DEV_MINOR(dev))
+
+/* __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
+
+
+/*
+ * struct for passing owner/requestor id
+ */
+typedef struct flid {
+#ifdef CELL_CAPABLE
+ pid_t fl_pid;
+ sysid_t fl_sysid;
+#endif
+} flid_t;
+
+#endif /* __KERNEL__ */
+
+#endif /* !__XFS_TYPES_H */
diff --git a/usr/src/lib/libparted/common/libparted/labels/aix.c b/usr/src/lib/libparted/common/libparted/labels/aix.c
new file mode 100644
index 0000000000..43c99d17df
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/aix.c
@@ -0,0 +1,295 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Matt Wilson <msw@redhat.com>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define AIX_LABEL_MAGIC 0xc9c2d4c1
+
+static PedDiskType aix_disk_type;
+
+static inline int
+aix_label_magic_get (const char *label)
+{
+ return *(unsigned int *)label;
+}
+
+static inline void
+aix_label_magic_set (char *label, int magic_val)
+{
+ *(unsigned int *)label = magic_val;
+}
+
+/* Read a single sector, of length DEV->sector_size, into malloc'd storage.
+ If the read fails, free the memory and return zero without modifying *BUF.
+ Otherwise, set *BUF to the new buffer and return 1. */
+static int
+read_sector (const PedDevice *dev, char **buf)
+{
+ char *b = ped_malloc (dev->sector_size);
+ PED_ASSERT (b != NULL, return 0);
+ if (!ped_device_read (dev, b, 0, 1)) {
+ ped_free (b);
+ return 0;
+ }
+ *buf = b;
+ return 1;
+}
+
+static int
+aix_probe (const PedDevice *dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+
+ char *label;
+ if (!read_sector (dev, &label))
+ return 0;
+ unsigned int magic = aix_label_magic_get (label);
+ ped_free (label);
+ return magic == AIX_LABEL_MAGIC;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+aix_clobber (PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (!aix_probe (dev))
+ return 0;
+
+ char *label;
+ if (!read_sector (dev, &label))
+ return 0;
+
+ aix_label_magic_set (label, 0);
+ int result = ped_device_write (dev, label, 0, 1);
+ ped_free (label);
+ return result;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk*
+aix_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+
+ disk = _ped_disk_alloc (dev, &aix_disk_type);
+ if (!disk)
+ return NULL;
+
+ return disk;
+}
+
+static PedDisk*
+aix_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &aix_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ return new_disk;
+}
+
+static void
+aix_free (PedDisk *disk)
+{
+ _ped_disk_free (disk);
+}
+
+static int
+aix_read (PedDisk* disk)
+{
+ ped_disk_delete_all (disk);
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for reading AIX disk labels is "
+ "is not implemented yet."));
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+aix_write (const PedDisk* disk)
+{
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for writing AIX disk labels is "
+ "is not implemented yet."));
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+aix_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for adding partitions to AIX disk "
+ "labels is not implemented yet."));
+ return NULL;
+}
+
+static PedPartition*
+aix_partition_duplicate (const PedPartition* part)
+{
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for duplicating partitions in AIX "
+ "disk labels is not implemented yet."));
+ return NULL;
+}
+
+static void
+aix_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ _ped_partition_free (part);
+}
+
+static int
+aix_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for setting system type of partitions "
+ "in AIX disk labels is not implemented yet."));
+ return 0;
+}
+
+static int
+aix_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ ped_exception_throw (PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Support for setting flags "
+ "in AIX disk labels is not implemented yet."));
+ return 0;
+}
+
+static int
+aix_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ return 0;
+}
+
+
+static int
+aix_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ return 0;
+}
+
+
+static int
+aix_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return 4;
+}
+
+static int
+aix_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ return 1;
+}
+
+static int
+aix_partition_enumerate (PedPartition* part)
+{
+ return 1;
+}
+
+static int
+aix_alloc_metadata (PedDisk* disk)
+{
+ return 1;
+}
+
+static PedDiskOps aix_disk_ops = {
+ .probe = aix_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = aix_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = aix_alloc,
+ .duplicate = aix_duplicate,
+ .free = aix_free,
+ .read = aix_read,
+#ifndef DISCOVER_ONLY
+ .write = aix_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = aix_partition_new,
+ .partition_duplicate = aix_partition_duplicate,
+ .partition_destroy = aix_partition_destroy,
+ .partition_set_system = aix_partition_set_system,
+ .partition_set_flag = aix_partition_set_flag,
+ .partition_get_flag = aix_partition_get_flag,
+ .partition_is_flag_available = aix_partition_is_flag_available,
+ .partition_align = aix_partition_align,
+ .partition_enumerate = aix_partition_enumerate,
+ .alloc_metadata = aix_alloc_metadata,
+ .get_max_primary_partition_count =
+ aix_get_max_primary_partition_count,
+
+ .partition_set_name = NULL,
+ .partition_get_name = NULL,
+};
+
+static PedDiskType aix_disk_type = {
+ .next = NULL,
+ .name = "aix",
+ .ops = &aix_disk_ops,
+ .features = 0
+};
+
+void
+ped_disk_aix_init ()
+{
+ ped_disk_type_register (&aix_disk_type);
+}
+
+void
+ped_disk_aix_done ()
+{
+ ped_disk_type_unregister (&aix_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/bsd.c b/usr/src/lib/libparted/common/libparted/labels/bsd.c
new file mode 100644
index 0000000000..c570d2a1e1
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/bsd.c
@@ -0,0 +1,633 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Matt Wilson <msw@redhat.com>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* struct's & #define's stolen from libfdisk, which probably came from
+ * Linux...
+ */
+
+#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
+#define BSD_MAXPARTITIONS 8
+#define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */
+#define BSD_LABEL_OFFSET 64
+
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+typedef struct _BSDRawPartition BSDRawPartition;
+typedef struct _BSDRawLabel BSDRawLabel;
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct _BSDRawPartition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* file system basic fragment size */
+ uint8_t p_fstype; /* file system type, see below */
+ uint8_t p_frag; /* file system fragments per block */
+ uint16_t p_cpg; /* file system cylinders per group */
+} __attribute__((packed));
+#ifdef __sun
+#pragma pack()
+#endif
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct _BSDRawLabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ int8_t d_typename[16]; /* type name, e.g. "eagle" */
+ int8_t d_packname[16]; /* pack identifier */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+#define NDDATA 5
+ uint32_t d_drivedata[NDDATA]; /* drive-type specific information */
+#define NSPARE 5
+ uint32_t d_spare[NSPARE]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+
+ /* file system and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+ BSDRawPartition d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+} __attribute__((packed));
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef struct {
+ char boot_code [512];
+} BSDDiskData;
+
+typedef struct {
+ uint8_t type;
+} BSDPartitionData;
+
+static PedDiskType bsd_disk_type;
+
+/* XXX fixme: endian? */
+static unsigned short
+xbsd_dkcksum (BSDRawLabel *lp) {
+ unsigned short *start, *end;
+ unsigned short sum = 0;
+
+ lp->d_checksum = 0;
+ start = (u_short*) lp;
+ end = (u_short*) &lp->d_partitions [
+ PED_LE16_TO_CPU (lp->d_npartitions)];
+ while (start < end)
+ sum ^= *start++;
+ return sum;
+}
+
+/* XXX fixme: endian? */
+static void
+alpha_bootblock_checksum (char *boot) {
+ uint64_t *dp, sum;
+ int i;
+
+ dp = (uint64_t *)boot;
+ sum = 0;
+ for (i = 0; i < 63; i++)
+ sum += dp[i];
+ dp[63] = sum;
+}
+
+
+static int
+bsd_probe (const PedDevice *dev)
+{
+ char boot[512];
+ BSDRawLabel *label;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ if (!ped_device_read (dev, boot, 0, 1))
+ return 0;
+
+ label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET);
+
+ alpha_bootblock_checksum(boot);
+
+ /* check magic */
+ if (PED_LE32_TO_CPU (label->d_magic) != BSD_DISKMAGIC)
+ return 0;
+
+ return 1;
+}
+
+static PedDisk*
+bsd_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ BSDDiskData* bsd_specific;
+ BSDRawLabel* label;
+
+ PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return 0);
+
+ disk = _ped_disk_alloc ((PedDevice*)dev, &bsd_disk_type);
+ if (!disk)
+ goto error;
+ disk->disk_specific = bsd_specific = ped_malloc (sizeof (BSDDiskData));
+ if (!bsd_specific)
+ goto error_free_disk;
+ /* Initialize the first byte to zero, so that the code in bsd_write
+ knows to call _probe_and_add_boot_code. Initializing all of the
+ remaining buffer is a little wasteful, but the alternative is to
+ figure out why a block at offset 340 would otherwise be used
+ uninitialized. */
+ memset(bsd_specific->boot_code, 0, sizeof (bsd_specific->boot_code));
+
+ label = (BSDRawLabel*) (bsd_specific->boot_code + BSD_LABEL_OFFSET);
+
+ label->d_magic = PED_CPU_TO_LE32 (BSD_DISKMAGIC);
+ label->d_type = PED_CPU_TO_LE16 (BSD_DTYPE_SCSI);
+ label->d_flags = 0;
+ label->d_secsize = PED_CPU_TO_LE16 (dev->sector_size);
+ label->d_nsectors = PED_CPU_TO_LE32 (dev->bios_geom.sectors);
+ label->d_ntracks = PED_CPU_TO_LE32 (dev->bios_geom.heads);
+ label->d_ncylinders = PED_CPU_TO_LE32 (dev->bios_geom.cylinders);
+ label->d_secpercyl = PED_CPU_TO_LE32 (dev->bios_geom.sectors
+ * dev->bios_geom.heads);
+ label->d_secperunit
+ = PED_CPU_TO_LE32 (dev->bios_geom.sectors
+ * dev->bios_geom.heads
+ * dev->bios_geom.cylinders);
+
+ label->d_rpm = PED_CPU_TO_LE16 (3600);
+ label->d_interleave = PED_CPU_TO_LE16 (1);;
+ label->d_trackskew = 0;
+ label->d_cylskew = 0;
+ label->d_headswitch = 0;
+ label->d_trkseek = 0;
+
+ label->d_magic2 = PED_CPU_TO_LE32 (BSD_DISKMAGIC);
+ label->d_bbsize = PED_CPU_TO_LE32 (BSD_BBSIZE);
+ label->d_sbsize = PED_CPU_TO_LE32 (BSD_SBSIZE);
+
+ label->d_npartitions = 0;
+ label->d_checksum = xbsd_dkcksum (label);
+ return disk;
+
+error_free_disk:
+ ped_free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk*
+bsd_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ BSDDiskData* new_bsd_data;
+ BSDDiskData* old_bsd_data = (BSDDiskData*) disk->disk_specific;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &bsd_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ new_bsd_data = (BSDDiskData*) new_disk->disk_specific;
+ memcpy (new_bsd_data->boot_code, old_bsd_data->boot_code, 512);
+ return new_disk;
+}
+
+static void
+bsd_free (PedDisk* disk)
+{
+ ped_free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+bsd_clobber (PedDevice* dev)
+{
+ char boot [512];
+ BSDRawLabel* label = (BSDRawLabel *) (boot + BSD_LABEL_OFFSET);
+
+ if (!ped_device_read (dev, boot, 0, 1))
+ return 0;
+ label->d_magic = 0;
+ return ped_device_write (dev, (void*) boot, 0, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+bsd_read (PedDisk* disk)
+{
+ BSDDiskData* bsd_specific = (BSDDiskData*) disk->disk_specific;
+ BSDRawLabel* label;
+ int i;
+
+ ped_disk_delete_all (disk);
+
+ if (!ped_device_read (disk->dev, bsd_specific->boot_code, 0, 1))
+ goto error;
+ label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET);
+
+ for (i = 1; i <= BSD_MAXPARTITIONS; i++) {
+ PedPartition* part;
+ BSDPartitionData* bsd_part_data;
+ PedSector start;
+ PedSector end;
+ PedConstraint* constraint_exact;
+
+ if (!label->d_partitions[i - 1].p_size
+ || !label->d_partitions[i - 1].p_fstype)
+ continue;
+ start = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset);
+ end = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset)
+ + PED_LE32_TO_CPU(label->d_partitions[i - 1].p_size) - 1;
+ part = ped_partition_new (disk, 0, NULL, start, end);
+ if (!part)
+ goto error;
+ bsd_part_data = part->disk_specific;
+ bsd_part_data->type = label->d_partitions[i - 1].p_fstype;
+ part->num = i;
+ part->fs_type = ped_file_system_probe (&part->geom);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error;
+ ped_constraint_destroy (constraint_exact);
+ }
+
+ return 1;
+
+error:
+ return 0;
+}
+
+static void
+_probe_and_add_boot_code (const PedDisk* disk)
+{
+ BSDDiskData* bsd_specific;
+ BSDRawLabel* old_label;
+ char old_boot_code [512];
+
+ bsd_specific = (BSDDiskData*) disk->disk_specific;
+ old_label = (BSDRawLabel*) (old_boot_code + BSD_LABEL_OFFSET);
+
+ if (!ped_device_read (disk->dev, old_boot_code, 0, 1))
+ return;
+ if (old_boot_code [0]
+ && old_label->d_magic == PED_CPU_TO_LE32 (BSD_DISKMAGIC))
+ memcpy (bsd_specific->boot_code, old_boot_code, 512);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+bsd_write (const PedDisk* disk)
+{
+ BSDDiskData* bsd_specific;
+ BSDRawLabel* label;
+ BSDPartitionData* bsd_data;
+ PedPartition* part;
+ int i;
+ int max_part = 0;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ bsd_specific = (BSDDiskData*) disk->disk_specific;
+ label = (BSDRawLabel *) (bsd_specific->boot_code + BSD_LABEL_OFFSET);
+
+ if (!bsd_specific->boot_code [0])
+ _probe_and_add_boot_code (disk);
+
+ memset (label->d_partitions, 0,
+ sizeof (BSDRawPartition) * BSD_MAXPARTITIONS);
+
+ for (i = 1; i <= BSD_MAXPARTITIONS; i++) {
+ part = ped_disk_get_partition (disk, i);
+ if (!part)
+ continue;
+ bsd_data = part->disk_specific;
+ label->d_partitions[i - 1].p_fstype = bsd_data->type;
+ label->d_partitions[i - 1].p_offset
+ = PED_CPU_TO_LE32 (part->geom.start);
+ label->d_partitions[i - 1].p_size
+ = PED_CPU_TO_LE32 (part->geom.length);
+ max_part = i;
+ }
+
+ label->d_npartitions = PED_CPU_TO_LE16 (max_part) + 1;
+ label->d_checksum = xbsd_dkcksum (label);
+
+ alpha_bootblock_checksum (bsd_specific->boot_code);
+
+ if (!ped_device_write (disk->dev, (void*) bsd_specific->boot_code,
+ 0, 1))
+ goto error;
+ return ped_device_sync (disk->dev);
+
+error:
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+bsd_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ BSDPartitionData* bsd_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = bsd_data = ped_malloc (sizeof (BSDPartitionData));
+ if (!bsd_data)
+ goto error_free_part;
+ bsd_data->type = 0;
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+
+ ped_free (bsd_data);
+error_free_part:
+ ped_free (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+bsd_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ BSDPartitionData* new_bsd_data;
+ BSDPartitionData* old_bsd_data;
+
+ new_part = ped_partition_new (part->disk, part->type,
+ part->fs_type, part->geom.start,
+ part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_bsd_data = (BSDPartitionData*) part->disk_specific;
+ new_bsd_data = (BSDPartitionData*) new_part->disk_specific;
+ new_bsd_data->type = old_bsd_data->type;
+ return new_part;
+}
+
+static void
+bsd_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part))
+ ped_free (part->disk_specific);
+ _ped_partition_free (part);
+}
+
+static int
+bsd_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ BSDPartitionData* bsd_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ if (!fs_type)
+ bsd_data->type = 0x8;
+ else if (!strcmp (fs_type->name, "linux-swap"))
+ bsd_data->type = 0x1;
+ else
+ bsd_data->type = 0x8;
+
+ return 1;
+}
+
+static int
+bsd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ /* no flags for bsd */
+ return 0;
+}
+
+static int
+bsd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ /* no flags for bsd */
+ return 0;
+}
+
+static int
+bsd_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ /* no flags for bsd */
+ return 0;
+}
+
+
+static int
+bsd_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return BSD_MAXPARTITIONS;
+}
+
+static PedConstraint*
+_get_constraint (const PedDevice* dev)
+{
+ PedGeometry max;
+
+ ped_geometry_init (&max, dev, 1, dev->length - 1);
+ return ped_constraint_new_from_max (&max);
+}
+
+static int
+bsd_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ if (_ped_partition_attempt_align (part, constraint,
+ _get_constraint (part->disk->dev)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+bsd_partition_enumerate (PedPartition* part)
+{
+ int i;
+ PedPartition* p;
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+ for (i = 1; i <= BSD_MAXPARTITIONS; i++) {
+ p = ped_disk_get_partition (part->disk, i);
+ if (!p) {
+ part->num = i;
+ return 1;
+ }
+ }
+
+ /* failed to allocate a number */
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unable to allocate a bsd disklabel slot."));
+#endif
+ return 0;
+}
+
+static int
+bsd_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* new_part;
+ PedConstraint* constraint_any = NULL;
+
+ PED_ASSERT (disk != NULL, goto error);
+ PED_ASSERT (disk->dev != NULL, goto error);
+
+ constraint_any = ped_constraint_any (disk->dev);
+
+ /* allocate 1 sector for the disk label at the start */
+ new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, 0, 0);
+ if (!new_part)
+ goto error;
+
+ if (!ped_disk_add_partition (disk, new_part, constraint_any)) {
+ ped_partition_destroy (new_part);
+ goto error;
+ }
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static PedDiskOps bsd_disk_ops = {
+ .probe = bsd_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = bsd_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = bsd_alloc,
+ .duplicate = bsd_duplicate,
+ .free = bsd_free,
+ .read = bsd_read,
+#ifndef DISCOVER_ONLY
+ .write = bsd_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = bsd_partition_new,
+ .partition_duplicate = bsd_partition_duplicate,
+ .partition_destroy = bsd_partition_destroy,
+ .partition_set_system = bsd_partition_set_system,
+ .partition_set_flag = bsd_partition_set_flag,
+ .partition_get_flag = bsd_partition_get_flag,
+ .partition_is_flag_available = bsd_partition_is_flag_available,
+ .partition_set_name = NULL,
+ .partition_get_name = NULL,
+ .partition_align = bsd_partition_align,
+ .partition_enumerate = bsd_partition_enumerate,
+
+ .alloc_metadata = bsd_alloc_metadata,
+ .get_max_primary_partition_count =
+ bsd_get_max_primary_partition_count
+};
+
+static PedDiskType bsd_disk_type = {
+ .next = NULL,
+ .name = "bsd",
+ .ops = &bsd_disk_ops,
+ .features = 0
+};
+
+void
+ped_disk_bsd_init ()
+{
+ PED_ASSERT (sizeof (BSDRawPartition) == 16, return);
+ PED_ASSERT (sizeof (BSDRawLabel) == 276, return);
+
+ ped_disk_type_register (&bsd_disk_type);
+}
+
+void
+ped_disk_bsd_done ()
+{
+ ped_disk_type_unregister (&bsd_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/dos.c b/usr/src/lib/libparted/common/libparted/labels/dos.c
new file mode 100644
index 0000000000..7b4b63c6bc
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/dos.c
@@ -0,0 +1,2280 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/time.h>
+#include <stdbool.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* this MBR boot code is loaded into 0000:7c00 by the BIOS. See mbr.s for
+ * the source, and how to build it
+ */
+
+static const unsigned char MBR_BOOT_CODE[] = {
+ 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00,
+ 0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
+ 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9,
+ 0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00,
+ 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b,
+ 0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75,
+ 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb,
+ 0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b,
+ 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00,
+ 0x00, 0xeb, 0xfe
+};
+
+#define MSDOS_MAGIC 0xAA55
+#define PARTITION_MAGIC_MAGIC 0xf6f6
+
+#define PARTITION_EMPTY 0x00
+#define PARTITION_FAT12 0x01
+#define PARTITION_FAT16_SM 0x04
+#define PARTITION_DOS_EXT 0x05
+#define PARTITION_FAT16 0x06
+#define PARTITION_NTFS 0x07
+#define PARTITION_HPFS 0x07
+#define PARTITION_FAT32 0x0b
+#define PARTITION_FAT32_LBA 0x0c
+#define PARTITION_FAT16_LBA 0x0e
+#define PARTITION_EXT_LBA 0x0f
+
+#define PART_FLAG_HIDDEN 0x10 /* Valid for FAT/NTFS only */
+#define PARTITION_FAT12_H (PARTITION_FAT12 | PART_FLAG_HIDDEN)
+#define PARTITION_FAT16_SM_H (PARTITION_FAT16_SM | PART_FLAG_HIDDEN)
+#define PARTITION_DOS_EXT_H (PARTITION_DOS_EXT | PART_FLAG_HIDDEN)
+#define PARTITION_FAT16_H (PARTITION_FAT16 | PART_FLAG_HIDDEN)
+#define PARTITION_NTFS_H (PARTITION_NTFS | PART_FLAG_HIDDEN)
+#define PARTITION_FAT32_H (PARTITION_FAT32 | PART_FLAG_HIDDEN)
+#define PARTITION_FAT32_LBA_H (PARTITION_FAT32_LBA | PART_FLAG_HIDDEN)
+#define PARTITION_FAT16_LBA_H (PARTITION_FAT16_LBA | PART_FLAG_HIDDEN)
+
+#define PARTITION_COMPAQ_DIAG 0x12
+#define PARTITION_LDM 0x42
+#define PARTITION_LINUX_SWAP 0x82
+#define PARTITION_LINUX 0x83
+#define PARTITION_LINUX_EXT 0x85
+#define PARTITION_LINUX_LVM 0x8e
+#define PARTITION_SUN_UFS 0xbf
+#define PARTITION_DELL_DIAG 0xde
+#define PARTITION_GPT 0xee
+#define PARTITION_PALO 0xf0
+#define PARTITION_PREP 0x41
+#define PARTITION_LINUX_RAID 0xfd
+#define PARTITION_LINUX_LVM_OLD 0xfe
+
+/* This constant contains the maximum cylinder number that can be represented
+ * in (C,H,S) notation. Higher cylinder numbers are reserved for
+ * "too big" indicators (in which case only LBA addressing can be used).
+ * Some partition tables in the wild indicate this number is 1021.
+ * (i.e. 1022 is sometimes used to indicate "use LBA").
+ */
+#define MAX_CHS_CYLINDER 1021
+
+typedef struct _DosRawPartition DosRawPartition;
+typedef struct _DosRawTable DosRawTable;
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+/* note: lots of bit-bashing here, thus, you shouldn't look inside it.
+ * Use chs_to_sector() and sector_to_chs() instead.
+ */
+#ifdef __sun
+#pragma pack(1)
+#endif
+typedef struct {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} __attribute__((packed)) RawCHS;
+
+/* ripped from Linux source */
+struct _DosRawPartition {
+ uint8_t boot_ind; /* 00: 0x80 - active */
+ RawCHS chs_start; /* 01: */
+ uint8_t type; /* 04: partition type */
+ RawCHS chs_end; /* 05: */
+ uint32_t start; /* 08: starting sector counting from 0 */
+ uint32_t length; /* 0c: nr of sectors in partition */
+} __attribute__((packed));
+
+struct _DosRawTable {
+ char boot_code [440];
+ uint32_t mbr_signature; /* really a unique ID */
+ uint16_t Unknown;
+ DosRawPartition partitions [4];
+ uint16_t magic;
+} __attribute__((packed));
+#ifdef __sun
+#pragma pack()
+#endif
+
+
+/* OrigState is information we want to preserve about the partition for
+ * dealing with CHS issues
+ */
+typedef struct {
+ PedGeometry geom;
+ DosRawPartition raw_part;
+ PedSector lba_offset; /* needed for computing start/end for
+ * logical partitions */
+} OrigState;
+
+typedef struct {
+ unsigned char system;
+ int boot;
+ int hidden;
+ int raid;
+ int lvm;
+ int lba;
+ int palo;
+ int prep;
+ OrigState* orig; /* used for CHS stuff */
+} DosPartitionData;
+
+static PedDiskType msdos_disk_type;
+
+/* FIXME: factor out this function: copied from aix.c, with changes to
+ the description, and an added sector number argument.
+ Read sector, SECTOR_NUM (which has length DEV->sector_size) into malloc'd
+ storage. If the read fails, free the memory and return zero without
+ modifying *BUF. Otherwise, set *BUF to the new buffer and return 1. */
+static int
+read_sector (const PedDevice *dev, PedSector sector_num, char **buf)
+{
+ char *b = ped_malloc (dev->sector_size);
+ PED_ASSERT (b != NULL, return 0);
+ if (!ped_device_read (dev, b, sector_num, 1)) {
+ ped_free (b);
+ return 0;
+ }
+ *buf = b;
+ return 1;
+}
+
+static int
+msdos_probe (const PedDevice *dev)
+{
+ PedDiskType* disk_type;
+ DosRawTable* part_table;
+ int i;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size < sizeof *part_table)
+ return 0;
+
+ char *label;
+ if (!read_sector (dev, 0, &label))
+ return 0;
+
+ part_table = (DosRawTable *) label;
+
+ /* check magic */
+ if (PED_LE16_TO_CPU (part_table->magic) != MSDOS_MAGIC)
+ goto probe_fail;
+
+ /* if this is a FAT fs, fail here. Note that the Smart Boot Manager
+ * Loader (SBML) signature indicates a partition table, not a file
+ * system.
+ */
+ if ((!strncmp (part_table->boot_code + 0x36, "FAT", 3)
+ && strncmp (part_table->boot_code + 0x40, "SBML", 4) != 0)
+ || !strncmp (part_table->boot_code + 0x52, "FAT", 3))
+ goto probe_fail;
+
+ /* If this is a GPT disk, fail here */
+ for (i = 0; i < 4; i++) {
+ if (part_table->partitions[i].type == PARTITION_GPT)
+ goto probe_fail;
+ }
+
+ /* If this is an AIX Physical Volume, fail here. IBMA in EBCDIC */
+ if (part_table->boot_code[0] == (char) 0xc9 &&
+ part_table->boot_code[1] == (char) 0xc2 &&
+ part_table->boot_code[2] == (char) 0xd4 &&
+ part_table->boot_code[3] == (char) 0xc1)
+ goto probe_fail;
+
+#ifdef ENABLE_PC98
+ /* HACK: it's impossible to tell PC98 and msdos disk labels apart.
+ * Someone made the signatures the same (very clever). Since
+ * PC98 has some idiosyncracies with it's boot-loader, it's detection
+ * is more reliable */
+ disk_type = ped_disk_type_get ("pc98");
+ if (disk_type && disk_type->ops->probe (dev))
+ goto probe_fail;
+#endif /* ENABLE_PC98 */
+
+ free (label);
+ return 1;
+
+ probe_fail:
+ free (label);
+ return 0;
+}
+
+static PedDisk*
+msdos_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ PED_ASSERT (dev != NULL, return NULL);
+
+ disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type);
+ if (disk)
+ disk->disk_specific = NULL;
+ return disk;
+}
+
+static PedDisk*
+msdos_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type);
+ if (!new_disk)
+ return NULL;
+ new_disk->disk_specific = NULL;
+ return new_disk;
+}
+
+static void
+msdos_free (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return);
+
+ _ped_disk_free (disk);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+msdos_clobber (PedDevice* dev)
+{
+ DosRawTable table;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (msdos_probe (dev), return 0);
+
+ if (!ped_device_read (dev, &table, 0, 1))
+ return 0;
+ table.magic = 0;
+ return ped_device_write (dev, (void*) &table, 0, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+chs_get_cylinder (const RawCHS* chs)
+{
+ return chs->cylinder + ((chs->sector >> 6) << 8);
+}
+
+static int
+chs_get_head (const RawCHS* chs)
+{
+ return chs->head;
+}
+
+/* counts from 0 */
+static int
+chs_get_sector (const RawCHS* chs)
+{
+ return (chs->sector & 0x3f) - 1;
+}
+
+static PedSector
+chs_to_sector (const PedDevice* dev, const PedCHSGeometry *bios_geom,
+ const RawCHS* chs)
+{
+ PedSector c; /* not measured in sectors, but need */
+ PedSector h; /* lots of bits */
+ PedSector s;
+
+ PED_ASSERT (bios_geom != NULL, return 0);
+ PED_ASSERT (chs != NULL, return 0);
+
+ c = chs_get_cylinder (chs);
+ h = chs_get_head (chs);
+ s = chs_get_sector (chs);
+
+ if (c > MAX_CHS_CYLINDER) /* MAGIC: C/H/S is irrelevant */
+ return 0;
+ if (s < 0)
+ return 0;
+ return ((c * bios_geom->heads + h) * bios_geom->sectors + s)
+ * (dev->sector_size / 512);
+}
+
+static void
+sector_to_chs (const PedDevice* dev, const PedCHSGeometry* bios_geom,
+ PedSector sector, RawCHS* chs)
+{
+ PedSector real_c, real_h, real_s;
+
+ PED_ASSERT (dev != NULL, return);
+ PED_ASSERT (chs != NULL, return);
+
+ if (!bios_geom)
+ bios_geom = &dev->bios_geom;
+
+ sector /= (dev->sector_size / 512);
+
+ real_c = sector / (bios_geom->heads * bios_geom->sectors);
+ real_h = (sector / bios_geom->sectors) % bios_geom->heads;
+ real_s = sector % bios_geom->sectors;
+
+ if (real_c > MAX_CHS_CYLINDER) {
+ real_c = 1023;
+ real_h = bios_geom->heads - 1;
+ real_s = bios_geom->sectors - 1;
+ }
+
+ chs->cylinder = real_c % 0x100;
+ chs->head = real_h;
+ chs->sector = real_s + 1 + (real_c >> 8 << 6);
+}
+
+static PedSector
+legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ const DosRawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start);
+}
+
+static PedSector
+legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ const DosRawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end);
+}
+
+static PedSector
+linear_start (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector offset)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ return offset
+ + PED_LE32_TO_CPU (raw_part->start)
+ * (disk->dev->sector_size / 512);
+}
+
+static PedSector
+linear_end (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector offset)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ return linear_start (disk, raw_part, offset)
+ + (PED_LE32_TO_CPU (raw_part->length) - 1)
+ * (disk->dev->sector_size / 512);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ PedSector leg_start, leg_end;
+ DosPartitionData* dos_data;
+ PedDisk* disk;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+ dos_data = part->disk_specific;
+
+ if (!dos_data->orig)
+ return 1;
+
+ disk = part->disk;
+ leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part);
+ leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part);
+
+ if (leg_start && leg_start != dos_data->orig->geom.start)
+ return 0;
+ if (leg_end && leg_end != dos_data->orig->geom.end)
+ return 0;
+ return 1;
+}
+
+static int
+disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
+{
+ PedPartition* part = NULL;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (!partition_check_bios_geometry (part, bios_geom))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL};
+ int i;
+ int found;
+ unsigned char* buf;
+ int sectors;
+ int heads;
+ int res = 0;
+
+ PED_ASSERT (bios_geom != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+ PED_ASSERT (part->disk->dev != NULL, return 0);
+ PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
+ return 0);
+
+ buf = ped_malloc (part->disk->dev->sector_size);
+
+ if (!buf)
+ return 0;
+
+ if (!part->fs_type)
+ goto end;
+
+ found = 0;
+ for (i = 0; ms_types[i]; i++) {
+ if (!strcmp(ms_types[i], part->fs_type->name))
+ found = 1;
+ }
+ if (!found)
+ goto end;
+
+ if (!ped_geometry_read(&part->geom, buf, 0, 1))
+ goto end;
+
+ /* shared by the start of all Microsoft file systems */
+ sectors = buf[0x18] + (buf[0x19] << 8);
+ heads = buf[0x1a] + (buf[0x1b] << 8);
+
+ if (sectors < 1 || sectors > 63)
+ goto end;
+ if (heads > 255 || heads < 1)
+ goto end;
+
+ bios_geom->sectors = sectors;
+ bios_geom->heads = heads;
+ bios_geom->cylinders = part->disk->dev->length / (sectors * heads);
+ res = 1;
+end:
+ ped_free(buf);
+ return res;
+}
+
+/* This function attempts to infer the BIOS CHS geometry of the hard disk
+ * from the CHS + LBA information contained in the partition table from
+ * a single partition's entry.
+ *
+ * This involves some maths. Let (c,h,s,a) be the starting cylinder,
+ * starting head, starting sector and LBA start address of the partition.
+ * Likewise, (C,H,S,A) the end addresses. Using both of these pieces
+ * of information, we want to deduce cyl_sectors and head_sectors which
+ * are the sizes of a single cylinder and a single head, respectively.
+ *
+ * The relationships are:
+ * c*cyl_sectors + h * head_sectors + s = a
+ * C*cyl_sectors + H * head_sectors + S = A
+ *
+ * We can rewrite this in matrix form:
+ *
+ * [ c h ] [ cyl_sectors ] = [ s - a ] = [ a_ ]
+ * [ C H ] [ head_sectors ] [ S - A ] [ A_ ].
+ *
+ * (s - a is abbreviated to a_to simplify the notation.)
+ *
+ * This can be abbreviated into augmented matrix form:
+ *
+ * [ c h | a_ ]
+ * [ C H | A_ ].
+ *
+ * Solving these equations requires following the row reduction algorithm. We
+ * need to be careful about a few things though:
+ * - the equations might be linearly dependent, in which case there
+ * are many solutions.
+ * - the equations might be inconsistent, in which case there
+ * are no solutions. (Inconsistent partition table entry!)
+ * - there might be zeros, so we need to be careful about applying
+ * the algorithm. We know, however, that C > 0.
+ */
+static int
+probe_partition_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ DosPartitionData* dos_data;
+ RawCHS* start_chs;
+ RawCHS* end_chs;
+ PedSector c, h, s, a, a_; /* start */
+ PedSector C, H, S, A, A_; /* end */
+ PedSector dont_overflow, denum;
+ PedSector cyl_size, head_size;
+ PedSector cylinders, heads, sectors;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+ PED_ASSERT (bios_geom != NULL, return 0);
+
+ dos_data = part->disk_specific;
+
+ if (!dos_data->orig)
+ return 0;
+
+ start_chs = &dos_data->orig->raw_part.chs_start;
+ c = chs_get_cylinder (start_chs);
+ h = chs_get_head (start_chs);
+ s = chs_get_sector (start_chs);
+ a = dos_data->orig->geom.start;
+ a_ = a - s;
+
+ end_chs = &dos_data->orig->raw_part.chs_end;
+ C = chs_get_cylinder (end_chs);
+ H = chs_get_head (end_chs);
+ S = chs_get_sector (end_chs);
+ A = dos_data->orig->geom.end;
+ A_ = A - S;
+
+ if (h < 0 || H < 0 || h > 254 || H > 254)
+ return 0;
+ if (c > C)
+ return 0;
+
+ /* If no geometry is feasible, then don't even bother.
+ * Useful for eliminating assertions for broken partition
+ * tables generated by Norton Ghost et al.
+ */
+ if (A > (C+1) * 255 * 63)
+ return 0;
+
+ /* Not enough information. In theory, we can do better. Should we? */
+ if (C > MAX_CHS_CYLINDER)
+ return 0;
+ if (C == 0)
+ return 0;
+
+ /* Calculate the maximum number that can be multiplied by
+ * any head count without overflowing a PedSector
+ * 2^8 = 256, 8 bits + 1(sign bit) = 9
+ */
+ dont_overflow = 1;
+ dont_overflow <<= (8*sizeof(dont_overflow)) - 9;
+ dont_overflow--;
+
+ if (a_ > dont_overflow || A_ > dont_overflow)
+ return 0;
+
+ /* The matrix is solved by :
+ *
+ * [ c h | a_] R1
+ * [ C H | A_] R2
+ *
+ * (cH - Ch) cyl_size = a_H - A_h H R1 - h R2
+ * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch)
+ *
+ * (Hc - hC) head_size = A_c - a_C c R2 - C R1
+ * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch)
+ *
+ * But this calculation of head_size would need
+ * not overflowing A_c or a_C
+ * So substitution is use instead, to minimize dimension
+ * of temporary results :
+ *
+ * If h != 0 : head_size = ( a_ - c cyl_size ) / h
+ * If H != 0 : head_size = ( A_ - C cyl_size ) / H
+ *
+ */
+ denum = c * H - C * h;
+ if (denum == 0)
+ return 0;
+
+ cyl_size = (a_*H - A_*h) / denum;
+ /* Check for non integer result */
+ if (cyl_size * denum != a_*H - A_*h)
+ return 0;
+
+ PED_ASSERT (cyl_size > 0, return 0);
+ PED_ASSERT (cyl_size <= 255 * 63, return 0);
+
+ if (h > 0)
+ head_size = ( a_ - c * cyl_size ) / h;
+ else if (H > 0)
+ head_size = ( A_ - C * cyl_size ) / H;
+ else {
+ /* should not happen because denum != 0 */
+ head_size = 0;
+ PED_ASSERT (0, return 0);
+ }
+
+ PED_ASSERT (head_size > 0, return 0);
+ PED_ASSERT (head_size <= 63, return 0);
+
+ cylinders = part->disk->dev->length / cyl_size;
+ heads = cyl_size / head_size;
+ sectors = head_size;
+
+ PED_ASSERT (heads > 0, return 0);
+ PED_ASSERT (heads < 256, return 0);
+
+ PED_ASSERT (sectors > 0, return 0);
+ PED_ASSERT (sectors <= 63, return 0);
+
+ /* Some broken OEM partitioning program(s) seem to have an out-by-one
+ * error on the end of partitions. We should offer to fix the
+ * partition table...
+ */
+ if (((C + 1) * heads + H) * sectors + S == A)
+ C++;
+
+ PED_ASSERT ((c * heads + h) * sectors + s == a, return 0);
+ PED_ASSERT ((C * heads + H) * sectors + S == A, return 0);
+
+ bios_geom->cylinders = cylinders;
+ bios_geom->heads = heads;
+ bios_geom->sectors = sectors;
+
+ return 1;
+}
+
+static void
+partition_probe_bios_geometry (const PedPartition* part,
+ PedCHSGeometry* bios_geom)
+{
+ PED_ASSERT (part != NULL, return);
+ PED_ASSERT (part->disk != NULL, return);
+ PED_ASSERT (bios_geom != NULL, return);
+
+ if (ped_partition_is_active (part)) {
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ if (part->type & PED_PARTITION_EXTENDED) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ }
+ }
+ if (part->type & PED_PARTITION_LOGICAL) {
+ PedPartition* ext_part;
+ ext_part = ped_disk_extended_partition (part->disk);
+ PED_ASSERT (ext_part != NULL, return);
+ partition_probe_bios_geometry (ext_part, bios_geom);
+ } else {
+ *bios_geom = part->disk->dev->bios_geom;
+ }
+}
+
+static void
+disk_probe_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
+{
+ PedPartition* part;
+
+ /* first look at the boot partition */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (!ped_partition_is_active (part))
+ continue;
+ if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ }
+ }
+
+ /* that didn't work... try all partition table entries */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ }
+ }
+
+ /* that didn't work... look at all file systems */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ }
+ }
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+raw_part_is_extended (const DosRawPartition* raw_part)
+{
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ switch (raw_part->type) {
+ case PARTITION_DOS_EXT:
+ case PARTITION_EXT_LBA:
+ case PARTITION_LINUX_EXT:
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+raw_part_is_hidden (const DosRawPartition* raw_part)
+{
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ switch (raw_part->type) {
+ case PARTITION_FAT12_H:
+ case PARTITION_FAT16_SM_H:
+ case PARTITION_FAT16_H:
+ case PARTITION_FAT32_H:
+ case PARTITION_NTFS_H:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_FAT16_LBA_H:
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+raw_part_is_lba (const DosRawPartition* raw_part)
+{
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ switch (raw_part->type) {
+ case PARTITION_FAT32_LBA:
+ case PARTITION_FAT16_LBA:
+ case PARTITION_EXT_LBA:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_FAT16_LBA_H:
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static PedPartition*
+raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector lba_offset, PedPartitionType type)
+{
+ PedPartition* part;
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (disk != NULL, return NULL);
+ PED_ASSERT (raw_part != NULL, return NULL);
+
+ part = ped_partition_new (
+ disk, type, NULL,
+ linear_start (disk, raw_part, lba_offset),
+ linear_end (disk, raw_part, lba_offset));
+ if (!part)
+ return NULL;
+ dos_data = part->disk_specific;
+ dos_data->system = raw_part->type;
+ dos_data->boot = raw_part->boot_ind != 0;
+ dos_data->hidden = raw_part_is_hidden (raw_part);
+ dos_data->raid = raw_part->type == PARTITION_LINUX_RAID;
+ dos_data->lvm = raw_part->type == PARTITION_LINUX_LVM_OLD
+ || raw_part->type == PARTITION_LINUX_LVM;
+ dos_data->lba = raw_part_is_lba (raw_part);
+ dos_data->palo = raw_part->type == PARTITION_PALO;
+ dos_data->prep = raw_part->type == PARTITION_PREP;
+ dos_data->orig = ped_malloc (sizeof (OrigState));
+ if (!dos_data->orig) {
+ ped_partition_destroy (part);
+ return NULL;
+ }
+ dos_data->orig->geom = part->geom;
+ dos_data->orig->raw_part = *raw_part;
+ dos_data->orig->lba_offset = lba_offset;
+ return part;
+}
+
+static int
+read_table (PedDisk* disk, PedSector sector, int is_extended_table)
+{
+ int i;
+ DosRawTable* table;
+ DosRawPartition* raw_part;
+ PedPartition* part;
+ PedPartitionType type;
+ PedSector lba_offset;
+ PedConstraint* constraint_exact;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ char *label = NULL;
+ if (!read_sector (disk->dev, sector, &label))
+ goto error;
+
+ table = (DosRawTable *) label;
+
+ /* weird: empty extended partitions are filled with 0xf6 by PM */
+ if (is_extended_table
+ && PED_LE16_TO_CPU (table->magic) == PARTITION_MAGIC_MAGIC)
+ goto read_ok;
+
+#ifndef DISCOVER_ONLY
+ if (PED_LE16_TO_CPU (table->magic) != MSDOS_MAGIC) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid partition table on %s "
+ "-- wrong signature %x."),
+ disk->dev->path,
+ PED_LE16_TO_CPU (table->magic))
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ goto read_ok;
+ }
+#endif
+
+ /* parse the partitions from this table */
+ for (i = 0; i < 4; i++) {
+ raw_part = &table->partitions [i];
+ if (raw_part->type == PARTITION_EMPTY || !raw_part->length)
+ continue;
+
+ /* process nested extended partitions after normal logical
+ * partitions, to make sure we get the order right.
+ */
+ if (is_extended_table && raw_part_is_extended (raw_part))
+ continue;
+
+ lba_offset = is_extended_table ? sector : 0;
+
+ if (linear_start (disk, raw_part, lba_offset) == sector) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid partition table - recursive "
+ "partition on %s."),
+ disk->dev->path)
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ continue; /* avoid infinite recursion */
+ }
+
+ if (is_extended_table)
+ type = PED_PARTITION_LOGICAL;
+ else if (raw_part_is_extended (raw_part))
+ type = PED_PARTITION_EXTENDED;
+ else
+ type = PED_PARTITION_NORMAL;
+
+ part = raw_part_parse (disk, raw_part, lba_offset, type);
+ if (!part)
+ goto error;
+ if (!is_extended_table)
+ part->num = i + 1;
+ if (type != PED_PARTITION_EXTENDED)
+ part->fs_type = ped_file_system_probe (&part->geom);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error;
+ ped_constraint_destroy (constraint_exact);
+
+ /* non-nested extended partition */
+ if (part->type == PED_PARTITION_EXTENDED) {
+ if (!read_table (disk, part->geom.start, 1))
+ goto error;
+ }
+ }
+
+ if (is_extended_table) {
+ /* process the nested extended partitions */
+ for (i = 0; i < 4; i++) {
+ PedSector part_start;
+
+ raw_part = &table->partitions [i];
+ if (!raw_part_is_extended (raw_part))
+ continue;
+
+ lba_offset = ped_disk_extended_partition
+ (disk)->geom.start;
+ part_start = linear_start (disk, raw_part, lba_offset);
+ if (part_start == sector) {
+ /* recursive table - already threw an
+ * exception above.
+ */
+ continue;
+ }
+ if (!read_table (disk, part_start, 1))
+ goto error;
+ }
+ }
+
+read_ok:
+ free (label);
+ return 1;
+
+error:
+ free (label);
+ ped_disk_delete_all (disk);
+ return 0;
+}
+
+static int
+msdos_read (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ ped_disk_delete_all (disk);
+ if (!read_table (disk, 0, 0))
+ return 0;
+
+#ifndef DISCOVER_ONLY
+ /* try to figure out the correct BIOS CHS values */
+ if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) {
+ PedCHSGeometry bios_geom = disk->dev->bios_geom;
+ disk_probe_bios_geometry (disk, &bios_geom);
+
+ /* if the geometry was wrong, then we should reread, to
+ * make sure the metadata is allocated in the right places.
+ */
+ if (disk->dev->bios_geom.cylinders != bios_geom.cylinders
+ || disk->dev->bios_geom.heads != bios_geom.heads
+ || disk->dev->bios_geom.sectors != bios_geom.sectors) {
+ disk->dev->bios_geom = bios_geom;
+ return msdos_read (disk);
+ }
+ }
+#endif
+
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+fill_raw_part (DosRawPartition* raw_part,
+ const PedPartition* part, PedSector offset)
+{
+ DosPartitionData* dos_data;
+ PedCHSGeometry bios_geom;
+
+ PED_ASSERT (raw_part != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+
+ partition_probe_bios_geometry (part, &bios_geom);
+
+ dos_data = part->disk_specific;
+
+ raw_part->boot_ind = 0x80 * dos_data->boot;
+ raw_part->type = dos_data->system;
+ raw_part->start = PED_CPU_TO_LE32 ((part->geom.start - offset)
+ / (part->disk->dev->sector_size / 512));
+ raw_part->length = PED_CPU_TO_LE32 (part->geom.length
+ / (part->disk->dev->sector_size / 512));
+
+ sector_to_chs (part->disk->dev, &bios_geom, part->geom.start,
+ &raw_part->chs_start);
+ sector_to_chs (part->disk->dev, &bios_geom, part->geom.end,
+ &raw_part->chs_end);
+
+ if (dos_data->orig) {
+ DosRawPartition* orig_raw_part = &dos_data->orig->raw_part;
+ if (dos_data->orig->geom.start == part->geom.start)
+ raw_part->chs_start = orig_raw_part->chs_start;
+ if (dos_data->orig->geom.end == part->geom.end)
+ raw_part->chs_end = orig_raw_part->chs_end;
+ }
+
+ return 1;
+}
+
+static int
+fill_ext_raw_part_geom (DosRawPartition* raw_part,
+ const PedCHSGeometry* bios_geom,
+ const PedGeometry* geom, PedSector offset)
+{
+ PED_ASSERT (raw_part != NULL, return 0);
+ PED_ASSERT (geom != NULL, return 0);
+ PED_ASSERT (geom->dev != NULL, return 0);
+
+ raw_part->boot_ind = 0;
+ raw_part->type = PARTITION_DOS_EXT;
+ raw_part->start = PED_CPU_TO_LE32 ((geom->start - offset)
+ / (geom->dev->sector_size / 512));
+ raw_part->length = PED_CPU_TO_LE32 (geom->length
+ / (geom->dev->sector_size / 512));
+
+ sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start);
+ sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1,
+ &raw_part->chs_end);
+
+ return 1;
+}
+
+static int
+write_ext_table (const PedDisk* disk,
+ PedSector sector, const PedPartition* logical)
+{
+ DosRawTable table;
+ PedPartition* part;
+ PedSector lba_offset;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (ped_disk_extended_partition (disk) != NULL, return 0);
+ PED_ASSERT (logical != NULL, return 0);
+
+ lba_offset = ped_disk_extended_partition (disk)->geom.start;
+
+ memset (&table, 0, sizeof (DosRawTable));
+ table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ if (!fill_raw_part (&table.partitions[0], logical, sector))
+ return 0;
+
+ part = ped_disk_get_partition (disk, logical->num + 1);
+ if (part) {
+ PedGeometry* geom;
+ PedCHSGeometry bios_geom;
+
+ geom = ped_geometry_new (disk->dev, part->prev->geom.start,
+ part->geom.end - part->prev->geom.start + 1);
+ if (!geom)
+ return 0;
+ partition_probe_bios_geometry (part, &bios_geom);
+ fill_ext_raw_part_geom (&table.partitions[1], &bios_geom,
+ geom, lba_offset);
+ ped_geometry_destroy (geom);
+
+ if (!write_ext_table (disk, part->prev->geom.start, part))
+ return 0;
+ }
+
+ return ped_device_write (disk->dev, (void*) &table, sector, 1);
+}
+
+static int
+write_empty_table (const PedDisk* disk, PedSector sector)
+{
+ DosRawTable table;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ memset (&table, 0, sizeof (DosRawTable));
+ table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ return ped_device_write (disk->dev, (void*) &table, sector, 1);
+}
+
+/* Find the first logical partition, and write the partition table for it.
+ */
+static int
+write_extended_partitions (const PedDisk* disk)
+{
+ PedPartition* ext_part;
+ PedPartition* part;
+ PedCHSGeometry bios_geom;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ ext_part = ped_disk_extended_partition (disk);
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ part = ped_disk_get_partition (disk, 5);
+ if (part)
+ return write_ext_table (disk, ext_part->geom.start, part);
+ else
+ return write_empty_table (disk, ext_part->geom.start);
+}
+
+static inline uint32_t generate_random_id (void)
+{
+ struct timeval tv;
+ int rc;
+ rc = gettimeofday(&tv, NULL);
+ if (rc == -1)
+ return 0;
+ return (uint32_t)(tv.tv_usec & 0xFFFFFFFFUL);
+}
+
+static int
+msdos_write (const PedDisk* disk)
+{
+ DosRawTable table;
+ PedPartition* part;
+ int i;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ ped_device_read (disk->dev, &table, 0, 1);
+
+ if (!table.boot_code[0]) {
+ memset (table.boot_code, 0, 512);
+ memcpy (table.boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE));
+ }
+
+ /* If there is no unique identifier, generate a random one */
+ if (!table.mbr_signature)
+ table.mbr_signature = generate_random_id();
+
+ memset (table.partitions, 0, sizeof (DosRawPartition) * 4);
+ table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ for (i=1; i<=4; i++) {
+ part = ped_disk_get_partition (disk, i);
+ if (!part)
+ continue;
+
+ if (!fill_raw_part (&table.partitions [i - 1], part, 0))
+ return 0;
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ if (!write_extended_partitions (disk))
+ return 0;
+ }
+ }
+
+ if (!ped_device_write (disk->dev, (void*) &table, 0, 1))
+ return 0;
+ return ped_device_sync (disk->dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+msdos_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ DosPartitionData* dos_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = dos_data = ped_malloc (sizeof (DosPartitionData));
+ if (!dos_data)
+ goto error_free_part;
+ dos_data->orig = NULL;
+ dos_data->system = PARTITION_LINUX;
+ dos_data->hidden = 0;
+ dos_data->boot = 0;
+ dos_data->raid = 0;
+ dos_data->lvm = 0;
+ dos_data->lba = 0;
+ dos_data->palo = 0;
+ dos_data->prep = 0;
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+
+ ped_free (dos_data);
+error_free_part:
+ ped_free (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+msdos_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ DosPartitionData* new_dos_data;
+ DosPartitionData* old_dos_data;
+
+ new_part = ped_partition_new (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_dos_data = (DosPartitionData*) part->disk_specific;
+ new_dos_data = (DosPartitionData*) new_part->disk_specific;
+ new_dos_data->system = old_dos_data->system;
+ new_dos_data->boot = old_dos_data->boot;
+ new_dos_data->hidden = old_dos_data->hidden;
+ new_dos_data->raid = old_dos_data->raid;
+ new_dos_data->lvm = old_dos_data->lvm;
+ new_dos_data->lba = old_dos_data->lba;
+ new_dos_data->palo = old_dos_data->palo;
+ new_dos_data->prep = old_dos_data->prep;
+
+ if (old_dos_data->orig) {
+ new_dos_data->orig = ped_malloc (sizeof (OrigState));
+ if (!new_dos_data->orig) {
+ ped_partition_destroy (new_part);
+ return NULL;
+ }
+ new_dos_data->orig->geom = old_dos_data->orig->geom;
+ new_dos_data->orig->raw_part = old_dos_data->orig->raw_part;
+ new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset;
+ }
+ return new_part;
+}
+
+static void
+msdos_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part)) {
+ DosPartitionData* dos_data;
+ dos_data = (DosPartitionData*) part->disk_specific;
+ if (dos_data->orig)
+ ped_free (dos_data->orig);
+ ped_free (part->disk_specific);
+ }
+ ped_free (part);
+}
+
+static int
+msdos_partition_set_system (PedPartition* part,
+ const PedFileSystemType* fs_type)
+{
+ DosPartitionData* dos_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ if (dos_data->hidden
+ && fs_type
+ && strncmp (fs_type->name, "fat", 3) != 0
+ && strcmp (fs_type->name, "ntfs") != 0)
+ dos_data->hidden = 0;
+
+ if (part->type & PED_PARTITION_EXTENDED) {
+ dos_data->raid = 0;
+ dos_data->lvm = 0;
+ dos_data->palo = 0;
+ dos_data->prep = 0;
+ if (dos_data->lba)
+ dos_data->system = PARTITION_EXT_LBA;
+ else
+ dos_data->system = PARTITION_DOS_EXT;
+ return 1;
+ }
+
+ if (dos_data->lvm) {
+ dos_data->system = PARTITION_LINUX_LVM;
+ return 1;
+ }
+ if (dos_data->raid) {
+ dos_data->system = PARTITION_LINUX_RAID;
+ return 1;
+ }
+ if (dos_data->palo) {
+ dos_data->system = PARTITION_PALO;
+ return 1;
+ }
+ if (dos_data->prep) {
+ dos_data->system = PARTITION_PREP;
+ return 1;
+ }
+
+ if (!fs_type)
+ dos_data->system = PARTITION_LINUX;
+ else if (!strcmp (fs_type->name, "fat16")) {
+ dos_data->system = dos_data->lba
+ ? PARTITION_FAT16_LBA : PARTITION_FAT16;
+ dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
+ } else if (!strcmp (fs_type->name, "fat32")) {
+ dos_data->system = dos_data->lba
+ ? PARTITION_FAT32_LBA : PARTITION_FAT32;
+ dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
+ } else if (!strcmp (fs_type->name, "ntfs")
+ || !strcmp (fs_type->name, "hpfs")) {
+ dos_data->system = PARTITION_NTFS;
+ dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
+ } else if (!strcmp (fs_type->name, "sun-ufs"))
+ dos_data->system = PARTITION_SUN_UFS;
+ else if (!strcmp (fs_type->name, "solaris"))
+ dos_data->system = PARTITION_SUN_UFS;
+ else if (!strcmp (fs_type->name, "linux-swap"))
+ dos_data->system = PARTITION_LINUX_SWAP;
+ else
+ dos_data->system = PARTITION_LINUX;
+
+ return 1;
+}
+
+static int
+msdos_partition_set_flag (PedPartition* part,
+ PedPartitionFlag flag, int state)
+{
+ PedDisk* disk;
+ PedPartition* walk;
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ dos_data = part->disk_specific;
+ disk = part->disk;
+
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ if (part->type == PED_PARTITION_EXTENDED) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Extended partitions cannot be hidden on "
+ "msdos disk labels."));
+ return 0;
+ }
+ dos_data->hidden = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_BOOT:
+ dos_data->boot = state;
+ if (!state)
+ return 1;
+
+ walk = ped_disk_next_partition (disk, NULL);
+ for (; walk; walk = ped_disk_next_partition (disk, walk)) {
+ if (walk == part || !ped_partition_is_active (walk))
+ continue;
+ msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0);
+ }
+ return 1;
+
+ case PED_PARTITION_RAID:
+ if (state) {
+ dos_data->hidden = 0;
+ dos_data->lvm = 0;
+ dos_data->palo = 0;
+ dos_data->prep = 0;
+ }
+ dos_data->raid = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_LVM:
+ if (state) {
+ dos_data->hidden = 0;
+ dos_data->raid = 0;
+ dos_data->palo = 0;
+ dos_data->prep = 0;
+ }
+ dos_data->lvm = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_LBA:
+ dos_data->lba = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_PALO:
+ if (state) {
+ dos_data->hidden = 0;
+ dos_data->raid = 0;
+ dos_data->lvm = 0;
+ }
+ dos_data->palo = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_PREP:
+ if (state) {
+ dos_data->hidden = 0;
+ dos_data->raid = 0;
+ dos_data->lvm = 0;
+ }
+ dos_data->prep = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ default:
+ return 0;
+ }
+}
+
+static int
+msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ dos_data = part->disk_specific;
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ return dos_data->hidden;
+
+ case PED_PARTITION_BOOT:
+ return dos_data->boot;
+
+ case PED_PARTITION_RAID:
+ return dos_data->raid;
+
+ case PED_PARTITION_LVM:
+ return dos_data->lvm;
+
+ case PED_PARTITION_LBA:
+ return dos_data->lba;
+
+ case PED_PARTITION_PALO:
+ return dos_data->palo;
+
+ case PED_PARTITION_PREP:
+ return dos_data->prep;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+msdos_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_BOOT:
+ case PED_PARTITION_RAID:
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_PALO:
+ case PED_PARTITION_PREP:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static PedGeometry*
+_try_constraint (const PedPartition* part, const PedConstraint* external,
+ PedConstraint* internal)
+{
+ PedConstraint* intersection;
+ PedGeometry* solution;
+
+ intersection = ped_constraint_intersect (external, internal);
+ ped_constraint_destroy (internal);
+ if (!intersection)
+ return NULL;
+
+ solution = ped_constraint_solve_nearest (intersection, &part->geom);
+ ped_constraint_destroy (intersection);
+ return solution;
+}
+
+static PedGeometry*
+_best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom,
+ PedGeometry* a, PedGeometry* b)
+{
+ PedSector cyl_size = bios_geom->heads * bios_geom->sectors;
+ int a_cylinder;
+ int b_cylinder;
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ a_cylinder = a->start / cyl_size;
+ b_cylinder = b->start / cyl_size;
+
+ if (a_cylinder == b_cylinder) {
+ if ( (a->start / bios_geom->sectors) % bios_geom->heads
+ < (b->start / bios_geom->sectors) % bios_geom->heads)
+ goto choose_a;
+ else
+ goto choose_b;
+ } else {
+ PedSector a_delta;
+ PedSector b_delta;
+
+ a_delta = abs (part->geom.start - a->start);
+ b_delta = abs (part->geom.start - b->start);
+
+ if (a_delta < b_delta)
+ goto choose_a;
+ else
+ goto choose_b;
+ }
+
+ return NULL; /* never get here! */
+
+choose_a:
+ ped_geometry_destroy (b);
+ return a;
+
+choose_b:
+ ped_geometry_destroy (a);
+ return b;
+}
+
+/* This constraint is for "normal" primary partitions, that start at the
+ * beginning of a cylinder, and end at the end of a cylinder.
+ * Note: you can't start a partition at the beginning of the 1st
+ * cylinder, because that's where the partition table is! There are different
+ * rules for that - see the _primary_start_constraint.
+ */
+static PedConstraint*
+_primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ PedGeometry* min_geom)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry start_geom;
+ PedGeometry end_geom;
+
+ if (!ped_alignment_init (&start_align, 0, cylinder_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+
+ if (min_geom) {
+ if (min_geom->start < cylinder_size)
+ return NULL;
+ if (!ped_geometry_init (&start_geom, dev, cylinder_size,
+ min_geom->start + 1 - cylinder_size))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, min_geom->end,
+ dev->length - min_geom->end))
+ return NULL;
+ } else {
+ if (!ped_geometry_init (&start_geom, dev, cylinder_size,
+ dev->length - cylinder_size))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
+ return NULL;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &start_geom,
+ &end_geom, 1, dev->length);
+}
+
+/* This constraint is for partitions starting on the first cylinder. They
+ * must start on the 2nd head of the 1st cylinder.
+ *
+ * NOTE: We don't always start on the 2nd head of the 1st cylinder. Windows
+ * Vista aligns starting partitions at sector 2048 (0x800) by default. See:
+ * http://support.microsoft.com/kb/923332
+ */
+static PedConstraint*
+_primary_start_constraint (const PedDisk* disk,
+ const PedPartition *part,
+ const PedCHSGeometry* bios_geom,
+ const PedGeometry* min_geom)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry start_geom;
+ PedGeometry end_geom;
+ PedSector start_pos;
+
+ if (part->geom.start == 2048)
+ /* check for known Windows Vista (NTFS >= 3.1) alignments */
+ /* sector 0x800 == 2048 */
+ start_pos = 2048;
+ else
+ /* all other primary partitions on a DOS label align to */
+ /* the 2nd head of the first cylinder (0x3F == 63) */
+ start_pos = bios_geom->sectors;
+
+ if (!ped_alignment_init (&start_align, start_pos, 0))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+ if (min_geom) {
+ if (!ped_geometry_init (&start_geom, dev, start_pos, 1))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, min_geom->end,
+ dev->length - min_geom->end))
+ return NULL;
+ } else {
+ if (!ped_geometry_init (&start_geom, dev, start_pos,
+ dev->length - start_pos))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
+ return NULL;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &start_geom,
+ &end_geom, 1, dev->length);
+}
+
+/* constraints for logical partitions:
+ * - start_offset is the offset in the start alignment. "normally",
+ * this is bios_geom->sectors. exceptions: MINOR > 5 at the beginning of the
+ * extended partition, or MINOR == 5 in the middle of the extended partition
+ * - is_start_part == 1 if the constraint is for the first cylinder of
+ * the extended partition, or == 0 if the constraint is for the second cylinder
+ * onwards of the extended partition.
+ */
+static PedConstraint*
+_logical_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ PedSector start_offset, int is_start_part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry max_geom;
+
+ PED_ASSERT (ext_part != NULL, return NULL);
+
+ if (!ped_alignment_init (&start_align, start_offset, cylinder_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+ if (is_start_part) {
+ if (!ped_geometry_init (&max_geom, dev,
+ ext_part->geom.start,
+ ext_part->geom.length))
+ return NULL;
+ } else {
+ PedSector min_start;
+ PedSector max_length;
+
+ min_start = ped_round_up_to (ext_part->geom.start + 1,
+ cylinder_size);
+ max_length = ext_part->geom.end - min_start + 1;
+ if (min_start >= ext_part->geom.end)
+ return NULL;
+
+ if (!ped_geometry_init (&max_geom, dev, min_start, max_length))
+ return NULL;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &max_geom,
+ &max_geom, 1, dev->length);
+}
+
+/* returns the minimum geometry for the extended partition, given that the
+ * extended partition must contain:
+ * * all logical partitions
+ * * all partition tables for all logical partitions (except the first)
+ * * the extended partition table
+ */
+static PedGeometry*
+_get_min_extended_part_geom (const PedPartition* ext_part,
+ const PedCHSGeometry* bios_geom)
+{
+ PedDisk* disk = ext_part->disk;
+ PedSector head_size = bios_geom ? bios_geom->sectors : 1;
+ PedPartition* walk;
+ PedGeometry* min_geom;
+
+ walk = ped_disk_get_partition (disk, 5);
+ if (!walk)
+ return NULL;
+
+ min_geom = ped_geometry_duplicate (&walk->geom);
+ if (!min_geom)
+ return NULL;
+ ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size);
+
+ for (walk = ext_part->part_list; walk; walk = walk->next) {
+ if (!ped_partition_is_active (walk) || walk->num == 5)
+ continue;
+ if (walk->geom.start < min_geom->start)
+ ped_geometry_set_start (min_geom,
+ walk->geom.start - 2 * head_size);
+ if (walk->geom.end > min_geom->end)
+ ped_geometry_set_end (min_geom, walk->geom.end);
+ }
+
+ return min_geom;
+}
+
+static int
+_align_primary (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedGeometry* min_geom = NULL;
+ PedGeometry* solution = NULL;
+
+ if (part->type == PED_PARTITION_EXTENDED)
+ min_geom = _get_min_extended_part_geom (part, bios_geom);
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, constraint,
+ _primary_start_constraint (disk, part,
+ bios_geom, min_geom)));
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, constraint,
+ _primary_constraint (disk, bios_geom,
+ min_geom)));
+
+ if (min_geom)
+ ped_geometry_destroy (min_geom);
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+_logical_min_start_head (const PedPartition* part,
+ const PedCHSGeometry* bios_geom,
+ const PedPartition* ext_part,
+ int is_start_ext_part)
+{
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedSector base_head;
+
+ if (is_start_ext_part)
+ base_head = 1 + (ext_part->geom.start % cylinder_size)
+ / bios_geom->sectors;
+ else
+ base_head = 0;
+
+ if (part->num == 5)
+ return base_head + 0;
+ else
+ return base_head + 1;
+}
+
+/* Shamelessly copied and adapted from _partition_get_overlap_constraint
+ * (in disk.c)
+ * This should get ride of the infamous Assertion (metadata_length > 0) failed
+ * bug for extended msdos disklabels generated by Parted.
+ * 1) There always is a partition table at the start of ext_part, so we leave
+ * a one sector gap there.
+ * 2)*The partition table of part5 is always at the beginning of the ext_part
+ * so there is no need to leave a one sector gap before part5.
+ * *There always is a partition table at the beginning of each partition != 5.
+ * We don't need to worry to much about consistency with
+ * _partition_get_overlap_constraint because missing it means we are in edge
+ * cases anyway, and we don't lose anything by just refusing to do the job in
+ * those cases.
+ */
+static PedConstraint*
+_log_meta_overlap_constraint (PedPartition* part, const PedGeometry* geom)
+{
+ PedGeometry safe_space;
+ PedSector min_start;
+ PedSector max_end;
+ PedPartition* ext_part = ped_disk_extended_partition (part->disk);
+ PedPartition* walk;
+ int not_5 = (part->num != 5);
+
+ PED_ASSERT (ext_part != NULL, return NULL);
+
+ walk = ext_part->part_list;
+
+ /* 1) 2) */
+ min_start = ext_part->geom.start + 1 + not_5;
+ max_end = ext_part->geom.end;
+
+ while (walk != NULL /* 2) 2) */
+ && ( walk->geom.start - (walk->num != 5) < geom->start - not_5
+ || walk->geom.start - (walk->num != 5) <= min_start )) {
+ if (walk != part && ped_partition_is_active (walk))
+ min_start = walk->geom.end + 1 + not_5; /* 2) */
+ walk = walk->next;
+ }
+
+ while (walk && (walk == part || !ped_partition_is_active (walk)))
+ walk = walk->next;
+
+ if (walk)
+ max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */
+
+ if (min_start >= max_end)
+ return NULL;
+
+ ped_geometry_init (&safe_space, part->disk->dev,
+ min_start, max_end - min_start + 1);
+ return ped_constraint_new_from_max (&safe_space);
+}
+
+static int
+_align_logical (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedSector cyl_size = bios_geom->sectors * bios_geom->heads;
+ PedSector start_base;
+ int head;
+ PedGeometry* solution = NULL;
+ PedConstraint *intersect, *log_meta_overlap;
+
+ PED_ASSERT (ext_part != NULL, return 0);
+
+ log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom);
+ intersect = ped_constraint_intersect (constraint, log_meta_overlap);
+ ped_constraint_destroy (log_meta_overlap);
+ if (!intersect)
+ return 0;
+
+ start_base = ped_round_down_to (part->geom.start, cyl_size);
+
+ for (head = _logical_min_start_head (part, bios_geom, ext_part, 0);
+ head < PED_MIN (5, bios_geom->heads); head++) {
+ PedConstraint* disk_constraint;
+ PedSector start = start_base + head * bios_geom->sectors;
+
+ if (head >= _logical_min_start_head (part, bios_geom,
+ ext_part, 1))
+ disk_constraint =
+ _logical_constraint (disk, bios_geom, start, 1);
+ else
+ disk_constraint =
+ _logical_constraint (disk, bios_geom, start, 0);
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, intersect,
+ disk_constraint));
+ }
+
+ ped_constraint_destroy (intersect);
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+_align (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ if (part->type == PED_PARTITION_LOGICAL)
+ return _align_logical (part, bios_geom, constraint);
+ else
+ return _align_primary (part, bios_geom, constraint);
+}
+
+static PedConstraint*
+_no_geom_constraint (const PedDisk* disk, PedSector start, PedSector end)
+{
+ PedGeometry max;
+
+ ped_geometry_init (&max, disk->dev, start, end - start + 1);
+ return ped_constraint_new_from_max (&max);
+}
+
+static PedConstraint*
+_no_geom_extended_constraint (const PedPartition* part)
+{
+ PedDevice* dev = part->disk->dev;
+ PedGeometry* min = _get_min_extended_part_geom (part, NULL);
+ PedGeometry start_range;
+ PedGeometry end_range;
+ PedConstraint* constraint;
+
+ if (min) {
+ ped_geometry_init (&start_range, dev, 1, min->start);
+ ped_geometry_init (&end_range, dev, min->end,
+ dev->length - min->end);
+ ped_geometry_destroy (min);
+ } else {
+ ped_geometry_init (&start_range, dev, 1, dev->length - 1);
+ ped_geometry_init (&end_range, dev, 1, dev->length - 1);
+ }
+ constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any,
+ &start_range, &end_range, 1, dev->length);
+ return constraint;
+}
+
+static int
+_align_primary_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedGeometry* solution;
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ solution = _try_constraint (part, constraint,
+ _no_geom_extended_constraint (part));
+ } else {
+ solution = _try_constraint (part, constraint,
+ _no_geom_constraint (disk, 1,
+ disk->dev->length - 1));
+ }
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_align_logical_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ PedGeometry* solution;
+
+ solution = _try_constraint (part, constraint,
+ _log_meta_overlap_constraint (part, &part->geom));
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_align_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ if (part->type == PED_PARTITION_LOGICAL)
+ return _align_logical_no_geom (part, constraint);
+ else
+ return _align_primary_no_geom (part, constraint);
+}
+
+static int
+msdos_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PedCHSGeometry bios_geom;
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ dos_data = part->disk_specific;
+ if (dos_data->system == PARTITION_LDM && dos_data->orig) {
+ PedGeometry *orig_geom = &dos_data->orig->geom;
+
+ if (ped_geometry_test_equal (&part->geom, orig_geom)
+ && ped_constraint_is_solution (constraint, &part->geom))
+ return 1;
+
+ ped_geometry_set (&part->geom, orig_geom->start,
+ orig_geom->length);
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Parted can't resize partitions managed by "
+ "Windows Dynamic Disk."));
+ return 0;
+ }
+
+ partition_probe_bios_geometry (part, &bios_geom);
+
+ if (_align (part, &bios_geom, constraint))
+ return 1;
+ if (_align_no_geom (part, constraint))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start,
+ PedSector end)
+{
+ PedPartition* new_part;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL,
+ start, end);
+ if (!new_part)
+ goto error;
+ if (!ped_disk_add_partition (disk, new_part, NULL))
+ goto error_destroy_new_part;
+
+ return 1;
+
+error_destroy_new_part:
+ ped_partition_destroy (new_part);
+error:
+ return 0;
+}
+
+/* There are a few objectives here:
+ * - avoid having lots of "free space" partitions lying around, to confuse
+ * the front end.
+ * - ensure that there's enough room to put in the extended partition
+ * tables, etc.
+ */
+static int
+add_logical_part_metadata (PedDisk* disk, const PedPartition* log_part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedPartition* prev = log_part->prev;
+ PedCHSGeometry bios_geom;
+ PedSector cyl_size;
+ PedSector metadata_start;
+ PedSector metadata_end;
+ PedSector metadata_length;
+
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ cyl_size = bios_geom.sectors * bios_geom.heads;
+
+ /* if there's metadata shortly before the partition (on the same
+ * cylinder), then make this new metadata partition touch the end of
+ * the other. No point having 63 bytes (or whatever) of free space
+ * partition - just confuses front-ends, etc.
+ * Otherwise, start the metadata at the start of the cylinder
+ */
+
+ metadata_end = log_part->geom.start - 1;
+ metadata_start = ped_round_down_to (metadata_end, cyl_size);
+ if (prev)
+ metadata_start = PED_MAX (metadata_start, prev->geom.end + 1);
+ else
+ metadata_start = PED_MAX (metadata_start,
+ ext_part->geom.start + 1);
+ metadata_length = metadata_end - metadata_start + 1;
+
+ /* partition 5 doesn't need to have any metadata */
+ if (log_part->num == 5 && metadata_length < bios_geom.sectors)
+ return 1;
+
+ PED_ASSERT (metadata_length > 0, return 0);
+
+ return add_metadata_part (disk, PED_PARTITION_LOGICAL,
+ metadata_start, metadata_end);
+}
+
+static PedPartition*
+get_last_part (const PedDisk* disk)
+{
+ PedPartition* first_part = disk->part_list;
+ PedPartition* walk;
+
+ if (!first_part)
+ return NULL;
+ for (walk = first_part; walk->next; walk = walk->next);
+ return walk;
+}
+
+/* Adds metadata placeholder partitions to cover the partition table (and
+ * "free" space after it that often has bootloader stuff), and the last
+ * incomplete cylinder at the end of the disk.
+ * Parted has to be mindful of the uncertainty of dev->bios_geom.
+ * It therefore makes sure this metadata doesn't overlap with partitions.
+ */
+static int
+add_startend_metadata (PedDisk* disk)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads;
+ PedPartition* first_part = disk->part_list;
+ PedPartition* last_part = get_last_part (disk);
+ PedSector start, end;
+
+ if (!first_part)
+ return 1;
+
+ start = 0;
+ end = PED_MIN (dev->bios_geom.sectors - 1, first_part->geom.start - 1);
+ if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
+ return 0;
+
+ start = PED_MAX (last_part->geom.end + 1,
+ ped_round_down_to (dev->length, cyl_size));
+ end = dev->length - 1;
+ if (start < end) {
+ if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+msdos_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* ext_part;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ if (!add_startend_metadata (disk))
+ return 0;
+
+ ext_part = ped_disk_extended_partition (disk);
+ if (ext_part) {
+ int i;
+ PedSector start, end;
+ PedCHSGeometry bios_geom;
+
+ for (i=5; 1; i++) {
+ PedPartition* log_part;
+ log_part = ped_disk_get_partition (disk, i);
+ if (!log_part)
+ break;
+ if (!add_logical_part_metadata (disk, log_part))
+ return 0;
+ }
+
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ start = ext_part->geom.start;
+ end = start + bios_geom.sectors - 1;
+ if (ext_part->part_list)
+ end = PED_MIN (end,
+ ext_part->part_list->geom.start - 1);
+ if (!add_metadata_part (disk, PED_PARTITION_LOGICAL,
+ start, end))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+next_primary (const PedDisk* disk)
+{
+ int i;
+ for (i=1; i<=4; i++) {
+ if (!ped_disk_get_partition (disk, i))
+ return i;
+ }
+ return 0;
+}
+
+static int
+next_logical (const PedDisk* disk)
+{
+ int i;
+ for (i=5; 1; i++) {
+ if (!ped_disk_get_partition (disk, i))
+ return i;
+ }
+}
+
+static int
+msdos_partition_enumerate (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ /* don't re-number a primary partition */
+ if (part->num != -1 && part->num <= 4)
+ return 1;
+
+ part->num = -1;
+
+ if (part->type & PED_PARTITION_LOGICAL)
+ part->num = next_logical (part->disk);
+ else
+ part->num = next_primary (part->disk);
+
+ return 1;
+}
+
+static int
+msdos_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return 4;
+}
+
+static PedDiskOps msdos_disk_ops = {
+ .probe = msdos_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = msdos_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = msdos_alloc,
+ .duplicate = msdos_duplicate,
+ .free = msdos_free,
+ .read = msdos_read,
+#ifndef DISCOVER_ONLY
+ .write = msdos_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = msdos_partition_new,
+ .partition_duplicate = msdos_partition_duplicate,
+ .partition_destroy = msdos_partition_destroy,
+ .partition_set_system = msdos_partition_set_system,
+ .partition_set_flag = msdos_partition_set_flag,
+ .partition_get_flag = msdos_partition_get_flag,
+ .partition_is_flag_available = msdos_partition_is_flag_available,
+ .partition_set_name = NULL,
+ .partition_get_name = NULL,
+ .partition_align = msdos_partition_align,
+ .partition_enumerate = msdos_partition_enumerate,
+
+ .alloc_metadata = msdos_alloc_metadata,
+ .get_max_primary_partition_count =
+ msdos_get_max_primary_partition_count
+};
+
+static PedDiskType msdos_disk_type = {
+ .next = NULL,
+ .name = "msdos",
+ .ops = &msdos_disk_ops,
+ .features = PED_DISK_TYPE_EXTENDED
+};
+
+void
+ped_disk_msdos_init ()
+{
+ PED_ASSERT (sizeof (DosRawPartition) == 16, return);
+ PED_ASSERT (sizeof (DosRawTable) == 512, return);
+
+ ped_disk_type_register (&msdos_disk_type);
+}
+
+void
+ped_disk_msdos_done ()
+{
+ ped_disk_type_unregister (&msdos_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/dvh.c b/usr/src/lib/libparted/common/libparted/labels/dvh.c
new file mode 100644
index 0000000000..c6275c7127
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/dvh.c
@@ -0,0 +1,911 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2002, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#include "dvh.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* Default size for volhdr part, same val as IRIX's fx uses */
+#define PTYPE_VOLHDR_DFLTSZ 4096
+
+/* Partition numbers that seem to be strongly held convention */
+#define PNUM_VOLHDR 8
+#define PNUM_VOLUME 10
+
+/* Other notes of interest:
+ * PED_PARTITION_EXTENDED is used for volume headers
+ * PED_PARTITION_LOGICAL is used for bootfiles
+ * PED_PARTITION_NORMAL is used for all else
+ */
+
+typedef struct _DVHDiskData {
+ struct device_parameters dev_params;
+ int swap; /* part num of swap, 0=none */
+ int root; /* part num of root, 0=none */
+ int boot; /* part num of boot, 0=none */
+} DVHDiskData;
+
+typedef struct _DVHPartData {
+ int type;
+ char name[VDNAMESIZE + 1]; /* boot volumes only */
+ int real_file_size; /* boot volumes only */
+} DVHPartData;
+
+static PedDiskType dvh_disk_type;
+
+static int
+dvh_probe (const PedDevice *dev)
+{
+ struct volume_header vh;
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ if (!ped_device_read (dev, &vh, 0, 1))
+ return 0;
+
+ return PED_BE32_TO_CPU (vh.vh_magic) == VHMAGIC;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+dvh_clobber (PedDevice* dev)
+{
+ char zeros[512];
+
+ memset (zeros, 0, 512);
+ return ped_device_write (dev, zeros, 0, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk*
+dvh_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ DVHDiskData* dvh_disk_data;
+ PedPartition* volume_part;
+ PedConstraint* constraint_any;
+
+ disk = _ped_disk_alloc (dev, &dvh_disk_type);
+ if (!disk)
+ goto error;
+
+ disk->disk_specific = dvh_disk_data
+ = ped_malloc (sizeof (DVHDiskData));
+ if (!dvh_disk_data)
+ goto error_free_disk;
+
+ memset (&dvh_disk_data->dev_params, 0,
+ sizeof (struct device_parameters));
+ dvh_disk_data->swap = 0;
+ dvh_disk_data->root = 0;
+ dvh_disk_data->boot = 0;
+
+ volume_part = ped_partition_new (disk, PED_PARTITION_EXTENDED, NULL,
+ 0, PTYPE_VOLHDR_DFLTSZ - 1);
+ if (!volume_part)
+ goto error_free_disk_specific;
+ volume_part->num = PNUM_VOLHDR + 1;
+ constraint_any = ped_constraint_any (dev);
+ if (!ped_disk_add_partition (disk, volume_part, constraint_any))
+ goto error_destroy_constraint_any;
+ ped_constraint_destroy (constraint_any);
+ return disk;
+
+error_destroy_constraint_any:
+ ped_constraint_destroy (constraint_any);
+ ped_partition_destroy (volume_part);
+error_free_disk_specific:
+ ped_free (disk->disk_specific);
+error_free_disk:
+ ped_free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk*
+dvh_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ DVHDiskData* new_dvh_disk_data;
+ DVHDiskData* old_dvh_disk_data = disk->disk_specific;
+
+ PED_ASSERT (old_dvh_disk_data != NULL, goto error);
+
+ new_disk = _ped_disk_alloc (disk->dev, &dvh_disk_type);
+ if (!new_disk)
+ goto error;
+
+ new_disk->disk_specific = new_dvh_disk_data
+ = ped_malloc (sizeof (DVHDiskData));
+ if (!new_dvh_disk_data)
+ goto error_free_new_disk;
+
+ new_dvh_disk_data->dev_params = old_dvh_disk_data->dev_params;
+ return new_disk;
+
+error_free_new_disk:
+ ped_free (new_disk);
+error:
+ return NULL;
+}
+
+static void
+dvh_free (PedDisk* disk)
+{
+ ped_free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+/* two's complement 32-bit checksum */
+static uint32_t
+_checksum (const uint32_t* base, size_t size)
+{
+ uint32_t sum = 0;
+ size_t i;
+
+ for (i = 0; i < size / sizeof (uint32_t); i++)
+ sum = sum - PED_BE32_TO_CPU (base[i]);
+
+ return sum;
+}
+
+/* try to make a reasonable volume header partition... */
+static PedExceptionOption
+_handle_no_volume_header (PedDisk* disk)
+{
+ PedExceptionOption ret;
+ PedPartition* part;
+ PedConstraint* constraint;
+
+ switch (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_FIX + PED_EXCEPTION_CANCEL,
+ _("%s has no extended partition (volume header partition)."),
+ disk->dev->path)) {
+ case PED_EXCEPTION_UNHANDLED:
+ case PED_EXCEPTION_FIX:
+ default:
+ part = ped_partition_new (
+ disk, PED_PARTITION_EXTENDED, NULL,
+ 0, PTYPE_VOLHDR_DFLTSZ - 1);
+ if (!part)
+ goto error;
+ part->num = PNUM_VOLHDR + 1;
+ constraint = ped_constraint_any (part->disk->dev);
+ if (!constraint)
+ goto error_destroy_part;
+ if (!ped_disk_add_partition (disk, part, constraint))
+ goto error_destroy_constraint;
+ ped_constraint_destroy (constraint);
+ ret = PED_EXCEPTION_FIX;
+ break;
+
+ case PED_EXCEPTION_CANCEL:
+ goto error;
+ }
+ return ret;
+
+error_destroy_constraint:
+ ped_constraint_destroy (constraint);
+error_destroy_part:
+ ped_partition_destroy (part);
+error:
+ return PED_EXCEPTION_CANCEL;
+}
+
+static PedPartition*
+_parse_partition (PedDisk* disk, struct partition_table* pt)
+{
+ PedPartition* part;
+ DVHPartData* dvh_part_data;
+ PedSector start = PED_BE32_TO_CPU (pt->pt_firstlbn);
+ PedSector length = PED_BE32_TO_CPU (pt->pt_nblks);
+
+ part = ped_partition_new (disk,
+ pt->pt_type ? 0 : PED_PARTITION_EXTENDED,
+ NULL,
+ start, start + length - 1);
+ if (!part)
+ return NULL;
+
+ dvh_part_data = part->disk_specific;
+ dvh_part_data->type = PED_BE32_TO_CPU (pt->pt_type);
+ strcpy (dvh_part_data->name, "");
+
+ return part;
+}
+
+static PedPartition*
+_parse_boot_file (PedDisk* disk, struct volume_directory* vd)
+{
+ PedPartition* part;
+ DVHPartData* dvh_part_data;
+ PedSector start = PED_BE32_TO_CPU (vd->vd_lbn);
+ int length = PED_BE32_TO_CPU (vd->vd_nbytes);
+
+ part = ped_partition_new (disk, PED_PARTITION_LOGICAL, NULL,
+ start, start + length/512 - 1);
+ if (!part)
+ return NULL;
+
+ dvh_part_data = part->disk_specific;
+ dvh_part_data->real_file_size = length;
+
+ strncpy (dvh_part_data->name, vd->vd_name, VDNAMESIZE);
+ dvh_part_data->name[VDNAMESIZE] = 0;
+ return part;
+}
+
+static int dvh_write (const PedDisk* disk);
+
+/* YUCK
+ *
+ * If you remove a boot/root/swap partition, the disk->disk_specific
+ * thing isn't updated. (Probably reflects a design bug somewhere...)
+ * Anyway, the workaround is: flush stale flags whenever we allocate
+ * new partition numbers, and before we write to disk.
+ */
+static void
+_flush_stale_flags (const PedDisk* disk)
+{
+ DVHDiskData* dvh_disk_data = disk->disk_specific;
+
+ if (dvh_disk_data->root
+ && !ped_disk_get_partition (disk, dvh_disk_data->root))
+ dvh_disk_data->root = 0;
+ if (dvh_disk_data->swap
+ && !ped_disk_get_partition (disk, dvh_disk_data->swap))
+ dvh_disk_data->swap = 0;
+ if (dvh_disk_data->boot
+ && !ped_disk_get_partition (disk, dvh_disk_data->boot))
+ dvh_disk_data->boot = 0;
+}
+
+static int
+dvh_read (PedDisk* disk)
+{
+ DVHDiskData* dvh_disk_data = disk->disk_specific;
+ int i;
+ struct volume_header vh;
+ char boot_name [BFNAMESIZE + 1];
+#ifndef DISCOVER_ONLY
+ int write_back = 0;
+#endif
+
+ PED_ASSERT (dvh_disk_data != NULL, return 0);
+
+ ped_disk_delete_all (disk);
+
+ if (!ped_device_read (disk->dev, &vh, 0, 1))
+ return 0;
+
+ if (_checksum ((uint32_t*) &vh, sizeof (struct volume_header))) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Checksum is wrong, indicating the partition "
+ "table is corrupt."))
+ == PED_EXCEPTION_CANCEL)
+ return 0;
+ }
+
+ PED_ASSERT (PED_BE32_TO_CPU (vh.vh_magic) == VHMAGIC, return 0);
+
+ dvh_disk_data->dev_params = vh.vh_dp;
+ strncpy (boot_name, vh.vh_bootfile, BFNAMESIZE);
+ boot_name[BFNAMESIZE] = 0;
+
+ /* normal partitions */
+ for (i = 0; i < NPARTAB; i++) {
+ PedPartition* part;
+ PedConstraint* constraint_exact;
+
+ if (!vh.vh_pt[i].pt_nblks)
+ continue;
+ /* Skip the whole-disk partition, parted disklikes overlap */
+ if (PED_BE32_TO_CPU (vh.vh_pt[i].pt_type) == PTYPE_VOLUME)
+ continue;
+
+ part = _parse_partition (disk, &vh.vh_pt[i]);
+ if (!part)
+ goto error_delete_all;
+
+ part->fs_type = ped_file_system_probe (&part->geom);
+ part->num = i + 1;
+
+ if (PED_BE16_TO_CPU (vh.vh_rootpt) == i)
+ ped_partition_set_flag (part, PED_PARTITION_ROOT, 1);
+ if (PED_BE16_TO_CPU (vh.vh_swappt) == i)
+ ped_partition_set_flag (part, PED_PARTITION_SWAP, 1);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition(disk, part, constraint_exact)) {
+ ped_partition_destroy (part);
+ goto error_delete_all;
+ }
+ ped_constraint_destroy (constraint_exact);
+ }
+
+ if (!ped_disk_extended_partition (disk)) {
+#ifdef DISCOVER_ONLY
+ return 1;
+#else
+ switch (_handle_no_volume_header (disk)) {
+ case PED_EXCEPTION_CANCEL:
+ return 0;
+ case PED_EXCEPTION_IGNORE:
+ return 1;
+ case PED_EXCEPTION_FIX:
+ write_back = 1;
+ break;
+ default:
+ break;
+ }
+#endif
+ }
+
+ /* boot partitions */
+ for (i = 0; i < NVDIR; i++) {
+ PedPartition* part;
+ PedConstraint* constraint_exact;
+
+ if (!vh.vh_vd[i].vd_nbytes)
+ continue;
+
+ part = _parse_boot_file (disk, &vh.vh_vd[i]);
+ if (!part)
+ goto error_delete_all;
+
+ part->fs_type = ped_file_system_probe (&part->geom);
+ part->num = NPARTAB + i + 1;
+
+ if (!strcmp (boot_name, ped_partition_get_name (part)))
+ ped_partition_set_flag (part, PED_PARTITION_BOOT, 1);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition(disk, part, constraint_exact)) {
+ ped_partition_destroy (part);
+ goto error_delete_all;
+ }
+ ped_constraint_destroy (constraint_exact);
+ }
+#ifndef DISCOVER_ONLY
+ if (write_back)
+ dvh_write (disk);
+#endif
+ return 1;
+
+error_delete_all:
+ ped_disk_delete_all (disk);
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+static void
+_generate_partition (PedPartition* part, struct partition_table* pt)
+{
+ DVHPartData* dvh_part_data = part->disk_specific;
+
+ /* Assert not a bootfile */
+ PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) == 0, return);
+
+ pt->pt_nblks = PED_CPU_TO_BE32 (part->geom.length);
+ pt->pt_firstlbn = PED_CPU_TO_BE32 (part->geom.start);
+ pt->pt_type = PED_CPU_TO_BE32 (dvh_part_data->type);
+}
+
+static void
+_generate_boot_file (PedPartition* part, struct volume_directory* vd)
+{
+ DVHPartData* dvh_part_data = part->disk_specific;
+
+ /* Assert it's a bootfile */
+ PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) != 0, return);
+
+ vd->vd_nbytes = PED_CPU_TO_BE32 (dvh_part_data->real_file_size);
+ vd->vd_lbn = PED_CPU_TO_BE32 (part->geom.start);
+
+ memset (vd->vd_name, 0, VDNAMESIZE);
+ strncpy (vd->vd_name, dvh_part_data->name, VDNAMESIZE);
+}
+
+static int
+dvh_write (const PedDisk* disk)
+{
+ DVHDiskData* dvh_disk_data = disk->disk_specific;
+ struct volume_header vh;
+ int i;
+
+ PED_ASSERT (dvh_disk_data != NULL, return 0);
+
+ _flush_stale_flags (disk);
+
+ memset (&vh, 0, sizeof (struct volume_header));
+
+ vh.vh_magic = PED_CPU_TO_BE32 (VHMAGIC);
+ vh.vh_rootpt = PED_CPU_TO_BE16 (dvh_disk_data->root - 1);
+ vh.vh_swappt = PED_CPU_TO_BE16 (dvh_disk_data->swap - 1);
+
+ if (dvh_disk_data->boot) {
+ PedPartition* boot_part;
+ boot_part = ped_disk_get_partition (disk, dvh_disk_data->boot);
+ strcpy (vh.vh_bootfile, ped_partition_get_name (boot_part));
+ }
+
+ vh.vh_dp = dvh_disk_data->dev_params;
+ /* Set up rudimentary device geometry */
+ vh.vh_dp.dp_cyls
+ = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.cylinders);
+ vh.vh_dp.dp_trks0 = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.heads);
+ vh.vh_dp.dp_secs
+ = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.sectors);
+ vh.vh_dp.dp_secbytes = PED_CPU_TO_BE16 ((short)disk->dev->sector_size);
+
+ for (i = 0; i < NPARTAB; i++) {
+ PedPartition* part = ped_disk_get_partition (disk, i + 1);
+ if (part)
+ _generate_partition (part, &vh.vh_pt[i]);
+ }
+
+ /* whole disk partition
+ * This is only ever written here, and never modified
+ * (or even shown) as it must contain the entire disk,
+ * and parted does not like overlapping partitions
+ */
+ vh.vh_pt[PNUM_VOLUME].pt_nblks = PED_CPU_TO_BE32 (disk->dev->length);
+ vh.vh_pt[PNUM_VOLUME].pt_firstlbn = PED_CPU_TO_BE32 (0);
+ vh.vh_pt[PNUM_VOLUME].pt_type = PED_CPU_TO_BE32 (PTYPE_VOLUME);
+
+ for (i = 0; i < NVDIR; i++) {
+ PedPartition* part = ped_disk_get_partition (disk,
+ i + 1 + NPARTAB);
+ if (part)
+ _generate_boot_file (part, &vh.vh_vd[i]);
+ }
+
+ vh.vh_csum = 0;
+ vh.vh_csum = PED_CPU_TO_BE32 (_checksum ((uint32_t*) &vh,
+ sizeof (struct volume_header)));
+
+ return ped_device_write (disk->dev, &vh, 0, 1)
+ && ped_device_sync (disk->dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+dvh_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ DVHPartData* dvh_part_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (!ped_partition_is_active (part)) {
+ part->disk_specific = NULL;
+ return part;
+ }
+
+ dvh_part_data = part->disk_specific =
+ ped_malloc (sizeof (DVHPartData));
+ if (!dvh_part_data)
+ goto error_free_part;
+
+ dvh_part_data->type = (part_type == PED_PARTITION_EXTENDED)
+ ? PTYPE_VOLHDR
+ : PTYPE_RAW;
+ strcpy (dvh_part_data->name, "");
+ dvh_part_data->real_file_size = part->geom.length * 512;
+ return part;
+
+error_free_part:
+ _ped_partition_free (part);
+error:
+ return NULL;
+}
+
+static PedPartition*
+dvh_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* result;
+ DVHPartData* part_data = part->disk_specific;
+ DVHPartData* result_data;
+
+ result = _ped_partition_alloc (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ if (!result)
+ goto error;
+ result->num = part->num;
+
+ if (!ped_partition_is_active (part)) {
+ result->disk_specific = NULL;
+ return result;
+ }
+
+ result_data = result->disk_specific =
+ ped_malloc (sizeof (DVHPartData));
+ if (!result_data)
+ goto error_free_part;
+
+ result_data->type = part_data->type;
+ strcpy (result_data->name, part_data->name);
+ result_data->real_file_size = part_data->real_file_size;
+ return result;
+
+error_free_part:
+ _ped_partition_free (result);
+error:
+ return NULL;
+}
+
+static void
+dvh_partition_destroy (PedPartition* part)
+{
+ if (ped_partition_is_active (part)) {
+ PED_ASSERT (part->disk_specific != NULL, return);
+ ped_free (part->disk_specific);
+ }
+ _ped_partition_free (part);
+}
+
+static int
+dvh_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ DVHPartData* dvh_part_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ dvh_part_data->type = PTYPE_VOLHDR;
+ return 1;
+ }
+
+ /* Is this a bootfile? */
+ if (part->type == PED_PARTITION_LOGICAL)
+ return 1;
+
+ if (fs_type && !strcmp (fs_type->name, "xfs"))
+ dvh_part_data->type = PTYPE_XFS;
+ else
+ dvh_part_data->type = PTYPE_RAW;
+ return 1;
+}
+
+static int
+dvh_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ DVHDiskData* dvh_disk_data = part->disk->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_ROOT:
+ if (part->type != 0 && state) {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Only primary partitions can be root "
+ "partitions."));
+#endif
+ return 0;
+ }
+ dvh_disk_data->root = state ? part->num : 0;
+ break;
+
+ case PED_PARTITION_SWAP:
+ if (part->type != 0 && state) {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Only primary partitions can be swap "
+ "partitions."));
+ return 0;
+#endif
+ }
+ dvh_disk_data->swap = state ? part->num : 0;
+ break;
+
+ case PED_PARTITION_BOOT:
+ if (part->type != PED_PARTITION_LOGICAL && state) {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Only logical partitions can be a boot "
+ "file."));
+#endif
+ return 0;
+ }
+ dvh_disk_data->boot = state ? part->num : 0;
+ break;
+
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_RAID:
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int
+dvh_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ DVHDiskData* dvh_disk_data = part->disk->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_ROOT:
+ return dvh_disk_data->root == part->num;
+
+ case PED_PARTITION_SWAP:
+ return dvh_disk_data->swap == part->num;
+
+ case PED_PARTITION_BOOT:
+ return dvh_disk_data->boot == part->num;
+
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_RAID:
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int
+dvh_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_SWAP:
+ case PED_PARTITION_BOOT:
+ return 1;
+
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_RAID:
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+dvh_partition_set_name (PedPartition* part, const char* name)
+{
+ DVHPartData* dvh_part_data = part->disk_specific;
+
+ if (part->type == PED_PARTITION_LOGICAL) {
+ /* Bootfile */
+ strncpy (dvh_part_data->name, name, VDNAMESIZE);
+ dvh_part_data->name[VDNAMESIZE] = 0;
+ } else {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Only logical partitions (boot files) have a name."));
+#endif
+ }
+}
+
+static const char*
+dvh_partition_get_name (const PedPartition* part)
+{
+ DVHPartData* dvh_part_data = part->disk_specific;
+ return dvh_part_data->name;
+}
+
+/* The constraint for the volume header partition is different, because it must
+ * contain the first sector of the disk.
+ */
+static PedConstraint*
+_get_extended_constraint (PedDisk* disk)
+{
+ PedGeometry min_geom;
+ if (!ped_geometry_init (&min_geom, disk->dev, 0, 1))
+ return NULL;
+ return ped_constraint_new_from_min (&min_geom);
+}
+
+static PedConstraint*
+_get_primary_constraint (PedDisk* disk)
+{
+ PedGeometry max_geom;
+ if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1))
+ return NULL;
+ return ped_constraint_new_from_max (&max_geom);
+}
+
+static int
+dvh_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ if (_ped_partition_attempt_align (
+ part, constraint,
+ (part->type == PED_PARTITION_EXTENDED)
+ ? _get_extended_constraint (part->disk)
+ : _get_primary_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+dvh_partition_enumerate (PedPartition* part)
+{
+ int i;
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+
+ _flush_stale_flags (part->disk);
+
+ if (part->type & PED_PARTITION_LOGICAL) {
+ /* Bootfile */
+ for (i = 1 + NPARTAB; i <= NPARTAB + NVDIR; i++) {
+ if (!ped_disk_get_partition (part->disk, i)) {
+ part->num = i;
+ return 1;
+ }
+ }
+ PED_ASSERT (0, return 0);
+ } else if (part->type & PED_PARTITION_EXTENDED) {
+ /* Volheader */
+ part->num = PNUM_VOLHDR + 1;
+ } else {
+ for (i = 1; i <= NPARTAB; i++) {
+ /* reserved for full volume partition */
+ if (i == PNUM_VOLUME + 1)
+ continue;
+
+ if (!ped_disk_get_partition (part->disk, i)) {
+ part->num = i;
+ return 1;
+ }
+ }
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Too many primary partitions"));
+ }
+
+ return 0;
+}
+
+static int
+dvh_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return NPARTAB;
+}
+
+static int
+dvh_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* part;
+ PedPartition* extended_part;
+ PedConstraint* constraint_exact;
+ PedPartitionType metadata_type;
+ PED_ASSERT(disk != NULL, return 0);
+
+ /* We don't need to "protect" the start of the disk from the volume
+ * header.
+ */
+ extended_part = ped_disk_extended_partition (disk);
+ if (extended_part && extended_part->geom.start == 0)
+ metadata_type = PED_PARTITION_METADATA | PED_PARTITION_LOGICAL;
+ else
+ metadata_type = PED_PARTITION_METADATA;
+
+ part = ped_partition_new (disk, metadata_type, NULL, 0, 0);
+ if (!part)
+ goto error;
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error_destroy_part;
+ ped_constraint_destroy (constraint_exact);
+ return 1;
+
+ ped_constraint_destroy (constraint_exact);
+error_destroy_part:
+ ped_partition_destroy (part);
+error:
+ return 0;
+}
+
+static PedDiskOps dvh_disk_ops = {
+ .probe = dvh_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = dvh_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = dvh_alloc,
+ .duplicate = dvh_duplicate,
+ .free = dvh_free,
+ .read = dvh_read,
+#ifndef DISCOVER_ONLY
+ .write = dvh_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = dvh_partition_new,
+ .partition_duplicate = dvh_partition_duplicate,
+ .partition_destroy = dvh_partition_destroy,
+ .partition_set_system = dvh_partition_set_system,
+ .partition_set_flag = dvh_partition_set_flag,
+ .partition_get_flag = dvh_partition_get_flag,
+ .partition_is_flag_available = dvh_partition_is_flag_available,
+ .partition_set_name = dvh_partition_set_name,
+ .partition_get_name = dvh_partition_get_name,
+ .partition_align = dvh_partition_align,
+ .partition_enumerate = dvh_partition_enumerate,
+
+ .alloc_metadata = dvh_alloc_metadata,
+ .get_max_primary_partition_count =
+ dvh_get_max_primary_partition_count
+};
+
+static PedDiskType dvh_disk_type = {
+ .next = NULL,
+ .name = "dvh",
+ .ops = &dvh_disk_ops,
+ .features = PED_DISK_TYPE_PARTITION_NAME | PED_DISK_TYPE_EXTENDED
+};
+
+void
+ped_disk_dvh_init ()
+{
+ PED_ASSERT (sizeof (struct volume_header) == 512, return);
+
+ ped_disk_type_register (&dvh_disk_type);
+}
+
+void
+ped_disk_dvh_done ()
+{
+ ped_disk_type_unregister (&dvh_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/dvh.h b/usr/src/lib/libparted/common/libparted/labels/dvh.h
new file mode 100644
index 0000000000..992f3ee87b
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/dvh.h
@@ -0,0 +1,178 @@
+/*
+ Copyright (C) 1985 MIPS Computer Systems, Inc.
+ Copyright (C) 2000 Silicon Graphics Computer Systems, Inc.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SYS_DVH_H
+#define _SYS_DVH_H
+
+/*
+ * Format for volume header information
+ *
+ * The volume header is a block located at the beginning of all disk
+ * media (sector 0). It contains information pertaining to physical
+ * device parameters and logical partition information.
+ *
+ * The volume header is manipulated by disk formatters/verifiers,
+ * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers.
+ *
+ * Previous versions of IRIX wrote a copy of the volume header is
+ * located at sector 0 of each track of cylinder 0. These copies were
+ * never used, and reduced the capacity of the volume header to hold large
+ * files, so this practice was discontinued.
+ * The volume header is constrained to be less than or equal to 512
+ * bytes long. A particular copy is assumed valid if no drive errors
+ * are detected, the magic number is correct, and the 32 bit 2's complement
+ * of the volume header is correct. The checksum is calculated by initially
+ * zeroing vh_csum, summing the entire structure and then storing the
+ * 2's complement of the sum. Thus a checksum to verify the volume header
+ * should be 0.
+ *
+ * The error summary table, bad sector replacement table, and boot blocks are
+ * located by searching the volume directory within the volume header.
+ *
+ * Tables are sized simply by the integral number of table records that
+ * will fit in the space indicated by the directory entry.
+ *
+ * The amount of space allocated to the volume header, replacement blocks,
+ * and other tables is user defined when the device is formatted.
+ */
+
+/*
+ * device parameters are in the volume header to determine mapping
+ * from logical block numbers to physical device addresses
+ *
+ * Linux doesn't care ...
+ */
+struct device_parameters {
+ unsigned char dp_skew; /* spiral addressing skew */
+ unsigned char dp_gap1; /* words of 0 before header */
+ unsigned char dp_gap2; /* words of 0 between hdr and data */
+ unsigned char dp_spares_cyl; /* This is for drives (such as SCSI
+ that support zone oriented sparing, where the zone is larger
+ than one track. It gets subracteded from the cylinder size
+ ( dp_trks0 * dp_sec) when doing partition size calculations */
+ unsigned short dp_cyls; /* number of usable cylinders (i.e.,
+ doesn't include cylinders reserved by the drive for badblocks,
+ etc.). For drives with variable geometry, this number may be
+ decreased so that:
+ dp_cyls * ((dp_heads * dp_trks0) - dp_spares_cyl) <= actualcapacity
+ This happens on SCSI drives such as the Wren IV and Toshiba 156
+ Also see dp_cylshi below */
+ unsigned short dp_shd0; /* starting head vol 0 */
+ unsigned short dp_trks0; /* number of tracks / cylinder vol 0*/
+ unsigned char dp_ctq_depth; /* Depth of CTQ queue */
+ unsigned char dp_cylshi; /* high byte of 24 bits of cylinder count */
+ unsigned short dp_unused; /* not used */
+ unsigned short dp_secs; /* number of sectors/track */
+ unsigned short dp_secbytes; /* length of sector in bytes */
+ unsigned short dp_interleave; /* sector interleave */
+ int dp_flags; /* controller characteristics */
+ int dp_datarate; /* bytes/sec for kernel stats */
+ int dp_nretries; /* max num retries on data error */
+ int dp_mspw; /* ms per word to xfer, for iostat */
+ unsigned short dp_xgap1; /* Gap 1 for xylogics controllers */
+ unsigned short dp_xsync; /* sync delay for xylogics controllers */
+ unsigned short dp_xrdly; /* read delay for xylogics controllers */
+ unsigned short dp_xgap2; /* gap 2 for xylogics controllers */
+ unsigned short dp_xrgate; /* read gate for xylogics controllers */
+ unsigned short dp_xwcont; /* write continuation for xylogics */
+};
+
+/*
+ * Device characterization flags
+ * (dp_flags)
+ */
+#define DP_SECTSLIP 0x00000001 /* sector slip to spare sector */
+#define DP_SECTFWD 0x00000002 /* forward to replacement sector */
+#define DP_TRKFWD 0x00000004 /* forward to replacement track */
+#define DP_MULTIVOL 0x00000008 /* multiple volumes per spindle */
+#define DP_IGNOREERRORS 0x00000010 /* transfer data regardless of errors */
+#define DP_RESEEK 0x00000020 /* recalibrate as last resort */
+#define DP_CTQ_EN 0x00000040 /* enable command tag queueing */
+
+/*
+ * Boot blocks, bad sector tables, and the error summary table, are located
+ * via the volume_directory.
+ */
+#define VDNAMESIZE 8
+
+struct volume_directory {
+ char vd_name[VDNAMESIZE]; /* name */
+ int vd_lbn; /* logical block number */
+ int vd_nbytes; /* file length in bytes */
+};
+
+/*
+ * partition table describes logical device partitions
+ * (device drivers examine this to determine mapping from logical units
+ * to cylinder groups, device formatters/verifiers examine this to determine
+ * location of replacement tracks/sectors, etc)
+ *
+ * NOTE: pt_firstlbn SHOULD BE CYLINDER ALIGNED
+ */
+struct partition_table { /* one per logical partition */
+ int pt_nblks; /* # of logical blks in partition */
+ int pt_firstlbn; /* first lbn of partition */
+ int pt_type; /* use of partition */
+};
+
+#define PTYPE_VOLHDR 0 /* partition is volume header */
+#define PTYPE_TRKREPL 1 /* partition is used for repl trks */
+#define PTYPE_SECREPL 2 /* partition is used for repl secs */
+#define PTYPE_RAW 3 /* partition is used for data */
+#define PTYPE_BSD42 4 /* partition is 4.2BSD file system */
+#define PTYPE_BSD 4 /* partition is 4.2BSD file system */
+#define PTYPE_SYSV 5 /* partition is SysV file system */
+#define PTYPE_VOLUME 6 /* partition is entire volume */
+#define PTYPE_EFS 7 /* partition is sgi EFS */
+#define PTYPE_LVOL 8 /* partition is part of a logical vol */
+#define PTYPE_RLVOL 9 /* part of a "raw" logical vol */
+#define PTYPE_XFS 10 /* partition is sgi XFS */
+#define PTYPE_XFSLOG 11 /* partition is sgi XFS log */
+#define PTYPE_XLV 12 /* partition is part of an XLV vol */
+#define PTYPE_XVM 13 /* partition is sgi XVM */
+#define NPTYPES 16
+
+#define VHMAGIC 0xbe5a941 /* randomly chosen value */
+#define NPARTAB 16 /* 16 unix partitions */
+#define NVDIR 15 /* max of 15 directory entries */
+#define BFNAMESIZE 16 /* max 16 chars in boot file name */
+
+/* Partition types for ARCS */
+#define NOT_USED 0 /* Not used */
+#define FAT_SHORT 1 /* FAT file system, 12-bit FAT entries */
+#define FAT_LONG 4 /* FAT file system, 16-bit FAT entries */
+#define EXTENDED 5 /* extended partition */
+#define HUGE 6 /* huge partition- MS/DOS 4.0 and later */
+
+/* Active flags for ARCS */
+#define BOOTABLE 0x00;
+#define NOT_BOOTABLE 0x80;
+
+struct volume_header {
+ int vh_magic; /* identifies volume header */
+ short vh_rootpt; /* root partition number */
+ short vh_swappt; /* swap partition number */
+ char vh_bootfile[BFNAMESIZE]; /* name of file to boot */
+ struct device_parameters vh_dp; /* device parameters */
+ struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */
+ struct partition_table vh_pt[NPARTAB]; /* device partition layout */
+ int vh_csum; /* volume header checksum */
+ int vh_fill; /* fill out to 512 bytes */
+};
+
+#endif /* _SYS_DVH_H */
diff --git a/usr/src/lib/libparted/common/libparted/labels/efi_crc32.c b/usr/src/lib/libparted/common/libparted/labels/efi_crc32.c
new file mode 100644
index 0000000000..327fb2dd4b
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/efi_crc32.c
@@ -0,0 +1,126 @@
+/*
+ * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com>
+ * - Copied crc32.c from the linux/drivers/net/cipe directory.
+ * - Now pass seed as an arg
+ * - changed unsigned long to uint32_t, added #include<stdint.h>
+ * - changed len to be an unsigned long
+ * - changed crc32val to be a register
+ * - License remains unchanged! It's still GPL-compatable!
+ */
+
+ /* ============================================================= */
+ /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */
+ /* code or tables extracted from it, as desired without restriction. */
+ /* */
+ /* First, the polynomial itself and its table of feedback terms. The */
+ /* polynomial is */
+ /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+ /* */
+ /* Note that we take it "backwards" and put the highest-order term in */
+ /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+ /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+ /* the MSB being 1. */
+ /* */
+ /* Note that the usual hardware shift register implementation, which */
+ /* is what we're using (we're merely optimizing it by doing eight-bit */
+ /* chunks at a time) shifts bits into the lowest-order term. In our */
+ /* implementation, that means shifting towards the right. Why do we */
+ /* do it this way? Because the calculated CRC must be transmitted in */
+ /* order from highest-order term to lowest-order term. UARTs transmit */
+ /* characters in order from LSB to MSB. By storing the CRC this way, */
+ /* we hand it to the UART in the order low-byte to high-byte; the UART */
+ /* sends each low-bit to hight-bit; and the result is transmission bit */
+ /* by bit from highest- to lowest-order term without requiring any bit */
+ /* shuffling on our part. Reception works similarly. */
+ /* */
+ /* The feedback terms table consists of 256, 32-bit entries. Notes: */
+ /* */
+ /* The table can be generated at runtime if desired; code to do so */
+ /* is shown later. It might not be obvious, but the feedback */
+ /* terms simply represent the results of eight shift/xor opera- */
+ /* tions for all combinations of data and CRC register values. */
+ /* */
+ /* The values must be right-shifted by eight bits by the "updcrc" */
+ /* logic; the shift must be unsigned (bring in zeroes). On some */
+ /* hardware you could probably optimize the shift in assembler by */
+ /* using byte-swap instructions. */
+ /* polynomial $edb88320 */
+ /* */
+ /* -------------------------------------------------------------------- */
+
+#include <config.h>
+#include <stdint.h>
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+ };
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+uint32_t
+__efi_crc32(const void *buf, unsigned long len, uint32_t seed)
+{
+ unsigned long i;
+ register uint32_t crc32val;
+ const unsigned char *s = buf;
+
+ crc32val = seed;
+ for (i = 0; i < len; i ++)
+ {
+ crc32val =
+ crc32_tab[(crc32val ^ s[i]) & 0xff] ^
+ (crc32val >> 8);
+ }
+ return crc32val;
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/gpt.c b/usr/src/lib/libparted/common/libparted/labels/gpt.c
new file mode 100644
index 0000000000..e7877dab45
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/gpt.c
@@ -0,0 +1,1538 @@
+/*
+ libparted - a library for manipulating disk partitions
+
+ original version by Matt Domsch <Matt_Domsch@dell.com>
+ Disclaimed into the Public Domain
+
+ Portions Copyright (C) 2001, 2002, 2003, 2005, 2006, 2007
+ Free Software Foundation, Inc.
+
+ EFI GUID Partition Table handling
+ Per Intel EFI Specification v1.02
+ http://developer.intel.com/technology/efi/efi.htm
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+#include <parted/crc32.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) gettext (String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define EFI_PMBR_OSTYPE_EFI 0xEE
+#define MSDOS_MBR_SIGNATURE 0xaa55
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL
+
+/* NOTE: the document that describes revision 1.00 is labelled "version 1.02",
+ * so some implementors got confused...
+ */
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+typedef uint16_t efi_char16_t; /* UNICODE character */
+typedef struct _GuidPartitionTableHeader_t GuidPartitionTableHeader_t;
+typedef struct _GuidPartitionEntryAttributes_t GuidPartitionEntryAttributes_t;
+typedef struct _GuidPartitionEntry_t GuidPartitionEntry_t;
+typedef struct _PartitionRecord_t PartitionRecord_t;
+typedef struct _LegacyMBR_t LegacyMBR_t;
+typedef struct _GPTDiskData GPTDiskData;
+typedef struct {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_hi_and_reserved;
+ uint8_t clock_seq_low;
+ uint8_t node[6];
+} /* __attribute__ ((packed)) */ efi_guid_t;
+/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed
+ * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized
+ * data. It turns out we don't need it in this case, so it doesn't break
+ * anything :)
+ */
+
+#define UNUSED_ENTRY_GUID \
+ ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+#define PARTITION_SYSTEM_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xC12A7328), PED_CPU_TO_LE16 (0xF81F), \
+ PED_CPU_TO_LE16 (0x11d2), 0xBA, 0x4B, \
+ { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }})
+#define LEGACY_MBR_PARTITION_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x024DEE41), PED_CPU_TO_LE16 (0x33E7), \
+ PED_CPU_TO_LE16 (0x11d3, 0x9D, 0x69, \
+ { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }})
+#define PARTITION_MSFT_RESERVED_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xE3C9E316), PED_CPU_TO_LE16 (0x0B5C), \
+ PED_CPU_TO_LE16 (0x4DB8), 0x81, 0x7D, \
+ { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE }})
+#define PARTITION_BASIC_DATA_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xEBD0A0A2), PED_CPU_TO_LE16 (0xB9E5), \
+ PED_CPU_TO_LE16 (0x4433), 0x87, 0xC0, \
+ { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 }})
+#define PARTITION_RAID_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xa19d880f), PED_CPU_TO_LE16 (0x05fc), \
+ PED_CPU_TO_LE16 (0x4d3b), 0xa0, 0x06, \
+ { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e }})
+#define PARTITION_SWAP_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x0657fd6d), PED_CPU_TO_LE16 (0xa4ab), \
+ PED_CPU_TO_LE16 (0x43c4), 0x84, 0xe5, \
+ { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f }})
+#define PARTITION_LVM_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xe6d6d379), PED_CPU_TO_LE16 (0xf507), \
+ PED_CPU_TO_LE16 (0x44c2), 0xa2, 0x3c, \
+ { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 }})
+#define PARTITION_RESERVED_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x8da63339), PED_CPU_TO_LE16 (0x0007), \
+ PED_CPU_TO_LE16 (0x60c0), 0xc4, 0x36, \
+ { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 }})
+#define PARTITION_HPSERVICE_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0xe2a1e728), PED_CPU_TO_LE16 (0x32e3), \
+ PED_CPU_TO_LE16 (0x11d6), 0xa6, 0x82, \
+ { 0x7b, 0x03, 0xa0, 0x00, 0x00, 0x00 }})
+#define PARTITION_APPLE_HFS_GUID \
+ ((efi_guid_t) { PED_CPU_TO_LE32 (0x48465300), PED_CPU_TO_LE16 (0x0000), \
+ PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \
+ { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }})
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _GuidPartitionTableHeader_t {
+ uint64_t Signature;
+ uint32_t Revision;
+ uint32_t HeaderSize;
+ uint32_t HeaderCRC32;
+ uint32_t Reserved1;
+ uint64_t MyLBA;
+ uint64_t AlternateLBA;
+ uint64_t FirstUsableLBA;
+ uint64_t LastUsableLBA;
+ efi_guid_t DiskGUID;
+ uint64_t PartitionEntryLBA;
+ uint32_t NumberOfPartitionEntries;
+ uint32_t SizeOfPartitionEntry;
+ uint32_t PartitionEntryArrayCRC32;
+ uint8_t* Reserved2;
+};
+
+struct __attribute__ ((packed)) _GuidPartitionEntryAttributes_t {
+#if defined(__GNUC__) || defined(__sun) /* XXX narrow this down to !TinyCC */
+ uint64_t RequiredToFunction:1;
+ uint64_t Reserved:47;
+ uint64_t GuidSpecific:16;
+#else
+ uint32_t RequiredToFunction:1;
+ uint32_t Reserved:32;
+ uint32_t LOST:5;
+ uint32_t GuidSpecific:16;
+#endif
+};
+
+struct __attribute__ ((packed)) _GuidPartitionEntry_t {
+ efi_guid_t PartitionTypeGuid;
+ efi_guid_t UniquePartitionGuid;
+ uint64_t StartingLBA;
+ uint64_t EndingLBA;
+ GuidPartitionEntryAttributes_t Attributes;
+ efi_char16_t PartitionName[72 / sizeof(efi_char16_t)];
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+#define GPT_PMBR_LBA 0
+#define GPT_PMBR_SECTORS 1
+#define GPT_PRIMARY_HEADER_LBA 1
+#define GPT_HEADER_SECTORS 1
+#define GPT_PRIMARY_PART_TABLE_LBA 2
+
+/*
+ These values are only defaults. The actual on-disk structures
+ may define different sizes, so use those unless creating a new GPT disk!
+*/
+
+#define GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE 16384
+
+/* Number of actual partition entries should be calculated as: */
+#define GPT_DEFAULT_PARTITION_ENTRIES \
+ (GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / \
+ sizeof(GuidPartitionEntry_t))
+
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _PartitionRecord_t {
+ /* Not used by EFI firmware. Set to 0x80 to indicate that this
+ is the bootable legacy partition. */
+ uint8_t BootIndicator;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartHead;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartSector;
+
+ /* Start of partition in CHS address, not used by EFI firmware. */
+ uint8_t StartTrack;
+
+ /* OS type. A value of 0xEF defines an EFI system partition.
+ Other values are reserved for legacy operating systems, and
+ allocated independently of the EFI specification. */
+ uint8_t OSType;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndHead;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndSector;
+
+ /* End of partition in CHS address, not used by EFI firmware. */
+ uint8_t EndTrack;
+
+ /* Starting LBA address of the partition on the disk. Used by
+ EFI firmware to define the start of the partition. */
+ uint32_t StartingLBA;
+
+ /* Size of partition in LBA. Used by EFI firmware to determine
+ the size of the partition. */
+ uint32_t SizeInLBA;
+};
+
+/* Protected Master Boot Record & Legacy MBR share same structure */
+/* Needs to be packed because the u16s force misalignment. */
+struct __attribute__ ((packed)) _LegacyMBR_t {
+ uint8_t BootCode[440];
+ uint32_t UniqueMBRSignature;
+ uint16_t Unknown;
+ PartitionRecord_t PartitionRecord[4];
+ uint16_t Signature;
+};
+
+/* uses libparted's disk_specific field in PedDisk, to store our info */
+struct __attribute__ ((packed)) _GPTDiskData {
+ PedGeometry data_area;
+ int entry_count;
+ efi_guid_t uuid;
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+/* uses libparted's disk_specific field in PedPartition, to store our info */
+typedef struct _GPTPartitionData {
+ efi_guid_t type;
+ efi_guid_t uuid;
+ char name[37];
+ int lvm;
+ int raid;
+ int boot;
+ int hp_service;
+ int hidden;
+ int msftres;
+} GPTPartitionData;
+
+static PedDiskType gpt_disk_type;
+
+
+static inline uint32_t
+pth_get_size (const PedDevice* dev)
+{
+ return GPT_HEADER_SECTORS * dev->sector_size;
+}
+
+
+static inline uint32_t
+pth_get_size_static (const PedDevice* dev)
+{
+ return sizeof (GuidPartitionTableHeader_t) - sizeof (uint8_t*);
+}
+
+
+static inline uint32_t
+pth_get_size_rsv2 (const PedDevice* dev)
+{
+ return pth_get_size(dev) - pth_get_size_static(dev);
+}
+
+
+static GuidPartitionTableHeader_t*
+pth_new (const PedDevice* dev)
+{
+ GuidPartitionTableHeader_t* pth = ped_malloc (
+ sizeof (GuidPartitionTableHeader_t)
+ + sizeof (uint8_t));
+
+ pth->Reserved2 = ped_malloc ( pth_get_size_rsv2 (dev) );
+
+ return pth;
+}
+
+
+static GuidPartitionTableHeader_t*
+pth_new_zeroed (const PedDevice* dev)
+{
+ GuidPartitionTableHeader_t* pth = pth_new (dev);
+
+ memset (pth, 0, pth_get_size_static (dev));
+ memset (pth->Reserved2, 0, pth_get_size_rsv2 (dev));
+
+ return (pth);
+}
+
+
+static GuidPartitionTableHeader_t*
+pth_new_from_raw (const PedDevice* dev, const uint8_t* pth_raw)
+{
+ GuidPartitionTableHeader_t* pth = pth_new (dev);
+
+ PED_ASSERT (pth_raw != NULL, return 0);
+
+ memcpy (pth, pth_raw, pth_get_size_static (dev));
+ memcpy (pth->Reserved2, pth_raw + pth_get_size_static (dev),
+ pth_get_size_rsv2 (dev));
+
+ return pth;
+}
+
+static void
+pth_free (GuidPartitionTableHeader_t* pth)
+{
+ PED_ASSERT (pth != NULL, return);
+ PED_ASSERT (pth->Reserved2 != NULL, return);
+
+ ped_free (pth->Reserved2);
+ ped_free (pth);
+}
+
+static uint8_t*
+pth_get_raw (const PedDevice* dev, const GuidPartitionTableHeader_t* pth)
+{
+ uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
+ int size_static = pth_get_size_static (dev);
+
+ PED_ASSERT (pth != NULL, return 0);
+ PED_ASSERT (pth->Reserved2 != NULL, return 0);
+
+ memcpy (pth_raw, pth, size_static);
+ memcpy (pth_raw + size_static, pth->Reserved2, pth_get_size_rsv2 (dev));
+
+ return pth_raw;
+}
+
+
+/**
+ * swap_uuid_and_efi_guid() - converts between uuid formats
+ * @uuid - uuid_t in either format (converts it to the other)
+ *
+ * There are two different representations for Globally Unique Identifiers
+ * (GUIDs or UUIDs).
+ *
+ * The RFC specifies a UUID as a string of 16 bytes, essentially
+ * a big-endian array of char.
+ * Intel, in their EFI Specification, references the same RFC, but
+ * then defines a GUID as a structure of little-endian fields.
+ * Coincidentally, both structures have the same format when unparsed.
+ *
+ * When read from disk, EFI GUIDs are in struct of little endian format,
+ * and need to be converted to be treated as uuid_t in memory.
+ *
+ * When writing to disk, uuid_ts need to be converted into EFI GUIDs.
+ *
+ * Blame Intel.
+ */
+static void
+swap_uuid_and_efi_guid(uuid_t uuid)
+{
+ efi_guid_t *guid = (efi_guid_t *)uuid;
+
+ PED_ASSERT(uuid != NULL, return);
+ guid->time_low = PED_SWAP32(guid->time_low);
+ guid->time_mid = PED_SWAP16(guid->time_mid);
+ guid->time_hi_and_version = PED_SWAP16(guid->time_hi_and_version);
+}
+
+/* returns the EFI-style CRC32 value for buf
+ * This function uses the crc32 function by Gary S. Brown,
+ * but seeds the function with ~0, and xor's with ~0 at the end.
+ */
+static inline uint32_t
+efi_crc32(const void *buf, unsigned long len)
+{
+ return (__efi_crc32(buf, len, ~0L) ^ ~0L);
+}
+
+static inline uint32_t
+pth_crc32(const PedDevice* dev, const GuidPartitionTableHeader_t* pth)
+{
+ uint8_t* pth_raw = pth_get_raw (dev, pth);
+ uint32_t crc32 = 0;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (pth != NULL, return 0);
+
+ crc32 = efi_crc32 (pth_raw, PED_LE32_TO_CPU (pth->HeaderSize));
+
+ ped_free (pth_raw);
+
+ return crc32;
+}
+
+static inline int
+guid_cmp (efi_guid_t left, efi_guid_t right)
+{
+ return memcmp(&left, &right, sizeof(efi_guid_t));
+}
+
+/* checks if 'mbr' is a protective MBR partition table */
+static inline int
+_pmbr_is_valid (const LegacyMBR_t* mbr)
+{
+ int i;
+
+ PED_ASSERT(mbr != NULL, return 0);
+
+ if (mbr->Signature != PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE))
+ return 0;
+ for (i = 0; i < 4; i++) {
+ if (mbr->PartitionRecord[i].OSType == EFI_PMBR_OSTYPE_EFI)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+gpt_probe (const PedDevice * dev)
+{
+ GuidPartitionTableHeader_t* gpt = NULL;
+ uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
+ LegacyMBR_t legacy_mbr;
+ int gpt_sig_found = 0;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (pth_raw != NULL, return 0);
+
+ if (ped_device_read(dev, pth_raw, 1, GPT_HEADER_SECTORS)
+ || ped_device_read(dev, pth_raw, dev->length - 1, GPT_HEADER_SECTORS)) {
+ gpt = pth_new_from_raw (dev, pth_raw);
+ if (gpt->Signature == PED_CPU_TO_LE64(GPT_HEADER_SIGNATURE))
+ gpt_sig_found = 1;
+ }
+
+ ped_free (pth_raw);
+
+ if (gpt)
+ pth_free (gpt);
+
+
+ if (!gpt_sig_found)
+ return 0;
+
+ if (ped_device_read(dev, &legacy_mbr, 0, GPT_HEADER_SECTORS)) {
+ if (!_pmbr_is_valid (&legacy_mbr)) {
+ int ex_status = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_YES_NO,
+ _("%s contains GPT signatures, indicating that it has "
+ "a GPT table. However, it does not have a valid "
+ "fake msdos partition table, as it should. Perhaps "
+ "it was corrupted -- possibly by a program that "
+ "doesn't understand GPT partition tables. Or "
+ "perhaps you deleted the GPT table, and are now "
+ "using an msdos partition table. Is this a GPT "
+ "partition table?"),
+ dev->path);
+ if (ex_status == PED_EXCEPTION_NO)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+/* writes zeros to the PMBR and the primary and alternate GPTHs and PTEs */
+static int
+gpt_clobber(PedDevice * dev)
+{
+ LegacyMBR_t pmbr;
+ uint8_t* zeroed_pth_raw = ped_malloc (pth_get_size (dev));
+ uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
+ GuidPartitionTableHeader_t* gpt;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ memset(&pmbr, 0, sizeof(pmbr));
+ memset(zeroed_pth_raw, 0, pth_get_size (dev));
+
+ /*
+ * TO DISCUSS: check whether checksum is correct?
+ * If not, we might get a wrong AlternateLBA field and destroy
+ * one sector of random data.
+ */
+ if (!ped_device_read(dev, pth_raw,
+ GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS))
+ goto error_free;
+
+ gpt = pth_new_from_raw (dev, pth_raw);
+
+ if (!ped_device_write(dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS))
+ goto error_free_with_gpt;
+ if (!ped_device_write(dev, &zeroed_pth_raw,
+ GPT_PRIMARY_HEADER_LBA, GPT_HEADER_SECTORS))
+ goto error_free_with_gpt;
+ if (!ped_device_write(dev, &zeroed_pth_raw, dev->length - GPT_HEADER_SECTORS,
+ GPT_HEADER_SECTORS))
+ goto error_free_with_gpt;
+
+ if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA) < dev->length - 1) {
+ if (!ped_device_write(dev, gpt,
+ PED_LE64_TO_CPU (gpt->AlternateLBA),
+ GPT_HEADER_SECTORS))
+ return 0;
+ }
+
+ pth_free (gpt);
+
+ return 1;
+
+error_free_with_gpt:
+ pth_free (gpt);
+error_free:
+ ped_free (pth_raw);
+ ped_free (zeroed_pth_raw);
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk *
+gpt_alloc (const PedDevice * dev)
+{
+ PedDisk* disk;
+ GPTDiskData *gpt_disk_data;
+ PedSector data_start, data_end;
+
+ disk = _ped_disk_alloc ((PedDevice*)dev, &gpt_disk_type);
+ if (!disk)
+ goto error;
+ disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData));
+ if (!disk->disk_specific)
+ goto error_free_disk;
+
+ data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+ data_end = dev->length - 2
+ - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size;
+ ped_geometry_init (&gpt_disk_data->data_area, dev, data_start,
+ data_end - data_start + 1);
+ gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES;
+ uuid_generate ((unsigned char*) &gpt_disk_data->uuid);
+ swap_uuid_and_efi_guid((unsigned char*)(&gpt_disk_data->uuid));
+ return disk;
+
+error_free_disk:
+ ped_free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk*
+gpt_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ GPTDiskData* new_disk_data;
+ GPTDiskData* old_disk_data;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &gpt_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ old_disk_data = disk->disk_specific;
+ new_disk_data = new_disk->disk_specific;
+
+ ped_geometry_init (&new_disk_data->data_area, disk->dev,
+ old_disk_data->data_area.start,
+ old_disk_data->data_area.length);
+ new_disk_data->entry_count = old_disk_data->entry_count;
+ new_disk_data->uuid = old_disk_data->uuid;
+ return new_disk;
+}
+
+static void
+gpt_free(PedDisk * disk)
+{
+ ped_disk_delete_all (disk);
+ ped_free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+static int
+_header_is_valid (const PedDevice* dev, GuidPartitionTableHeader_t* gpt)
+{
+ uint32_t crc, origcrc;
+
+ if (PED_LE64_TO_CPU (gpt->Signature) != GPT_HEADER_SIGNATURE)
+ return 0;
+ /*
+ * "While the GUID Partition Table Header's size may increase
+ * in the future it cannot span more than one block on the
+ * device." EFI Specification, version 1.10, 11.2.2.1
+ */
+ if (PED_LE32_TO_CPU (gpt->HeaderSize) < pth_get_size_static (dev)
+ || PED_LE32_TO_CPU (gpt->HeaderSize) > dev->sector_size)
+ return 0;
+
+ origcrc = gpt->HeaderCRC32;
+ gpt->HeaderCRC32 = 0;
+ crc = pth_crc32 (dev, gpt);
+ gpt->HeaderCRC32 = origcrc;
+
+ return crc == PED_LE32_TO_CPU (origcrc);
+}
+
+static int
+_read_header (const PedDevice* dev, GuidPartitionTableHeader_t** gpt,
+ PedSector where)
+{
+ uint8_t* pth_raw = ped_malloc (pth_get_size (dev));
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (!ped_device_read (dev, pth_raw, where, GPT_HEADER_SECTORS)) {
+ ped_free (pth_raw);
+ return 0;
+ }
+
+ *gpt = pth_new_from_raw (dev, pth_raw);
+
+ ped_free (pth_raw);
+
+ if (_header_is_valid (dev, *gpt))
+ return 1;
+
+ pth_free (*gpt);
+ return 0;
+}
+
+static int
+_parse_header (PedDisk* disk, GuidPartitionTableHeader_t* gpt,
+ int *update_needed)
+{
+ GPTDiskData* gpt_disk_data = disk->disk_specific;
+ PedSector first_usable;
+ PedSector last_usable;
+ PedSector last_usable_if_grown, last_usable_min_default;
+ static int asked_already;
+
+ PED_ASSERT (_header_is_valid (disk->dev, gpt), return 0);
+
+#ifndef DISCOVER_ONLY
+ if (PED_LE32_TO_CPU (gpt->Revision) > GPT_HEADER_REVISION_V1_02) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The format of the GPT partition table is version "
+ "%x, which is newer than what Parted can "
+ "recognise. Please tell us! bug-parted@gnu.org"),
+ PED_LE32_TO_CPU (gpt->Revision))
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+#endif
+
+ first_usable = PED_LE64_TO_CPU (gpt->FirstUsableLBA);
+ last_usable = PED_LE64_TO_CPU (gpt->LastUsableLBA);
+
+
+/*
+ Need to check whether the volume has grown, the LastUsableLBA is
+ normally set to disk->dev->length - 2 - ptes_size (at least for parted
+ created volumes), where ptes_size is the number of entries *
+ size of each entry / sector size or 16k / sector size, whatever the greater.
+ If the volume has grown, offer the user the chance to use the new
+ space or continue with the current usable area. Only ask once per
+ parted invocation.
+*/
+
+ last_usable_if_grown
+ = PED_CPU_TO_LE64 (disk->dev->length - 2 -
+ ((PedSector)(PED_LE32_TO_CPU(gpt->NumberOfPartitionEntries)) *
+ (PedSector)(PED_LE32_TO_CPU(gpt->SizeOfPartitionEntry)) /
+ disk->dev->sector_size));
+
+ last_usable_min_default = disk->dev->length - 2 -
+ GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / disk->dev->sector_size;
+
+ if ( last_usable_if_grown > last_usable_min_default ) {
+
+ last_usable_if_grown = last_usable_min_default;
+ }
+
+
+ PED_ASSERT (last_usable > first_usable, return 0);
+ PED_ASSERT (last_usable <= disk->dev->length, return 0);
+
+ PED_ASSERT (last_usable_if_grown > first_usable, return 0);
+ PED_ASSERT (last_usable_if_grown <= disk->dev->length, return 0);
+
+ if ( !asked_already && last_usable < last_usable_if_grown ) {
+
+ PedExceptionOption q;
+
+ q = ped_exception_throw (PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE,
+ _("Not all of the space available to %s appears "
+ "to be used, you can fix the GPT to use all of the "
+ "space (an extra %llu blocks) or continue with the "
+ "current setting? "), disk->dev->path,
+ (uint64_t)(last_usable_if_grown - last_usable));
+
+
+ if (q == PED_EXCEPTION_FIX) {
+
+ last_usable = last_usable_if_grown;
+ *update_needed = 1;
+
+ }
+ else if (q != PED_EXCEPTION_UNHANDLED ) {
+
+ asked_already = 1;
+ }
+ }
+
+ ped_geometry_init (&gpt_disk_data->data_area, disk->dev,
+ first_usable, last_usable - first_usable + 1);
+
+
+ gpt_disk_data->entry_count
+ = PED_LE32_TO_CPU (gpt->NumberOfPartitionEntries);
+ PED_ASSERT (gpt_disk_data->entry_count > 0, return 0);
+ PED_ASSERT (gpt_disk_data->entry_count <= 8192, return 0);
+
+ gpt_disk_data->uuid = gpt->DiskGUID;
+
+ return 1;
+}
+
+static PedPartition*
+_parse_part_entry (PedDisk* disk, GuidPartitionEntry_t* pte)
+{
+ PedPartition* part;
+ GPTPartitionData* gpt_part_data;
+ unsigned int i;
+
+ part = ped_partition_new (disk, 0, NULL,
+ PED_LE64_TO_CPU(pte->StartingLBA),
+ PED_LE64_TO_CPU(pte->EndingLBA));
+ if (!part)
+ return NULL;
+
+ gpt_part_data = part->disk_specific;
+ gpt_part_data->type = pte->PartitionTypeGuid;
+ gpt_part_data->uuid = pte->UniquePartitionGuid;
+ for (i = 0; i < 72 / sizeof (efi_char16_t); i++)
+ gpt_part_data->name[i] = (efi_char16_t) PED_LE16_TO_CPU(
+ (uint16_t) pte->PartitionName[i]);
+ gpt_part_data->name[i] = 0;
+
+ gpt_part_data->lvm = gpt_part_data->raid
+ = gpt_part_data->boot = gpt_part_data->hp_service
+ = gpt_part_data->hidden = gpt_part_data->msftres = 0;
+
+ if (pte->Attributes.RequiredToFunction & 0x1)
+ gpt_part_data->hidden = 1;
+
+ if (!guid_cmp (gpt_part_data->type, PARTITION_SYSTEM_GUID))
+ gpt_part_data->boot = 1;
+ else if (!guid_cmp (gpt_part_data->type, PARTITION_RAID_GUID))
+ gpt_part_data->raid = 1;
+ else if (!guid_cmp (gpt_part_data->type, PARTITION_LVM_GUID))
+ gpt_part_data->lvm = 1;
+ else if (!guid_cmp (gpt_part_data->type, PARTITION_HPSERVICE_GUID))
+ gpt_part_data->hp_service = 1;
+ else if (!guid_cmp (gpt_part_data->type, PARTITION_MSFT_RESERVED_GUID))
+ gpt_part_data->msftres = 1;
+
+ return part;
+}
+
+/************************************************************
+ * Intel is changing the EFI Spec. (after v1.02) to say that a
+ * disk is considered to have a GPT label only if the GPT
+ * structures are correct, and the MBR is actually a Protective
+ * MBR (has one 0xEE type partition).
+ * Problem occurs when a GPT-partitioned disk is then
+ * edited with a legacy (non-GPT-aware) application, such as
+ * fdisk (which doesn't generally erase the PGPT or AGPT).
+ * How should such a disk get handled? As a GPT disk (throwing
+ * away the fdisk changes), or as an MSDOS disk (throwing away
+ * the GPT information). Previously, I've taken the GPT-is-right,
+ * MBR is wrong, approach, to stay consistent with the EFI Spec.
+ * Intel disagrees, saying the disk should then be treated
+ * as having a msdos label, not a GPT label. If this is true,
+ * then what's the point of having an AGPT, since if the PGPT
+ * is screwed up, likely the PMBR is too, and the PMBR becomes
+ * a single point of failure.
+ * So, in the Linux kernel, I'm going to test for PMBR, and
+ * warn if it's not there, and treat the disk as MSDOS, with a note
+ * for users to use Parted to "fix up" their disk if they
+ * really want it to be considered GPT.
+ ************************************************************/
+static int
+gpt_read (PedDisk * disk)
+{
+ GPTDiskData *gpt_disk_data = disk->disk_specific;
+ GuidPartitionTableHeader_t* gpt;
+ GuidPartitionEntry_t* ptes;
+ int ptes_size;
+ int i;
+#ifndef DISCOVER_ONLY
+ int write_back = 0;
+#endif
+
+ ped_disk_delete_all (disk);
+
+ /*
+ * motivation: let the user decide about the pmbr... during
+ * ped_disk_probe(), they probably didn't get a choice...
+ */
+ if (!gpt_probe (disk->dev))
+ goto error;
+
+ if (_read_header (disk->dev, &gpt, 1)) {
+ PED_ASSERT ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
+ <= disk->dev->length - 1, goto error_free_gpt);
+ if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
+ < disk->dev->length - 1) {
+ char* zeros = ped_malloc (pth_get_size (disk->dev));
+
+#ifndef DISCOVER_ONLY
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
+ _("The backup GPT table is not at the end of the disk, as it "
+ "should be. This might mean that another operating system "
+ "believes the disk is smaller. Fix, by moving the backup "
+ "to the end (and removing the old backup)?"))
+ == PED_EXCEPTION_CANCEL)
+ goto error_free_gpt;
+
+ write_back = 1;
+ memset (zeros, 0, disk->dev->sector_size);
+ ped_device_write (disk->dev, zeros,
+ PED_LE64_TO_CPU (gpt->AlternateLBA),
+ 1);
+#endif /* !DISCOVER_ONLY */
+ }
+ } else { /* primary GPT *not* ok */
+ int alternate_ok = 0;
+
+#ifndef DISCOVER_ONLY
+ write_back = 1;
+#endif
+
+ if ((PedSector) PED_LE64_TO_CPU (gpt->AlternateLBA)
+ < disk->dev->length - 1) {
+ alternate_ok = _read_header (disk->dev, &gpt,
+ PED_LE64_TO_CPU(gpt->AlternateLBA));
+ }
+ if (!alternate_ok) {
+ alternate_ok = _read_header (disk->dev, &gpt,
+ disk->dev->length - 1);
+ }
+
+ if (alternate_ok) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_OK_CANCEL,
+ _("The primary GPT table is corrupt, but the "
+ "backup appears OK, so that will be used."))
+ == PED_EXCEPTION_CANCEL)
+ goto error_free_gpt;
+ } else {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Both the primary and backup GPT tables "
+ "are corrupt. Try making a fresh table, "
+ "and using Parted's rescue feature to "
+ "recover partitions."));
+ goto error;
+ }
+ }
+
+ if (!_parse_header (disk, gpt, &write_back))
+ goto error_free_gpt;
+
+ /*
+ * ptes_size is in bytes and must be a multiple of sector_size.
+ */
+ ptes_size = ped_round_up_to(
+ sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count,
+ disk->dev->sector_size);
+ ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size);
+
+ if (!ped_device_read (disk->dev, ptes,
+ PED_LE64_TO_CPU(gpt->PartitionEntryLBA),
+ ptes_size / disk->dev->sector_size))
+ goto error_free_ptes;
+
+ for (i = 0; i < gpt_disk_data->entry_count; i++) {
+ PedPartition* part;
+ PedConstraint* constraint_exact;
+
+ if (!guid_cmp (ptes[i].PartitionTypeGuid, UNUSED_ENTRY_GUID))
+ continue;
+
+ part = _parse_part_entry (disk, &ptes[i]);
+ if (!part)
+ goto error_delete_all;
+
+ part->fs_type = ped_file_system_probe (&part->geom);
+ part->num = i + 1;
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition(disk, part, constraint_exact)) {
+ ped_partition_destroy (part);
+ goto error_delete_all;
+ }
+ ped_constraint_destroy (constraint_exact);
+ }
+ ped_free (ptes);
+
+#ifndef DISCOVER_ONLY
+ if (write_back)
+ ped_disk_commit_to_dev (disk);
+#endif
+
+ return 1;
+
+error_delete_all:
+ ped_disk_delete_all (disk);
+error_free_ptes:
+ ped_free (ptes);
+error_free_gpt:
+ pth_free (gpt);
+error:
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+/* Writes the protective MBR (to keep DOS happy) */
+static int
+_write_pmbr (PedDevice * dev)
+{
+ LegacyMBR_t pmbr;
+
+ memset(&pmbr, 0, sizeof(pmbr));
+ pmbr.Signature = PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE);
+ pmbr.PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI;
+ pmbr.PartitionRecord[0].StartSector = 1;
+ pmbr.PartitionRecord[0].EndHead = 0xFE;
+ pmbr.PartitionRecord[0].EndSector = 0xFF;
+ pmbr.PartitionRecord[0].EndTrack = 0xFF;
+ pmbr.PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32(1);
+ if ((dev->length - 1ULL) > 0xFFFFFFFFULL)
+ pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(0xFFFFFFFF);
+ else
+ pmbr.PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32(dev->length - 1UL);
+
+ return ped_device_write (dev, &pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS);
+}
+
+static void
+_generate_header (const PedDisk* disk, int alternate, uint32_t ptes_crc,
+ GuidPartitionTableHeader_t** gpt_p)
+{
+ GPTDiskData* gpt_disk_data = disk->disk_specific;
+ GuidPartitionTableHeader_t* gpt;
+
+ *gpt_p = pth_new_zeroed (disk->dev);
+
+ gpt = *gpt_p;
+
+ gpt->Signature = PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE);
+ gpt->Revision = PED_CPU_TO_LE32 (GPT_HEADER_REVISION_V1_00);
+
+ /* per 1.00 spec */
+ gpt->HeaderSize = PED_CPU_TO_LE32 (pth_get_size_static (disk->dev));
+ gpt->HeaderCRC32 = 0;
+ gpt->Reserved1 = 0;
+
+ if (alternate) {
+ /*
+ * ptes_size is in sectors
+ */
+ PedSector ptes_size = ped_div_round_up(
+ gpt_disk_data->entry_count *
+ sizeof (GuidPartitionEntry_t),
+ disk->dev->sector_size);
+
+ gpt->MyLBA = PED_CPU_TO_LE64 (disk->dev->length - 1);
+ gpt->AlternateLBA = PED_CPU_TO_LE64 (1);
+ gpt->PartitionEntryLBA
+ = PED_CPU_TO_LE64 (disk->dev->length - 1 - ptes_size);
+ } else {
+ gpt->MyLBA = PED_CPU_TO_LE64 (1);
+ gpt->AlternateLBA = PED_CPU_TO_LE64 (disk->dev->length - 1);
+ gpt->PartitionEntryLBA = PED_CPU_TO_LE64 (2);
+ }
+
+ gpt->FirstUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.start);
+ gpt->LastUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.end);
+ gpt->DiskGUID = gpt_disk_data->uuid;
+ gpt->NumberOfPartitionEntries
+ = PED_CPU_TO_LE32 (gpt_disk_data->entry_count);
+ gpt->SizeOfPartitionEntry
+ = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t));
+ gpt->PartitionEntryArrayCRC32 = PED_CPU_TO_LE32 (ptes_crc);
+ gpt->HeaderCRC32 = PED_CPU_TO_LE32 (pth_crc32 (disk->dev, gpt));
+}
+
+static void
+_partition_generate_part_entry (PedPartition* part, GuidPartitionEntry_t* pte)
+{
+ GPTPartitionData* gpt_part_data = part->disk_specific;
+ unsigned int i;
+
+ PED_ASSERT (gpt_part_data != NULL, return);
+
+ pte->PartitionTypeGuid = gpt_part_data->type;
+ pte->UniquePartitionGuid = gpt_part_data->uuid;
+ pte->StartingLBA = PED_CPU_TO_LE64(part->geom.start);
+ pte->EndingLBA = PED_CPU_TO_LE64(part->geom.end);
+ memset (&pte->Attributes, 0, sizeof (GuidPartitionEntryAttributes_t));
+
+ if (gpt_part_data->hidden)
+ pte->Attributes.RequiredToFunction = 1;
+
+ for (i = 0; i < 72 / sizeof(efi_char16_t); i++)
+ pte->PartitionName[i]
+ = (efi_char16_t) PED_CPU_TO_LE16(
+ (uint16_t) gpt_part_data->name[i]);
+}
+
+static int
+gpt_write(const PedDisk * disk)
+{
+ GPTDiskData* gpt_disk_data;
+ GuidPartitionEntry_t* ptes;
+ uint32_t ptes_crc;
+ uint8_t* pth_raw = ped_malloc (pth_get_size (disk->dev));
+ GuidPartitionTableHeader_t* gpt;
+ PedPartition* part;
+ int ptes_size;
+
+ PED_ASSERT (disk != NULL, goto error);
+ PED_ASSERT (disk->dev != NULL, goto error);
+ PED_ASSERT (disk->disk_specific != NULL, goto error);
+
+ gpt_disk_data = disk->disk_specific;
+
+ /*
+ * ptes_size is in bytes and must be a multiple of sector_size.
+ */
+ ptes_size = ped_round_up_to(
+ sizeof (GuidPartitionEntry_t) * gpt_disk_data->entry_count,
+ disk->dev->sector_size);
+ ptes = (GuidPartitionEntry_t*) ped_malloc (ptes_size);
+ if (!ptes)
+ goto error;
+ memset (ptes, 0, ptes_size);
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part)) {
+ if (part->type != 0)
+ continue;
+ _partition_generate_part_entry (part, &ptes[part->num - 1]);
+ }
+
+ ptes_crc = efi_crc32 (ptes, ptes_size);
+
+ /* Write protective MBR */
+ if (!_write_pmbr (disk->dev))
+ goto error_free_ptes;
+
+ /* Write PTH and PTEs */
+ _generate_header (disk, 0, ptes_crc, &gpt);
+ pth_raw = pth_get_raw (disk->dev, gpt);
+ if (!ped_device_write (disk->dev, pth_raw, 1, 1))
+ goto error_free_ptes;
+ if (!ped_device_write (disk->dev, ptes, 2, ptes_size / disk->dev->sector_size))
+ goto error_free_ptes;
+
+ /* Write Alternate PTH & PTEs */
+ _generate_header (disk, 1, ptes_crc, &gpt);
+ pth_raw = pth_get_raw (disk->dev, gpt);
+ if (!ped_device_write (disk->dev, pth_raw, disk->dev->length - 1, 1))
+ goto error_free_ptes;
+ if (!ped_device_write (disk->dev, ptes,
+ disk->dev->length - 1 - ptes_size / disk->dev->sector_size,
+ ptes_size / disk->dev->sector_size))
+ goto error_free_ptes;
+
+ ped_free (ptes);
+ return ped_device_sync (disk->dev);
+
+error_free_ptes:
+ ped_free (ptes);
+error:
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+add_metadata_part(PedDisk * disk, PedSector start, PedSector length)
+{
+ PedPartition* part;
+ PedConstraint* constraint_exact;
+ PED_ASSERT(disk != NULL, return 0);
+
+ part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ start, start + length - 1);
+ if (!part)
+ goto error;
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error_destroy_constraint;
+ ped_constraint_destroy (constraint_exact);
+ return 1;
+
+error_destroy_constraint:
+ ped_constraint_destroy (constraint_exact);
+ ped_partition_destroy (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+gpt_partition_new (const PedDisk* disk,
+ PedPartitionType part_type, const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ GPTPartitionData* gpt_part_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (part_type != 0)
+ return part;
+
+ gpt_part_data = part->disk_specific =
+ ped_malloc (sizeof (GPTPartitionData));
+ if (!gpt_part_data)
+ goto error_free_part;
+
+ gpt_part_data->type = PARTITION_BASIC_DATA_GUID;
+ gpt_part_data->lvm = 0;
+ gpt_part_data->raid = 0;
+ gpt_part_data->boot = 0;
+ gpt_part_data->hp_service = 0;
+ gpt_part_data->hidden = 0;
+ gpt_part_data->msftres = 0;
+ uuid_generate ((unsigned char*) &gpt_part_data->uuid);
+ swap_uuid_and_efi_guid((unsigned char*)(&gpt_part_data->uuid));
+ strcpy (gpt_part_data->name, "");
+ return part;
+
+error_free_part:
+ _ped_partition_free (part);
+error:
+ return NULL;
+}
+
+static PedPartition*
+gpt_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* result;
+ GPTPartitionData* part_data = part->disk_specific;
+ GPTPartitionData* result_data;
+
+ result = _ped_partition_alloc (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ if (!result)
+ goto error;
+ result->num = part->num;
+
+ if (result->type != 0)
+ return result;
+
+ result_data = result->disk_specific =
+ ped_malloc (sizeof (GPTPartitionData));
+ if (!result_data)
+ goto error_free_part;
+
+ result_data->type = part_data->type;
+ result_data->uuid = part_data->uuid;
+ strcpy (result_data->name, part_data->name);
+ return result;
+
+error_free_part:
+ _ped_partition_free (result);
+error:
+ return NULL;
+}
+
+static void
+gpt_partition_destroy (PedPartition *part)
+{
+ if (part->type == 0) {
+ PED_ASSERT (part->disk_specific != NULL, return);
+ ped_free (part->disk_specific);
+ }
+
+ _ped_partition_free (part);
+}
+
+static int
+gpt_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ GPTPartitionData* gpt_part_data = part->disk_specific;
+
+ PED_ASSERT (gpt_part_data != NULL, return 0);
+
+ part->fs_type = fs_type;
+
+ if (gpt_part_data->lvm) {
+ gpt_part_data->type = PARTITION_LVM_GUID;
+ return 1;
+ }
+ if (gpt_part_data->raid) {
+ gpt_part_data->type = PARTITION_RAID_GUID;
+ return 1;
+ }
+ if (gpt_part_data->boot) {
+ gpt_part_data->type = PARTITION_SYSTEM_GUID;
+ return 1;
+ }
+ if (gpt_part_data->hp_service) {
+ gpt_part_data->type = PARTITION_HPSERVICE_GUID;
+ return 1;
+ }
+ if (gpt_part_data->msftres) {
+ gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID;
+ return 1;
+ }
+
+ if (fs_type) {
+ if (strncmp (fs_type->name, "fat", 3) == 0
+ || strcmp (fs_type->name, "ntfs") == 0) {
+ gpt_part_data->type = PARTITION_MSFT_RESERVED_GUID;
+ return 1;
+ }
+ if (strncmp (fs_type->name, "hfs", 3) == 0) {
+ gpt_part_data->type = PARTITION_APPLE_HFS_GUID;
+ return 1;
+ }
+ if (strstr (fs_type->name, "swap")) {
+ gpt_part_data->type = PARTITION_SWAP_GUID;
+ return 1;
+ }
+ }
+
+ gpt_part_data->type = PARTITION_BASIC_DATA_GUID;
+ return 1;
+}
+
+/* Allocate metadata partitions for the GPTH and PTES */
+static int
+gpt_alloc_metadata (PedDisk * disk)
+{
+ PedSector gptlength, pteslength = 0;
+ GPTDiskData *gpt_disk_data;
+
+ PED_ASSERT(disk != NULL, return 0);
+ PED_ASSERT(disk->dev != NULL, return 0);
+ PED_ASSERT(disk->disk_specific != NULL, return 0);
+ gpt_disk_data = disk->disk_specific;
+
+ gptlength = ped_div_round_up (sizeof (GuidPartitionTableHeader_t),
+ disk->dev->sector_size);
+ pteslength = ped_div_round_up (gpt_disk_data->entry_count
+ * sizeof (GuidPartitionEntry_t), disk->dev->sector_size);
+
+ /* metadata at the start of the disk includes the MBR */
+ if (!add_metadata_part(disk, GPT_PMBR_LBA,
+ GPT_PMBR_SECTORS + gptlength + pteslength))
+ return 0;
+
+ /* metadata at the end of the disk */
+ if (!add_metadata_part(disk, disk->dev->length - gptlength - pteslength,
+ gptlength + pteslength))
+ return 0;
+
+ return 1;
+}
+
+/* Does nothing, as the read/new/destroy functions maintain part->num */
+static int
+gpt_partition_enumerate (PedPartition* part)
+{
+ GPTDiskData* gpt_disk_data = part->disk->disk_specific;
+ int i;
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+
+ for (i = 1; i <= gpt_disk_data->entry_count; i++) {
+ if (!ped_disk_get_partition (part->disk, i)) {
+ part->num = i;
+ return 1;
+ }
+ }
+
+ PED_ASSERT (0, return 0);
+
+ return 0; /* used if debug is disabled */
+}
+
+static int
+gpt_partition_set_flag(PedPartition *part,
+ PedPartitionFlag flag,
+ int state)
+{
+ GPTPartitionData *gpt_part_data;
+ PED_ASSERT(part != NULL, return 0);
+ PED_ASSERT(part->disk_specific != NULL, return 0);
+ gpt_part_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ gpt_part_data->boot = state;
+ if (state)
+ gpt_part_data->raid
+ = gpt_part_data->lvm
+ = gpt_part_data->hp_service
+ = gpt_part_data->msftres = 0;
+ return gpt_partition_set_system (part, part->fs_type);
+ case PED_PARTITION_RAID:
+ gpt_part_data->raid = state;
+ if (state)
+ gpt_part_data->boot
+ = gpt_part_data->lvm
+ = gpt_part_data->hp_service
+ = gpt_part_data->msftres = 0;
+ return gpt_partition_set_system (part, part->fs_type);
+ case PED_PARTITION_LVM:
+ gpt_part_data->lvm = state;
+ if (state)
+ gpt_part_data->boot
+ = gpt_part_data->raid
+ = gpt_part_data->hp_service
+ = gpt_part_data->msftres = 0;
+ return gpt_partition_set_system (part, part->fs_type);
+ case PED_PARTITION_HPSERVICE:
+ gpt_part_data->hp_service = state;
+ if (state)
+ gpt_part_data->boot
+ = gpt_part_data->raid
+ = gpt_part_data->lvm
+ = gpt_part_data->msftres = 0;
+ return gpt_partition_set_system (part, part->fs_type);
+ case PED_PARTITION_MSFT_RESERVED:
+ gpt_part_data->msftres = state;
+ if (state)
+ gpt_part_data->boot
+ = gpt_part_data->raid
+ = gpt_part_data->lvm
+ = gpt_part_data->hp_service = 0;
+ return gpt_partition_set_system (part, part->fs_type);
+ case PED_PARTITION_HIDDEN:
+ gpt_part_data->hidden = state;
+ return 1;
+ case PED_PARTITION_SWAP:
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_LBA:
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int
+gpt_partition_get_flag(const PedPartition *part, PedPartitionFlag flag)
+{
+ GPTPartitionData *gpt_part_data;
+ PED_ASSERT(part->disk_specific != NULL, return 0);
+ gpt_part_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_RAID:
+ return gpt_part_data->raid;
+ case PED_PARTITION_LVM:
+ return gpt_part_data->lvm;
+ case PED_PARTITION_BOOT:
+ return gpt_part_data->boot;
+ case PED_PARTITION_HPSERVICE:
+ return gpt_part_data->hp_service;
+ case PED_PARTITION_MSFT_RESERVED:
+ return gpt_part_data->msftres;
+ case PED_PARTITION_HIDDEN:
+ return gpt_part_data->hidden;
+ case PED_PARTITION_SWAP:
+ case PED_PARTITION_LBA:
+ case PED_PARTITION_ROOT:
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static int
+gpt_partition_is_flag_available(const PedPartition * part,
+ PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_RAID:
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_BOOT:
+ case PED_PARTITION_HPSERVICE:
+ case PED_PARTITION_MSFT_RESERVED:
+ case PED_PARTITION_HIDDEN:
+ return 1;
+ case PED_PARTITION_SWAP:
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_LBA:
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static void
+gpt_partition_set_name (PedPartition *part, const char *name)
+{
+ GPTPartitionData *gpt_part_data = part->disk_specific;
+
+ strncpy (gpt_part_data->name, name, 36);
+ gpt_part_data->name [36] = 0;
+}
+
+static const char *
+gpt_partition_get_name (const PedPartition * part)
+{
+ GPTPartitionData* gpt_part_data = part->disk_specific;
+ return gpt_part_data->name;
+}
+
+static int
+gpt_get_max_primary_partition_count (const PedDisk *disk)
+{
+ const GPTDiskData* gpt_disk_data = disk->disk_specific;
+ return gpt_disk_data->entry_count;
+}
+
+static PedConstraint*
+_non_metadata_constraint (const PedDisk* disk)
+{
+ GPTDiskData* gpt_disk_data = disk->disk_specific;
+
+ return ped_constraint_new_from_max (&gpt_disk_data->data_area);
+}
+
+static int
+gpt_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _non_metadata_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static PedDiskOps gpt_disk_ops = {
+ .probe = gpt_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = gpt_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = gpt_alloc,
+ .duplicate = gpt_duplicate,
+ .free = gpt_free,
+ .read = gpt_read,
+#ifndef DISCOVER_ONLY
+ .write = gpt_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = gpt_partition_new,
+ .partition_duplicate = gpt_partition_duplicate,
+ .partition_destroy = gpt_partition_destroy,
+ .partition_set_system = gpt_partition_set_system,
+ .partition_set_flag = gpt_partition_set_flag,
+ .partition_get_flag = gpt_partition_get_flag,
+ .partition_is_flag_available = gpt_partition_is_flag_available,
+ .partition_set_name = gpt_partition_set_name,
+ .partition_get_name = gpt_partition_get_name,
+ .partition_align = gpt_partition_align,
+ .partition_enumerate = gpt_partition_enumerate,
+ .alloc_metadata = gpt_alloc_metadata,
+ .get_max_primary_partition_count = gpt_get_max_primary_partition_count
+};
+
+static PedDiskType gpt_disk_type = {
+ .next = NULL,
+ .name = "gpt",
+ .ops = &gpt_disk_ops,
+ .features = PED_DISK_TYPE_PARTITION_NAME
+};
+
+void
+ped_disk_gpt_init()
+{
+ PED_ASSERT (sizeof (GuidPartitionEntryAttributes_t) == 8, return);
+ PED_ASSERT (sizeof (GuidPartitionEntry_t) == 128, return);
+
+ ped_disk_type_register (&gpt_disk_type);
+}
+
+void
+ped_disk_gpt_done()
+{
+ ped_disk_type_unregister (&gpt_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/loop.c b/usr/src/lib/libparted/common/libparted/labels/loop.c
new file mode 100644
index 0000000000..36a00d5d19
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/loop.c
@@ -0,0 +1,333 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define LOOP_SIGNATURE "GNU Parted Loopback 0"
+
+static PedDiskType loop_disk_type;
+
+static PedDisk* loop_alloc (const PedDevice* dev);
+static void loop_free (PedDisk* disk);
+
+static int
+loop_probe (const PedDevice* dev)
+{
+ PedDisk* disk;
+ char buf [512];
+ int result;
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ disk = loop_alloc (dev);
+ if (!disk)
+ goto error;
+
+ if (!ped_device_read (dev, buf, 0, 1))
+ goto error_destroy_disk;
+ if (strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)) == 0) {
+ result = 1;
+ } else {
+ PedGeometry* geom;
+
+ geom = ped_geometry_new (dev, 0, disk->dev->length);
+ if (!geom)
+ goto error_destroy_disk;
+ result = ped_file_system_probe (geom) != NULL;
+ ped_geometry_destroy (geom);
+ }
+ loop_free (disk);
+ return result;
+
+error_destroy_disk:
+ loop_free (disk);
+error:
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+loop_clobber (PedDevice* dev)
+{
+ char buf [512];
+ PedSector i = 0;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ memset (buf, 0, 512);
+
+ while (loop_probe (dev)) {
+ if (!ped_device_write (dev, buf, i++, 1))
+ return 0;
+ }
+ return 1;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk*
+loop_alloc (const PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->length < 256)
+ return NULL;
+ return _ped_disk_alloc ((PedDevice*)dev, &loop_disk_type);
+}
+
+static PedDisk*
+loop_duplicate (const PedDisk* disk)
+{
+ return ped_disk_new_fresh (disk->dev, &loop_disk_type);
+}
+
+static void
+loop_free (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return);
+
+ _ped_disk_free (disk);
+}
+
+static int
+loop_read (PedDisk* disk)
+{
+ PedDevice* dev = NULL;
+ char buf [512];
+ PedGeometry* geom;
+ PedFileSystemType* fs_type;
+ PedPartition* part;
+ PedConstraint* constraint_any;
+
+ PED_ASSERT (disk != NULL, return 0);
+ dev = disk->dev;
+ constraint_any = ped_constraint_any (dev);
+
+ ped_disk_delete_all (disk);
+
+ if (!ped_device_read (dev, buf, 0, 1))
+ goto error;
+ if (!strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)))
+ return 1;
+
+ geom = ped_geometry_new (dev, 0, dev->length);
+ if (!geom)
+ goto error;
+
+ fs_type = ped_file_system_probe (geom);
+ if (!fs_type)
+ goto error_free_geom;
+
+ part = ped_partition_new (disk, 0, fs_type, geom->start, geom->end);
+ ped_geometry_destroy (geom);
+ if (!part)
+ goto error;
+ part->fs_type = fs_type;
+
+ if (!ped_disk_add_partition (disk, part, constraint_any))
+ goto error;
+ ped_constraint_destroy (constraint_any);
+ return 1;
+
+error_free_geom:
+ ped_geometry_destroy (geom);
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+loop_write (const PedDisk* disk)
+{
+ char buf [512];
+
+ if (ped_disk_get_partition (disk, 1)) {
+ if (!ped_device_read (disk->dev, buf, 0, 1))
+ return 0;
+ if (strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)) != 0)
+ return 1;
+ memset (buf, 0, strlen (LOOP_SIGNATURE));
+ return ped_device_write (disk->dev, buf, 0, 1);
+ }
+
+ memset (buf, 0, 512);
+ strcpy (buf, LOOP_SIGNATURE);
+
+ return ped_device_write (disk->dev, buf, 0, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+loop_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ return NULL;
+ part->disk_specific = NULL;
+ return part;
+}
+
+static PedPartition*
+loop_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* result;
+
+ result = ped_partition_new (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ result->num = part->num;
+ return result;
+}
+
+static void
+loop_partition_destroy (PedPartition* part)
+{
+ ped_free (part);
+}
+
+static int
+loop_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ part->fs_type = fs_type;
+ return 1;
+}
+
+static int
+loop_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ return 0;
+}
+
+static int
+loop_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ return 0;
+}
+
+static int
+loop_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PedGeometry* new_geom;
+
+ new_geom = ped_constraint_solve_nearest (constraint, &part->geom);
+ if (!new_geom) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the "
+ "partition."));
+ return 0;
+ }
+ ped_geometry_set (&part->geom, new_geom->start, new_geom->length);
+ ped_geometry_destroy (new_geom);
+ return 1;
+}
+
+static int
+loop_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ return 0;
+}
+
+static int
+loop_partition_enumerate (PedPartition* part)
+{
+ part->num = 1;
+ return 1;
+}
+
+static int
+loop_alloc_metadata (PedDisk* disk)
+{
+ return 1;
+}
+
+static int
+loop_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return 1;
+}
+
+static PedDiskOps loop_disk_ops = {
+ .probe = loop_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = loop_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = loop_alloc,
+ .duplicate = loop_duplicate,
+ .free = loop_free,
+ .read = loop_read,
+#ifndef DISCOVER_ONLY
+ .write = loop_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = loop_partition_new,
+ .partition_duplicate = loop_partition_duplicate,
+ .partition_destroy = loop_partition_destroy,
+ .partition_set_system = loop_partition_set_system,
+ .partition_set_flag = loop_partition_set_flag,
+ .partition_get_flag = loop_partition_get_flag,
+ .partition_is_flag_available = loop_partition_is_flag_available,
+ .partition_set_name = NULL,
+ .partition_get_name = NULL,
+ .partition_align = loop_partition_align,
+ .partition_enumerate = loop_partition_enumerate,
+
+ .alloc_metadata = loop_alloc_metadata,
+ .get_max_primary_partition_count =
+ loop_get_max_primary_partition_count
+};
+
+static PedDiskType loop_disk_type = {
+ .next = NULL,
+ .name = "loop",
+ .ops = &loop_disk_ops,
+ .features = 0
+};
+
+void
+ped_disk_loop_init ()
+{
+ ped_disk_type_register (&loop_disk_type);
+}
+
+void
+ped_disk_loop_done ()
+{
+ ped_disk_type_unregister (&loop_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/mac.c b/usr/src/lib/libparted/common/libparted/labels/mac.c
new file mode 100644
index 0000000000..25ca6ab728
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/mac.c
@@ -0,0 +1,1629 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2002, 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* struct's hacked from Linux source: fs/partitions/mac.h
+ * I believe it was originally written by Paul Mackerras (from comments in
+ * Quik source)
+ *
+ * See also:
+ * http://developer.apple.com/documentation/mac/Devices/Devices-126.html
+ * http://developer.apple.com/documentation/mac/Devices/Devices-121.html
+ * http://devworld.apple.com/technotes/tn/tn1189.html
+ *
+ * Partition types:
+ * Apple_Bootstrap new-world (HFS) boot partition
+ * Apple_partition_map partition map (table)
+ * Apple_Driver device driver
+ * Apple_Driver43 SCSI Manager 4.3 device driver
+ * Apple_MFS original Macintosh File System
+ * Apple_HFS Hierarchical File System (and +)
+ * Apple_HFSX HFS+ with case sensitivity and more
+ * Apple_UNIX_SVR2 UNIX file system (UFS?)
+ * Apple_PRODOS ProDOS file system
+ * Apple_Free unused space
+ * Apple_Scratch empty
+ * Apple_Void padding for iso9660
+ * Apple_Extra an unused partition map entry
+ *
+ * Quick explanation:
+ * ------------------
+ * Terminology:
+ *
+ * Parted Apple
+ * ------ -----
+ * device disk/device
+ * disk no equivalent.
+ * partition volume or partition
+ * sector block
+ *
+ * * All space must be accounted for, except block 0 (driver block) and
+ * block 1-X (the partition map: i.e. lots of MacRawPartitions)
+ *
+ * * It's really hard to grow/shrink the number of MacRawPartition
+ * entries in the partition map, because the first partition starts
+ * immediately after the partition map. When we can move the start of
+ * HFS and ext2 partitions, this problem will disappear ;-)
+ */
+
+#define MAC_PARTITION_MAGIC_1 0x5453 /* old */
+#define MAC_PARTITION_MAGIC_2 0x504d
+#define MAC_DISK_MAGIC 0x4552
+
+#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */
+
+typedef struct _MacRawPartition MacRawPartition;
+typedef struct _MacRawDisk MacRawDisk;
+typedef struct _MacDeviceDriver MacDeviceDriver;
+typedef struct _MacPartitionData MacPartitionData;
+typedef struct _MacDiskData MacDiskData;
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _MacRawPartition {
+ uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */
+ uint16_t res1;
+ uint32_t map_count; /* # blocks in partition map */
+ uint32_t start_block; /* absolute starting block # of partition */
+ uint32_t block_count; /* number of blocks in partition */
+ char name[32]; /* partition name */
+ char type[32]; /* string type description */
+ uint32_t data_start; /* rel block # of first data block */
+ uint32_t data_count; /* number of data blocks */
+ uint32_t status; /* partition status bits */
+ uint32_t boot_start;
+ uint32_t boot_count;
+ uint32_t boot_load;
+ uint32_t boot_load2;
+ uint32_t boot_entry;
+ uint32_t boot_entry2;
+ uint32_t boot_cksum;
+ char processor[16]; /* Contains 680x0, x=0,2,3,4; or empty */
+ uint32_t driver_sig;
+ char _padding[372];
+};
+
+/* Driver descriptor structure, in block 0 */
+struct __attribute__ ((packed)) _MacRawDisk {
+ uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */
+ uint16_t block_size; /* physical sector size */
+ uint32_t block_count; /* size of device in blocks */
+ uint16_t dev_type; /* reserved */
+ uint16_t dev_id; /* reserved */
+ uint32_t data; /* reserved */
+ uint16_t driver_count; /* # of driver descriptor entries */
+ uint8_t driverlist[488];/* info about available drivers */
+ uint16_t padding[3]; /* pad to 512 bytes */
+};
+
+struct __attribute__ ((packed)) _MacDeviceDriver {
+ uint32_t block; /* startblock in MacRawDisk->block_size units */
+ uint16_t size; /* size in 512 byte units */
+ uint16_t type; /* operating system type (MacOS = 1) */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+struct _MacPartitionData {
+ char volume_name[33]; /* eg: "Games" */
+ char system_name[33]; /* eg: "Apple_Unix_SVR2" */
+ char processor_name[17];
+
+ int is_boot;
+ int is_driver;
+ int has_driver;
+ int is_root;
+ int is_swap;
+ int is_lvm;
+ int is_raid;
+
+ PedSector data_region_length;
+ PedSector boot_region_length;
+
+ uint32_t boot_base_address;
+ uint32_t boot_entry_address;
+ uint32_t boot_checksum;
+
+ uint32_t status;
+ uint32_t driver_sig;
+};
+
+struct _MacDiskData {
+ int ghost_size; /* sectors per "driver" block */
+ int part_map_entry_count; /* # entries (incl. ghost) */
+ int part_map_entry_num; /* partition map location */
+
+ int active_part_entry_count; /* # real partitions */
+ int free_part_entry_count; /* # free space */
+ int last_part_entry_num; /* last entry number */
+
+ uint16_t block_size; /* physical sector size */
+ uint16_t driver_count;
+ MacDeviceDriver driverlist[1 + 60]; /* 488 bytes */
+};
+
+static PedDiskType mac_disk_type;
+
+static int
+_check_signature (MacRawDisk* raw_disk)
+{
+ if (PED_BE16_TO_CPU (raw_disk->signature) != MAC_DISK_MAGIC) {
+#ifdef DISCOVER_ONLY
+ return 0;
+#else
+ return ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid signature %x for Mac disk labels."),
+ (int) PED_BE16_TO_CPU (raw_disk->signature))
+ == PED_EXCEPTION_IGNORE;
+#endif
+ }
+
+ return 1;
+}
+
+static int
+_rawpart_check_signature (MacRawPartition* raw_part)
+{
+ int sig = (int) PED_BE16_TO_CPU (raw_part->signature);
+ return sig == MAC_PARTITION_MAGIC_1 || sig == MAC_PARTITION_MAGIC_2;
+}
+
+static int
+mac_probe (const PedDevice * dev)
+{
+ MacRawDisk buf;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ if (!ped_device_read (dev, &buf, 0, 1))
+ return 0;
+
+ return _check_signature (&buf);
+}
+
+static int
+_disk_add_part_map_entry (PedDisk* disk, int warn)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ PedPartition* new_part;
+ MacPartitionData* mac_part_data;
+ PedSector part_map_size;
+ PedConstraint* constraint_any = ped_constraint_any (disk->dev);
+
+#ifndef DISCOVER_ONLY
+ if (warn && ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL,
+ _("Partition map has no partition map entry!"))
+ != PED_EXCEPTION_FIX)
+ goto error;
+#endif /* !DISCOVER_ONLY */
+
+ part_map_size
+ = ped_round_up_to (mac_disk_data->last_part_entry_num, 64);
+ if (part_map_size == 0)
+ part_map_size = 64;
+
+ new_part = ped_partition_new (disk, 0, NULL, 1, part_map_size - 1);
+ if (!new_part)
+ goto error;
+
+ mac_part_data = new_part->disk_specific;
+ strcpy (mac_part_data->volume_name, "Apple");
+ strcpy (mac_part_data->system_name, "Apple_partition_map");
+
+ if (!ped_disk_add_partition (disk, new_part, constraint_any))
+ goto error_destroy_new_part;
+
+ mac_disk_data->part_map_entry_num = new_part->num;
+ mac_disk_data->part_map_entry_count
+ = new_part->geom.end - mac_disk_data->ghost_size;
+ ped_constraint_destroy (constraint_any);
+ return 1;
+
+error_destroy_new_part:
+ ped_partition_destroy (new_part);
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static PedDisk*
+mac_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ MacDiskData* mac_disk_data;
+
+ PED_ASSERT (dev != NULL, return NULL);
+
+#ifndef DISCOVER_ONLY
+ if (dev->length < 256) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s is too small for a Mac disk label!"),
+ dev->path);
+ goto error;
+ }
+#endif
+
+ disk = _ped_disk_alloc (dev, &mac_disk_type);
+ if (!disk)
+ goto error;
+
+ mac_disk_data = (MacDiskData*) ped_malloc (sizeof (MacDiskData));
+ if (!mac_disk_data)
+ goto error_free_disk;
+ disk->disk_specific = mac_disk_data;
+ mac_disk_data->ghost_size = disk->dev->sector_size / 512;
+ mac_disk_data->active_part_entry_count = 0;
+ mac_disk_data->free_part_entry_count = 1;
+ mac_disk_data->last_part_entry_num = 1;
+ mac_disk_data->block_size = 0;
+ mac_disk_data->driver_count = 0;
+ memset(&mac_disk_data->driverlist[0], 0, sizeof(mac_disk_data->driverlist));
+
+ if (!_disk_add_part_map_entry (disk, 0))
+ goto error_free_disk;
+ return disk;
+
+error_free_disk:
+ _ped_disk_free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk*
+mac_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ MacDiskData* new_mac_data;
+ MacDiskData* old_mac_data = (MacDiskData*) disk->disk_specific;
+ PedPartition* partition_map;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &mac_disk_type);
+ if (!new_disk)
+ goto error;
+
+ new_mac_data = (MacDiskData*) new_disk->disk_specific;
+
+ /* remove the partition map partition - it will be duplicated
+ * later.
+ */
+ partition_map = ped_disk_get_partition_by_sector (new_disk, 1);
+ PED_ASSERT (partition_map != NULL, return 0);
+ ped_disk_remove_partition (new_disk, partition_map);
+
+ /* ugly, but C is ugly :p */
+ memcpy (new_mac_data, old_mac_data, sizeof (MacDiskData));
+ return new_disk;
+
+ _ped_disk_free (new_disk);
+error:
+ return NULL;
+}
+
+static void
+mac_free (PedDisk* disk)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+
+ _ped_disk_free (disk);
+ ped_free (mac_disk_data);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+_clobber_part_map (PedDevice* dev)
+{
+ MacRawPartition raw_part;
+ PedSector sector;
+
+ for (sector=1; 1; sector++) {
+ if (!ped_device_read (dev, &raw_part, sector, 1))
+ return 0;
+ if (!_rawpart_check_signature (&raw_part))
+ return 1;
+ memset (&raw_part, 0, 512);
+ if (!ped_device_write (dev, &raw_part, sector, 1))
+ return 0;
+ }
+}
+
+static int
+mac_clobber (PedDevice* dev)
+{
+ MacRawDisk raw_disk;
+
+ if (!ped_device_read (dev, &raw_disk, 0, 1))
+ return 0;
+ if (!_check_signature (&raw_disk))
+ return 0;
+ memset (&raw_disk, 0, 512);
+ if (!ped_device_write (dev, &raw_disk, 0, 1))
+ return 0;
+
+ return _clobber_part_map (dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+_rawpart_cmp_type (MacRawPartition* raw_part, char* type)
+{
+ return strncasecmp (raw_part->type, type, 32) == 0;
+}
+
+static int
+_rawpart_cmp_name (MacRawPartition* raw_part, char* name)
+{
+ return strncasecmp (raw_part->name, name, 32) == 0;
+}
+
+static int
+_rawpart_is_partition_map (MacRawPartition* raw_part)
+{
+ return _rawpart_cmp_type (raw_part, "Apple_partition_map");
+}
+
+static int
+strncasestr (const char* haystack, const char* needle, int n)
+{
+ int needle_size = strlen (needle);
+ int i;
+
+ for (i = 0; haystack[i] && i < n - needle_size; i++) {
+ if (strncasecmp (haystack + i, needle, needle_size) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+_rawpart_is_boot (MacRawPartition* raw_part)
+{
+ if (!strcasecmp(raw_part->type, "Apple_Bootstrap"))
+ return 1;
+
+ if (!strcasecmp(raw_part->type, "Apple_Boot"))
+ return 1;
+
+ return 0;
+}
+
+static int
+_rawpart_is_driver (MacRawPartition* raw_part)
+{
+ if (strncmp (raw_part->type, "Apple_", 6) != 0)
+ return 0;
+ if (!strncasestr (raw_part->type, "driver", 32))
+ return 0;
+ return 1;
+}
+
+static int
+_rawpart_has_driver (MacRawPartition* raw_part, MacDiskData* mac_disk_data)
+{
+ MacDeviceDriver *driverlist;
+ uint16_t i, bsz;
+ uint32_t driver_bs, driver_be, part_be;
+
+ driverlist = &mac_disk_data->driverlist[0];
+ bsz = mac_disk_data->block_size / 512;
+ for (i = 0; i < mac_disk_data->driver_count; i++) {
+ driver_bs = driverlist->block * bsz;
+ driver_be = driver_bs + driverlist->size;
+ part_be = raw_part->start_block + raw_part->block_count;
+ if (driver_bs >= raw_part->start_block && driver_be <= part_be)
+ return 1;
+ driverlist++;
+ }
+ return 0;
+}
+
+static int
+_rawpart_is_root (MacRawPartition* raw_part)
+{
+ if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2"))
+ return 0;
+ if (strcmp (raw_part->name, "root") != 0)
+ return 0;
+ return 1;
+}
+
+static int
+_rawpart_is_swap (MacRawPartition* raw_part)
+{
+ if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2"))
+ return 0;
+ if (strcmp (raw_part->name, "swap") != 0)
+ return 0;
+ return 1;
+}
+
+static int
+_rawpart_is_lvm (MacRawPartition* raw_part)
+{
+ if (strcmp (raw_part->type, "Linux_LVM") != 0)
+ return 0;
+ return 1;
+}
+
+static int
+_rawpart_is_raid (MacRawPartition* raw_part)
+{
+ if (strcmp (raw_part->type, "Linux_RAID") != 0)
+ return 0;
+ return 1;
+}
+
+static int
+_rawpart_is_void (MacRawPartition* raw_part)
+{
+ return _rawpart_cmp_type (raw_part, "Apple_Void");
+}
+
+/* returns 1 if the raw_part represents a partition that is "unused space", or
+ * doesn't represent a partition at all. NOTE: some people make Apple_Free
+ * partitions with MacOS, because they can't select another type. So, if the
+ * name is anything other than "Extra" or "", it is treated as a "real"
+ * partition.
+ */
+static int
+_rawpart_is_active (MacRawPartition* raw_part)
+{
+ if (_rawpart_cmp_type (raw_part, "Apple_Free")
+ && (_rawpart_cmp_name (raw_part, "Extra")
+ || _rawpart_cmp_name (raw_part, "")))
+ return 0;
+ if (_rawpart_cmp_type (raw_part, "Apple_Void"))
+ return 0;
+ if (_rawpart_cmp_type (raw_part, "Apple_Scratch"))
+ return 0;
+ if (_rawpart_cmp_type (raw_part, "Apple_Extra"))
+ return 0;
+
+ return 1;
+}
+
+static PedPartition*
+_rawpart_analyse (MacRawPartition* raw_part, PedDisk* disk, int num)
+{
+ MacDiskData* mac_disk_data;
+ PedPartition* part;
+ MacPartitionData* mac_part_data;
+ PedSector block_size;
+ PedSector start, length;
+
+ if (!_rawpart_check_signature (raw_part)) {
+#ifndef DISCOVER_ONLY
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Partition %d has an invalid signature %x."),
+ num,
+ (int) PED_BE16_TO_CPU (raw_part->signature))
+ != PED_EXCEPTION_IGNORE)
+#endif
+ goto error;
+ }
+
+ mac_disk_data = (MacDiskData*) disk->disk_specific;
+ block_size = disk->dev->sector_size / 512;
+
+ start = PED_BE32_TO_CPU (raw_part->start_block) * block_size;
+ length = PED_BE32_TO_CPU (raw_part->block_count) * block_size;
+ if (length == 0) {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Partition %d has an invalid length of 0 bytes!"),
+ num);
+#endif
+ return NULL;
+ }
+ part = ped_partition_new (disk, 0, NULL, start, start + length - 1);
+ if (!part)
+ goto error;
+
+ mac_part_data = part->disk_specific;
+
+ strncpy (mac_part_data->volume_name, raw_part->name, 32);
+ strncpy (mac_part_data->system_name, raw_part->type, 32);
+ strncpy (mac_part_data->processor_name, raw_part->processor, 16);
+
+ mac_part_data->is_boot = _rawpart_is_boot (raw_part);
+ mac_part_data->is_driver = _rawpart_is_driver (raw_part);
+ if (mac_part_data->is_driver)
+ mac_part_data->has_driver = _rawpart_has_driver(raw_part, mac_disk_data);
+ mac_part_data->is_root = _rawpart_is_root (raw_part);
+ mac_part_data->is_swap = _rawpart_is_swap (raw_part);
+ mac_part_data->is_lvm = _rawpart_is_lvm (raw_part);
+ mac_part_data->is_raid = _rawpart_is_raid (raw_part);
+
+ /* "data" region */
+#ifndef DISCOVER_ONLY
+ if (raw_part->data_start) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The data region doesn't start at the start "
+ "of the partition."));
+ goto error_destroy_part;
+ }
+#endif /* !DISCOVER_ONLY */
+ mac_part_data->data_region_length
+ = PED_BE32_TO_CPU (raw_part->data_count) * block_size;
+
+ /* boot region - we have no idea what this is for, but Mac OSX
+ * seems to put garbage here, and doesn't pay any attention to
+ * it afterwards. [clausen, dan burcaw]
+ */
+#if 0
+ if (raw_part->boot_start) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("The boot region doesn't start at the start "
+ "of the partition."));
+ goto error_destroy_part;
+ }
+#endif
+ mac_part_data->boot_region_length
+ = PED_BE32_TO_CPU (raw_part->boot_count) * block_size;
+
+#ifndef DISCOVER_ONLY
+ if (mac_part_data->has_driver) {
+ if (mac_part_data->boot_region_length < part->geom.length) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The partition's boot region doesn't occupy "
+ "the entire partition."))
+ != PED_EXCEPTION_IGNORE)
+ goto error_destroy_part;
+ }
+ } else {
+ if (mac_part_data->data_region_length < part->geom.length &&
+ !mac_part_data->is_boot) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The partition's data region doesn't occupy "
+ "the entire partition."))
+ != PED_EXCEPTION_IGNORE)
+ goto error_destroy_part;
+ }
+ }
+#endif /* !DISCOVER_ONLY */
+
+ mac_part_data->boot_base_address
+ = PED_BE32_TO_CPU (raw_part->boot_load);
+ mac_part_data->boot_entry_address
+ = PED_BE32_TO_CPU (raw_part->boot_entry);
+ mac_part_data->boot_checksum
+ = PED_BE32_TO_CPU (raw_part->boot_cksum);
+
+ mac_part_data->status = PED_BE32_TO_CPU (raw_part->status);
+ mac_part_data->driver_sig = PED_BE32_TO_CPU (raw_part->driver_sig);
+
+ return part;
+
+error_destroy_part:
+ ped_partition_destroy (part);
+error:
+ return NULL;
+}
+
+/* looks at the partition map size field in a mac raw partition, and calculates
+ * what the size of the partition map should be, from it
+ */
+static int
+_rawpart_get_partmap_size (MacRawPartition* raw_part, PedDisk* disk)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ PedSector sector_size = disk->dev->sector_size / 512;
+ PedSector part_map_start;
+ PedSector part_map_end;
+
+ part_map_start = mac_disk_data->ghost_size;
+ part_map_end = sector_size * PED_BE32_TO_CPU (raw_part->map_count);
+
+ return part_map_end - part_map_start + 1;
+}
+
+static int
+_disk_analyse_block_size (PedDisk* disk, MacRawDisk* raw_disk)
+{
+ PedSector block_size;
+
+ if (PED_BE16_TO_CPU (raw_disk->block_size) % 512) {
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Weird block size on device descriptor: %d bytes is "
+ "not divisible by 512."),
+ (int) PED_BE16_TO_CPU (raw_disk->block_size));
+#endif
+ goto error;
+ }
+
+ block_size = PED_BE16_TO_CPU (raw_disk->block_size) / 512;
+ if (block_size != disk->dev->sector_size / 512) {
+#ifndef DISCOVER_ONLY
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The driver descriptor says the physical block size "
+ "is %d bytes, but Linux says it is %d bytes."),
+ (int) block_size * 512,
+ (int) disk->dev->sector_size)
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+#endif
+ disk->dev->sector_size = block_size * 512;
+ }
+
+ return 1;
+
+error:
+ return 0;
+}
+
+/* Tries to figure out the block size used by the drivers, for the ghost
+ * partitioning scheme. Ghost partitioning works like this: the OpenFirmware
+ * (OF) sees 512 byte blocks, but some drivers use 2048 byte blocks (and,
+ * perhaps, some other number?). To remain compatible, the partition map
+ * only has "real" partition map entries on ghost-aligned block numbers (and
+ * the others are padded with Apple_Void partitions). This function tries
+ * to figure out what the "ghost-aligned" size is... (which, believe-it-or-not,
+ * doesn't always equal 2048!!!)
+ */
+static int
+_disk_analyse_ghost_size (PedDisk* disk)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ MacRawPartition raw_part;
+ int i;
+
+ for (i = 1; i < 64; i *= 2) {
+ if (!ped_device_read (disk->dev, &raw_part, i, 1))
+ return 0;
+ if (_rawpart_check_signature (&raw_part)
+ && !_rawpart_is_void (&raw_part)) {
+ mac_disk_data->ghost_size = i;
+ PED_ASSERT (i <= disk->dev->sector_size / 512,
+ return 0);
+ return 1;
+ }
+ }
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("No valid partition map found."));
+#endif
+ return 0;
+}
+
+static int
+mac_read (PedDisk* disk)
+{
+ MacRawDisk raw_disk;
+ MacRawPartition raw_part;
+ MacDiskData* mac_disk_data;
+ PedPartition* part;
+ int num;
+ PedSector ghost_size;
+ PedConstraint* constraint_exact;
+ int last_part_entry_num = 0;
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ mac_disk_data = disk->disk_specific;
+ mac_disk_data->part_map_entry_num = 0; /* 0 == none */
+
+ if (!ped_device_read (disk->dev, &raw_disk, 0, 1))
+ goto error;
+ if (!_check_signature (&raw_disk))
+ goto error;
+
+ if (!_disk_analyse_block_size (disk, &raw_disk))
+ goto error;
+ if (!_disk_analyse_ghost_size (disk))
+ goto error;
+ ghost_size = mac_disk_data->ghost_size;
+
+ if (!ped_disk_delete_all (disk))
+ goto error;
+
+ if (raw_disk.driver_count && raw_disk.driver_count < 62) {
+ memcpy(&mac_disk_data->driverlist[0], &raw_disk.driverlist[0],
+ sizeof(mac_disk_data->driverlist));
+ mac_disk_data->driver_count = raw_disk.driver_count;
+ mac_disk_data->block_size = raw_disk.block_size;
+ }
+
+ for (num=1; num==1 || num <= last_part_entry_num; num++) {
+ if (!ped_device_read (disk->dev, &raw_part,
+ num * ghost_size, 1))
+ goto error_delete_all;
+
+ if (!_rawpart_check_signature (&raw_part))
+ continue;
+
+ if (num == 1)
+ last_part_entry_num
+ = _rawpart_get_partmap_size (&raw_part, disk);
+ if (_rawpart_get_partmap_size (&raw_part, disk)
+ != last_part_entry_num) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Conflicting partition map entry sizes! "
+ "Entry 1 says it is %d, but entry %d says "
+ "it is %d!"),
+ last_part_entry_num,
+ _rawpart_get_partmap_size (&raw_part, disk))
+ != PED_EXCEPTION_IGNORE)
+ goto error_delete_all;
+ }
+
+ if (!_rawpart_is_active (&raw_part))
+ continue;
+
+ part = _rawpart_analyse (&raw_part, disk, num);
+ if (!part)
+ goto error_delete_all;
+ part->num = num;
+ part->fs_type = ped_file_system_probe (&part->geom);
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error_delete_all;
+ ped_constraint_destroy (constraint_exact);
+
+ if (_rawpart_is_partition_map (&raw_part)) {
+ if (mac_disk_data->part_map_entry_num
+ && ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Weird! There are 2 partitions "
+ "map entries!"))
+ != PED_EXCEPTION_IGNORE)
+ goto error_delete_all;
+
+ mac_disk_data->part_map_entry_num = num;
+ mac_disk_data->part_map_entry_count
+ = part->geom.end - ghost_size + 1;
+ }
+ }
+
+ if (!mac_disk_data->part_map_entry_num) {
+ if (!_disk_add_part_map_entry (disk, 1))
+ goto error_delete_all;
+ ped_disk_commit_to_dev (disk);
+ }
+ return 1;
+
+error_delete_all:
+ ped_disk_delete_all (disk);
+error:
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+/* The Ghost partition: is a blank entry, used to pad out each block (where
+ * there physical block size > 512 bytes). This is because OpenFirmware uses
+ * 512 byte blocks, but device drivers Think Different TM, with a different
+ * lbock size, so we need to do this to avoid a clash (!)
+ */
+static int
+_pad_raw_part (PedDisk* disk, int num, MacRawPartition* part_map)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ MacRawPartition ghost_entry;
+ int i;
+
+ memset (&ghost_entry, 0, sizeof (ghost_entry));
+ ghost_entry.signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2);
+ strcpy (ghost_entry.type, "Apple_Void");
+ ghost_entry.map_count
+ = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num);
+
+ for (i=0; i < mac_disk_data->ghost_size - 1; i++)
+ memcpy (&part_map [i + (num - 1) * mac_disk_data->ghost_size],
+ &ghost_entry, sizeof (MacRawPartition));
+
+ return 1;
+}
+
+static void
+_update_driver_count (MacRawPartition* part_map_entry,
+ MacDiskData *mac_driverdata, const MacDiskData* mac_disk_data)
+{
+ uint16_t i, count_orig, count_cur, bsz;
+ uint32_t driver_bs, driver_be, part_be;
+
+ bsz = mac_disk_data->block_size / 512;
+ count_cur = mac_driverdata->driver_count;
+ count_orig = mac_disk_data->driver_count;
+ for (i = 0; i < count_orig; i++) {
+ driver_bs = mac_disk_data->driverlist[i].block * bsz;
+ driver_be = driver_bs + mac_disk_data->driverlist[i].size;
+ part_be = part_map_entry->start_block + part_map_entry->block_count;
+ if (driver_bs >= part_map_entry->start_block
+ && driver_be <= part_be) {
+ mac_driverdata->driverlist[count_cur].block
+ = mac_disk_data->driverlist[i].block;
+ mac_driverdata->driverlist[count_cur].size
+ = mac_disk_data->driverlist[i].size;
+ mac_driverdata->driverlist[count_cur].type
+ = mac_disk_data->driverlist[i].type;
+ mac_driverdata->driver_count++;
+ break;
+ }
+ }
+}
+
+static int
+_generate_raw_part (PedDisk* disk, PedPartition* part,
+ MacRawPartition* part_map, MacDiskData *mac_driverdata)
+{
+ MacDiskData* mac_disk_data;
+ MacPartitionData* mac_part_data;
+ MacRawPartition* part_map_entry;
+ PedSector block_size = disk->dev->sector_size / 512;
+
+ PED_ASSERT (part->num > 0, goto error);
+
+ mac_disk_data = disk->disk_specific;
+ mac_part_data = part->disk_specific;
+
+ part_map_entry = &part_map [part->num * mac_disk_data->ghost_size - 1];
+
+ part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2);
+ part_map_entry->map_count
+ = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num);
+ part_map_entry->start_block
+ = PED_CPU_TO_BE32 (part->geom.start / block_size);
+ part_map_entry->block_count
+ = PED_CPU_TO_BE32 (part->geom.length / block_size);
+ strcpy (part_map_entry->name, mac_part_data->volume_name);
+ strcpy (part_map_entry->type, mac_part_data->system_name);
+
+ if (mac_part_data->is_driver) {
+ mac_part_data->boot_region_length = part->geom.length;
+ if (mac_part_data->has_driver)
+ _update_driver_count(part_map_entry, mac_driverdata,
+ mac_disk_data);
+ } else
+ mac_part_data->data_region_length = part->geom.length;
+ part_map_entry->data_count = PED_CPU_TO_BE32 (
+ mac_part_data->data_region_length / block_size);
+ part_map_entry->boot_count = PED_CPU_TO_BE32 (
+ mac_part_data->boot_region_length / block_size);
+ part_map_entry->status = PED_CPU_TO_BE32 (mac_part_data->status);
+ part_map_entry->driver_sig
+ = PED_CPU_TO_BE32 (mac_part_data->driver_sig);
+
+ part_map_entry->boot_load =
+ PED_CPU_TO_BE32 (mac_part_data->boot_base_address);
+ part_map_entry->boot_entry =
+ PED_CPU_TO_BE32 (mac_part_data->boot_entry_address);
+ part_map_entry->boot_cksum =
+ PED_CPU_TO_BE32 (mac_part_data->boot_checksum);
+
+ strncpy (part_map_entry->processor, mac_part_data->processor_name, 16);
+
+ if (!_pad_raw_part (disk, part->num, part_map))
+ goto error;
+
+ return 1;
+
+error:
+ return 0;
+}
+
+static int
+_generate_raw_freespace_part (PedDisk* disk, PedGeometry* geom, int num,
+ MacRawPartition* part_map)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ MacRawPartition* part_map_entry;
+ PedSector block_size = disk->dev->sector_size / 512;
+
+ PED_ASSERT (num > 0, goto error);
+
+ part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1];
+
+ part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2);
+ part_map_entry->map_count
+ = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num);
+ part_map_entry->start_block
+ = PED_CPU_TO_BE32 (geom->start / block_size);
+ part_map_entry->block_count
+ = PED_CPU_TO_BE32 (geom->length / block_size);
+ strcpy (part_map_entry->name, "Extra");
+ strcpy (part_map_entry->type, "Apple_Free");
+
+ part_map_entry->data_count = PED_CPU_TO_BE32 (geom->length);
+ part_map_entry->status = 0;
+ part_map_entry->driver_sig = 0;
+
+ if (!_pad_raw_part (disk, num, part_map))
+ goto error;
+
+ return 1;
+
+error:
+ return 0;
+}
+
+static int
+_generate_empty_part (PedDisk* disk, int num, MacRawPartition* part_map)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ MacRawPartition* part_map_entry;
+
+ PED_ASSERT (num > 0, return 0);
+
+ part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1];
+ part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2);
+ part_map_entry->map_count
+ = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num);
+ strcpy (part_map_entry->type, "Apple_Void");
+
+ return _pad_raw_part (disk, num, part_map);
+}
+
+/* returns the first empty entry in the partition map */
+static int
+_get_first_empty_part_entry (PedDisk* disk, MacRawPartition* part_map)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ int i;
+
+ for (i=1; i <= mac_disk_data->last_part_entry_num; i++) {
+ if (!part_map[i * mac_disk_data->ghost_size - 1].signature)
+ return i;
+ }
+
+ return 0;
+}
+
+static int
+write_block_zero (PedDisk* disk, MacDiskData* mac_driverdata)
+{
+ PedDevice* dev = disk->dev;
+ MacRawDisk raw_disk;
+
+ if (!ped_device_read (dev, &raw_disk, 0, 1))
+ return 0;
+
+ raw_disk.signature = PED_CPU_TO_BE16 (MAC_DISK_MAGIC);
+ raw_disk.block_size = PED_CPU_TO_BE16 (dev->sector_size);
+ raw_disk.block_count
+ = PED_CPU_TO_BE32 (dev->length / (dev->sector_size / 512));
+
+ raw_disk.driver_count = mac_driverdata->driver_count;
+ memcpy(&raw_disk.driverlist[0], &mac_driverdata->driverlist[0],
+ sizeof(raw_disk.driverlist));
+
+ return ped_device_write (dev, &raw_disk, 0, 1);
+}
+
+static int
+mac_write (PedDisk* disk)
+{
+ MacRawPartition* part_map;
+ MacDiskData* mac_disk_data;
+ MacDiskData* mac_driverdata; /* updated driver list */
+ PedPartition* part;
+ int num;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->disk_specific != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+ PED_ASSERT (!disk->update_mode, return 0);
+
+ mac_disk_data = disk->disk_specific;
+
+ if (!ped_disk_get_partition (disk, mac_disk_data->part_map_entry_num)) {
+ if (!_disk_add_part_map_entry (disk, 1))
+ goto error;
+ }
+
+ mac_driverdata = ped_malloc(sizeof(MacDiskData));
+ if (!mac_driverdata)
+ goto error;
+ memset (mac_driverdata, 0, sizeof(MacDiskData));
+
+ part_map = (MacRawPartition*)
+ ped_malloc (mac_disk_data->part_map_entry_count * 512);
+ if (!part_map)
+ goto error_free_driverdata;
+ memset (part_map, 0, mac_disk_data->part_map_entry_count * 512);
+
+/* write (to memory) the "real" partitions */
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part)) {
+ if (!ped_partition_is_active (part))
+ continue;
+ if (!_generate_raw_part (disk, part, part_map, mac_driverdata))
+ goto error_free_part_map;
+ }
+
+/* write the "free space" partitions */
+ for (part = ped_disk_next_partition (disk, NULL); part;
+ part = ped_disk_next_partition (disk, part)) {
+ if (part->type != PED_PARTITION_FREESPACE)
+ continue;
+ num = _get_first_empty_part_entry (disk, part_map);
+ if (!_generate_raw_freespace_part (disk, &part->geom, num,
+ part_map))
+ goto error_free_part_map;
+ }
+
+/* write the "void" (empty) partitions */
+ for (num = _get_first_empty_part_entry (disk, part_map); num;
+ num = _get_first_empty_part_entry (disk, part_map))
+ _generate_empty_part (disk, num, part_map);
+
+/* write to disk */
+ if (!ped_device_write (disk->dev, part_map, 1,
+ mac_disk_data->part_map_entry_count))
+ goto error_free_part_map;
+ ped_free (part_map);
+ return write_block_zero (disk, mac_driverdata);
+
+error_free_part_map:
+ ped_free (part_map);
+error_free_driverdata:
+ ped_free (mac_driverdata);
+error:
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+mac_partition_new (
+ const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type, PedSector start, PedSector end)
+{
+ PedPartition* part;
+ MacPartitionData* mac_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = mac_data = ped_malloc (sizeof (MacPartitionData));
+ if (!mac_data)
+ goto error_free_part;
+
+ memset (mac_data, 0, sizeof (MacPartitionData));
+ strcpy (mac_data->volume_name, "untitled");
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+
+ ped_free (mac_data);
+error_free_part:
+ ped_free (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+mac_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ MacPartitionData* new_mac_data;
+ MacPartitionData* old_mac_data;
+
+ new_part = ped_partition_new (part->disk, part->type,
+ part->fs_type, part->geom.start,
+ part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_mac_data = (MacPartitionData*) part->disk_specific;
+ new_mac_data = (MacPartitionData*) new_part->disk_specific;
+
+ /* ugly, but C is ugly :p */
+ memcpy (new_mac_data, old_mac_data, sizeof (MacPartitionData));
+ return new_part;
+}
+
+static void
+mac_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part))
+ ped_free (part->disk_specific);
+ ped_free (part);
+}
+
+static int
+mac_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ MacPartitionData* mac_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ if (fs_type && !strcmp (fs_type->name, "linux-swap"))
+ ped_partition_set_flag (part, PED_PARTITION_SWAP, 1);
+
+ if (mac_data->is_boot) {
+ strcpy (mac_data->system_name, "Apple_Bootstrap");
+ mac_data->status = 0x33;
+ return 1;
+ }
+
+ if (fs_type && (!strcmp (fs_type->name, "hfs")
+ || !strcmp (fs_type->name, "hfs+"))) {
+ strcpy (mac_data->system_name, "Apple_HFS");
+ mac_data->status |= 0x7f;
+ } else if (fs_type && !strcmp (fs_type->name, "hfsx")) {
+ strcpy (mac_data->system_name, "Apple_HFSX");
+ mac_data->status |= 0x7f;
+ } else {
+ strcpy (mac_data->system_name, "Apple_UNIX_SVR2");
+ mac_data->status = 0x33;
+ }
+
+ return 1;
+}
+
+static int
+mac_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ MacPartitionData* mac_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ mac_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ mac_data->is_boot = state;
+
+ if (part->fs_type)
+ return mac_partition_set_system (part, part->fs_type);
+
+ if (state) {
+ strcpy (mac_data->system_name, "Apple_Bootstrap");
+ mac_data->status = 0x33;
+ }
+ return 1;
+
+ case PED_PARTITION_ROOT:
+ if (state) {
+ strcpy (mac_data->volume_name, "root");
+ mac_data->is_swap = 0;
+ } else {
+ if (mac_data->is_root)
+ strcpy (mac_data->volume_name, "untitled");
+ }
+ mac_data->is_root = state;
+ return 1;
+
+ case PED_PARTITION_SWAP:
+ if (state) {
+ strcpy (mac_data->volume_name, "swap");
+ mac_data->is_root = 0;
+ } else {
+ if (mac_data->is_swap)
+ strcpy (mac_data->volume_name, "untitled");
+ }
+ mac_data->is_swap = state;
+ return 1;
+
+ case PED_PARTITION_LVM:
+ if (state) {
+ strcpy (mac_data->system_name, "Linux_LVM");
+ mac_data->is_lvm = state;
+ } else {
+ if (mac_data->is_lvm)
+ mac_partition_set_system (part, part->fs_type);
+ }
+ return 1;
+
+ case PED_PARTITION_RAID:
+ if (state) {
+ strcpy (mac_data->system_name, "Linux_RAID");
+ mac_data->is_raid = state;
+ } else {
+ if (mac_data->is_raid)
+ mac_partition_set_system (part, part->fs_type);
+ }
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+mac_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ MacPartitionData* mac_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ mac_data = part->disk_specific;
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ return mac_data->is_boot;
+
+ case PED_PARTITION_ROOT:
+ return mac_data->is_root;
+
+ case PED_PARTITION_SWAP:
+ return mac_data->is_swap;
+
+ case PED_PARTITION_LVM:
+ return mac_data->is_lvm;
+
+ case PED_PARTITION_RAID:
+ return mac_data->is_raid;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+mac_partition_is_flag_available (
+ const PedPartition* part, PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_SWAP:
+ case PED_PARTITION_LVM:
+ case PED_PARTITION_RAID:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+mac_partition_set_name (PedPartition* part, const char* name)
+{
+ MacPartitionData* mac_data;
+ int i;
+
+ PED_ASSERT (part != NULL, return);
+ PED_ASSERT (part->disk_specific != NULL, return);
+ mac_data = part->disk_specific;
+
+#ifndef DISCOVER_ONLY
+ if (mac_data->is_root || mac_data->is_swap) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Changing the name of a root or swap partition "
+ "will prevent Linux from recognising it as such."))
+ != PED_EXCEPTION_IGNORE)
+ return;
+ mac_data->is_root = mac_data->is_swap = 0;
+ }
+#endif
+
+ strncpy (mac_data->volume_name, name, 32);
+ mac_data->volume_name [32] = 0;
+ for (i = strlen (mac_data->volume_name) - 1;
+ mac_data->volume_name[i] == ' '; i--)
+ mac_data->volume_name [i] = 0;
+}
+
+static const char*
+mac_partition_get_name (const PedPartition* part)
+{
+ MacPartitionData* mac_data;
+
+ PED_ASSERT (part != NULL, return NULL);
+ PED_ASSERT (part->disk_specific != NULL, return NULL);
+ mac_data = part->disk_specific;
+
+ return mac_data->volume_name;
+}
+
+static PedConstraint*
+_primary_constraint (PedDisk* disk)
+{
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry max_geom;
+ PedSector sector_size;
+
+ sector_size = disk->dev->sector_size / 512;
+
+ if (!ped_alignment_init (&start_align, 0, sector_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, sector_size))
+ return NULL;
+ if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1))
+ return NULL;
+
+ return ped_constraint_new (&start_align, &end_align, &max_geom,
+ &max_geom, 1, disk->dev->length);
+}
+
+static int
+mac_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _primary_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+mac_partition_enumerate (PedPartition* part)
+{
+ PedDisk* disk;
+ MacDiskData* mac_disk_data;
+ int i;
+ int max_part_count;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ disk = part->disk;
+ mac_disk_data = (MacDiskData*) disk->disk_specific;
+
+ max_part_count = ped_disk_get_max_primary_partition_count (disk);
+
+ if (part->num > 0 && part->num <= mac_disk_data->part_map_entry_count)
+ return 1;
+
+ for (i = 1; i <= max_part_count; i++) {
+ if (!ped_disk_get_partition (disk, i)) {
+ part->num = i;
+ return 1;
+ }
+ }
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't add another partition -- the partition map is too "
+ "small!"));
+#endif
+
+ return 0;
+}
+
+static int
+_disk_count_partitions (PedDisk* disk)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ PedPartition* part = NULL;
+ PedPartition* last = NULL;
+
+ PED_ASSERT (disk->update_mode, return 0);
+
+ mac_disk_data->active_part_entry_count = 0;
+ mac_disk_data->free_part_entry_count = 0;
+ mac_disk_data->last_part_entry_num = 0;
+
+ /* subtle: we only care about free space after the partition map.
+ * the partition map is an "active" partition, BTW... */
+ for (part = ped_disk_next_partition (disk, part); part;
+ part = ped_disk_next_partition (disk, part)) {
+ if (!ped_partition_is_active (part))
+ continue;
+
+ mac_disk_data->active_part_entry_count++;
+ if (last && last->geom.end + 1 < part->geom.start)
+ mac_disk_data->free_part_entry_count++;
+ mac_disk_data->last_part_entry_num
+ = PED_MAX (mac_disk_data->last_part_entry_num,
+ part->num);
+
+ last = part;
+ }
+
+ if (last && last->geom.end < disk->dev->length - 1)
+ mac_disk_data->free_part_entry_count++;
+
+ mac_disk_data->last_part_entry_num
+ = PED_MAX (mac_disk_data->last_part_entry_num,
+ mac_disk_data->active_part_entry_count
+ + mac_disk_data->free_part_entry_count);
+ return 1;
+}
+
+static int
+add_metadata_part (PedDisk* disk, PedSector start, PedSector end)
+{
+ PedPartition* new_part;
+ PedConstraint* constraint_any = ped_constraint_any (disk->dev);
+
+ PED_ASSERT (disk != NULL, return 0);
+
+ new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ start, end);
+ if (!new_part)
+ goto error;
+ if (!ped_disk_add_partition (disk, new_part, constraint_any))
+ goto error_destroy_new_part;
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+
+error_destroy_new_part:
+ ped_partition_destroy (new_part);
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static int
+mac_alloc_metadata (PedDisk* disk)
+{
+ MacDiskData* mac_disk_data;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->disk_specific != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ mac_disk_data = disk->disk_specific;
+
+ if (!add_metadata_part (disk, 0, disk->dev->sector_size / 512 - 1))
+ return 0;
+
+ /* hack: this seems to be a good place, to update the partition map
+ * entry count, since mac_alloc_metadata() gets called during
+ * _disk_pop_update_mode()
+ */
+ return _disk_count_partitions (disk);
+}
+
+static int
+mac_get_max_primary_partition_count (const PedDisk* disk)
+{
+ MacDiskData* mac_disk_data = disk->disk_specific;
+ PedPartition* part_map_partition;
+
+ part_map_partition = ped_disk_get_partition (disk,
+ mac_disk_data->part_map_entry_num);
+
+ /* HACK: if we haven't found the partition map partition (yet),
+ * we return this.
+ */
+ if (!part_map_partition) {
+ mac_disk_data->part_map_entry_num = 0;
+ return 65536;
+ }
+
+ /* HACK: since Mac labels need an entry for free-space regions, we
+ * must allow half plus 1 entries for free-space partitions. I hate
+ * this, but things get REALLY complicated, otherwise.
+ * (I'm prepared to complicate things later, but I want to get
+ * everything working, first)
+ */
+ return mac_disk_data->part_map_entry_count / mac_disk_data->ghost_size
+ - mac_disk_data->free_part_entry_count + 1;
+}
+
+static PedDiskOps mac_disk_ops = {
+ .probe = mac_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = mac_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = mac_alloc,
+ .duplicate = mac_duplicate,
+ .free = mac_free,
+ .read = mac_read,
+#ifndef DISCOVER_ONLY
+ /* FIXME: remove this cast, once mac_write is fixed not to
+ modify its *DISK parameter. */
+ .write = (int (*) (const PedDisk*)) mac_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = mac_partition_new,
+ .partition_duplicate = mac_partition_duplicate,
+ .partition_destroy = mac_partition_destroy,
+ .partition_set_system = mac_partition_set_system,
+ .partition_set_flag = mac_partition_set_flag,
+ .partition_get_flag = mac_partition_get_flag,
+ .partition_is_flag_available = mac_partition_is_flag_available,
+ .partition_set_name = mac_partition_set_name,
+ .partition_get_name = mac_partition_get_name,
+ .partition_align = mac_partition_align,
+ .partition_enumerate = mac_partition_enumerate,
+
+ .alloc_metadata = mac_alloc_metadata,
+ .get_max_primary_partition_count =
+ mac_get_max_primary_partition_count
+};
+
+static PedDiskType mac_disk_type = {
+ .next = NULL,
+ .name = "mac",
+ .ops = &mac_disk_ops,
+ .features = PED_DISK_TYPE_PARTITION_NAME
+};
+
+void
+ped_disk_mac_init ()
+{
+ PED_ASSERT (sizeof (MacRawPartition) == 512, return);
+ PED_ASSERT (sizeof (MacRawDisk) == 512, return);
+
+ ped_disk_type_register (&mac_disk_type);
+}
+
+void
+ped_disk_mac_done ()
+{
+ ped_disk_type_unregister (&mac_disk_type);
+}
+
diff --git a/usr/src/lib/libparted/common/libparted/labels/pc98.c b/usr/src/lib/libparted/common/libparted/labels/pc98.c
new file mode 100644
index 0000000000..dc7565760e
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/pc98.c
@@ -0,0 +1,896 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* hacked from Linux/98 source: fs/partitions/nec98.h
+ *
+ * See also:
+ * http://people.FreeBSD.org/~kato/pc98.html
+ * http://www.kmc.kyoto-u.ac.jp/proj/linux98/index-english.html
+ *
+ * Partition types:
+ *
+ * id0(mid):
+ * bit 7: 1=bootable, 0=not bootable
+ * # Linux uses this flag to make a distinction between ext2 and swap.
+ * bit 6--0:
+ * 00H : N88-BASIC(data)?, PC-UX(data)?
+ * 04H : PC-UX(data)
+ * 06H : N88-BASIC
+ * 10H : N88-BASIC
+ * 14H : *BSD, PC-UX
+ * 20H : DOS(data), Windows95/98/NT, Linux
+ * 21H..2FH : DOS(system#1 .. system#15)
+ * 40H : Minix
+ *
+ * id1(sid):
+ * bit 7: 1=active, 0=sleep(hidden)
+ * # PC-UX uses this flag to make a distinction between its file system
+ * # and its swap.
+ * bit 6--0:
+ * 01H: FAT12
+ * 11H: FAT16, <32MB [accessible to DOS 3.3]
+ * 21H: FAT16, >=32MB [Large Partition]
+ * 31H: NTFS
+ * 28H: Windows NT (Volume/Stripe Set?)
+ * 41H: Windows NT (Volume/Stripe Set?)
+ * 48H: Windows NT (Volume/Stripe Set?)
+ * 61H: FAT32
+ * 04H: PC-UX
+ * 06H: N88-BASIC
+ * 44H: *BSD
+ * 62H: ext2, linux-swap
+ */
+
+#define MAX_PART_COUNT 16
+#define PC9800_EXTFMT_MAGIC 0xAA55
+
+#define BIT(x) (1 << (x))
+#define GET_BIT(n,bit) (((n) & BIT(bit)) != 0)
+#define SET_BIT(n,bit,val) n = (val)? (n | BIT(bit)) : (n & ~BIT(bit))
+
+typedef struct _PC98RawPartition PC98RawPartition;
+typedef struct _PC98RawTable PC98RawTable;
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+/* ripped from Linux/98 source */
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct _PC98RawPartition {
+ uint8_t mid; /* 0x80 - boot */
+ uint8_t sid; /* 0x80 - active */
+ uint8_t dum1; /* dummy for padding */
+ uint8_t dum2; /* dummy for padding */
+ uint8_t ipl_sect; /* IPL sector */
+ uint8_t ipl_head; /* IPL head */
+ uint16_t ipl_cyl; /* IPL cylinder */
+ uint8_t sector; /* starting sector */
+ uint8_t head; /* starting head */
+ uint16_t cyl; /* starting cylinder */
+ uint8_t end_sector; /* end sector */
+ uint8_t end_head; /* end head */
+ uint16_t end_cyl; /* end cylinder */
+ char name[16];
+} __attribute__((packed));
+
+struct _PC98RawTable {
+ uint8_t boot_code [510];
+ uint16_t magic;
+ PC98RawPartition partitions [MAX_PART_COUNT];
+} __attribute__((packed));
+#ifdef __sun
+#pragma pack()
+#endif
+
+typedef struct {
+ PedSector ipl_sector;
+ int system;
+ int boot;
+ int hidden;
+ char name [17];
+} PC98PartitionData;
+
+/* this MBR boot code is dummy */
+static const unsigned char MBR_BOOT_CODE[] = {
+ 0xcb, /* retf */
+ 0x00, 0x00, 0x00, /* */
+ 0x49, 0x50, 0x4c, 0x31 /* "IPL1" */
+};
+
+static PedDiskType pc98_disk_type;
+
+static PedSector chs_to_sector (const PedDevice* dev, int c, int h, int s);
+static void sector_to_chs (const PedDevice* dev, PedSector sector,
+ int* c, int* h, int* s);
+
+/* magic(?) check */
+static int
+pc98_check_magic (const PC98RawTable *part_table)
+{
+ /* check "extended-format" (have partition table?) */
+ if (PED_LE16_TO_CPU(part_table->magic) != PC9800_EXTFMT_MAGIC)
+ return 0;
+
+ return 1;
+}
+
+static int
+pc98_check_ipl_signature (const PC98RawTable *part_table)
+{
+ return !memcmp (part_table->boot_code + 4, "IPL1", 4);
+}
+
+static int
+check_partition_consistency (const PedDevice* dev,
+ const PC98RawPartition* raw_part)
+{
+ if (raw_part->ipl_sect >= dev->hw_geom.sectors
+ || raw_part->sector >= dev->hw_geom.sectors
+ || raw_part->end_sector >= dev->hw_geom.sectors
+ || raw_part->ipl_head >= dev->hw_geom.heads
+ || raw_part->head >= dev->hw_geom.heads
+ || raw_part->end_head >= dev->hw_geom.heads
+ || PED_LE16_TO_CPU(raw_part->ipl_cyl) >= dev->hw_geom.cylinders
+ || PED_LE16_TO_CPU(raw_part->cyl) >= dev->hw_geom.cylinders
+ || PED_LE16_TO_CPU(raw_part->end_cyl) >= dev->hw_geom.cylinders
+ || PED_LE16_TO_CPU(raw_part->cyl)
+ > PED_LE16_TO_CPU(raw_part->end_cyl)
+#if 0
+ || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->ipl_cyl),
+ raw_part->ipl_head, raw_part->ipl_sect)
+ || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->cyl),
+ raw_part->head, raw_part->sector)
+ || !chs_to_sector(dev, PED_LE16_TO_CPU(raw_part->end_cyl),
+ raw_part->end_head, raw_part->end_sector)
+#endif
+ || PED_LE16_TO_CPU(raw_part->end_cyl)
+ < PED_LE16_TO_CPU(raw_part->cyl))
+ return 0;
+
+ return 1;
+}
+
+static int
+pc98_probe (const PedDevice *dev)
+{
+ PC98RawTable part_table;
+ int empty;
+ const PC98RawPartition* p;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ if (!ped_device_read (dev, &part_table, 0, 2))
+ return 0;
+
+ /* check magic */
+ if (!pc98_check_magic (&part_table))
+ return 0;
+
+ /* check consistency */
+ empty = 1;
+ for (p = part_table.partitions;
+ p < part_table.partitions + MAX_PART_COUNT;
+ p++)
+ {
+ if (p->mid == 0 && p->sid == 0)
+ continue;
+ empty = 0;
+ if (!check_partition_consistency (dev, p))
+ return 0;
+ }
+
+ /* check boot loader */
+ if (pc98_check_ipl_signature (&part_table))
+ return 1;
+ else if (part_table.boot_code[0]) /* invalid boot loader */
+ return 0;
+
+ /* Not to mistake msdos disk map for PC-9800's empty disk map */
+ if (empty)
+ return 0;
+
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+pc98_clobber (PedDevice* dev)
+{
+ PC98RawTable table;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (pc98_probe (dev), return 0);
+
+ if (!ped_device_read (dev, &table, 0, 1))
+ return 0;
+
+ memset (table.partitions, 0, sizeof (table.partitions));
+ table.magic = PED_CPU_TO_LE16(0);
+
+ if (pc98_check_ipl_signature (&table))
+ memset (table.boot_code, 0, sizeof (table.boot_code));
+
+ if (!ped_device_write (dev, (void*) &table, 0, 1))
+ return 0;
+ return ped_device_sync (dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk*
+pc98_alloc (const PedDevice* dev)
+{
+ PED_ASSERT (dev != NULL, return 0);
+
+ return _ped_disk_alloc (dev, &pc98_disk_type);
+}
+
+static PedDisk*
+pc98_duplicate (const PedDisk* disk)
+{
+ return ped_disk_new_fresh (disk->dev, &pc98_disk_type);
+}
+
+static void
+pc98_free (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return);
+
+ _ped_disk_free (disk);
+}
+
+static PedSector
+chs_to_sector (const PedDevice* dev, int c, int h, int s)
+{
+ PED_ASSERT (dev != NULL, return 0);
+ return (c * dev->hw_geom.heads + h) * dev->hw_geom.sectors + s;
+}
+
+static void
+sector_to_chs (const PedDevice* dev, PedSector sector, int* c, int* h, int* s)
+{
+ PedSector cyl_size;
+
+ PED_ASSERT (dev != NULL, return);
+ PED_ASSERT (c != NULL, return);
+ PED_ASSERT (h != NULL, return);
+ PED_ASSERT (s != NULL, return);
+
+ cyl_size = dev->hw_geom.heads * dev->hw_geom.sectors;
+
+ *c = sector / cyl_size;
+ *h = (sector) % cyl_size / dev->hw_geom.sectors;
+ *s = (sector) % cyl_size % dev->hw_geom.sectors;
+}
+
+static PedSector
+legacy_start (const PedDisk* disk, const PC98RawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ return chs_to_sector (disk->dev, PED_LE16_TO_CPU(raw_part->cyl),
+ raw_part->head, raw_part->sector);
+}
+
+static PedSector
+legacy_end (const PedDisk* disk, const PC98RawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (raw_part != NULL, return 0);
+
+ if (raw_part->end_head == 0 && raw_part->end_sector == 0) {
+ return chs_to_sector (disk->dev,
+ PED_LE16_TO_CPU(raw_part->end_cyl),
+ disk->dev->hw_geom.heads - 1,
+ disk->dev->hw_geom.sectors - 1);
+ } else {
+ return chs_to_sector (disk->dev,
+ PED_LE16_TO_CPU(raw_part->end_cyl),
+ raw_part->end_head,
+ raw_part->end_sector);
+ }
+}
+
+static int
+is_unused_partition(const PC98RawPartition* raw_part)
+{
+ if (raw_part->mid || raw_part->sid
+ || raw_part->ipl_sect
+ || raw_part->ipl_head
+ || PED_LE16_TO_CPU(raw_part->ipl_cyl)
+ || raw_part->sector
+ || raw_part->head
+ || PED_LE16_TO_CPU(raw_part->cyl)
+ || raw_part->end_sector
+ || raw_part->end_head
+ || PED_LE16_TO_CPU(raw_part->end_cyl))
+ return 0;
+ return 1;
+}
+
+static int
+read_table (PedDisk* disk)
+{
+ int i;
+ PC98RawTable table;
+ PedConstraint* constraint_any;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ constraint_any = ped_constraint_any (disk->dev);
+
+ if (!ped_device_read (disk->dev, (void*) &table, 0, 2))
+ goto error;
+
+ if (!pc98_check_magic(&table)) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid partition table on %s."),
+ disk->dev->path))
+ goto error;
+ }
+
+ for (i = 0; i < MAX_PART_COUNT; i++) {
+ PC98RawPartition* raw_part;
+ PedPartition* part;
+ PC98PartitionData* pc98_data;
+ PedSector part_start;
+ PedSector part_end;
+
+ raw_part = &table.partitions [i];
+
+ if (is_unused_partition(raw_part))
+ continue;
+
+ part_start = legacy_start (disk, raw_part);
+ part_end = legacy_end (disk, raw_part);
+
+ part = ped_partition_new (disk, 0, NULL, part_start, part_end);
+ if (!part)
+ goto error;
+ pc98_data = part->disk_specific;
+ PED_ASSERT (pc98_data != NULL, goto error);
+
+ pc98_data->system = (raw_part->mid << 8) | raw_part->sid;
+ pc98_data->boot = GET_BIT(raw_part->mid, 7);
+ pc98_data->hidden = !GET_BIT(raw_part->sid, 7);
+
+ ped_partition_set_name (part, raw_part->name);
+
+ pc98_data->ipl_sector = chs_to_sector (
+ disk->dev,
+ PED_LE16_TO_CPU(raw_part->ipl_cyl),
+ raw_part->ipl_head,
+ raw_part->ipl_sect);
+
+ /* hack */
+ if (pc98_data->ipl_sector == part->geom.start)
+ pc98_data->ipl_sector = 0;
+
+ part->num = i + 1;
+
+ if (!ped_disk_add_partition (disk, part, constraint_any))
+ goto error;
+
+ if (part->geom.start != part_start
+ || part->geom.end != part_end) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Partition %d isn't aligned to cylinder "
+ "boundaries. This is still unsupported."),
+ part->num);
+ goto error;
+ }
+
+ part->fs_type = ped_file_system_probe (&part->geom);
+ }
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+
+error:
+ ped_disk_delete_all (disk);
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static int
+pc98_read (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ ped_disk_delete_all (disk);
+ return read_table (disk);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+fill_raw_part (PC98RawPartition* raw_part, const PedPartition* part)
+{
+ PC98PartitionData* pc98_data;
+ int c, h, s;
+ const char* name;
+
+ PED_ASSERT (raw_part != NULL, return 0);
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ pc98_data = part->disk_specific;
+ raw_part->mid = (pc98_data->system >> 8) & 0xFF;
+ raw_part->sid = pc98_data->system & 0xFF;
+
+ SET_BIT(raw_part->mid, 7, pc98_data->boot);
+ SET_BIT(raw_part->sid, 7, !pc98_data->hidden);
+
+ memset (raw_part->name, ' ', sizeof(raw_part->name));
+ name = ped_partition_get_name (part);
+ PED_ASSERT (name != NULL, return 0);
+ PED_ASSERT (strlen (name) <= 16, return 0);
+ if (!strlen (name) && part->fs_type)
+ name = part->fs_type->name;
+ memcpy (raw_part->name, name, strlen (name));
+
+ sector_to_chs (part->disk->dev, part->geom.start, &c, &h, &s);
+ raw_part->cyl = PED_CPU_TO_LE16(c);
+ raw_part->head = h;
+ raw_part->sector = s;
+
+ if (pc98_data->ipl_sector) {
+ sector_to_chs (part->disk->dev, pc98_data->ipl_sector,
+ &c, &h, &s);
+ raw_part->ipl_cyl = PED_CPU_TO_LE16(c);
+ raw_part->ipl_head = h;
+ raw_part->ipl_sect = s;
+ } else {
+ raw_part->ipl_cyl = raw_part->cyl;
+ raw_part->ipl_head = raw_part->head;
+ raw_part->ipl_sect = raw_part->sector;
+ }
+
+ sector_to_chs (part->disk->dev, part->geom.end, &c, &h, &s);
+ if (h != part->disk->dev->hw_geom.heads - 1
+ || s != part->disk->dev->hw_geom.sectors - 1) {
+ ped_exception_throw (
+ PED_EXCEPTION_NO_FEATURE,
+ PED_EXCEPTION_CANCEL,
+ _("Partition %d isn't aligned to cylinder "
+ "boundaries. This is still unsupported."),
+ part->num);
+ return 0;
+ }
+ raw_part->end_cyl = PED_CPU_TO_LE16(c);
+#if 0
+ raw_part->end_head = h;
+ raw_part->end_sector = s;
+#else
+ raw_part->end_head = 0;
+ raw_part->end_sector = 0;
+#endif
+
+ return 1;
+}
+
+static int
+pc98_write (const PedDisk* disk)
+{
+ PC98RawTable table;
+ PedPartition* part;
+ int i;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ if (!ped_device_read (disk->dev, &table, 0, 2))
+ return 0;
+
+ if (!pc98_check_ipl_signature (&table)) {
+ memset (table.boot_code, 0, sizeof(table.boot_code));
+ memcpy (table.boot_code, MBR_BOOT_CODE, sizeof(MBR_BOOT_CODE));
+ }
+
+ memset (table.partitions, 0, sizeof (table.partitions));
+ table.magic = PED_CPU_TO_LE16(PC9800_EXTFMT_MAGIC);
+
+ for (i = 1; i <= MAX_PART_COUNT; i++) {
+ part = ped_disk_get_partition (disk, i);
+ if (!part)
+ continue;
+
+ if (!fill_raw_part (&table.partitions [i - 1], part))
+ return 0;
+ }
+
+ if (!ped_device_write (disk->dev, (void*) &table, 0, 2))
+ return 0;
+ return ped_device_sync (disk->dev);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+pc98_partition_new (
+ const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type, PedSector start, PedSector end)
+{
+ PedPartition* part;
+ PC98PartitionData* pc98_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = pc98_data = ped_malloc (sizeof (PC98PartitionData));
+ if (!pc98_data)
+ goto error_free_part;
+ pc98_data->ipl_sector = 0;
+ pc98_data->hidden = 0;
+ pc98_data->boot = 0;
+ strcpy (pc98_data->name, "");
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+
+ ped_free (pc98_data);
+error_free_part:
+ ped_free (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+pc98_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ PC98PartitionData* new_pc98_data;
+ PC98PartitionData* old_pc98_data;
+
+ new_part = ped_partition_new (part->disk, part->type,
+ part->fs_type, part->geom.start,
+ part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_pc98_data = (PC98PartitionData*) part->disk_specific;
+ new_pc98_data = (PC98PartitionData*) new_part->disk_specific;
+
+ /* ugly, but C is ugly :p */
+ memcpy (new_pc98_data, old_pc98_data, sizeof (PC98PartitionData));
+ return new_part;
+}
+
+static void
+pc98_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part))
+ ped_free (part->disk_specific);
+ ped_free (part);
+}
+
+static int
+pc98_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ PC98PartitionData* pc98_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ pc98_data->system = 0x2062;
+ if (fs_type) {
+ if (!strcmp (fs_type->name, "fat16")) {
+ if (part->geom.length * 512 >= 32 * 1024 * 1024)
+ pc98_data->system = 0x2021;
+ else
+ pc98_data->system = 0x2011;
+ } else if (!strcmp (fs_type->name, "fat32")) {
+ pc98_data->system = 0x2061;
+ } else if (!strcmp (fs_type->name, "ntfs")) {
+ pc98_data->system = 0x2031;
+ } else if (!strncmp (fs_type->name, "ufs", 3)) {
+ pc98_data->system = 0x2044;
+ } else { /* ext2, reiser, xfs, etc. */
+ /* ext2 partitions must be marked boot */
+ pc98_data->boot = 1;
+ pc98_data->system = 0xa062;
+ }
+ }
+
+ if (pc98_data->boot)
+ pc98_data->system |= 0x8000;
+ if (!pc98_data->hidden)
+ pc98_data->system |= 0x0080;
+ return 1;
+}
+
+static int
+pc98_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ PC98PartitionData* pc98_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ pc98_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ pc98_data->hidden = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_BOOT:
+ pc98_data->boot = state;
+ return ped_partition_set_system (part, part->fs_type);
+
+ default:
+ return 0;
+ }
+}
+
+static int
+pc98_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ PC98PartitionData* pc98_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ pc98_data = part->disk_specific;
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ return pc98_data->hidden;
+
+ case PED_PARTITION_BOOT:
+ return pc98_data->boot;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+pc98_partition_is_flag_available (
+ const PedPartition* part, PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_BOOT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void
+pc98_partition_set_name (PedPartition* part, const char* name)
+{
+ PC98PartitionData* pc98_data;
+ int i;
+
+ PED_ASSERT (part != NULL, return);
+ PED_ASSERT (part->disk_specific != NULL, return);
+ pc98_data = part->disk_specific;
+
+ strncpy (pc98_data->name, name, 16);
+ pc98_data->name [16] = 0;
+ for (i = strlen (pc98_data->name) - 1; pc98_data->name[i] == ' '; i--)
+ pc98_data->name [i] = 0;
+}
+
+static const char*
+pc98_partition_get_name (const PedPartition* part)
+{
+ PC98PartitionData* pc98_data;
+
+ PED_ASSERT (part != NULL, return NULL);
+ PED_ASSERT (part->disk_specific != NULL, return NULL);
+ pc98_data = part->disk_specific;
+
+ return pc98_data->name;
+}
+
+static PedConstraint*
+_primary_constraint (PedDisk* disk)
+{
+ PedDevice* dev = disk->dev;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry max_geom;
+ PedSector cylinder_size;
+
+ cylinder_size = dev->hw_geom.sectors * dev->hw_geom.heads;
+
+ if (!ped_alignment_init (&start_align, 0, cylinder_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+ if (!ped_geometry_init (&max_geom, dev, cylinder_size,
+ dev->length - cylinder_size))
+ return NULL;
+
+ return ped_constraint_new (&start_align, &end_align, &max_geom,
+ &max_geom, 1, dev->length);
+}
+
+static int
+pc98_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _primary_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+next_primary (PedDisk* disk)
+{
+ int i;
+ for (i=1; i<=MAX_PART_COUNT; i++) {
+ if (!ped_disk_get_partition (disk, i))
+ return i;
+ }
+ return 0;
+}
+
+static int
+pc98_partition_enumerate (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ /* don't re-number a partition */
+ if (part->num != -1)
+ return 1;
+
+ PED_ASSERT (ped_partition_is_active (part), return 0);
+
+ part->num = next_primary (part->disk);
+ if (!part->num) {
+ ped_exception_throw (PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Can't add another partition."));
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+pc98_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* new_part;
+ PedConstraint* constraint_any = NULL;
+ PedSector cyl_size;
+
+ PED_ASSERT (disk != NULL, goto error);
+ PED_ASSERT (disk->dev != NULL, goto error);
+
+ constraint_any = ped_constraint_any (disk->dev);
+
+ cyl_size = disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads;
+ new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ 0, cyl_size - 1);
+ if (!new_part)
+ goto error;
+
+ if (!ped_disk_add_partition (disk, new_part, constraint_any)) {
+ ped_partition_destroy (new_part);
+ goto error;
+ }
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static int
+pc98_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return MAX_PART_COUNT;
+}
+
+static PedDiskOps pc98_disk_ops = {
+ .probe = pc98_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = pc98_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = pc98_alloc,
+ .duplicate = pc98_duplicate,
+ .free = pc98_free,
+ .read = pc98_read,
+#ifndef DISCOVER_ONLY
+ .write = pc98_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = pc98_partition_new,
+ .partition_duplicate = pc98_partition_duplicate,
+ .partition_destroy = pc98_partition_destroy,
+ .partition_set_system = pc98_partition_set_system,
+ .partition_set_flag = pc98_partition_set_flag,
+ .partition_get_flag = pc98_partition_get_flag,
+ .partition_is_flag_available = pc98_partition_is_flag_available,
+ .partition_set_name = pc98_partition_set_name,
+ .partition_get_name = pc98_partition_get_name,
+ .partition_align = pc98_partition_align,
+ .partition_enumerate = pc98_partition_enumerate,
+
+ .alloc_metadata = pc98_alloc_metadata,
+ .get_max_primary_partition_count =
+ pc98_get_max_primary_partition_count
+};
+
+static PedDiskType pc98_disk_type = {
+ .next = NULL,
+ .name = "pc98",
+ .ops = &pc98_disk_ops,
+ .features = PED_DISK_TYPE_PARTITION_NAME
+};
+
+void
+ped_disk_pc98_init ()
+{
+ PED_ASSERT (sizeof (PC98RawTable) == 512 * 2, return);
+ ped_disk_type_register (&pc98_disk_type);
+}
+
+void
+ped_disk_pc98_done ()
+{
+ ped_disk_type_unregister (&pc98_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/rdb.c b/usr/src/lib/libparted/common/libparted/labels/rdb.c
new file mode 100644
index 0000000000..11ff6795a6
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/rdb.c
@@ -0,0 +1,1183 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+
+ libparted - a library for manipulating disk partitions
+ disk_amiga.c - libparted module to manipulate amiga RDB partition tables.
+ Copyright (C) 2000, 2001, 2004, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Sven Luther <luther@debian.org>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#ifndef MAX
+# define MAX(a,b) ((a) < (b) ? (b) : (a))
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* String manipulation */
+static void _amiga_set_bstr (const char *cstr, char *bstr, int maxsize) {
+ int size = strlen (cstr);
+ int i;
+
+ if (size >= maxsize) return;
+ bstr[0] = size;
+ for (i = 0; i<size; i++) bstr[i+1] = cstr[i];
+}
+static const char * _amiga_get_bstr (char * bstr) {
+ char * cstr = bstr + 1;
+ int size = bstr[0];
+
+ cstr[size] = '\0';
+ return cstr;
+}
+
+#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */
+#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */
+#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */
+#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */
+#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */
+#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */
+#define IDNAME_FREE (uint32_t)0xffffffff
+
+static const char *
+_amiga_block_id (uint32_t id) {
+ switch (id) {
+ case IDNAME_RIGIDDISK :
+ return "RDSK";
+ case IDNAME_BADBLOCK :
+ return "BADB";
+ case IDNAME_PARTITION :
+ return "PART";
+ case IDNAME_FILESYSHEADER :
+ return "FSHD";
+ case IDNAME_LOADSEG :
+ return "LSEG";
+ case IDNAME_BOOT :
+ return "BOOT";
+ case IDNAME_FREE :
+ return "<free>";
+ default :
+ return "<unknown>";
+ }
+}
+
+struct AmigaIds {
+ uint32_t ID;
+ struct AmigaIds *next;
+};
+
+static struct AmigaIds *
+_amiga_add_id (uint32_t id, struct AmigaIds *ids) {
+ struct AmigaIds *newid;
+
+ if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL)
+ return 0;
+ newid->ID = id;
+ newid->next = ids;
+ return newid;
+}
+
+static void
+_amiga_free_ids (struct AmigaIds *ids) {
+ struct AmigaIds *current, *next;
+
+ for (current = ids; current != NULL; current = next) {
+ next = current->next;
+ ped_free (current);
+ }
+}
+static int
+_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) {
+ struct AmigaIds *current;
+
+ for (current = ids; current != NULL; current = current->next) {
+ if (id == current->ID)
+ return 1;
+ }
+ return 0;
+}
+
+struct AmigaBlock {
+ uint32_t amiga_ID; /* Identifier 32 bit word */
+ uint32_t amiga_SummedLongss; /* Size of the structure for checksums */
+ int32_t amiga_ChkSum; /* Checksum of the structure */
+};
+#define AMIGA(pos) ((struct AmigaBlock *)(pos))
+
+static int
+_amiga_checksum (struct AmigaBlock *blk) {
+ uint32_t *rdb = (uint32_t *) blk;
+ uint32_t sum;
+ int i, end;
+
+ sum = PED_BE32_TO_CPU (rdb[0]);
+ end = PED_BE32_TO_CPU (rdb[1]);
+
+ if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT;
+
+ for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]);
+
+ return sum;
+}
+
+static void
+_amiga_calculate_checksum (struct AmigaBlock *blk) {
+ blk->amiga_ChkSum = PED_CPU_TO_BE32(
+ PED_BE32_TO_CPU(blk->amiga_ChkSum) -
+ _amiga_checksum((struct AmigaBlock *) blk));
+ return;
+}
+
+static struct AmigaBlock *
+_amiga_read_block (const PedDevice *dev, struct AmigaBlock *blk,
+ PedSector block, struct AmigaIds *ids)
+{
+ if (!ped_device_read (dev, blk, block, 1))
+ return NULL;
+ if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids))
+ return NULL;
+ if (_amiga_checksum (blk) != 0) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL,
+ _("%s : Bad checksum on block %llu of type %s."),
+ __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID))))
+ {
+ case PED_EXCEPTION_CANCEL :
+ return NULL;
+ case PED_EXCEPTION_FIX :
+ _amiga_calculate_checksum(AMIGA(blk));
+ if (!ped_device_write ((PedDevice*)dev, blk, block, 1))
+ return NULL;
+ case PED_EXCEPTION_IGNORE :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return blk;
+ }
+ }
+ return blk;
+}
+
+struct RigidDiskBlock {
+ uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */
+ uint32_t rdb_SummedLongs; /* Size of the structure for checksums */
+ int32_t rdb_ChkSum; /* Checksum of the structure */
+ uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t rdb_BlockBytes; /* Size of disk blocks */
+ uint32_t rdb_Flags; /* RDB Flags */
+ /* block list heads */
+ uint32_t rdb_BadBlockList; /* Bad block list */
+ uint32_t rdb_PartitionList; /* Partition list */
+ uint32_t rdb_FileSysHeaderList; /* File system header list */
+ uint32_t rdb_DriveInit; /* Drive specific init code */
+ uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */
+ uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */
+ /* physical drive characteristics */
+ uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */
+ uint32_t rdb_Sectors; /* Number of sectors of the drive */
+ uint32_t rdb_Heads; /* Number of heads of the drive */
+ uint32_t rdb_Interleave; /* Interleave */
+ uint32_t rdb_Park; /* Head parking cylinder */
+ uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */
+ uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */
+ uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */
+ uint32_t rdb_StepRate; /* Step rate of the drive */
+ uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */
+ /* logical drive characteristics */
+ uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */
+ uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */
+ uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */
+ uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */
+ uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */
+ uint32_t rdb_AutoParkSeconds; /* zero for no auto park */
+ uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */
+ /* (not including replacement bad blocks) */
+ uint32_t rdb_Reserved4;
+ /* drive identification */
+ char rdb_DiskVendor[8];
+ char rdb_DiskProduct[16];
+ char rdb_DiskRevision[4];
+ char rdb_ControllerVendor[8];
+ char rdb_ControllerProduct[16];
+ char rdb_ControllerRevision[4];
+ uint32_t rdb_Reserved5[10];
+};
+
+#define RDSK(pos) ((struct RigidDiskBlock *)(pos))
+
+#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff)
+#define RDB_LOCATION_LIMIT 16
+#define AMIGA_MAX_PARTITIONS 128
+#define MAX_RDB_BLOCK (RDB_LOCATION_LIMIT + 2 * AMIGA_MAX_PARTITIONS + 2)
+
+static uint32_t
+_amiga_find_rdb (const PedDevice *dev, struct RigidDiskBlock *rdb) {
+ int i;
+ struct AmigaIds *ids;
+
+ ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL);
+
+ for (i = 0; i<RDB_LOCATION_LIMIT; i++) {
+ if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) {
+ continue;
+ }
+ if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) {
+ _amiga_free_ids (ids);
+ return i;
+ }
+ }
+ _amiga_free_ids (ids);
+ return AMIGA_RDB_NOT_FOUND;
+}
+
+struct PartitionBlock {
+ uint32_t pb_ID; /* Identifier 32 bit word : 'PART' */
+ uint32_t pb_SummedLongs; /* Size of the structure for checksums */
+ int32_t pb_ChkSum; /* Checksum of the structure */
+ uint32_t pb_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t pb_Next; /* Block number of the next PartitionBlock */
+ uint32_t pb_Flags; /* Part Flags (NOMOUNT and BOOTABLE) */
+ uint32_t pb_Reserved1[2];
+ uint32_t pb_DevFlags; /* Preferred flags for OpenDevice */
+ char pb_DriveName[32]; /* Preferred DOS device name: BSTR form */
+ uint32_t pb_Reserved2[15];
+ uint32_t de_TableSize; /* Size of Environment vector */
+ /* Size of the blocks in 32 bit words, usually 128 */
+ uint32_t de_SizeBlock;
+ uint32_t de_SecOrg; /* Not used; must be 0 */
+ uint32_t de_Surfaces; /* Number of heads (surfaces) */
+ /* Disk sectors per block, used with SizeBlock, usually 1 */
+ uint32_t de_SectorPerBlock;
+ uint32_t de_BlocksPerTrack; /* Blocks per track. drive specific */
+ uint32_t de_Reserved; /* DOS reserved blocks at start of partition. */
+ uint32_t de_PreAlloc; /* DOS reserved blocks at end of partition */
+ uint32_t de_Interleave; /* Not used, usually 0 */
+ uint32_t de_LowCyl; /* First cylinder of the partition */
+ uint32_t de_HighCyl; /* Last cylinder of the partition */
+ uint32_t de_NumBuffers; /* Initial # DOS of buffers. */
+ uint32_t de_BufMemType; /* Type of mem to allocate for buffers */
+ uint32_t de_MaxTransfer; /* Max number of bytes to transfer at a time */
+ uint32_t de_Mask; /* Address Mask to block out certain memory */
+ int32_t de_BootPri; /* Boot priority for autoboot */
+ uint32_t de_DosType; /* Dostype of the file system */
+ uint32_t de_Baud; /* Baud rate for serial handler */
+ uint32_t de_Control; /* Control word for handler/filesystem */
+ uint32_t de_BootBlocks; /* Number of blocks containing boot code */
+ uint32_t pb_EReserved[12];
+};
+
+#define PART(pos) ((struct PartitionBlock *)(pos))
+
+#define PBFB_BOOTABLE 0 /* this partition is intended to be bootable */
+#define PBFF_BOOTABLE 1L /* (expected directories and files exist) */
+#define PBFB_NOMOUNT 1 /* do not mount this partition (e.g. manually */
+#define PBFF_NOMOUNT 2L /* mounted, but space reserved here) */
+#define PBFB_RAID 2 /* this partition is intended to be part of */
+#define PBFF_RAID 4L /* a RAID array */
+#define PBFB_LVM 3 /* this partition is intended to be part of */
+#define PBFF_LVM 8L /* a LVM volume group */
+
+
+struct LinkedBlock {
+ uint32_t lk_ID; /* Identifier 32 bit word */
+ uint32_t lk_SummedLongs; /* Size of the structure for checksums */
+ int32_t lk_ChkSum; /* Checksum of the structure */
+ uint32_t pb_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t lk_Next; /* Block number of the next PartitionBlock */
+};
+struct Linked2Block {
+ uint32_t lk2_ID; /* Identifier 32 bit word */
+ uint32_t lk2_SummedLongs; /* Size of the structure for checksums */
+ int32_t lk2_ChkSum; /* Checksum of the structure */
+ uint32_t lk2_HostID; /* SCSI Target ID of host, not really used */
+ uint32_t lk2_Next; /* Block number of the next PartitionBlock */
+ uint32_t lk2_Reverved[13];
+ uint32_t lk2_Linked; /* Secondary linked list */
+};
+#define LINK_END (uint32_t)0xffffffff
+#define LNK(pos) ((struct LinkedBlock *)(pos))
+#define LNK2(pos) ((struct Linked2Block *)(pos))
+
+
+static PedDiskType amiga_disk_type;
+
+static int
+amiga_probe (const PedDevice *dev)
+{
+ struct RigidDiskBlock *rdb;
+ uint32_t found;
+ PED_ASSERT(dev != NULL, return 0);
+
+ if ((rdb=RDSK(ped_malloc(dev->sector_size)))==NULL)
+ return 0;
+ found = _amiga_find_rdb (dev, rdb);
+ ped_free (rdb);
+
+ return (found == AMIGA_RDB_NOT_FOUND ? 0 : 1);
+}
+
+static PedDisk*
+amiga_alloc (const PedDevice* dev)
+{
+ PedDisk *disk;
+ struct RigidDiskBlock *rdb;
+ PedSector cyl_size;
+ int highest_cylinder, highest_block;
+
+ PED_ASSERT(dev != NULL, return NULL);
+ cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads;
+
+ if (!(disk = _ped_disk_alloc (dev, &amiga_disk_type)))
+ return NULL;
+
+ if (!(disk->disk_specific = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) {
+ ped_free (disk);
+ return NULL;
+ }
+ rdb = disk->disk_specific;
+
+ memset(rdb, 0, sizeof(struct RigidDiskBlock));
+
+ rdb->rdb_ID = PED_CPU_TO_BE32 (IDNAME_RIGIDDISK);
+ rdb->rdb_SummedLongs = PED_CPU_TO_BE32 (64);
+ rdb->rdb_HostID = PED_CPU_TO_BE32 (0);
+ rdb->rdb_BlockBytes = PED_CPU_TO_BE32 (PED_SECTOR_SIZE_DEFAULT);
+ rdb->rdb_Flags = PED_CPU_TO_BE32 (0);
+
+ /* Block lists */
+ rdb->rdb_BadBlockList = PED_CPU_TO_BE32 (LINK_END);
+ rdb->rdb_PartitionList = PED_CPU_TO_BE32 (LINK_END);
+ rdb->rdb_FileSysHeaderList = PED_CPU_TO_BE32 (LINK_END);
+ rdb->rdb_DriveInit = PED_CPU_TO_BE32 (LINK_END);
+ rdb->rdb_BootBlockList = PED_CPU_TO_BE32 (LINK_END);
+
+ /* Physical drive characteristics */
+ rdb->rdb_Cylinders = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
+ rdb->rdb_Sectors = PED_CPU_TO_BE32 (dev->hw_geom.sectors);
+ rdb->rdb_Heads = PED_CPU_TO_BE32 (dev->hw_geom.heads);
+ rdb->rdb_Interleave = PED_CPU_TO_BE32 (0);
+ rdb->rdb_Park = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
+ rdb->rdb_WritePreComp = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
+ rdb->rdb_ReducedWrite = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
+ rdb->rdb_StepRate = PED_CPU_TO_BE32 (0);
+
+ highest_cylinder = 1 + MAX_RDB_BLOCK / cyl_size;
+ highest_block = highest_cylinder * cyl_size - 1;
+
+ /* Logical driver characteristics */
+ rdb->rdb_RDBBlocksLo = PED_CPU_TO_BE32 (0);
+ rdb->rdb_RDBBlocksHi = PED_CPU_TO_BE32 (highest_block);
+ rdb->rdb_LoCylinder = PED_CPU_TO_BE32 (highest_cylinder);
+ rdb->rdb_HiCylinder = PED_CPU_TO_BE32 (dev->hw_geom.cylinders -1);
+ rdb->rdb_CylBlocks = PED_CPU_TO_BE32 (cyl_size);
+ rdb->rdb_AutoParkSeconds = PED_CPU_TO_BE32 (0);
+ /* rdb_HighRDSKBlock will only be set when writing */
+ rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32 (0);
+
+ /* Driver identification */
+ _amiga_set_bstr("", rdb->rdb_DiskVendor, 8);
+ _amiga_set_bstr(dev->model, rdb->rdb_DiskProduct, 16);
+ _amiga_set_bstr("", rdb->rdb_DiskRevision, 4);
+ _amiga_set_bstr("", rdb->rdb_ControllerVendor, 8);
+ _amiga_set_bstr("", rdb->rdb_ControllerProduct, 16);
+ _amiga_set_bstr("", rdb->rdb_ControllerRevision, 4);
+
+ /* And calculate the checksum */
+ _amiga_calculate_checksum ((struct AmigaBlock *) rdb);
+
+ return disk;
+}
+
+static PedDisk*
+amiga_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ struct RigidDiskBlock * new_rdb;
+ struct RigidDiskBlock * old_rdb;
+ PED_ASSERT(disk != NULL, return NULL);
+ PED_ASSERT(disk->dev != NULL, return NULL);
+ PED_ASSERT(disk->disk_specific != NULL, return NULL);
+
+ old_rdb = (struct RigidDiskBlock *) disk->disk_specific;
+
+ if (!(new_disk = ped_disk_new_fresh (disk->dev, &amiga_disk_type)))
+ return NULL;
+
+ new_rdb = (struct RigidDiskBlock *) new_disk->disk_specific;
+ memcpy (new_rdb, old_rdb, 256);
+ return new_disk;
+}
+
+static void
+amiga_free (PedDisk* disk)
+{
+ PED_ASSERT(disk != NULL, return);
+ PED_ASSERT(disk->disk_specific != NULL, return);
+
+ ped_free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+#ifndef DISCOVER_ONLY
+static int
+amiga_clobber (PedDevice* dev)
+{
+ struct RigidDiskBlock *rdb;
+ uint32_t i;
+ int result = 0;
+ PED_ASSERT(dev != NULL, return 0);
+
+ if ((rdb=RDSK(ped_malloc(PED_SECTOR_SIZE_DEFAULT)))==NULL)
+ return 0;
+
+ while ((i = _amiga_find_rdb (dev, rdb)) != AMIGA_RDB_NOT_FOUND) {
+ rdb->rdb_ID = PED_CPU_TO_BE32 (0);
+ result = ped_device_write (dev, (void*) rdb, i, 1);
+ }
+
+ ped_free (rdb);
+
+ return result;
+}
+#endif /* !DISCOVER_ONLY */
+
+static int
+_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max)
+{
+ uint32_t i;
+
+ for (i = 0; i < max; i++)
+ if (block == blocklist[i]) {
+ /* We are looping, let's stop. */
+ return 1;
+ }
+ blocklist[max] = block;
+ return 0;
+}
+
+/* We have already allocated a rdb, we are now reading it from the disk */
+static int
+amiga_read (PedDisk* disk)
+{
+ struct RigidDiskBlock *rdb;
+ struct PartitionBlock *partition;
+ uint32_t partblock;
+ uint32_t partlist[AMIGA_MAX_PARTITIONS];
+ PedSector cylblocks;
+ int i;
+
+ PED_ASSERT(disk != NULL, return 0);
+ PED_ASSERT(disk->dev != NULL, return 0);
+ PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
+ return 0);
+ PED_ASSERT(disk->disk_specific != NULL, return 0);
+ rdb = RDSK(disk->disk_specific);
+
+ if (_amiga_find_rdb (disk->dev, rdb) == AMIGA_RDB_NOT_FOUND) {
+ ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("%s : Didn't find rdb block, should never happen."), __func__);
+ return 0;
+ }
+
+ /* Let's copy the rdb read geometry to the dev */
+ /* FIXME: should this go into disk->dev->bios_geom instead? */
+ disk->dev->hw_geom.cylinders = PED_BE32_TO_CPU (rdb->rdb_Cylinders);
+ disk->dev->hw_geom.heads = PED_BE32_TO_CPU (rdb->rdb_Heads);
+ disk->dev->hw_geom.sectors = PED_BE32_TO_CPU (rdb->rdb_Sectors);
+ cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) *
+ (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors);
+
+ /* Remove all partitions in the former in memory table */
+ ped_disk_delete_all (disk);
+
+ /* Let's allocate a partition block */
+ if (!(partition = ped_malloc (disk->dev->sector_size)))
+ return 0;
+
+ /* We initialize the hardblock free list to detect loops */
+ for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = LINK_END;
+
+ for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList);
+ i < AMIGA_MAX_PARTITIONS && partblock != LINK_END;
+ i++, partblock = PED_BE32_TO_CPU(partition->pb_Next))
+ {
+ PedPartition *part;
+ PedSector start, end;
+ PedConstraint *constraint_exact;
+
+ /* Let's look for loops in the partition table */
+ if (_amiga_loop_check(partblock, partlist, i)) {
+ break;
+ }
+
+ /* Let's allocate and read a partition block to get its geometry*/
+ if (!_amiga_read_block (disk->dev, AMIGA(partition),
+ (PedSector)partblock, NULL)) {
+ ped_free(partition);
+ return 0;
+ }
+
+ start = ((PedSector) PED_BE32_TO_CPU (partition->de_LowCyl))
+ * cylblocks;
+ end = (((PedSector) PED_BE32_TO_CPU (partition->de_HighCyl))
+ + 1) * cylblocks - 1;
+
+ /* We can now construct a new partition */
+ if (!(part = ped_partition_new (disk, 0, NULL, start, end))) {
+ ped_free(partition);
+ return 0;
+ }
+ /* And copy over the partition block */
+ memcpy(part->disk_specific, partition, 256);
+
+ part->num = i;
+ part->type = 0;
+ /* Let's probe what file system is present on the disk */
+ part->fs_type = ped_file_system_probe (&part->geom);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact)) {
+ ped_partition_destroy(part);
+ ped_free(partition);
+ return 0;
+ }
+ ped_constraint_destroy (constraint_exact);
+ }
+ ped_free(partition);
+ return 1;
+}
+
+static int
+_amiga_find_free_blocks(const PedDisk *disk, uint32_t *table,
+ struct LinkedBlock *block, uint32_t first, uint32_t type)
+{
+ PedSector next;
+
+ PED_ASSERT(disk != NULL, return 0);
+ PED_ASSERT(disk->dev != NULL, return 0);
+
+ for (next = first; next != LINK_END; next = PED_BE32_TO_CPU(block->lk_Next)) {
+ if (table[next] != IDNAME_FREE) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL,
+ _("%s : Loop detected at block %d."), __func__, next))
+ {
+ case PED_EXCEPTION_CANCEL :
+ return 0;
+ case PED_EXCEPTION_FIX :
+ /* TODO : Need to add fixing code */
+ case PED_EXCEPTION_IGNORE :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return 1;
+ }
+ }
+
+ if (!_amiga_read_block (disk->dev, AMIGA(block), next, NULL)) {
+ return 0;
+ }
+ if (PED_BE32_TO_CPU(block->lk_ID) != type) {
+ switch (ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : The %s list seems bad at block %s."),
+ __func__, _amiga_block_id(PED_BE32_TO_CPU(block->lk_ID)), next))
+ {
+ /* TODO : to more subtile things here */
+ case PED_EXCEPTION_CANCEL :
+ case PED_EXCEPTION_UNHANDLED :
+ default :
+ return 0;
+ }
+ }
+ table[next] = type;
+ if (PED_BE32_TO_CPU(block->lk_ID) == IDNAME_FILESYSHEADER) {
+ if (_amiga_find_free_blocks(disk, table, block,
+ PED_BE32_TO_CPU(LNK2(block)->lk2_Linked),
+ IDNAME_LOADSEG) == 0) return 0;
+ }
+ }
+ return 1;
+}
+static uint32_t
+_amiga_next_free_block(uint32_t *table, uint32_t start, uint32_t type) {
+ int i;
+
+ for (i = start; table[i] != type && table[i] != IDNAME_FREE; i++);
+ return i;
+}
+static PedPartition *
+_amiga_next_real_partition(const PedDisk *disk, PedPartition *part) {
+ PedPartition *next;
+
+ for (next = ped_disk_next_partition (disk, part);
+ next != NULL && !ped_partition_is_active (next);
+ next = ped_disk_next_partition (disk, next));
+ return next;
+}
+#ifndef DISCOVER_ONLY
+static int
+amiga_write (const PedDisk* disk)
+{
+ struct RigidDiskBlock *rdb;
+ struct LinkedBlock *block;
+ struct PartitionBlock *partition;
+ PedPartition *part, *next_part;
+ PedSector cylblocks, first_hb, last_hb, last_used_hb;
+ uint32_t * table;
+ uint32_t i;
+ uint32_t rdb_num, part_num, block_num, next_num;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+ PED_ASSERT (disk->disk_specific != NULL, return 0);
+
+ if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT)))
+ return 0;
+
+ /* Let's read the rdb */
+ if ((rdb_num = _amiga_find_rdb (disk->dev, rdb)) == AMIGA_RDB_NOT_FOUND) {
+ rdb_num = 2;
+ size_t pb_size = sizeof (struct PartitionBlock);
+ /* Initialize only the part that won't be copied over
+ with a partition block in amiga_read. */
+ memset ((char *)(RDSK(disk->disk_specific)) + pb_size,
+ 0, PED_SECTOR_SIZE_DEFAULT - pb_size);
+ } else {
+ memcpy (RDSK(disk->disk_specific), rdb, PED_SECTOR_SIZE_DEFAULT);
+ }
+ ped_free (rdb);
+ rdb = RDSK(disk->disk_specific);
+
+ cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) *
+ (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors);
+ first_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksLo);
+ last_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksHi);
+ last_used_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock);
+
+ /* Allocate a free block table and initialize it.
+ There must be room for at least RDB_NUM + 2 entries, since
+ the first RDB_NUM+1 entries get IDNAME_RIGIDDISK, and the
+ following one must have LINK_END to serve as sentinel. */
+ size_t tab_size = 2 + MAX (last_hb - first_hb, rdb_num);
+ if (!(table = ped_malloc (tab_size * sizeof *table)))
+ return 0;
+
+ for (i = 0; i <= rdb_num; i++)
+ table[i] = IDNAME_RIGIDDISK;
+ for ( ; i < tab_size; i++)
+ table[i] = LINK_END;
+
+ /* Let's allocate a partition block */
+ if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) {
+ ped_free (table);
+ return 0;
+ }
+
+ /* And fill the free block table */
+ if (_amiga_find_free_blocks(disk, table, block,
+ PED_BE32_TO_CPU (rdb->rdb_BadBlockList), IDNAME_BADBLOCK) == 0)
+ {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to list bad blocks."), __func__);
+ goto error_free_table;
+ }
+ if (_amiga_find_free_blocks(disk, table, block,
+ PED_BE32_TO_CPU (rdb->rdb_PartitionList), IDNAME_PARTITION) == 0)
+ {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to list partition blocks."), __func__);
+ goto error_free_table;
+ }
+ if (_amiga_find_free_blocks(disk, table, block,
+ PED_BE32_TO_CPU (rdb->rdb_FileSysHeaderList), IDNAME_FILESYSHEADER) == 0)
+ {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to list file system blocks."), __func__);
+ goto error_free_table;
+ }
+ if (_amiga_find_free_blocks(disk, table, block,
+ PED_BE32_TO_CPU (rdb->rdb_BootBlockList), IDNAME_BOOT) == 0)
+ {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("%s : Failed to list boot blocks."), __func__);
+ goto error_free_table;
+ }
+
+ block_num = next_num = part_num = _amiga_next_free_block(table, rdb_num+1,
+ IDNAME_PARTITION);
+ part = _amiga_next_real_partition(disk, NULL);
+ rdb->rdb_PartitionList = PED_CPU_TO_BE32(part ? part_num : LINK_END);
+ for (; part != NULL; part = next_part, block_num = next_num) {
+ PED_ASSERT(part->disk_specific != NULL, return 0);
+ PED_ASSERT(part->geom.start % cylblocks == 0, return 0);
+ PED_ASSERT((part->geom.end + 1) % cylblocks == 0, return 0);
+
+ next_part = _amiga_next_real_partition(disk, part);
+ next_num = _amiga_next_free_block(table, block_num+1, IDNAME_PARTITION);
+
+ partition = PART(part->disk_specific);
+ if (next_part == NULL)
+ partition->pb_Next = PED_CPU_TO_BE32(LINK_END);
+ else
+ partition->pb_Next = PED_CPU_TO_BE32(next_num);
+ partition->de_LowCyl = PED_CPU_TO_BE32(part->geom.start/cylblocks);
+ partition->de_HighCyl = PED_CPU_TO_BE32((part->geom.end+1)/cylblocks-1);
+ _amiga_calculate_checksum(AMIGA(partition));
+ if (!ped_device_write (disk->dev, (void*) partition, block_num, 1)) {
+ ped_exception_throw(PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Failed to write partition block at %d."),
+ block_num);
+ goto error_free_table;
+ /* WARNING : If we fail here, we stop everything,
+ * and the partition table is lost. A better
+ * solution should be found, using the second
+ * half of the hardblocks to not overwrite the
+ * old partition table. It becomes problematic
+ * if we use more than half of the hardblocks. */
+ }
+ }
+
+ if (block_num > PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock))
+ rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32(block_num);
+
+ _amiga_calculate_checksum(AMIGA(rdb));
+ if (!ped_device_write (disk->dev, (void*) disk->disk_specific, rdb_num, 1))
+ goto error_free_table;
+
+ ped_free (table);
+ ped_free (block);
+ return ped_device_sync (disk->dev);
+
+error_free_table:
+ ped_free (table);
+ ped_free (block);
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+amiga_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition *part;
+ PedDevice *dev;
+ PedSector cyl;
+ struct PartitionBlock *partition;
+ struct RigidDiskBlock *rdb;
+
+ PED_ASSERT(disk != NULL, return NULL);
+ PED_ASSERT(disk->dev != NULL, return NULL);
+ PED_ASSERT(disk->disk_specific != NULL, return NULL);
+ dev = disk->dev;
+ cyl = (PedSector) (dev->hw_geom.sectors * dev->hw_geom.heads);
+ rdb = RDSK(disk->disk_specific);
+
+ if (!(part = _ped_partition_alloc (disk, part_type, fs_type, start, end)))
+ return NULL;
+
+ if (ped_partition_is_active (part)) {
+ if (!(part->disk_specific = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) {
+ ped_free (part);
+ return NULL;
+ }
+ partition = PART(part->disk_specific);
+ memset(partition, 0, sizeof(struct PartitionBlock));
+
+ partition->pb_ID = PED_CPU_TO_BE32(IDNAME_PARTITION);
+ partition->pb_SummedLongs = PED_CPU_TO_BE32(64);
+ partition->pb_HostID = rdb->rdb_HostID;
+ partition->pb_Flags = PED_CPU_TO_BE32(0);
+ /* TODO : use a scheme including the device name and the
+ * partition number, if it is possible */
+ _amiga_set_bstr("dhx", partition->pb_DriveName, 32);
+
+ partition->de_TableSize = PED_CPU_TO_BE32(19);
+ partition->de_SizeBlock = PED_CPU_TO_BE32(128);
+ partition->de_SecOrg = PED_CPU_TO_BE32(0);
+ partition->de_Surfaces = PED_CPU_TO_BE32(dev->hw_geom.heads);
+ partition->de_SectorPerBlock = PED_CPU_TO_BE32(1);
+ partition->de_BlocksPerTrack
+ = PED_CPU_TO_BE32(dev->hw_geom.sectors);
+ partition->de_Reserved = PED_CPU_TO_BE32(2);
+ partition->de_PreAlloc = PED_CPU_TO_BE32(0);
+ partition->de_Interleave = PED_CPU_TO_BE32(0);
+ partition->de_LowCyl = PED_CPU_TO_BE32(start/cyl);
+ partition->de_HighCyl = PED_CPU_TO_BE32((end+1)/cyl-1);
+ partition->de_NumBuffers = PED_CPU_TO_BE32(30);
+ partition->de_BufMemType = PED_CPU_TO_BE32(0);
+ partition->de_MaxTransfer = PED_CPU_TO_BE32(0x7fffffff);
+ partition->de_Mask = PED_CPU_TO_BE32(0xffffffff);
+ partition->de_BootPri = PED_CPU_TO_BE32(0);
+ partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800);
+ partition->de_Baud = PED_CPU_TO_BE32(0);
+ partition->de_Control = PED_CPU_TO_BE32(0);
+ partition->de_BootBlocks = PED_CPU_TO_BE32(0);
+
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+}
+
+static PedPartition*
+amiga_partition_duplicate (const PedPartition* part)
+{
+ PedPartition *new_part;
+ struct PartitionBlock *new_amiga_part;
+ struct PartitionBlock *old_amiga_part;
+
+ PED_ASSERT(part != NULL, return NULL);
+ PED_ASSERT(part->disk != NULL, return NULL);
+ PED_ASSERT(part->disk_specific != NULL, return NULL);
+ old_amiga_part = (struct PartitionBlock *) part->disk_specific;
+
+ new_part = ped_partition_new (part->disk, part->type,
+ part->fs_type, part->geom.start,
+ part->geom.end);
+ if (!new_part)
+ return NULL;
+
+ new_amiga_part = (struct PartitionBlock *) new_part->disk_specific;
+ memcpy (new_amiga_part, old_amiga_part, 256);
+
+ return new_part;
+}
+
+static void
+amiga_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part)) {
+ PED_ASSERT (part->disk_specific != NULL, return);
+ ped_free (part->disk_specific);
+ }
+ _ped_partition_free (part);
+}
+
+static int
+amiga_partition_set_system (PedPartition* part,
+ const PedFileSystemType* fs_type)
+{
+ struct PartitionBlock *partition;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+ partition = PART(part->disk_specific);
+
+ part->fs_type = fs_type;
+
+ if (!fs_type)
+ partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */
+ else if (!strcmp (fs_type->name, "ext2"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */
+ else if (!strcmp (fs_type->name, "ext3"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x45585403); /* 'EXT\3' */
+ else if (!strcmp (fs_type->name, "linux-swap"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x53575000); /* 'SWP\0' */
+ else if (!strcmp (fs_type->name, "fat16"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x46415400); /* 'FAT\0' */
+ else if (!strcmp (fs_type->name, "fat32"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x46415401); /* 'FAT\1'*/
+ else if (!strcmp (fs_type->name, "hfs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x48465300); /* 'HFS\0' */
+ else if (!strcmp (fs_type->name, "jfs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x4a465300); /* 'JFS\0' */
+ else if (!strcmp (fs_type->name, "ntfs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x4e544653); /* 'NTFS' */
+ else if (!strcmp (fs_type->name, "reiserfs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x52465300); /* 'RFS\0' */
+ else if (!strcmp (fs_type->name, "sun-ufs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x53554653); /* 'SUFS' */
+ else if (!strcmp (fs_type->name, "hp-ufs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x48554653); /* 'HUFS' */
+ else if (!strcmp (fs_type->name, "xfs"))
+ partition->de_DosType = PED_CPU_TO_BE32(0x58465300); /* 'XFS\0' */
+ else
+ partition->de_DosType = PED_CPU_TO_BE32(0x00000000); /* unknown */
+ return 1;
+}
+
+static int
+amiga_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ struct PartitionBlock *partition;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ partition = PART(part->disk_specific);
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_BOOTABLE);
+ else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_BOOTABLE));
+ return 1;
+ case PED_PARTITION_HIDDEN:
+ if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_NOMOUNT);
+ else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_NOMOUNT));
+ return 1;
+ case PED_PARTITION_RAID:
+ if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_RAID);
+ else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_RAID));
+ return 1;
+ case PED_PARTITION_LVM:
+ if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_LVM);
+ else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_LVM));
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+amiga_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ struct PartitionBlock *partition;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ partition = PART(part->disk_specific);
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_BOOTABLE));
+ case PED_PARTITION_HIDDEN:
+ return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_NOMOUNT));
+ case PED_PARTITION_RAID:
+ return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_RAID));
+ case PED_PARTITION_LVM:
+ return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_LVM));
+ default:
+ return 0;
+ }
+}
+
+static int
+amiga_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ case PED_PARTITION_HIDDEN:
+ case PED_PARTITION_RAID:
+ case PED_PARTITION_LVM:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void
+amiga_partition_set_name (PedPartition* part, const char* name)
+{
+ struct PartitionBlock *partition;
+
+ PED_ASSERT (part != NULL, return);
+ PED_ASSERT (part->disk_specific != NULL, return);
+
+ partition = PART(part->disk_specific);
+ _amiga_set_bstr(name, partition->pb_DriveName, 32);
+}
+static const char*
+amiga_partition_get_name (const PedPartition* part)
+{
+ struct PartitionBlock *partition;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ partition = PART(part->disk_specific);
+
+ return _amiga_get_bstr(partition->pb_DriveName);
+}
+
+static PedConstraint*
+_amiga_get_constraint (const PedDisk *disk)
+{
+ PedDevice *dev = disk->dev;
+ PedAlignment start_align, end_align;
+ PedGeometry max_geom;
+ PedSector cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads;
+
+ if (!ped_alignment_init(&start_align, 0, cyl_size))
+ return NULL;
+ if (!ped_alignment_init(&end_align, -1, cyl_size))
+ return NULL;
+ if (!ped_geometry_init(&max_geom, dev, MAX_RDB_BLOCK + 1,
+ dev->length - MAX_RDB_BLOCK - 1))
+ return NULL;
+
+ return ped_constraint_new (&start_align, &end_align,
+ &max_geom, &max_geom, 1, dev->length);
+}
+
+static int
+amiga_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _amiga_get_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+amiga_partition_enumerate (PedPartition* part)
+{
+ int i;
+ PedPartition* p;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk != NULL, return 0);
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+ for (i = 1; i <= AMIGA_MAX_PARTITIONS; i++) {
+ p = ped_disk_get_partition (part->disk, i);
+ if (!p) {
+ part->num = i;
+ return 1;
+ }
+ }
+
+ /* failed to allocate a number */
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unable to allocate a partition number."));
+#endif
+ return 0;
+}
+
+static int
+amiga_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* new_part;
+ PedConstraint* constraint_any = NULL;
+
+ PED_ASSERT (disk != NULL, goto error);
+ PED_ASSERT (disk->dev != NULL, goto error);
+
+ constraint_any = ped_constraint_any (disk->dev);
+
+ /* Allocate space for the RDB */
+ new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ 0, MAX_RDB_BLOCK);
+ if (!new_part)
+ goto error;
+
+ if (!ped_disk_add_partition (disk, new_part, constraint_any)) {
+ ped_partition_destroy (new_part);
+ goto error;
+ }
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static int
+amiga_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return AMIGA_MAX_PARTITIONS;
+}
+
+static PedDiskOps amiga_disk_ops = {
+ .probe = amiga_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = amiga_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = amiga_alloc,
+ .duplicate = amiga_duplicate,
+ .free = amiga_free,
+ .read = amiga_read,
+#ifndef DISCOVER_ONLY
+ .write = amiga_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = amiga_partition_new,
+ .partition_duplicate = amiga_partition_duplicate,
+ .partition_destroy = amiga_partition_destroy,
+ .partition_set_system = amiga_partition_set_system,
+ .partition_set_flag = amiga_partition_set_flag,
+ .partition_get_flag = amiga_partition_get_flag,
+ .partition_is_flag_available =
+ amiga_partition_is_flag_available,
+ .partition_set_name = amiga_partition_set_name,
+ .partition_get_name = amiga_partition_get_name,
+ .partition_align = amiga_partition_align,
+ .partition_enumerate = amiga_partition_enumerate,
+
+
+ .alloc_metadata = amiga_alloc_metadata,
+ .get_max_primary_partition_count =
+ amiga_get_max_primary_partition_count
+};
+
+static PedDiskType amiga_disk_type = {
+ .next = NULL,
+ .name = "amiga",
+ .ops = &amiga_disk_ops,
+ .features = PED_DISK_TYPE_PARTITION_NAME
+};
+
+void
+ped_disk_amiga_init ()
+{
+ PED_ASSERT (sizeof (struct AmigaBlock) != 3, return);
+ PED_ASSERT (sizeof (struct RigidDiskBlock) != 64, return);
+ PED_ASSERT (sizeof (struct PartitionBlock) != 64, return);
+ PED_ASSERT (sizeof (struct LinkedBlock) != 5, return);
+ PED_ASSERT (sizeof (struct Linked2Block) != 18, return);
+
+ ped_disk_type_register (&amiga_disk_type);
+}
+
+void
+ped_disk_amiga_done ()
+{
+ ped_disk_type_unregister (&amiga_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/labels/sun.c b/usr/src/lib/libparted/common/libparted/labels/sun.c
new file mode 100644
index 0000000000..50e8be2e9a
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/labels/sun.c
@@ -0,0 +1,868 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2000, 2001, 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+
+ Contributor: Ben Collins <bcollins@debian.org>
+*/
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+/* Most of this came from util-linux's sun support, which was mostly done
+ by Jakub Jelinek. */
+
+#define SUN_DISK_MAGIC 0xDABE /* Disk magic number */
+#define SUN_DISK_MAXPARTITIONS 8
+
+#define WHOLE_DISK_ID 0x05
+#define WHOLE_DISK_PART 2 /* as in 0, 1, 2 (3rd partition) */
+#define LINUX_SWAP_ID 0x82
+
+typedef struct _SunRawPartition SunRawPartition;
+typedef struct _SunPartitionInfo SunPartitionInfo;
+typedef struct _SunRawLabel SunRawLabel;
+typedef struct _SunPartitionData SunPartitionData;
+typedef struct _SunDiskData SunDiskData;
+
+#if defined(__sun)
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+#endif
+
+#ifdef __sun
+#define __attribute__(X) /*nothing*/
+#endif /* __sun */
+
+#ifdef __sun
+#pragma pack(1)
+#endif
+struct __attribute__ ((packed)) _SunRawPartition {
+ u_int32_t start_cylinder; /* where the part starts... */
+ u_int32_t num_sectors; /* ...and it's length */
+};
+
+struct __attribute__ ((packed)) _SunPartitionInfo {
+ u_int8_t spare1;
+ u_int8_t id; /* Partition type */
+ u_int8_t spare2;
+ u_int8_t flags; /* Partition flags */
+};
+
+struct __attribute__ ((packed)) _SunRawLabel {
+ char info[128]; /* Informative text string */
+ u_int8_t spare0[14];
+ SunPartitionInfo infos[SUN_DISK_MAXPARTITIONS];
+ u_int8_t spare1[246]; /* Boot information etc. */
+ u_int16_t rspeed; /* Disk rotational speed */
+ u_int16_t pcylcount; /* Physical cylinder count */
+ u_int16_t sparecyl; /* extra sects per cylinder */
+ u_int8_t spare2[4]; /* More magic... */
+ u_int16_t ilfact; /* Interleave factor */
+ u_int16_t ncyl; /* Data cylinder count */
+ u_int16_t nacyl; /* Alt. cylinder count */
+ u_int16_t ntrks; /* Tracks per cylinder */
+ u_int16_t nsect; /* Sectors per track */
+ u_int8_t spare3[4]; /* Even more magic... */
+ SunRawPartition partitions[SUN_DISK_MAXPARTITIONS];
+ u_int16_t magic; /* Magic number */
+ u_int16_t csum; /* Label xor'd checksum */
+};
+#ifdef __sun
+#pragma pack()
+#endif
+
+struct _SunPartitionData {
+ u_int8_t type;
+ int is_boot;
+ int is_root;
+ int is_lvm;
+};
+
+struct _SunDiskData {
+ PedSector length; /* This is based on cyl - alt-cyl */
+ SunRawLabel raw_label;
+};
+
+static PedDiskType sun_disk_type;
+
+/* Checksum computation */
+static void
+sun_compute_checksum (SunRawLabel *label)
+{
+ u_int16_t *ush = (u_int16_t *)label;
+ u_int16_t csum = 0;
+
+ while(ush < (u_int16_t *)(&label->csum))
+ csum ^= *ush++;
+ label->csum = csum;
+}
+
+/* Checksum Verification */
+static int
+sun_verify_checksum (SunRawLabel *label)
+{
+ u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1;
+ u_int16_t csum = 0;
+
+ while (ush >= (u_int16_t *)label)
+ csum ^= *ush--;
+
+ return !csum;
+}
+
+static int
+sun_probe (const PedDevice *dev)
+{
+ SunRawLabel label;
+
+ PED_ASSERT (dev != NULL, return 0);
+
+ if (dev->sector_size != 512)
+ return 0;
+
+ if (!ped_device_read (dev, &label, 0, 1))
+ return 0;
+
+ /* check magic */
+ if (PED_BE16_TO_CPU (label.magic) != SUN_DISK_MAGIC)
+ return 0;
+
+#ifndef DISCOVER_ONLY
+ if (!sun_verify_checksum(&label)) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Corrupted Sun disk label detected."));
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+sun_clobber (PedDevice* dev)
+{
+ SunRawLabel label;
+
+ PED_ASSERT (dev != NULL, return 0);
+ PED_ASSERT (sun_probe (dev), return 0);
+
+ if (!ped_device_read (dev, &label, 0, 1))
+ return 0;
+
+ label.magic = 0;
+ return ped_device_write (dev, &label, 0, 1);
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedDisk*
+sun_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ SunRawLabel* label;
+ SunDiskData* sun_specific;
+ PedCHSGeometry* bios_geom = &((PedDevice*)dev)->bios_geom;
+ PedSector cyl_size = bios_geom->sectors * bios_geom->heads;
+
+ disk = _ped_disk_alloc (dev, &sun_disk_type);
+ if (!disk)
+ goto error;
+
+ disk->disk_specific = (SunDiskData*) ped_malloc (sizeof (SunDiskData));
+ if (!disk->disk_specific)
+ goto error_free_disk;
+ sun_specific = (SunDiskData*) disk->disk_specific;
+
+ bios_geom->cylinders = dev->length / cyl_size;
+ sun_specific->length = bios_geom->cylinders * cyl_size;
+
+ label = &sun_specific->raw_label;
+ memset(label, 0, sizeof(SunRawLabel));
+
+ /* #gentoo-sparc people agree that nacyl = 0 is the best option */
+ label->magic = PED_CPU_TO_BE16 (SUN_DISK_MAGIC);
+ label->nacyl = 0;
+ label->pcylcount = PED_CPU_TO_BE16 (bios_geom->cylinders);
+ label->rspeed = PED_CPU_TO_BE16 (5400);
+ label->ilfact = PED_CPU_TO_BE16 (1);
+ label->sparecyl = 0;
+ label->ntrks = PED_CPU_TO_BE16 (bios_geom->heads);
+ label->nsect = PED_CPU_TO_BE16 (bios_geom->sectors);
+ label->ncyl = PED_CPU_TO_BE16 (bios_geom->cylinders - 0);
+
+ /* Add a whole disk partition at a minimum */
+ label->infos[WHOLE_DISK_PART].id = WHOLE_DISK_ID;
+ label->partitions[WHOLE_DISK_PART].start_cylinder = 0;
+ label->partitions[WHOLE_DISK_PART].num_sectors =
+ PED_CPU_TO_BE32(bios_geom->cylinders * cyl_size);
+
+ /* Now a neato string to describe this label */
+ snprintf(label->info, sizeof(label->info) - 1,
+ "GNU Parted Custom cyl %d alt %d hd %d sec %d",
+ PED_BE16_TO_CPU(label->ncyl),
+ PED_BE16_TO_CPU(label->nacyl),
+ PED_BE16_TO_CPU(label->ntrks),
+ PED_BE16_TO_CPU(label->nsect));
+
+ sun_compute_checksum(label);
+ return disk;
+
+error_free_disk:
+ _ped_disk_free (disk);
+error:
+ return NULL;
+}
+
+static PedDisk*
+sun_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+ SunDiskData* new_sun_data;
+ SunDiskData* old_sun_data = (SunDiskData*) disk->disk_specific;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &sun_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ new_sun_data = (SunDiskData*) new_disk->disk_specific;
+ memcpy (new_sun_data, old_sun_data, sizeof (SunDiskData));
+ return new_disk;
+}
+
+static void
+sun_free (PedDisk *disk)
+{
+ ped_free (disk->disk_specific);
+ _ped_disk_free (disk);
+}
+
+static int
+_check_geometry_sanity (PedDisk* disk, SunRawLabel* label)
+{
+ PedDevice* dev = disk->dev;
+
+ if (PED_BE16_TO_CPU(label->nsect) == dev->hw_geom.sectors &&
+ PED_BE16_TO_CPU(label->ntrks) == dev->hw_geom.heads)
+ dev->bios_geom = dev->hw_geom;
+
+ if (PED_BE16_TO_CPU(label->nsect) != dev->bios_geom.sectors ||
+ PED_BE16_TO_CPU(label->ntrks) != dev->bios_geom.heads) {
+#ifndef DISCOVER_ONLY
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The disk CHS geometry (%d,%d,%d) reported "
+ "by the operating system does not match "
+ "the geometry stored on the disk label "
+ "(%d,%d,%d)."),
+ dev->bios_geom.cylinders,
+ dev->bios_geom.heads,
+ dev->bios_geom.sectors,
+ PED_BE16_TO_CPU(label->pcylcount),
+ PED_BE16_TO_CPU(label->ntrks),
+ PED_BE16_TO_CPU(label->nsect))
+ == PED_EXCEPTION_CANCEL)
+ return 0;
+#endif
+ dev->bios_geom.sectors = PED_BE16_TO_CPU(label->nsect);
+ dev->bios_geom.heads = PED_BE16_TO_CPU(label->ntrks);
+ dev->bios_geom.cylinders = PED_BE16_TO_CPU(label->pcylcount);
+
+ if (dev->bios_geom.sectors * dev->bios_geom.heads
+ * dev->bios_geom.cylinders > dev->length) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The disk label describes a disk bigger than "
+ "%s."),
+ dev->path)
+ != PED_EXCEPTION_IGNORE)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+sun_read (PedDisk* disk)
+{
+ SunRawLabel* label;
+ SunPartitionData* sun_data;
+ SunDiskData* disk_data;
+ int i;
+ PedPartition* part;
+ PedSector end, start, block;
+ PedConstraint* constraint_exact;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+ PED_ASSERT (disk->disk_specific != NULL, return 0);
+
+ disk_data = (SunDiskData*) disk->disk_specific;
+ label = &disk_data->raw_label;
+
+ ped_disk_delete_all (disk);
+
+ if (!ped_device_read (disk->dev, label, 0, 1))
+ goto error;
+ if (!_check_geometry_sanity (disk, label))
+ goto error;
+
+ block = disk->dev->bios_geom.sectors * disk->dev->bios_geom.heads;
+ disk_data->length = block * disk->dev->bios_geom.cylinders;
+
+ for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) {
+ if (!PED_BE32_TO_CPU(label->partitions[i].num_sectors))
+ continue;
+ if (!label->infos[i].id)
+ continue;
+ if (label->infos[i].id == WHOLE_DISK_ID)
+ continue;
+
+ start = PED_BE32_TO_CPU(label->partitions[i].start_cylinder)
+ * block;
+ end = start
+ + PED_BE32_TO_CPU(label->partitions[i].num_sectors) - 1;
+
+ part = ped_partition_new (disk, 0, NULL, start, end);
+ if (!part)
+ goto error;
+
+ sun_data = part->disk_specific;
+ sun_data->type = label->infos[i].id;
+ sun_data->is_boot = sun_data->type == 0x1;
+ sun_data->is_root = sun_data->type == 0x2;
+ sun_data->is_lvm = sun_data->type == 0x8e;
+
+ part->num = i + 1;
+ part->fs_type = ped_file_system_probe (&part->geom);
+
+ constraint_exact = ped_constraint_exact (&part->geom);
+ if (!ped_disk_add_partition (disk, part, constraint_exact))
+ goto error;
+ ped_constraint_destroy (constraint_exact);
+ }
+
+ return 1;
+
+ error:
+ return 0;
+}
+
+#ifndef DISCOVER_ONLY
+static void
+_probe_and_use_old_info (const PedDisk* disk)
+{
+ SunDiskData* sun_specific;
+ SunRawLabel old_label;
+
+ sun_specific = (SunDiskData*) disk->disk_specific;
+
+ if (!ped_device_read (disk->dev, &old_label, 0, 1))
+ return;
+ if (old_label.info [0]
+ && PED_BE16_TO_CPU (old_label.magic) == SUN_DISK_MAGIC)
+ memcpy (&sun_specific->raw_label, &old_label, 512);
+}
+
+static int
+sun_write (const PedDisk* disk)
+{
+ SunRawLabel* label;
+ SunPartitionData* sun_data;
+ SunDiskData* disk_data;
+ PedPartition* part;
+ int i;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ _probe_and_use_old_info (disk);
+
+ disk_data = (SunDiskData*) disk->disk_specific;
+ label = &disk_data->raw_label;
+
+ memset (label->partitions, 0,
+ sizeof (SunRawPartition) * SUN_DISK_MAXPARTITIONS);
+ memset (label->infos, 0,
+ sizeof (SunPartitionInfo) * SUN_DISK_MAXPARTITIONS);
+
+ for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) {
+ part = ped_disk_get_partition (disk, i + 1);
+
+ if (!part && i == WHOLE_DISK_PART) {
+ /* Ok, nothing explicitly in the whole disk
+ partition, so let's put it there for safety
+ sake. */
+
+ label->infos[i].id = WHOLE_DISK_ID;
+ label->partitions[i].start_cylinder = 0;
+ label->partitions[i].num_sectors =
+ PED_CPU_TO_BE32(disk_data->length);
+ continue;
+ }
+ if (!part)
+ continue;
+
+ sun_data = part->disk_specific;
+ label->infos[i].id = sun_data->type;
+ label->partitions[i].start_cylinder
+ = PED_CPU_TO_BE32 (part->geom.start
+ / (disk->dev->bios_geom.sectors
+ * disk->dev->bios_geom.heads));
+ label->partitions[i].num_sectors
+ = PED_CPU_TO_BE32 (part->geom.end
+ - part->geom.start + 1);
+ }
+
+ /* We assume the harddrive is always right, and that the label may
+ be wrong. I don't think this will cause any problems, since the
+ cylinder count is always enforced by our alignment, and we
+ sanity checked the sectors/heads when we detected the device. The
+ worst that could happen here is that the drive seems bigger or
+ smaller than it really is, but we'll have that problem even if we
+ don't do this. */
+
+ if (disk->dev->bios_geom.cylinders > 65536) {
+ ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE,
+ _("The disk has %d cylinders, which is greater than "
+ "the maximum of 65536."),
+ disk->dev->bios_geom.cylinders);
+ }
+
+ label->pcylcount = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders);
+ label->ncyl = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders
+ - PED_BE16_TO_CPU (label->nacyl));
+
+ sun_compute_checksum (label);
+
+ if (!ped_device_write (disk->dev, label, 0, 1))
+ goto error;
+ return ped_device_sync (disk->dev);
+
+error:
+ return 0;
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+sun_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ SunPartitionData* sun_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = sun_data = ped_malloc (sizeof (SunPartitionData));
+ if (!sun_data)
+ goto error_free_part;
+ sun_data->type = 0;
+ sun_data->is_boot = 0;
+ sun_data->is_root = 0;
+ sun_data->is_lvm = 0;
+ } else {
+ part->disk_specific = NULL;
+ }
+
+ return part;
+
+ ped_free (sun_data);
+error_free_part:
+ ped_free (part);
+error:
+ return NULL;
+}
+
+static PedPartition*
+sun_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ SunPartitionData* new_sun_data;
+ SunPartitionData* old_sun_data;
+
+ new_part = ped_partition_new (part->disk, part->type,
+ part->fs_type, part->geom.start,
+ part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_sun_data = (SunPartitionData*) part->disk_specific;
+ new_sun_data = (SunPartitionData*) new_part->disk_specific;
+ new_sun_data->type = old_sun_data->type;
+ new_sun_data->is_boot = old_sun_data->is_boot;
+ new_sun_data->is_root = old_sun_data->is_root;
+ new_sun_data->is_lvm = old_sun_data->is_lvm;
+ return new_part;
+}
+
+static void
+sun_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL, return);
+
+ if (ped_partition_is_active (part))
+ ped_free (part->disk_specific);
+ ped_free (part);
+}
+
+static int
+sun_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
+{
+ SunPartitionData* sun_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ if (sun_data->is_boot) {
+ sun_data->type = 0x1;
+ return 1;
+ }
+ if (sun_data->is_root) {
+ sun_data->type = 0x2;
+ return 1;
+ }
+ if (sun_data->is_lvm) {
+ sun_data->type = 0x8e;
+ return 1;
+ }
+
+ sun_data->type = 0x83;
+ if (fs_type) {
+ if (!strcmp (fs_type->name, "linux-swap"))
+ sun_data->type = 0x82;
+ else if (!strcmp (fs_type->name, "ufs"))
+ sun_data->type = 0x6;
+ }
+
+ return 1;
+}
+
+static int
+sun_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
+{
+ SunPartitionData* sun_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+ PED_ASSERT (ped_partition_is_flag_available (part, flag), return 0);
+
+ sun_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ sun_data->is_boot = state;
+ if (state)
+ sun_data->is_root = sun_data->is_lvm = 0;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_ROOT:
+ sun_data->is_root = state;
+ if (state)
+ sun_data->is_boot = sun_data->is_lvm = 0;
+ return ped_partition_set_system (part, part->fs_type);
+
+ case PED_PARTITION_LVM:
+ sun_data->is_lvm = state;
+ if (state)
+ sun_data->is_root = sun_data->is_boot = 0;
+ return ped_partition_set_system (part, part->fs_type);
+
+ default:
+ return 0;
+ }
+}
+
+
+static int
+sun_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ SunPartitionData* sun_data;
+
+ PED_ASSERT (part != NULL, return 0);
+ PED_ASSERT (part->disk_specific != NULL, return 0);
+
+ sun_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ return sun_data->is_boot;
+ case PED_PARTITION_ROOT:
+ return sun_data->is_root;
+ case PED_PARTITION_LVM:
+ return sun_data->is_lvm;
+
+ default:
+ return 0;
+ }
+}
+
+
+static int
+sun_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ switch (flag) {
+ case PED_PARTITION_BOOT:
+ case PED_PARTITION_ROOT:
+ case PED_PARTITION_LVM:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static int
+sun_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return SUN_DISK_MAXPARTITIONS;
+}
+
+static PedConstraint*
+_get_strict_constraint (PedDisk* disk)
+{
+ PedDevice* dev = disk->dev;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry max_geom;
+ SunDiskData* disk_data = disk->disk_specific;
+ PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads;
+
+ if (!ped_alignment_init (&start_align, 0, block))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, block))
+ return NULL;
+ if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length))
+ return NULL;
+
+ return ped_constraint_new (&start_align, &end_align, &max_geom,
+ &max_geom, 1, dev->length);
+}
+
+static PedConstraint*
+_get_lax_constraint (PedDisk* disk)
+{
+ PedDevice* dev = disk->dev;
+ PedAlignment start_align;
+ PedGeometry max_geom;
+ SunDiskData* disk_data = disk->disk_specific;
+ PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads;
+
+ if (!ped_alignment_init (&start_align, 0, block))
+ return NULL;
+ if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length))
+ return NULL;
+
+ return ped_constraint_new (&start_align, ped_alignment_any, &max_geom,
+ &max_geom, 1, dev->length);
+}
+
+/* _get_strict_constraint() will align the partition to the end of the cylinder.
+ * This isn't required, but since partitions must start at the start of the
+ * cylinder, space between the end of a partition and the end of a cylinder
+ * is unusable, so there's no point wasting space!
+ * However, if they really insist (via constraint)... which they will
+ * if they're reading a weird table of the disk... then we allow the end to
+ * be anywhere, with _get_lax_constraint()
+ */
+static int
+sun_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PED_ASSERT (part != NULL, return 0);
+
+ if (_ped_partition_attempt_align (part, constraint,
+ _get_strict_constraint (part->disk)))
+ return 1;
+ if (_ped_partition_attempt_align (part, constraint,
+ _get_lax_constraint (part->disk)))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+sun_partition_enumerate (PedPartition* part)
+{
+ int i;
+ PedPartition* p;
+
+ /* never change the partition numbers */
+ if (part->num != -1)
+ return 1;
+ for (i = 1; i <= SUN_DISK_MAXPARTITIONS; i++) {
+ /* skip the Whole Disk partition for now */
+ if (i == WHOLE_DISK_PART + 1)
+ continue;
+ p = ped_disk_get_partition (part->disk, i);
+ if (!p) {
+ part->num = i;
+ return 1;
+ }
+ }
+
+#ifndef DISCOVER_ONLY
+ /* Ok, now allocate the Whole disk if it isn't already */
+ p = ped_disk_get_partition (part->disk, WHOLE_DISK_PART + 1);
+ if (!p) {
+ int j = ped_exception_throw (
+ PED_EXCEPTION_WARNING,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("The Whole Disk partition is the only "
+ "available one left. Generally, it is not a "
+ "good idea to overwrite this partition with "
+ "a real one. Solaris may not be able to "
+ "boot without it, and SILO (the sparc boot "
+ "loader) appreciates it as well."));
+ if (j == PED_EXCEPTION_IGNORE) {
+ /* bad bad bad...you will suffer your own fate */
+ part->num = WHOLE_DISK_PART + 1;
+ return 1;
+ }
+ }
+
+ /* failed to allocate a number, this means we are full */
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Sun disk label is full."));
+#endif
+ return 0;
+}
+
+static int
+sun_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* new_part;
+ SunDiskData* disk_data;
+ PedConstraint* constraint_any;
+
+ PED_ASSERT (disk != NULL, return 0);
+ PED_ASSERT (disk->disk_specific != NULL, return 0);
+ PED_ASSERT (disk->dev != NULL, return 0);
+
+ constraint_any = ped_constraint_any (disk->dev);
+
+ /* Sun disk label does not need to allocate a sector. The disk
+ label is contained within the first 512 bytes, which should not
+ be overwritten by any boot loader or superblock. It is safe for
+ most partitions to start at sector 0. We do however, allocate
+ the space used by alt-cyl's, since we cannot use those. Put them
+ at the end of the disk. */
+
+ disk_data = disk->disk_specific;
+
+ if (disk->dev->length <= 0 ||
+ disk_data->length <= 0 ||
+ disk->dev->length == disk_data->length)
+ goto error;
+
+ new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL,
+ disk_data->length, disk->dev->length - 1);
+ if (!new_part)
+ goto error;
+
+ if (!ped_disk_add_partition (disk, new_part, constraint_any)) {
+ ped_partition_destroy (new_part);
+ goto error;
+ }
+
+ ped_constraint_destroy (constraint_any);
+ return 1;
+error:
+ ped_constraint_destroy (constraint_any);
+ return 0;
+}
+
+static PedDiskOps sun_disk_ops = {
+ .probe = sun_probe,
+#ifndef DISCOVER_ONLY
+ .clobber = sun_clobber,
+#else
+ .clobber = NULL,
+#endif
+ .alloc = sun_alloc,
+ .duplicate = sun_duplicate,
+ .free = sun_free,
+ .read = sun_read,
+#ifndef DISCOVER_ONLY
+ .write = sun_write,
+#else
+ .write = NULL,
+#endif
+
+ .partition_new = sun_partition_new,
+ .partition_duplicate = sun_partition_duplicate,
+ .partition_destroy = sun_partition_destroy,
+ .partition_set_system = sun_partition_set_system,
+ .partition_set_flag = sun_partition_set_flag,
+ .partition_get_flag = sun_partition_get_flag,
+ .partition_is_flag_available = sun_partition_is_flag_available,
+ .partition_align = sun_partition_align,
+ .partition_enumerate = sun_partition_enumerate,
+ .alloc_metadata = sun_alloc_metadata,
+ .get_max_primary_partition_count =
+ sun_get_max_primary_partition_count,
+
+ .partition_set_name = NULL,
+ .partition_get_name = NULL,
+};
+
+static PedDiskType sun_disk_type = {
+ .next = NULL,
+ .name = "sun",
+ .ops = &sun_disk_ops,
+ .features = 0
+};
+
+void
+ped_disk_sun_init ()
+{
+ PED_ASSERT (sizeof (SunRawLabel) == 512, return);
+ ped_disk_type_register (&sun_disk_type);
+}
+
+void
+ped_disk_sun_done ()
+{
+ ped_disk_type_unregister (&sun_disk_type);
+}
diff --git a/usr/src/lib/libparted/common/libparted/libparted.c b/usr/src/lib/libparted/common/libparted/libparted.c
new file mode 100644
index 0000000000..7b019608d0
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/libparted.c
@@ -0,0 +1,351 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999, 2000, 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include "configmake.h"
+
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#ifdef linux
+# include <parted/linux.h>
+#elif defined(__BEOS__)
+# include <parted/beos.h>
+#elif defined(__sun)
+# include <parted/solaris.h>
+#else
+# include <parted/gnu.h>
+#endif
+
+#if ENABLE_NLS
+# include <locale.h>
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+const PedArchitecture* ped_architecture;
+
+/* ped_malloc() debugging. Stick the address and size of memory blocks that
+ * weren't ped_free()d in here, and an exception will be thrown when it is
+ * allocated. That way, you can find out what, exactly, the allocated thing
+ * is, and where it is created.
+ */
+typedef struct
+{
+ void* pointer;
+ size_t size;
+} pointer_size_type;
+
+/* IMHO, none of the DEBUG-related code below is useful, and the
+ ped_malloc memset code is actually quite harmful: it masked at
+ least two nasty bugs that were fixed in June of 2007. */
+#undef DEBUG
+#ifdef DEBUG
+static pointer_size_type dodgy_malloc_list[] = {
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0},
+ {0, 0}
+};
+
+static int dodgy_memory_active[100];
+#endif /* DEBUG */
+
+int
+ped_set_architecture (const PedArchitecture* arch)
+{
+ PED_ASSERT (ped_device_get_next (NULL) == NULL, return 0);
+
+ ped_architecture = arch;
+ return 1;
+}
+
+extern void ped_disk_aix_init ();
+extern void ped_disk_bsd_init ();
+extern void ped_disk_dvh_init ();
+extern void ped_disk_gpt_init ();
+extern void ped_disk_loop_init ();
+extern void ped_disk_mac_init ();
+extern void ped_disk_msdos_init ();
+extern void ped_disk_pc98_init ();
+extern void ped_disk_sun_init ();
+extern void ped_disk_amiga_init ();
+extern void ped_disk_dasd_init ();
+
+static void
+init_disk_types ()
+{
+ ped_disk_loop_init (); /* must be last in the probe list */
+
+#if defined(__s390__) || defined(__s390x__)
+ ped_disk_dasd_init();
+#endif
+
+ ped_disk_sun_init ();
+#ifdef ENABLE_PC98
+ ped_disk_pc98_init ();
+#endif
+ ped_disk_msdos_init ();
+ ped_disk_mac_init ();
+ ped_disk_gpt_init ();
+ ped_disk_dvh_init ();
+ ped_disk_bsd_init ();
+ ped_disk_amiga_init ();
+ ped_disk_aix_init ();
+}
+
+#ifdef ENABLE_FS
+extern void ped_file_system_amiga_init (void);
+extern void ped_file_system_xfs_init (void);
+extern void ped_file_system_ufs_init (void);
+extern void ped_file_system_reiserfs_init (void);
+extern void ped_file_system_ntfs_init (void);
+extern void ped_file_system_linux_swap_init (void);
+extern void ped_file_system_jfs_init (void);
+extern void ped_file_system_hfs_init (void);
+extern void ped_file_system_fat_init (void);
+extern void ped_file_system_ext2_init (void);
+extern void ped_file_system_solaris_x86_init (void);
+
+static void
+init_file_system_types ()
+{
+ ped_file_system_amiga_init ();
+ ped_file_system_xfs_init ();
+ ped_file_system_ufs_init ();
+ ped_file_system_reiserfs_init ();
+ ped_file_system_ntfs_init ();
+ ped_file_system_linux_swap_init ();
+ ped_file_system_jfs_init ();
+ ped_file_system_hfs_init ();
+ ped_file_system_fat_init ();
+ ped_file_system_ext2_init ();
+ ped_file_system_solaris_x86_init ();
+}
+#endif /* ENABLE_FS */
+
+extern void ped_disk_aix_done ();
+extern void ped_disk_bsd_done ();
+extern void ped_disk_dvh_done ();
+extern void ped_disk_gpt_done ();
+extern void ped_disk_loop_done ();
+extern void ped_disk_mac_done ();
+extern void ped_disk_msdos_done ();
+extern void ped_disk_pc98_done ();
+extern void ped_disk_sun_done ();
+extern void ped_disk_amiga_done ();
+extern void ped_disk_dasd_done ();
+
+static void
+done_disk_types ()
+{
+#if defined(__s390__) || (__s390x__)
+ ped_disk_dasd_done ();
+#endif
+ ped_disk_sun_done ();
+#ifdef ENABLE_PC98
+ ped_disk_pc98_done ();
+#endif
+ ped_disk_msdos_done ();
+ ped_disk_mac_done ();
+ ped_disk_loop_done ();
+ ped_disk_gpt_done ();
+ ped_disk_dvh_done ();
+ ped_disk_bsd_done ();
+ ped_disk_amiga_done ();
+ ped_disk_aix_done ();
+}
+
+static void _init() __attribute__ ((constructor));
+
+static void
+_init()
+{
+#ifdef ENABLE_NLS
+ bindtextdomain (PACKAGE, LOCALEDIR);
+#endif
+
+ init_disk_types ();
+
+#ifdef ENABLE_FS
+ init_file_system_types ();
+#endif
+
+ /* FIXME: a better way of doing this? */
+#ifdef linux
+ ped_set_architecture (&ped_linux_arch);
+#elif defined(__BEOS__)
+ ped_set_architecture (&ped_beos_arch);
+#elif defined (__sun)
+ ped_set_architecture (&ped_solaris_arch);
+#else
+ ped_set_architecture (&ped_gnu_arch);
+#endif
+
+#ifdef DEBUG
+ memset (dodgy_memory_active, 0, sizeof (dodgy_memory_active));
+#endif
+}
+
+#ifdef ENABLE_FS
+extern void ped_file_system_ext2_done (void);
+extern void ped_file_system_fat_done (void);
+extern void ped_file_system_hfs_done (void);
+extern void ped_file_system_jfs_done (void);
+extern void ped_file_system_linux_swap_done (void);
+extern void ped_file_system_ntfs_done (void);
+extern void ped_file_system_reiserfs_done (void);
+extern void ped_file_system_ufs_done (void);
+extern void ped_file_system_xfs_done (void);
+extern void ped_file_system_amiga_done (void);
+
+static void
+done_file_system_types ()
+{
+ ped_file_system_ext2_done ();
+ ped_file_system_fat_done ();
+ ped_file_system_hfs_done ();
+ ped_file_system_jfs_done ();
+ ped_file_system_linux_swap_done ();
+ ped_file_system_ntfs_done ();
+ ped_file_system_reiserfs_done ();
+ ped_file_system_ufs_done ();
+ ped_file_system_xfs_done ();
+ ped_file_system_amiga_done ();
+}
+#endif /* ENABLE_FS */
+
+static void _done() __attribute__ ((destructor));
+
+static void
+_done()
+{
+ ped_device_free_all ();
+
+ done_disk_types ();
+
+#ifdef ENABLE_FS
+ done_file_system_types ();
+#endif
+}
+
+const char*
+ped_get_version ()
+{
+ return VERSION;
+}
+
+#ifdef DEBUG
+static void
+_check_dodgy_pointer (const void* ptr, size_t size, int is_malloc)
+{
+ int i;
+
+ for (i=0; dodgy_malloc_list[i].pointer; i++) {
+ if (dodgy_malloc_list[i].pointer != ptr)
+ continue;
+ if (is_malloc && dodgy_malloc_list[i].size != size)
+ continue;
+ if (!is_malloc && !dodgy_memory_active[i])
+ continue;
+
+
+ if (is_malloc) {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ "Dodgy malloc(%x) == %p occurred (active==%d)",
+ size, ptr, dodgy_memory_active[i]);
+ dodgy_memory_active[i]++;
+ } else {
+ ped_exception_throw (
+ PED_EXCEPTION_INFORMATION,
+ PED_EXCEPTION_OK,
+ "Dodgy free(%p) occurred (active==%d)",
+ ptr, dodgy_memory_active[i]);
+ dodgy_memory_active[i]--;
+ }
+
+ return;
+ }
+}
+#endif /* DEBUG */
+
+void*
+ped_malloc (size_t size)
+{
+ void* mem;
+
+ mem = (void*) malloc (size);
+ if (!mem) {
+ ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL,
+ _("Out of memory."));
+ return NULL;
+ }
+
+#ifdef DEBUG
+ memset (mem, 0xff, size);
+ _check_dodgy_pointer (mem, size, 1);
+#endif
+
+ return mem;
+}
+
+int
+ped_realloc (void** old, size_t size)
+{
+ void* mem;
+
+ mem = (void*) realloc (*old, size);
+ if (!mem) {
+ ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL,
+ _("Out of memory."));
+ return 0;
+ }
+ *old = mem;
+ return 1;
+}
+
+
+void* ped_calloc (size_t size)
+{
+ void* buf = ped_malloc (size);
+
+ memset (buf, 0, size);
+
+ return buf;
+}
+
+
+void
+ped_free (void* ptr)
+{
+#ifdef DEBUG
+ _check_dodgy_pointer (ptr, 0, 0);
+#endif
+
+ free (ptr);
+}
diff --git a/usr/src/lib/libparted/common/libparted/timer.c b/usr/src/lib/libparted/common/libparted/timer.c
new file mode 100644
index 0000000000..3e962ae84d
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/timer.c
@@ -0,0 +1,245 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2001, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file timer.c */
+
+/**
+ * \addtogroup PedTimer
+ *
+ * \brief A PedTimer keeps track of the progress of a single (possibly
+ * compound) operation.
+ *
+ * The user of libparted constructs a PedTimer, and passes it to libparted
+ * functions that are likely to be expensive operations
+ * (like ped_file_system_resize). Use of timers is optional... you may
+ * pass NULL instead.
+ *
+ * When you create a PedTimer, you must specify a timer handler function.
+ * This will be called when there's an update on how work is progressing.
+ *
+ * Timers may be nested. When a timer is constructed, you can choose
+ * to assign it a parent, along with an estimate of what proportion of
+ * the total (parent's) time will be used in the nested operation. In
+ * this case, the nested timer's handler is internal to libparted,
+ * and simply updates the parent's progress, and calls its handler.
+ *
+ * @{
+ */
+
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#define PED_TIMER_START_DELAY 2
+
+typedef struct {
+ PedTimer* parent;
+ float nest_frac;
+ float start_frac;
+} NestedContext;
+
+
+/**
+ * \brief Creates a timer.
+ *
+ * Context will be passed in the \p context
+ * argument to the \p handler, when it is invoked.
+ *
+ * \return a new PedTimer
+ */
+PedTimer*
+ped_timer_new (PedTimerHandler* handler, void* context)
+{
+ PedTimer* timer;
+
+ PED_ASSERT (handler != NULL, return NULL);
+
+ timer = (PedTimer*) ped_malloc (sizeof (PedTimer));
+ if (!timer)
+ return NULL;
+
+ timer->handler = handler;
+ timer->context = context;
+ ped_timer_reset (timer);
+ return timer;
+}
+
+
+/**
+ * \brief Destroys a \p timer.
+ */
+void
+ped_timer_destroy (PedTimer* timer)
+{
+ if (!timer)
+ return;
+
+ ped_free (timer);
+}
+
+/* This function is used by ped_timer_new_nested() as the timer->handler
+ * function.
+ */
+static void
+_nest_handler (PedTimer* timer, void* context)
+{
+ NestedContext* ncontext = (NestedContext*) context;
+
+ ped_timer_update (
+ ncontext->parent,
+ ncontext->start_frac + ncontext->nest_frac * timer->frac);
+}
+
+
+/**
+ * \brief Creates a new nested timer.
+ *
+ * This function creates a "nested" timer that describes the progress
+ * of a subtask. \p parent is the parent timer, and \p nested_frac is
+ * the estimated proportion (between 0 and 1) of the time that will be
+ * spent doing the nested timer's operation. The timer should only be
+ * constructed immediately prior to starting the nested operation.
+ * (It will be inaccurate, otherwise).
+ * Updates to the progress of the subtask are propagated
+ * back through to the parent task's timer.
+ *
+ * \return nested timer
+ */
+PedTimer*
+ped_timer_new_nested (PedTimer* parent, float nest_frac)
+{
+ NestedContext* context;
+
+ if (!parent)
+ return NULL;
+
+ PED_ASSERT (nest_frac >= 0.0, return NULL);
+ PED_ASSERT (nest_frac <= 1.0, return NULL);
+
+ context = (NestedContext*) ped_malloc (sizeof (NestedContext));
+ if (!context)
+ return NULL;
+ context->parent = parent;
+ context->nest_frac = nest_frac;
+ context->start_frac = parent->frac;
+
+ return ped_timer_new (_nest_handler, context);
+}
+
+/**
+ * \brief Destroys a nested \p timer.
+ */
+void
+ped_timer_destroy_nested (PedTimer* timer)
+{
+ if (!timer)
+ return;
+
+ ped_free (timer->context);
+ ped_timer_destroy (timer);
+}
+
+/**
+ * \internal
+ *
+ * \brief This function calls the update handler, making sure that it has
+ * the latest time.
+ *
+ * First it updates \p timer->now and recomputes \p timer->predicted_end,
+ * and then calls the handler.
+ */
+void
+ped_timer_touch (PedTimer* timer)
+{
+ if (!timer)
+ return;
+
+ timer->now = time (NULL);
+ if (timer->now > timer->predicted_end)
+ timer->predicted_end = timer->now;
+
+ timer->handler (timer, timer->context);
+}
+
+/**
+ * \internal
+ *
+ * \brief This function sets the \p timer into a "start of task" position.
+ *
+ * It resets the \p timer, by setting \p timer->start and \p timer->now
+ * to the current time.
+ */
+void
+ped_timer_reset (PedTimer* timer)
+{
+ if (!timer)
+ return;
+
+ timer->start = timer->now = timer->predicted_end = time (NULL);
+ timer->state_name = NULL;
+ timer->frac = 0;
+
+ ped_timer_touch (timer);
+}
+
+/**
+ * \internal
+ *
+ * \brief This function tells a \p timer what fraction \p frac of the task
+ * has been completed.
+ *
+ * Sets the new \p timer->frac, and calls ped_timer_touch().
+ */
+void
+ped_timer_update (PedTimer* timer, float frac)
+{
+ if (!timer)
+ return;
+
+ timer->now = time (NULL);
+ timer->frac = frac;
+
+ if (frac)
+ timer->predicted_end
+ = timer->start
+ + (long) ((timer->now - timer->start) / frac);
+
+ ped_timer_touch (timer);
+}
+
+/**
+ * \internal
+ *
+ * \brief This function changes the description of the current task that the
+ * \p timer describes.
+ *
+ * Sets a new name - \p state_name - for the current "phase" of the operation,
+ * and calls ped_timer_touch().
+ */
+void
+ped_timer_set_state_name (PedTimer* timer, const char* state_name)
+{
+ if (!timer)
+ return;
+
+ timer->state_name = state_name;
+ ped_timer_touch (timer);
+}
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/libparted/unit.c b/usr/src/lib/libparted/common/libparted/unit.c
new file mode 100644
index 0000000000..29c4ed50ce
--- /dev/null
+++ b/usr/src/lib/libparted/common/libparted/unit.c
@@ -0,0 +1,565 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 2005, 2007 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \file unit.c */
+
+/**
+ * \addtogroup PedUnit
+ *
+ * \brief The PedUnit module provides a standard mechanism for describing
+ * and parsing locations within devices in human-friendly plain text.
+ *
+ * Internally, libparted uses PedSector (which is typedef'ed to be long long
+ * in <parted/device.h>) to describe device locations such as the start and
+ * end of partitions. However, sector numbers are often long and unintuitive.
+ * For example, my extended partition starts at sector 208845. PedUnit allows
+ * this location to be represented in more intutitive ways, including "106Mb",
+ * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities
+ * to provide a consistent system for describing device locations all
+ * throughout libparted.
+ *
+ * PedUnit provides two basic services: converting a PedSector into a text
+ * representation, and parsing a text representation into a PedSector.
+ * PedUnit currently supports these units:
+ *
+ * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact,
+ * cylinder and percent.
+ *
+ * PedUnit has a global variable that contains the default unit for all
+ * conversions.
+ *
+ * @{
+ */
+
+
+
+
+#include <config.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <float.h>
+
+#define N_(String) String
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+
+static PedUnit default_unit = PED_UNIT_COMPACT;
+static const char* unit_names[] = {
+ "s",
+ "B",
+ "kB",
+ "MB",
+ "GB",
+ "TB",
+ "compact",
+ "cyl",
+ "chs",
+ "%",
+ "kiB",
+ "MiB",
+ "GiB",
+ "TiB"
+};
+
+
+/**
+ * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
+ *
+ * In particular, this affects how locations inside error messages
+ * (exceptions) are displayed.
+ */
+void
+ped_unit_set_default (PedUnit unit)
+{
+ default_unit = unit;
+}
+
+
+/**
+ * \brief Get the current default unit.
+ */
+PedUnit
+ped_unit_get_default ()
+{
+ return default_unit;
+}
+
+/**
+ * Get the byte size of a given \p unit.
+ */
+long long
+ped_unit_get_size (const PedDevice* dev, PedUnit unit)
+{
+ PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
+
+ switch (unit) {
+ case PED_UNIT_SECTOR: return dev->sector_size;
+ case PED_UNIT_BYTE: return 1;
+ case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE;
+ case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE;
+ case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE;
+ case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE;
+ case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE;
+ case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE;
+ case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE;
+ case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE;
+ case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size;
+ case PED_UNIT_CHS: return dev->sector_size;
+
+ case PED_UNIT_PERCENT:
+ return dev->length * dev->sector_size / 100;
+
+ case PED_UNIT_COMPACT:
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Cannot get unit size for special unit "
+ "'COMPACT'."));
+ return 0;
+ }
+
+ /* never reached */
+ PED_ASSERT(0, return 0);
+ return 0;
+}
+
+/**
+ * Get a textual (non-internationalized) representation of a \p unit.
+ *
+ * For example, the textual representation of PED_UNIT_SECTOR is "s".
+ */
+const char*
+ped_unit_get_name (PedUnit unit)
+{
+ return unit_names[unit];
+}
+
+/**
+ * Get a unit based on its textual representation: \p unit_name.
+ *
+ * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
+ */
+PedUnit
+ped_unit_get_by_name (const char* unit_name)
+{
+ PedUnit unit;
+ for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
+ if (!strcasecmp (unit_names[unit], unit_name))
+ return unit;
+ }
+ return -1;
+}
+
+static char*
+ped_strdup (const char *str)
+{
+ char *result;
+ result = ped_malloc (strlen (str) + 1);
+ if (!result)
+ return NULL;
+ strcpy (result, str);
+ return result;
+}
+
+/**
+ * \brief Get a string that describes the location of the \p byte on
+ * device \p dev.
+ *
+ * The string is described with the desired \p unit.
+ * The returned string must be freed with ped_free().
+ */
+char*
+ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
+{
+ char buf[100];
+ PedSector sector = byte / dev->sector_size;
+ double d, w;
+ int p;
+
+ PED_ASSERT (dev != NULL, return NULL);
+
+ /* CHS has a special comma-separated format. */
+ if (unit == PED_UNIT_CHS) {
+ const PedCHSGeometry *chs = &dev->bios_geom;
+ snprintf (buf, 100, "%lld,%lld,%lld",
+ sector / chs->sectors / chs->heads,
+ (sector / chs->sectors) % chs->heads,
+ sector % chs->sectors);
+ return ped_strdup (buf);
+ }
+
+ /* Cylinders, sectors and bytes should be rounded down... */
+ if (unit == PED_UNIT_CYLINDER
+ || unit == PED_UNIT_SECTOR
+ || unit == PED_UNIT_BYTE) {
+ snprintf (buf, 100, "%lld%s",
+ byte / ped_unit_get_size (dev, unit),
+ ped_unit_get_name (unit));
+ return ped_strdup (buf);
+ }
+
+ if (unit == PED_UNIT_COMPACT) {
+ if (byte >= 10LL * PED_TERABYTE_SIZE)
+ unit = PED_UNIT_TERABYTE;
+ else if (byte >= 10LL * PED_GIGABYTE_SIZE)
+ unit = PED_UNIT_GIGABYTE;
+ else if (byte >= 10LL * PED_MEGABYTE_SIZE)
+ unit = PED_UNIT_MEGABYTE;
+ else if (byte >= 10LL * PED_KILOBYTE_SIZE)
+ unit = PED_UNIT_KILOBYTE;
+ else
+ unit = PED_UNIT_BYTE;
+ }
+
+ /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */
+ /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */
+ /* This just divide by 2 the natural IEEE754 extended precision */
+ /* and won't cause any trouble before 1000 TB */
+ d = ((double)byte / (double)ped_unit_get_size (dev, unit))
+ * (1. + DBL_EPSILON);
+ w = d + ( (d < 10. ) ? 0.005 :
+ (d < 100.) ? 0.05 :
+ 0.5 );
+ p = (w < 10. ) ? 2 :
+ (w < 100.) ? 1 :
+ 0 ;
+
+#ifdef __BEOS__
+ snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
+#else
+ snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
+#endif
+
+ return ped_strdup (buf);
+}
+
+/**
+ * \brief Get a string that describes the location of the \p byte on
+ * device \p dev.
+ *
+ * The string is described with the default unit, which is set
+ * by ped_unit_set_default().
+ * The returned string must be freed with ped_free().
+ */
+char*
+ped_unit_format_byte (const PedDevice* dev, PedSector byte)
+{
+ PED_ASSERT (dev != NULL, return NULL);
+ return ped_unit_format_custom_byte (dev, byte, default_unit);
+}
+
+/**
+ * \brief Get a string that describes the location \p sector on device \p dev.
+ *
+ * The string is described with the desired \p unit.
+ * The returned string must be freed with ped_free().
+ */
+char*
+ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
+{
+ PED_ASSERT (dev != NULL, return NULL);
+ return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
+}
+
+/**
+ * \brief Get a string that describes the location \p sector on device \p dev.
+ *
+ * The string is described with the default unit, which is set
+ * by ped_unit_set_default().
+ * The returned string must be freed with ped_free().
+ */
+char*
+ped_unit_format (const PedDevice* dev, PedSector sector)
+{
+ PED_ASSERT (dev != NULL, return NULL);
+ return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
+ default_unit);
+}
+
+/**
+ * If \p str contains a valid description of a location on \p dev,
+ * then \p *sector is modified to describe the location and a geometry
+ * is created in \p *range describing a 2 units large area centered on
+ * \p *sector. If the \p range as described here would be partially outside
+ * the device \p dev, the geometry returned is the intersection between the
+ * former and the whole device geometry. If no units are specified, then the
+ * default unit is assumed.
+ *
+ * \return \c 1 if \p str is a valid location description, \c 0 otherwise
+ */
+int
+ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
+ PedGeometry** range)
+{
+ return ped_unit_parse_custom (str, dev, default_unit, sector, range);
+}
+
+/* Inefficiently removes all spaces from a string, in-place. */
+static void
+strip_string (char* str)
+{
+ int i;
+
+ for (i = 0; str[i] != 0; i++) {
+ if (isspace (str[i])) {
+ int j;
+ for (j = i + 1; str[j] != 0; j++)
+ str[j - 1] = str[j];
+ }
+ }
+}
+
+
+/* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to
+ * "Mb". */
+static char*
+find_suffix (const char* str)
+{
+ while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
+ str++;
+ return (char *) str;
+}
+
+static void
+remove_punct (char* str)
+{
+ int i = 0;
+
+ for (i = 0; str[i]; i++) {
+ if (ispunct (str[i]))
+ str[i] = ' ';
+ }
+}
+
+static int
+is_chs (const char* str)
+{
+ int punct_count = 0;
+ int i = 0;
+
+ for (i = 0; str[i]; i++)
+ punct_count += ispunct (str[i]) != 0;
+ return punct_count == 2;
+}
+
+static int
+parse_chs (const char* str, const PedDevice* dev, PedSector* sector,
+ PedGeometry** range)
+{
+ PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
+ char* copy = ped_strdup (str);
+ PedCHSGeometry chs;
+
+ copy = ped_strdup (str);
+ if (!copy)
+ return 0;
+ strip_string (copy);
+ remove_punct (copy);
+
+ if (sscanf (copy, "%d %d %d",
+ &chs.cylinders, &chs.heads, &chs.sectors) != 3) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("\"%s\" has invalid syntax for locations."),
+ copy);
+ goto error_free_copy;
+ }
+
+ if (chs.heads >= dev->bios_geom.heads) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("The maximum head value is %d."),
+ dev->bios_geom.heads - 1);
+ goto error_free_copy;
+ }
+ if (chs.sectors >= dev->bios_geom.sectors) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("The maximum sector value is %d."),
+ dev->bios_geom.sectors - 1);
+ goto error_free_copy;
+ }
+
+ *sector = 1LL * chs.cylinders * cyl_size
+ + chs.heads * dev->bios_geom.sectors
+ + chs.sectors;
+
+ if (*sector >= dev->length) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("The location %s is outside of the "
+ "device %s."),
+ str, dev->path);
+ goto error_free_copy;
+ }
+ if (range)
+ *range = ped_geometry_new (dev, *sector, 1);
+ ped_free (copy);
+ return !range || *range != NULL;
+
+error_free_copy:
+ ped_free (copy);
+ *sector = 0;
+ if (range)
+ *range = NULL;
+ return 0;
+}
+
+static PedSector
+clip (const PedDevice* dev, PedSector sector)
+{
+ if (sector < 0)
+ return 0;
+ if (sector > dev->length - 1)
+ return dev->length - 1;
+ return sector;
+}
+
+static PedGeometry*
+geometry_from_centre_radius (const PedDevice* dev,
+ PedSector sector, PedSector radius)
+{
+ PedSector start = clip (dev, sector - radius);
+ PedSector end = clip (dev, sector + radius);
+ if (sector - end > radius || start - sector > radius)
+ return NULL;
+ return ped_geometry_new (dev, start, end - start + 1);
+}
+
+static PedUnit
+parse_unit_suffix (const char* suffix, PedUnit suggested_unit)
+{
+ if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') {
+ switch (tolower (suffix[0])) {
+ case 'k': return PED_UNIT_KIBIBYTE;
+ case 'm': return PED_UNIT_MEBIBYTE;
+ case 'g': return PED_UNIT_GIBIBYTE;
+ case 't': return PED_UNIT_TEBIBYTE;
+ }
+ } else if (strlen (suffix) > 0) {
+ switch (tolower (suffix[0])) {
+ case 's': return PED_UNIT_SECTOR;
+ case 'b': return PED_UNIT_BYTE;
+ case 'k': return PED_UNIT_KILOBYTE;
+ case 'm': return PED_UNIT_MEGABYTE;
+ case 'g': return PED_UNIT_GIGABYTE;
+ case 't': return PED_UNIT_TERABYTE;
+ case 'c': return PED_UNIT_CYLINDER;
+ case '%': return PED_UNIT_PERCENT;
+ }
+ }
+
+ if (suggested_unit == PED_UNIT_COMPACT) {
+ if (default_unit == PED_UNIT_COMPACT)
+ return PED_UNIT_MEGABYTE;
+ else
+ return default_unit;
+ }
+
+ return suggested_unit;
+}
+
+/**
+ * If \p str contains a valid description of a location on \p dev, then
+ * \p *sector is modified to describe the location and a geometry is created
+ * in \p *range describing a 2 units large area centered on \p *sector. If the
+ * \p range as described here would be partially outside the device \p dev, the
+ * geometry returned is the intersection between the former and the whole
+ * device geometry. If no units are specified, then the default unit is
+ * assumed.
+ *
+ * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
+ * location
+ * \throws PED_EXCEPTION_ERROR if location described by \p str
+ * is outside of the device \p dev->path
+ *
+ * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
+ */
+int
+ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
+ PedSector* sector, PedGeometry** range)
+{
+ char* copy;
+ char* suffix;
+ double num;
+ long long unit_size;
+ PedSector radius;
+
+ if (is_chs (str))
+ return parse_chs (str, dev, sector, range);
+
+ copy = ped_strdup (str);
+ if (!copy)
+ goto error;
+ strip_string (copy);
+
+ suffix = find_suffix (copy);
+ unit = parse_unit_suffix (suffix, unit);
+ suffix[0] = 0;
+
+ if (sscanf (copy, "%lf", &num) != 1) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Invalid number."));
+ goto error_free_copy;
+ }
+
+ unit_size = ped_unit_get_size (dev, unit);
+ radius = ped_div_round_up (unit_size, dev->sector_size) - 1;
+ if (radius < 0)
+ radius = 0;
+
+ *sector = num * unit_size / dev->sector_size;
+ /* negative numbers count from the end */
+ if (copy[0] == '-')
+ *sector += dev->length;
+ if (range) {
+ *range = geometry_from_centre_radius (dev, *sector, radius);
+ if (!*range) {
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("The location %s is outside of the "
+ "device %s."),
+ str, dev->path);
+ goto error_free_copy;
+ }
+ }
+ *sector = clip (dev, *sector);
+
+ ped_free (copy);
+ return 1;
+
+error_free_copy:
+ ped_free (copy);
+error:
+ *sector = 0;
+ if (range)
+ *range = NULL;
+ return 0;
+}
+
+
+/** @} */
diff --git a/usr/src/lib/libparted/common/mapfile-vers b/usr/src/lib/libparted/common/mapfile-vers
new file mode 100644
index 0000000000..de4219c918
--- /dev/null
+++ b/usr/src/lib/libparted/common/mapfile-vers
@@ -0,0 +1,150 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNW_1.1 {
+ global:
+ close_stdout;
+ error;
+ ped_alignment_any;
+ ped_assert;
+ ped_constraint_any;
+ ped_constraint_destroy;
+ ped_constraint_done;
+ ped_constraint_exact;
+ ped_constraint_init;
+ ped_constraint_intersect;
+ ped_constraint_new;
+ ped_debug;
+ ped_device_close;
+ ped_device_free_all;
+ ped_device_get;
+ ped_device_get_constraint;
+ ped_device_get_next;
+ ped_device_is_busy;
+ ped_device_open;
+ ped_device_probe_all;
+ ped_disk_add_partition;
+ ped_disk_check;
+ ped_disk_commit;
+ ped_disk_delete_partition;
+ ped_disk_destroy;
+ ped_disk_extended_partition;
+ ped_disk_get_max_primary_partition_count;
+ ped_disk_get_partition;
+ ped_disk_get_partition_by_sector;
+ ped_disk_new;
+ ped_disk_new_fresh;
+ ped_disk_next_partition;
+ ped_disk_probe;
+ ped_disk_remove_partition;
+ ped_disk_set_partition_geom;
+ ped_disk_type_check_feature;
+ ped_disk_type_get;
+ ped_disk_type_get_next;
+ ped_exception_catch;
+ ped_exception_fetch_all;
+ ped_exception_get_option_string;
+ ped_exception_get_type_string;
+ ped_exception_leave_all;
+ ped_exception_set_handler;
+ ped_exception_throw;
+ ped_file_system_check;
+ ped_file_system_close;
+ ped_file_system_copy;
+ ped_file_system_create;
+ ped_file_system_get_copy_constraint;
+ ped_file_system_get_resize_constraint;
+ ped_file_system_open;
+ ped_file_system_probe;
+ ped_file_system_probe_specific;
+ ped_file_system_resize;
+ ped_file_system_type_get;
+ ped_file_system_type_get_next;
+ ped_free;
+ ped_geometry_destroy;
+ ped_geometry_init;
+ ped_geometry_new;
+ ped_geometry_set;
+ ped_geometry_test_inside;
+ ped_geometry_test_overlap;
+ ped_geometry_test_sector_inside;
+ ped_malloc;
+ ped_partition_destroy;
+ ped_partition_flag_get_by_name;
+ ped_partition_flag_get_name;
+ ped_partition_flag_next;
+ ped_partition_get_flag;
+ ped_partition_get_name;
+ ped_partition_get_path;
+ ped_partition_is_active;
+ ped_partition_is_busy;
+ ped_partition_is_flag_available;
+ ped_partition_new;
+ ped_partition_set_flag;
+ ped_partition_set_name;
+ ped_partition_set_system;
+ ped_partition_type_get_name;
+ ped_realloc;
+ ped_timer_destroy;
+ ped_timer_new;
+ ped_timer_reset;
+ ped_timer_set_state_name;
+ ped_timer_update;
+ ped_unit_format;
+ ped_unit_format_byte;
+ ped_unit_format_custom;
+ ped_unit_get_by_name;
+ ped_unit_get_default;
+ ped_unit_get_name;
+ ped_unit_parse;
+ ped_unit_set_default;
+ program_name = PARENT;
+ version_etc;
+ xalloc_die;
+ xmalloc;
+ xrealloc;
+ xstrdup;
+};
+
+SUNWprivate_1.1 {
+ global:
+ SUNWprivate_1.1;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libparted/i386/Makefile b/usr/src/lib/libparted/i386/Makefile
new file mode 100644
index 0000000000..77b0c2e552
--- /dev/null
+++ b/usr/src/lib/libparted/i386/Makefile
@@ -0,0 +1,52 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+SUBDIRS= pics/lib \
+ pics/libparted \
+ pics/libparted/arch \
+ pics/libparted/cs \
+ pics/libparted/fs/amiga \
+ pics/libparted/fs/ext2 \
+ pics/libparted/fs/fat \
+ pics/libparted/fs/hfs \
+ pics/libparted/fs/jfs \
+ pics/libparted/fs/linux_swap \
+ pics/libparted/fs/ntfs \
+ pics/libparted/fs/reiserfs \
+ pics/libparted/fs/solaris_x86 \
+ pics/libparted/fs/ufs \
+ pics/libparted/fs/xfs \
+ pics/libparted/labels
+
+.KEEP_STATE:
+
+all: $(SUBDIRS) $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(DYNLINKLIB)
+
+$(SUBDIRS):
+ mkdir -p $@