summaryrefslogtreecommitdiff
authorXindong Xu <xindong.xu@amlogic.com>2017-05-24 06:18:47 (GMT)
committer Xindong Xu <xindong.xu@amlogic.com>2017-05-25 00:58:39 (GMT)
commite83a818003617a36bf1abe3ce862077e4fc79503 (patch)
treedd3385499d09999041522cd70c483e1aff3dccd8
parent8d295b2453309f548a7844bf4af0cdf987048ba8 (diff)
parent889425812b5cda8b3394d73253cbde7355fb1115 (diff)
downloadbusybox-e83a818003617a36bf1abe3ce862077e4fc79503.zip
busybox-e83a818003617a36bf1abe3ce862077e4fc79503.tar.gz
busybox-e83a818003617a36bf1abe3ce862077e4fc79503.tar.bz2
merge with busybox 1.26.2
Change-Id: I07e5a570500246f79180975fa02e2e084e1435b4
Diffstat
-rw-r--r--AUTHORS4
-rw-r--r--Config.in338
-rw-r--r--Makefile51
-rw-r--r--Makefile.custom8
-rw-r--r--Makefile.flags30
-rw-r--r--Makefile.help4
-rw-r--r--README2
-rw-r--r--TODO2
-rw-r--r--android/librpc/libintl.h0
-rw-r--r--applets/Kbuild.src18
-rw-r--r--applets/applet_tables.c135
-rwxr-xr-xapplets/install.sh26
-rwxr-xr-xapplets/usage_compressed9
-rw-r--r--applets/usage_pod.c2
-rw-r--r--archival/Config.src4
-rw-r--r--archival/Kbuild.src2
-rw-r--r--archival/ar.c16
-rw-r--r--archival/bbunzip.c165
-rw-r--r--archival/bzip2.c2
-rw-r--r--archival/cpio.c58
-rw-r--r--archival/dpkg.c21
-rw-r--r--archival/dpkg_deb.c96
-rw-r--r--archival/gzip.c139
-rw-r--r--archival/libarchive/Kbuild.src17
-rw-r--r--archival/libarchive/bz/compress.c1
-rw-r--r--archival/libarchive/common.c9
-rw-r--r--archival/libarchive/data_extract_all.c118
-rw-r--r--archival/libarchive/data_extract_to_command.c5
-rw-r--r--archival/libarchive/decompress_bunzip2.c13
-rw-r--r--archival/libarchive/decompress_gunzip.c82
-rw-r--r--archival/libarchive/decompress_uncompress.c13
-rw-r--r--archival/libarchive/decompress_unlzma.c35
-rw-r--r--archival/libarchive/decompress_unxz.c8
-rw-r--r--archival/libarchive/filter_accept_list_reassign.c10
-rw-r--r--archival/libarchive/get_header_cpio.c9
-rw-r--r--archival/libarchive/get_header_tar.c122
-rw-r--r--archival/libarchive/get_header_tar_bz2.c2
-rw-r--r--archival/libarchive/get_header_tar_gz.c2
-rw-r--r--archival/libarchive/get_header_tar_lzma.c2
-rw-r--r--archival/libarchive/get_header_tar_xz.c21
-rw-r--r--archival/libarchive/init_handle.c4
-rw-r--r--archival/libarchive/lzo1x_9x.c1
-rw-r--r--archival/libarchive/open_transformer.c230
-rw-r--r--archival/libarchive/unpack_ar_archive.c2
-rw-r--r--archival/libarchive/unsafe_prefix.c36
-rw-r--r--archival/libarchive/unxz/xz_dec_lzma2.c3
-rw-r--r--archival/libarchive/unxz/xz_dec_stream.c2
-rw-r--r--archival/lzop.c55
-rw-r--r--archival/rpm.c5
-rw-r--r--archival/tar.c248
-rw-r--r--archival/unzip.c191
-rw-r--r--busybox-full.config484
-rw-r--r--busybox-full.sources22
-rw-r--r--busybox-minimal.sources4
-rw-r--r--configs/TEST_nommu_defconfig4
-rw-r--r--configs/TEST_noprintf_defconfig4
-rw-r--r--configs/TEST_rh9_defconfig4
-rw-r--r--configs/android2_defconfig5
-rw-r--r--configs/android_502_defconfig1140
-rw-r--r--configs/android_defconfig5
-rw-r--r--configs/android_ndk_defconfig188
-rw-r--r--configs/cygwin_defconfig5
-rw-r--r--configs/freebsd_defconfig4
-rw-r--r--console-tools/Config.src166
-rw-r--r--console-tools/Kbuild.src16
-rw-r--r--console-tools/chvt.c11
-rw-r--r--console-tools/clear.c9
-rw-r--r--console-tools/deallocvt.c10
-rw-r--r--console-tools/dumpkmap.c14
-rw-r--r--console-tools/fgconsole.c10
-rw-r--r--console-tools/kbd_mode.c10
-rw-r--r--console-tools/loadfont.c57
-rw-r--r--console-tools/loadkmap.c18
-rw-r--r--console-tools/openvt.c11
-rw-r--r--console-tools/reset.c13
-rw-r--r--console-tools/resize.c27
-rw-r--r--console-tools/setconsole.c18
-rw-r--r--console-tools/setkeycodes.c11
-rw-r--r--console-tools/setlogcons.c14
-rw-r--r--console-tools/showkey.c11
-rw-r--r--coreutils/Config.src746
-rw-r--r--coreutils/Kbuild.src86
-rw-r--r--coreutils/basename.c15
-rw-r--r--coreutils/cal.c18
-rw-r--r--coreutils/cat.c16
-rw-r--r--coreutils/catv.c32
-rw-r--r--coreutils/chgrp.c9
-rw-r--r--coreutils/chmod.c13
-rw-r--r--coreutils/chown.c27
-rw-r--r--coreutils/chroot.c10
-rw-r--r--coreutils/cksum.c13
-rw-r--r--coreutils/comm.c10
-rw-r--r--coreutils/cp.c53
-rw-r--r--coreutils/cut.c10
-rw-r--r--coreutils/date.c18
-rw-r--r--coreutils/dd.c232
-rw-r--r--coreutils/df.c104
-rw-r--r--coreutils/dirname.c10
-rw-r--r--coreutils/dos2unix.c52
-rw-r--r--coreutils/du.c74
-rw-r--r--coreutils/echo.c26
-rw-r--r--coreutils/env.c29
-rw-r--r--coreutils/expand.c31
-rw-r--r--coreutils/expr.c34
-rw-r--r--coreutils/false.c17
-rw-r--r--coreutils/fold.c9
-rw-r--r--coreutils/fsync.c9
-rw-r--r--coreutils/head.c19
-rw-r--r--coreutils/hostid.c5
-rw-r--r--coreutils/id.c11
-rw-r--r--coreutils/install.c72
-rw-r--r--coreutils/length.c.disabled31
-rw-r--r--coreutils/libcoreutils/getopt_mk_fifo_nod.c4
-rw-r--r--coreutils/ln.c10
-rw-r--r--coreutils/logname.c16
-rw-r--r--coreutils/ls.c107
-rw-r--r--coreutils/md5_sha1_sum.c145
-rw-r--r--coreutils/mkdir.c36
-rw-r--r--coreutils/mkfifo.c10
-rw-r--r--coreutils/mknod.c10
-rw-r--r--coreutils/mv.c29
-rw-r--r--coreutils/nice.c9
-rw-r--r--coreutils/nohup.c9
-rw-r--r--coreutils/od.c9
-rw-r--r--coreutils/od_bloaty.c249
-rw-r--r--coreutils/printenv.c9
-rw-r--r--coreutils/printf.c23
-rw-r--r--coreutils/pwd.c11
-rw-r--r--coreutils/readlink.c17
-rw-r--r--coreutils/realpath.c15
-rw-r--r--coreutils/rm.c19
-rw-r--r--coreutils/rmdir.c27
-rw-r--r--coreutils/seq.c9
-rw-r--r--coreutils/shuf.c153
-rw-r--r--coreutils/sleep.c44
-rw-r--r--coreutils/sort.c126
-rw-r--r--coreutils/split.c26
-rw-r--r--coreutils/stat.c91
-rw-r--r--coreutils/stty.c21
-rw-r--r--coreutils/sum.c16
-rw-r--r--coreutils/sync.c26
-rw-r--r--coreutils/tac.c14
-rw-r--r--coreutils/tail.c46
-rw-r--r--coreutils/tee.c21
-rw-r--r--coreutils/test.c116
-rw-r--r--coreutils/touch.c16
-rw-r--r--coreutils/tr.c30
-rw-r--r--coreutils/true.c17
-rw-r--r--coreutils/truncate.c87
-rw-r--r--coreutils/tty.c10
-rw-r--r--coreutils/uname.c39
-rw-r--r--coreutils/uniq.c14
-rw-r--r--coreutils/unlink.c33
-rw-r--r--coreutils/usleep.c9
-rw-r--r--coreutils/uudecode.c28
-rw-r--r--coreutils/uuencode.c9
-rw-r--r--coreutils/wc.c25
-rw-r--r--coreutils/who.c18
-rw-r--r--coreutils/whoami.c12
-rw-r--r--coreutils/yes.c17
-rw-r--r--debianutils/Config.src75
-rw-r--r--debianutils/Kbuild.src5
-rw-r--r--[-rwxr-xr-x]debianutils/mktemp.c10
-rw-r--r--debianutils/pipe_progress.c9
-rw-r--r--debianutils/run_parts.c45
-rw-r--r--debianutils/start_stop_daemon.c40
-rw-r--r--debianutils/which.c95
-rw-r--r--docs/busybox_footer.pod2
-rw-r--r--docs/cgi/env.html2
-rw-r--r--docs/ifupdown_design.txt2
-rw-r--r--docs/keep_data_small.txt14
-rw-r--r--docs/logging_and_backgrounding.txt2
-rw-r--r--docs/new-applet-HOWTO.txt104
-rw-r--r--docs/posix_conformance.txt1
-rw-r--r--docs/unit-tests.txt50
-rw-r--r--e2fsprogs/Config.src28
-rw-r--r--e2fsprogs/Kbuild.src6
-rw-r--r--e2fsprogs/chattr.c26
-rw-r--r--e2fsprogs/e2fs_lib.c4
-rw-r--r--e2fsprogs/fsck.c318
-rw-r--r--e2fsprogs/lsattr.c20
-rw-r--r--e2fsprogs/old_e2fsprogs/Config.src69
-rw-r--r--e2fsprogs/old_e2fsprogs/Kbuild.src18
-rw-r--r--e2fsprogs/old_e2fsprogs/README3
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/Kbuild.src26
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/blkid.h104
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/blkidP.h182
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c179
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/cache.c125
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/dev.c213
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/devname.c367
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/devno.c222
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/list.c110
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/list.h73
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/probe.c726
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/probe.h374
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/read.c459
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/resolve.c139
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/save.c189
-rw-r--r--e2fsprogs/old_e2fsprogs/blkid/tag.c431
-rw-r--r--e2fsprogs/old_e2fsprogs/chattr.c220
-rw-r--r--e2fsprogs/old_e2fsprogs/e2fsbb.h43
-rw-r--r--e2fsprogs/old_e2fsprogs/e2fsck.c13516
-rw-r--r--e2fsprogs/old_e2fsprogs/e2fsck.h638
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/Kbuild.src18
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/e2p.h64
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/feature.c187
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c70
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c70
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/hashstr.c70
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/iod.c52
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/ls.c273
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/mntopts.c134
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/ostype.c72
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/parse_num.c65
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/pe.c32
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/pf.c74
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/ps.c27
-rw-r--r--e2fsprogs/old_e2fsprogs/e2p/uuid.c78
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src26
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/alloc.c173
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c58
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c53
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c114
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c328
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c64
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c262
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c211
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bitops.c90
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bitops.h105
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/block.c437
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bmap.c261
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/bmove.c155
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/brel.h86
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c196
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c69
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/closefs.c380
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c72
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dblist.c260
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c76
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c219
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c132
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c234
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c95
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/e2image.h39
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c127
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h116
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h52
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h569
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h112
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h2
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h922
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h87
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c365
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c101
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/fileio.c377
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/finddev.c199
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/flushb.c83
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/freefs.c127
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c49
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c156
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c58
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/getsize.c291
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/icount.c467
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/imager.c377
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c69
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/initialize.c388
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/inline.c32
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/inode.c766
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c270
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c70
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/irel.h115
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c367
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c357
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h63
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h235
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h113
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/link.c135
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/lookup.c68
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c139
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c426
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/namei.c204
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/newdir.c72
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/openfs.c330
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c96
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c96
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c220
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c106
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c294
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/sparse.c79
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c234
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/test_io.c380
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c703
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/unlink.c99
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c57
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/version.c51
-rw-r--r--e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c35
-rw-r--r--e2fsprogs/old_e2fsprogs/fsck.c1375
-rw-r--r--e2fsprogs/old_e2fsprogs/fsck.h16
-rw-r--r--e2fsprogs/old_e2fsprogs/lsattr.c129
-rw-r--r--e2fsprogs/old_e2fsprogs/mke2fs.c1333
-rw-r--r--e2fsprogs/old_e2fsprogs/tune2fs.c710
-rw-r--r--e2fsprogs/old_e2fsprogs/util.c263
-rw-r--r--e2fsprogs/old_e2fsprogs/util.h22
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/Kbuild.src16
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/compare.c55
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c304
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/pack.c69
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/parse.c80
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/unpack.c63
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/unparse.c77
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/uuid.h103
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/uuidP.h60
-rw-r--r--e2fsprogs/old_e2fsprogs/uuid/uuid_time.c161
-rw-r--r--e2fsprogs/tune2fs.c45
-rw-r--r--editors/awk.c139
-rw-r--r--editors/diff.c19
-rw-r--r--editors/ed.c9
-rw-r--r--editors/patch.c45
-rw-r--r--editors/patch_toybox.c7
-rw-r--r--editors/sed.c179
-rw-r--r--editors/vi.c929
-rwxr-xr-xexamples/android-build2
-rw-r--r--examples/mdev.conf2
-rw-r--r--examples/mdev_fat.conf12
-rwxr-xr-xexamples/udhcp/sample.bound18
-rwxr-xr-xexamples/udhcp/sample.renew18
-rwxr-xr-xexamples/undeb74
-rwxr-xr-xexamples/unrpm65
-rw-r--r--examples/var_service/README233
-rw-r--r--examples/var_service/README_distro_proposal.txt291
-rw-r--r--examples/var_service/dhcp_if/README5
-rwxr-xr-xexamples/var_service/dhcp_if/convert2ntpconf2
-rwxr-xr-xexamples/var_service/dhcp_if/dhcp_handler13
-rwxr-xr-xexamples/var_service/dhcp_if/finish17
-rwxr-xr-xexamples/var_service/dhcp_if/log/run2
-rw-r--r--examples/var_service/dhcp_if_pinger/README5
-rwxr-xr-xexamples/var_service/dhcp_if_pinger/run44
-rw-r--r--examples/var_service/dhcpd_if/README5
-rwxr-xr-xexamples/var_service/dhcpd_if/log/run21
-rwxr-xr-xexamples/var_service/dhcpd_if/p_log4
-rwxr-xr-xexamples/var_service/dhcpd_if/run23
-rw-r--r--examples/var_service/dhcpd_if/udhcpc.conf28
-rwxr-xr-xexamples/var_service/dhcpd_if/w_dumpleases3
-rwxr-xr-xexamples/var_service/dhcpd_if/w_dumpleases_countdown3
-rwxr-xr-xexamples/var_service/dhcpd_if/w_log4
-rw-r--r--examples/var_service/ftpd/README5
-rwxr-xr-xexamples/var_service/ftpd/log/run2
-rw-r--r--examples/var_service/fw/README5
-rwxr-xr-xexamples/var_service/fw/run50
-rw-r--r--examples/var_service/getty_tty1/README5
-rw-r--r--examples/var_service/gpm/README5
-rw-r--r--examples/var_service/httpd/README5
-rwxr-xr-xexamples/var_service/httpd/log/run2
-rw-r--r--examples/var_service/ifplugd_if/README5
-rwxr-xr-xexamples/var_service/ifplugd_if/log/run2
-rwxr-xr-xexamples/var_service/ifplugd_if/run3
-rw-r--r--examples/var_service/inetd/README5
-rwxr-xr-xexamples/var_service/inetd/log/run2
-rw-r--r--examples/var_service/nmeter/README5
-rw-r--r--examples/var_service/ntpd/README5
-rwxr-xr-xexamples/var_service/ntpd/log/run2
-rwxr-xr-xexamples/var_service/ntpd/ntp.script21
-rwxr-xr-xexamples/var_service/ntpd/p_log_important4
-rwxr-xr-xexamples/var_service/ntpd/run2
-rw-r--r--examples/var_service/supplicant_if/README5
-rwxr-xr-xexamples/var_service/supplicant_if/log/run21
-rwxr-xr-xexamples/var_service/supplicant_if/p_log4
-rwxr-xr-xexamples/var_service/supplicant_if/run21
-rwxr-xr-xexamples/var_service/supplicant_if/w_log4
-rw-r--r--examples/var_service/supplicant_if/wpa_supplicant.conf28
-rw-r--r--examples/var_service/tftpd/README5
-rwxr-xr-xexamples/var_service/tftpd/log/run2
-rw-r--r--examples/var_service/zcip_if/README5
-rwxr-xr-xexamples/var_service/zcip_if/convert2ipconf24
-rwxr-xr-xexamples/var_service/zcip_if/finish13
-rwxr-xr-xexamples/var_service/zcip_if/log/run21
-rwxr-xr-xexamples/var_service/zcip_if/p_log4
-rwxr-xr-xexamples/var_service/zcip_if/run20
-rwxr-xr-xexamples/var_service/zcip_if/w_log4
-rwxr-xr-xexamples/var_service/zcip_if/zcip_handler47
-rw-r--r--findutils/find.c263
-rw-r--r--findutils/grep.c130
-rw-r--r--findutils/xargs.c190
-rwxr-xr-xinclude/applets.h.sh23
-rw-r--r--include/applets.src.h368
-rw-r--r--include/bb_archive.h61
-rw-r--r--include/busybox.h19
-rw-r--r--include/grp_.h65
-rw-r--r--include/libbb.h419
-rw-r--r--include/platform.h104
-rw-r--r--include/pwd_.h53
-rw-r--r--include/shadow_.h26
-rw-r--r--init/bootchartd.c7
-rw-r--r--init/halt.c50
-rw-r--r--init/init.c144
-rw-r--r--libbb/Config.src174
-rw-r--r--libbb/Kbuild.src16
-rw-r--r--libbb/appletlib.c327
-rw-r--r--libbb/auto_string.c23
-rw-r--r--libbb/bb_askpass.c11
-rw-r--r--libbb/bb_pwd.c48
-rw-r--r--libbb/bbunit.c65
-rw-r--r--libbb/change_identity.c23
-rw-r--r--libbb/common_bufsiz.c83
-rw-r--r--libbb/compare_string_array.c82
-rw-r--r--libbb/copy_file.c41
-rw-r--r--libbb/copyfd.c87
-rw-r--r--libbb/correct_password.c96
-rw-r--r--libbb/dump.c51
-rw-r--r--libbb/execable.c86
-rw-r--r--libbb/executable.c101
-rw-r--r--libbb/fflush_stdout_and_exit.c15
-rw-r--r--libbb/getopt32.c82
-rw-r--r--libbb/getpty.c2
-rw-r--r--libbb/hash_md5_sha.c386
-rw-r--r--libbb/human_readable.c20
-rw-r--r--libbb/in_ether.c1
-rw-r--r--libbb/inet_common.c84
-rw-r--r--libbb/info_msg.c62
-rw-r--r--libbb/inode_hash.c38
-rw-r--r--libbb/kernel_version.c8
-rw-r--r--libbb/lineedit.c256
-rw-r--r--libbb/logenv.c24
-rw-r--r--libbb/login.c40
-rw-r--r--libbb/loop.c29
-rw-r--r--libbb/make_directory.c19
-rw-r--r--libbb/match_fstype.c7
-rw-r--r--libbb/messages.c9
-rw-r--r--libbb/missing_syscalls.c7
-rw-r--r--libbb/mode_string.c4
-rw-r--r--libbb/obscure.c40
-rw-r--r--libbb/parse_config.c4
-rw-r--r--libbb/parse_mode.c16
-rw-r--r--libbb/platform.c37
-rw-r--r--libbb/printable_string.c10
-rw-r--r--libbb/procps.c28
-rw-r--r--libbb/progress.c20
-rw-r--r--libbb/pw_encrypt.c21
-rw-r--r--libbb/read_key.c12
-rw-r--r--libbb/read_printf.c8
-rw-r--r--libbb/remove_file.c8
-rw-r--r--libbb/replace.c45
-rw-r--r--libbb/rtc.c54
-rw-r--r--libbb/run_shell.c29
-rw-r--r--libbb/skip_whitespace.c2
-rw-r--r--libbb/speed_table.c84
-rw-r--r--libbb/strrstr.c19
-rw-r--r--libbb/sysconf.c30
-rw-r--r--libbb/systemd_support.c62
-rw-r--r--libbb/time.c1
-rw-r--r--libbb/u_signal_names.c2
-rw-r--r--libbb/ubi.c43
-rw-r--r--libbb/udp_io.c14
-rw-r--r--libbb/update_passwd.c60
-rw-r--r--libbb/utmp.c58
-rw-r--r--libbb/verror_msg.c34
-rw-r--r--libbb/vfork_daemon_rexec.c47
-rw-r--r--libbb/xatonum.c40
-rw-r--r--libbb/xconnect.c34
-rw-r--r--libbb/xfunc_die.c26
-rw-r--r--libbb/xfuncs.c76
-rw-r--r--libbb/xfuncs_printf.c39
-rw-r--r--libbb/xreadlink.c11
-rw-r--r--libpwdgrp/pwd_grp.c1277
-rw-r--r--libpwdgrp/pwd_grp_internal.c61
-rw-r--r--libpwdgrp/uidgid_get.c44
-rw-r--r--loginutils/Config.src237
-rw-r--r--loginutils/Kbuild.src12
-rw-r--r--loginutils/README2
-rw-r--r--loginutils/add-remove-shell.c17
-rw-r--r--loginutils/addgroup.c44
-rw-r--r--loginutils/adduser.c84
-rw-r--r--loginutils/chpasswd.c51
-rw-r--r--loginutils/cryptpw.c46
-rw-r--r--loginutils/deluser.c75
-rw-r--r--loginutils/getty.c60
-rw-r--r--loginutils/login.c111
-rw-r--r--loginutils/passwd.c26
-rw-r--r--loginutils/su.c65
-rw-r--r--loginutils/sulogin.c46
-rw-r--r--loginutils/vlock.c16
-rw-r--r--mailutils/Config.src43
-rw-r--r--mailutils/mail.c2
-rw-r--r--mailutils/makemime.c10
-rw-r--r--mailutils/popmaildir.c22
-rw-r--r--mailutils/reformime.c20
-rw-r--r--mailutils/sendmail.c138
-rwxr-xr-xmake_single_applets.sh73
-rw-r--r--miscutils/Config.src584
-rw-r--r--miscutils/Kbuild.src39
-rw-r--r--miscutils/adjtimex.c42
-rw-r--r--miscutils/bbconfig.c24
-rw-r--r--miscutils/beep.c28
-rw-r--r--miscutils/chat.c74
-rw-r--r--miscutils/chrt.c10
-rw-r--r--miscutils/conspy.c20
-rw-r--r--miscutils/crond.c346
-rw-r--r--miscutils/crontab.c14
-rw-r--r--miscutils/dc.c95
-rw-r--r--miscutils/devfsd.c91
-rw-r--r--miscutils/devmem.c10
-rw-r--r--miscutils/eject.c46
-rw-r--r--miscutils/fbsplash.c34
-rw-r--r--miscutils/flash_eraseall.c12
-rw-r--r--miscutils/flash_lock_unlock.c19
-rw-r--r--miscutils/flashcp.c12
-rw-r--r--miscutils/hdparm.c131
-rw-r--r--miscutils/i2c_tools.c1343
-rw-r--r--miscutils/inotifyd.c20
-rw-r--r--miscutils/ionice.c16
-rw-r--r--miscutils/last.c39
-rw-r--r--miscutils/last_fancy.c25
-rw-r--r--miscutils/less.c482
-rw-r--r--miscutils/makedevs.c44
-rw-r--r--miscutils/man.c213
-rw-r--r--miscutils/microcom.c18
-rw-r--r--miscutils/mountpoint.c9
-rw-r--r--miscutils/mt.c11
-rw-r--r--miscutils/nandwrite.c74
-rw-r--r--miscutils/raidautorun.c11
-rw-r--r--miscutils/readahead.c21
-rw-r--r--miscutils/runlevel.c25
-rw-r--r--miscutils/rx.c12
-rw-r--r--miscutils/setserial.c8
-rw-r--r--miscutils/setsid.c28
-rw-r--r--miscutils/strings.c42
-rw-r--r--miscutils/taskset.c127
-rw-r--r--miscutils/time.c12
-rw-r--r--miscutils/timeout.c13
-rw-r--r--miscutils/ttysize.c12
-rw-r--r--miscutils/ubi_tools.c68
-rw-r--r--miscutils/ubirename.c94
-rw-r--r--miscutils/volname.c9
-rw-r--r--miscutils/wall.c8
-rw-r--r--miscutils/watchdog.c15
-rw-r--r--modutils/Config.src93
-rw-r--r--modutils/Kbuild.src7
-rw-r--r--modutils/depmod.c136
-rw-r--r--modutils/insmod.c14
-rw-r--r--modutils/lsmod.c22
-rw-r--r--modutils/modinfo.c160
-rw-r--r--modutils/modprobe-small.c480
-rw-r--r--modutils/modprobe.c210
-rw-r--r--modutils/modutils-24.c4
-rw-r--r--modutils/modutils.c101
-rw-r--r--modutils/modutils.h30
-rw-r--r--modutils/rmmod.c24
-rw-r--r--networking/Config.src941
-rw-r--r--networking/Kbuild.src39
-rw-r--r--networking/arp.c21
-rw-r--r--networking/arping.c62
-rw-r--r--networking/brctl.c94
-rw-r--r--networking/dnsd.c11
-rw-r--r--networking/ether-wake.c10
-rw-r--r--networking/ftpd.c227
-rw-r--r--networking/ftpgetput.c35
-rw-r--r--networking/hostname.c26
-rw-r--r--networking/httpd.c238
-rw-r--r--networking/ifconfig.c51
-rw-r--r--networking/ifenslave.c19
-rw-r--r--networking/ifplugd.c61
-rw-r--r--networking/ifupdown.c239
-rw-r--r--networking/inetd.c85
-rw-r--r--networking/interface.c33
-rw-r--r--networking/ip.c192
-rw-r--r--networking/ipcalc.c28
-rw-r--r--networking/isrv.c4
-rw-r--r--networking/isrv.h26
-rw-r--r--networking/isrv_identd.c52
-rw-r--r--networking/libiproute/Kbuild.src11
-rw-r--r--networking/libiproute/ip_common.h2
-rw-r--r--networking/libiproute/ip_parse_common_args.c2
-rw-r--r--networking/libiproute/ipaddress.c74
-rw-r--r--networking/libiproute/iplink.c196
-rw-r--r--networking/libiproute/ipneigh.c357
-rw-r--r--networking/libiproute/iproute.c172
-rw-r--r--networking/libiproute/iprule.c61
-rw-r--r--networking/libiproute/iptunnel.c37
-rw-r--r--networking/libiproute/libnetlink.c87
-rw-r--r--networking/libiproute/ll_map.c39
-rw-r--r--networking/libiproute/ll_map.h2
-rw-r--r--networking/libiproute/ll_proto.c4
-rw-r--r--networking/libiproute/ll_types.c4
-rw-r--r--networking/libiproute/rt_names.c93
-rw-r--r--networking/libiproute/rt_names.h11
-rw-r--r--networking/libiproute/rtm_map.c6
-rw-r--r--networking/libiproute/rtm_map.h2
-rw-r--r--networking/libiproute/utils.c48
-rw-r--r--networking/libiproute/utils.h12
-rw-r--r--networking/nameif.c12
-rw-r--r--networking/nbd-client.c7
-rw-r--r--networking/nc.c14
-rw-r--r--networking/nc_bloaty.c8
-rw-r--r--networking/netstat.c34
-rw-r--r--networking/nslookup.c9
-rw-r--r--networking/ntpd.c593
-rw-r--r--networking/ntpd.diff24
-rw-r--r--networking/ntpd_simple.c1008
-rw-r--r--networking/ping.c110
-rw-r--r--networking/pscan.c9
-rw-r--r--networking/route.c64
-rw-r--r--networking/slattach.c22
-rwxr-xr-xnetworking/ssl_helper-wolfssl/00cfg-wolfssl-3.6.822
-rwxr-xr-xnetworking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.839
-rw-r--r--networking/ssl_helper-wolfssl/README27
-rw-r--r--networking/ssl_helper-wolfssl/ssl_helper.c480
-rwxr-xr-xnetworking/ssl_helper-wolfssl/ssl_helper.sh12
-rw-r--r--networking/ssl_helper/README16
-rw-r--r--networking/ssl_helper/ssl_helper.c406
-rwxr-xr-xnetworking/ssl_helper/ssl_helper.sh11
-rw-r--r--networking/tc.c59
-rw-r--r--networking/tcpudp.c102
-rw-r--r--networking/telnet.c46
-rw-r--r--networking/telnetd.IAC_test.sh87
-rw-r--r--networking/telnetd.c389
-rw-r--r--networking/telnetd.ctrlSQ.patch4
-rw-r--r--networking/tftp.c110
-rw-r--r--networking/traceroute.c287
-rw-r--r--networking/tunctl.c20
-rw-r--r--networking/udhcp/Config.src13
-rw-r--r--networking/udhcp/Kbuild.src4
-rw-r--r--networking/udhcp/arpping.c13
-rw-r--r--networking/udhcp/common.c40
-rw-r--r--networking/udhcp/common.h11
-rw-r--r--networking/udhcp/d6_dhcpc.c85
-rw-r--r--networking/udhcp/d6_packet.c12
-rw-r--r--networking/udhcp/d6_socket.c2
-rw-r--r--networking/udhcp/dhcpc.c298
-rw-r--r--networking/udhcp/dhcpd.c569
-rw-r--r--networking/udhcp/dhcpd.h35
-rw-r--r--networking/udhcp/dhcprelay.c8
-rw-r--r--networking/udhcp/domain_codec.c21
-rw-r--r--networking/udhcp/dumpleases.c31
-rw-r--r--networking/udhcp/files.c216
-rw-r--r--networking/udhcp/leases.c203
-rw-r--r--networking/udhcp/packet.c12
-rw-r--r--networking/udhcp/socket.c4
-rw-r--r--networking/udhcp/static_leases.c77
-rw-r--r--networking/vconfig.c11
-rw-r--r--networking/wget.c578
-rw-r--r--networking/whois.c147
-rw-r--r--networking/zcip.c536
-rw-r--r--printutils/Config.src18
-rw-r--r--printutils/Kbuild.src4
-rw-r--r--printutils/lpd.c19
-rw-r--r--printutils/lpr.c17
-rw-r--r--procps/Config.src140
-rw-r--r--procps/Kbuild.src17
-rw-r--r--procps/free.c94
-rw-r--r--procps/fuser.c15
-rw-r--r--procps/iostat.c9
-rw-r--r--procps/kill.c48
-rw-r--r--procps/mpstat.c18
-rw-r--r--procps/nmeter.c208
-rw-r--r--procps/pgrep.c24
-rw-r--r--procps/pidof.c29
-rw-r--r--procps/pmap.c4
-rw-r--r--procps/powertop.c16
-rw-r--r--procps/ps.c78
-rw-r--r--procps/pstree.c2
-rw-r--r--procps/pwdx.c4
-rw-r--r--procps/renice.c23
-rw-r--r--procps/sysctl.c12
-rw-r--r--procps/top.c268
-rw-r--r--procps/uptime.c15
-rw-r--r--procps/watch.c17
-rw-r--r--qemu_multiarch_testing/README63
-rwxr-xr-xqemu_multiarch_testing/extract_od_binary.sh6
-rwxr-xr-xqemu_multiarch_testing/hdc.dir/build50
-rwxr-xr-xqemu_multiarch_testing/hdc.dir/init9
-rwxr-xr-xqemu_multiarch_testing/make-hdc-img.sh30
-rwxr-xr-xqemu_multiarch_testing/parallel-build-hdc-img.sh40
-rw-r--r--runit/Config.src79
-rw-r--r--runit/Kbuild.src11
-rw-r--r--runit/chpst.c52
-rw-r--r--runit/runsv.c35
-rw-r--r--runit/runsvdir.c98
-rw-r--r--runit/sv.c141
-rw-r--r--runit/svlogd.c24
-rw-r--r--scripts/Makefile.build3
-rw-r--r--scripts/basic/docproc.c4
-rw-r--r--scripts/basic/fixdep.c2
-rwxr-xr-xscripts/gen_build_files.sh40
-rwxr-xr-xscripts/generate_BUFSIZ.sh137
-rw-r--r--scripts/kconfig/confdata.c3
-rwxr-xr-x[-rw-r--r--]scripts/kconfig/lxdialog/check-lxdialog.sh17
-rw-r--r--scripts/kconfig/util.c3
-rw-r--r--scripts/kconfig/zconf.hash.c_shipped14
-rwxr-xr-xscripts/randomtest26
-rwxr-xr-xscripts/randomtest.loop1
-rwxr-xr-xscripts/trylink81
-rw-r--r--selinux/Config.src113
-rw-r--r--selinux/Kbuild.src12
-rw-r--r--selinux/chcon.c20
-rw-r--r--selinux/getenforce.c10
-rw-r--r--selinux/getsebool.c10
-rw-r--r--selinux/load_policy.c10
-rw-r--r--selinux/matchpathcon.c11
-rw-r--r--selinux/runcon.c27
-rw-r--r--selinux/selinuxenabled.c11
-rw-r--r--selinux/sestatus.c10
-rw-r--r--selinux/setenforce.c10
-rw-r--r--selinux/setfiles.c49
-rw-r--r--selinux/setsebool.c11
-rw-r--r--shell/Config.src45
-rw-r--r--shell/Kbuild.src2
-rw-r--r--shell/ash.c88
-rw-r--r--shell/ash_test/.gitignore7
-rw-r--r--shell/ash_test/ash-glob/glob1.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob1.tests2
-rw-r--r--shell/ash_test/ash-glob/glob2.right18
-rwxr-xr-xshell/ash_test/ash-glob/glob2.tests27
-rw-r--r--shell/ash_test/ash-glob/glob3.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob3.tests2
-rw-r--r--shell/ash_test/ash-glob/glob_and_assign.right6
-rwxr-xr-xshell/ash_test/ash-glob/glob_and_assign.tests10
-rw-r--r--shell/ash_test/ash-glob/glob_dir.right19
-rwxr-xr-xshell/ash_test/ash-glob/glob_dir.tests25
-rw-r--r--shell/ash_test/ash-glob/glob_redir.right2
-rwxr-xr-xshell/ash_test/ash-glob/glob_redir.tests9
-rw-r--r--shell/ash_test/ash-heredoc/heredoc1.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc1.tests3
-rw-r--r--shell/ash_test/ash-heredoc/heredoc2.right2
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc2.tests7
-rw-r--r--shell/ash_test/ash-heredoc/heredoc3.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc3.tests9
-rw-r--r--shell/ash_test/ash-heredoc/heredoc4.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc4.tests3
-rw-r--r--shell/ash_test/ash-heredoc/heredoc5.right (copied from shell/hush_test/hush-misc/heredoc2.right)0
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc5.tests (copied from shell/hush_test/hush-misc/heredoc2.tests)0
-rw-r--r--shell/ash_test/ash-heredoc/heredoc6.right2
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc6.tests4
-rw-r--r--shell/ash_test/ash-heredoc/heredoc7.right1
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc7.tests3
-rw-r--r--shell/ash_test/ash-heredoc/heredoc_huge.right (copied from shell/hush_test/hush-misc/heredoc_huge.right)0
-rwxr-xr-xshell/ash_test/ash-heredoc/heredoc_huge.tests (copied from shell/hush_test/hush-misc/heredoc_huge.tests)0
-rw-r--r--shell/ash_test/ash-misc/and-or.right18
-rwxr-xr-xshell/ash_test/ash-misc/and-or.tests34
-rw-r--r--shell/ash_test/ash-misc/assignment1.right9
-rwxr-xr-xshell/ash_test/ash-misc/assignment1.tests42
-rw-r--r--shell/ash_test/ash-misc/assignment3.right2
-rwxr-xr-xshell/ash_test/ash-misc/assignment3.tests5
-rw-r--r--shell/ash_test/ash-misc/assignment4.right1
-rwxr-xr-xshell/ash_test/ash-misc/assignment4.tests3
-rw-r--r--shell/ash_test/ash-misc/break1.right2
-rwxr-xr-xshell/ash_test/ash-misc/break1.tests2
-rw-r--r--shell/ash_test/ash-misc/break2.right3
-rwxr-xr-xshell/ash_test/ash-misc/break2.tests6
-rw-r--r--shell/ash_test/ash-misc/break3.right2
-rwxr-xr-xshell/ash_test/ash-misc/break3.tests2
-rw-r--r--shell/ash_test/ash-misc/break4.right6
-rwxr-xr-xshell/ash_test/ash-misc/break4.tests12
-rw-r--r--shell/ash_test/ash-misc/break5.right13
-rwxr-xr-xshell/ash_test/ash-misc/break5.tests4
-rw-r--r--shell/ash_test/ash-misc/builtin1.right2
-rwxr-xr-xshell/ash_test/ash-misc/builtin1.tests6
-rw-r--r--shell/ash_test/ash-misc/case1.right22
-rwxr-xr-xshell/ash_test/ash-misc/case1.tests40
-rw-r--r--shell/ash_test/ash-misc/colon.right2
-rwxr-xr-xshell/ash_test/ash-misc/colon.tests5
-rw-r--r--shell/ash_test/ash-misc/command.right1
-rwxr-xr-xshell/ash_test/ash-misc/command.tests1
-rw-r--r--shell/ash_test/ash-misc/command2.right2
-rwxr-xr-xshell/ash_test/ash-misc/command2.tests6
-rw-r--r--shell/ash_test/ash-misc/compound.right14
-rwxr-xr-xshell/ash_test/ash-misc/compound.tests21
-rw-r--r--shell/ash_test/ash-misc/continue1.right8
-rwxr-xr-xshell/ash_test/ash-misc/continue1.tests4
-rw-r--r--shell/ash_test/ash-misc/continue2.right1
-rwxr-xr-xshell/ash_test/ash-misc/continue2.tests3
-rw-r--r--shell/ash_test/ash-misc/continue3.right2
-rwxr-xr-xshell/ash_test/ash-misc/continue3.tests3
-rwxr-xr-x[-rw-r--r--]shell/ash_test/ash-misc/echo_write_error.tests0
-rw-r--r--shell/ash_test/ash-misc/empty_for.right1
-rwxr-xr-xshell/ash_test/ash-misc/empty_for.tests3
-rw-r--r--shell/ash_test/ash-misc/empty_for2.right4
-rwxr-xr-xshell/ash_test/ash-misc/empty_for2.tests6
-rw-r--r--shell/ash_test/ash-misc/errexit1.right (copied from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/ash_test/ash-misc/errexit1.tests5
-rw-r--r--shell/ash_test/ash-misc/eval1.right1
-rwxr-xr-xshell/ash_test/ash-misc/eval1.tests4
-rw-r--r--shell/ash_test/ash-misc/eval2.right3
-rwxr-xr-xshell/ash_test/ash-misc/eval2.tests4
-rw-r--r--shell/ash_test/ash-misc/exec.right2
-rwxr-xr-xshell/ash_test/ash-misc/exec.tests3
-rw-r--r--shell/ash_test/ash-misc/exit1.right1
-rwxr-xr-xshell/ash_test/ash-misc/exit1.tests4
-rw-r--r--shell/ash_test/ash-misc/exitcode1.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode1.tests2
-rw-r--r--shell/ash_test/ash-misc/exitcode2.right4
-rwxr-xr-xshell/ash_test/ash-misc/exitcode2.tests12
-rw-r--r--shell/ash_test/ash-misc/exitcode_EACCES.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_EACCES.tests (copied from shell/msh_test/msh-execution/exitcode_EACCES.tests)0
-rw-r--r--shell/ash_test/ash-misc/exitcode_ENOENT.right2
-rwxr-xr-xshell/ash_test/ash-misc/exitcode_ENOENT.tests (copied from shell/msh_test/msh-execution/exitcode_ENOENT.tests)0
-rw-r--r--shell/ash_test/ash-misc/for.right (renamed from shell/msh_test/msh-parsing/argv0.right)0
-rwxr-xr-xshell/ash_test/ash-misc/for.tests5
-rw-r--r--shell/ash_test/ash-misc/for_with_bslashes.right8
-rwxr-xr-xshell/ash_test/ash-misc/for_with_bslashes.tests10
-rw-r--r--shell/ash_test/ash-misc/for_with_keywords.right4
-rwxr-xr-xshell/ash_test/ash-misc/for_with_keywords.tests2
-rw-r--r--shell/ash_test/ash-misc/func1.right6
-rwxr-xr-xshell/ash_test/ash-misc/func1.tests16
-rw-r--r--shell/ash_test/ash-misc/func2.right5
-rwxr-xr-xshell/ash_test/ash-misc/func2.tests9
-rw-r--r--shell/ash_test/ash-misc/func3.right4
-rwxr-xr-xshell/ash_test/ash-misc/func3.tests8
-rw-r--r--shell/ash_test/ash-misc/func4.right2
-rwxr-xr-xshell/ash_test/ash-misc/func4.tests7
-rw-r--r--shell/ash_test/ash-misc/func5.right6
-rwxr-xr-xshell/ash_test/ash-misc/func5.tests13
-rw-r--r--shell/ash_test/ash-misc/func6.right2
-rwxr-xr-xshell/ash_test/ash-misc/func6.tests11
-rw-r--r--shell/ash_test/ash-misc/func_args1.right5
-rwxr-xr-xshell/ash_test/ash-misc/func_args1.tests8
-rw-r--r--shell/ash_test/ash-misc/func_bash1.right12
-rwxr-xr-xshell/ash_test/ash-misc/func_bash1.tests28
-rw-r--r--shell/ash_test/ash-misc/func_local1.right3
-rwxr-xr-xshell/ash_test/ash-misc/func_local1.tests5
-rw-r--r--shell/ash_test/ash-misc/func_local2.right14
-rwxr-xr-xshell/ash_test/ash-misc/func_local2.tests7
-rw-r--r--shell/ash_test/ash-misc/group_in_braces.right5
-rwxr-xr-xshell/ash_test/ash-misc/group_in_braces.tests11
-rw-r--r--shell/ash_test/ash-misc/if_false_exitcode.right1
-rwxr-xr-xshell/ash_test/ash-misc/if_false_exitcode.tests2
-rw-r--r--shell/ash_test/ash-misc/local1.right4
-rwxr-xr-xshell/ash_test/ash-misc/local1.tests11
-rw-r--r--shell/ash_test/ash-misc/local2.right1
-rwxr-xr-xshell/ash_test/ash-misc/local2.tests1
-rw-r--r--shell/ash_test/ash-misc/nommu1.right7
-rwxr-xr-xshell/ash_test/ash-misc/nommu1.tests12
-rw-r--r--shell/ash_test/ash-misc/nommu2.right5
-rwxr-xr-xshell/ash_test/ash-misc/nommu2.tests5
-rw-r--r--shell/ash_test/ash-misc/nommu3.right2
-rwxr-xr-xshell/ash_test/ash-misc/nommu3.tests15
-rw-r--r--shell/ash_test/ash-misc/opts1.right2
-rwxr-xr-xshell/ash_test/ash-misc/opts1.tests5
-rw-r--r--shell/ash_test/ash-misc/pid.right1
-rwxr-xr-xshell/ash_test/ash-misc/pid.tests1
-rw-r--r--shell/ash_test/ash-misc/pipefail.right40
-rwxr-xr-xshell/ash_test/ash-misc/pipefail.tests45
-rw-r--r--shell/ash_test/ash-misc/read.right (renamed from shell/msh_test/msh-bugs/read.right)0
-rwxr-xr-xshell/ash_test/ash-misc/read.tests (renamed from shell/msh_test/msh-bugs/read.tests)0
-rw-r--r--shell/ash_test/ash-misc/return1.right1
-rwxr-xr-xshell/ash_test/ash-misc/return1.tests4
-rw-r--r--shell/ash_test/ash-misc/shift.right (renamed from shell/msh_test/msh-bugs/shift.right)0
-rwxr-xr-xshell/ash_test/ash-misc/shift.tests (renamed from shell/msh_test/msh-bugs/shift.tests)0
-rw-r--r--shell/ash_test/ash-misc/sigint1.right1
-rwxr-xr-xshell/ash_test/ash-misc/sigint1.tests41
-rw-r--r--shell/ash_test/ash-misc/source3.right2
-rwxr-xr-xshell/ash_test/ash-misc/source3.tests6
-rw-r--r--shell/ash_test/ash-misc/source5.right4
-rwxr-xr-xshell/ash_test/ash-misc/source5.tests8
-rw-r--r--shell/ash_test/ash-misc/tickquote1.right1
-rwxr-xr-xshell/ash_test/ash-misc/tickquote1.tests1
-rw-r--r--shell/ash_test/ash-misc/unicode1.right3
-rwxr-xr-xshell/ash_test/ash-misc/unicode1.tests13
-rw-r--r--shell/ash_test/ash-misc/until1.right3
-rwxr-xr-xshell/ash_test/ash-misc/until1.tests11
-rw-r--r--shell/ash_test/ash-misc/wait4.right1
-rwxr-xr-xshell/ash_test/ash-misc/wait4.tests2
-rw-r--r--shell/ash_test/ash-misc/wait5.right2
-rwxr-xr-xshell/ash_test/ash-misc/wait5.tests5
-rw-r--r--shell/ash_test/ash-misc/while1.right1
-rwxr-xr-xshell/ash_test/ash-misc/while1.tests2
-rw-r--r--shell/ash_test/ash-misc/while2.right2
-rwxr-xr-xshell/ash_test/ash-misc/while2.tests2
-rw-r--r--shell/ash_test/ash-misc/while4.right1
-rwxr-xr-xshell/ash_test/ash-misc/while4.tests6
-rw-r--r--shell/ash_test/ash-misc/while_in_subshell.right1
-rwxr-xr-xshell/ash_test/ash-misc/while_in_subshell.tests2
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests2
-rw-r--r--shell/ash_test/ash-quoting/dollar_squote_bash1.right1
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_squote_bash1.tests1
-rw-r--r--shell/ash_test/ash-quoting/dollar_squote_bash2.right6
-rwxr-xr-xshell/ash_test/ash-quoting/dollar_squote_bash2.tests10
-rwxr-xr-xshell/ash_test/ash-read/read_r.tests6
-rw-r--r--shell/ash_test/ash-redir/redir1.right (copied from shell/hush_test/hush-misc/redir1.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir1.tests (copied from shell/hush_test/hush-misc/redir1.tests)0
-rwxr-xr-xshell/ash_test/ash-redir/redir7.tests6
-rwxr-xr-xshell/ash_test/ash-redir/redir8.tests8
-rw-r--r--shell/ash_test/ash-redir/redir_escapednum.right (copied from shell/hush_test/hush-misc/redir2.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_escapednum.tests (copied from shell/hush_test/hush-misc/redir2.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_expand.right (copied from shell/hush_test/hush-misc/redir4.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_expand.tests (copied from shell/hush_test/hush-misc/redir4.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_leak.right6
-rwxr-xr-xshell/ash_test/ash-redir/redir_leak.tests10
-rw-r--r--shell/ash_test/ash-redir/redir_multi.right (copied from shell/hush_test/hush-misc/redir6.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_multi.tests (copied from shell/hush_test/hush-misc/redir6.tests)0
-rw-r--r--shell/ash_test/ash-redir/redir_script.right1
-rwxr-xr-xshell/ash_test/ash-redir/redir_script.tests29
-rw-r--r--shell/ash_test/ash-redir/redir_space.right (copied from shell/hush_test/hush-parsing/redir_space.right)0
-rwxr-xr-xshell/ash_test/ash-redir/redir_space.tests (copied from shell/hush_test/hush-parsing/redir_space.tests)0
-rw-r--r--shell/ash_test/ash-signals/continue_and_trap1.right1
-rwxr-xr-xshell/ash_test/ash-signals/continue_and_trap1.tests7
-rw-r--r--shell/ash_test/ash-signals/return_in_trap1.right4
-rwxr-xr-xshell/ash_test/ash-signals/return_in_trap1.tests18
-rw-r--r--shell/ash_test/ash-signals/save-ret.right (copied from shell/hush_test/hush-trap/save-ret.right)0
-rwxr-xr-xshell/ash_test/ash-signals/save-ret.tests (copied from shell/hush_test/hush-trap/save-ret.tests)0
-rwxr-xr-xshell/ash_test/ash-signals/signal1.tests2
-rw-r--r--shell/ash_test/ash-signals/sigquit_exec.right2
-rwxr-xr-xshell/ash_test/ash-signals/sigquit_exec.tests4
-rw-r--r--shell/ash_test/ash-vars/empty.right3
-rwxr-xr-xshell/ash_test/ash-vars/empty.tests5
-rw-r--r--shell/ash_test/ash-vars/glob_and_vars.right1
-rwxr-xr-xshell/ash_test/ash-vars/glob_and_vars.tests2
-rw-r--r--shell/ash_test/ash-vars/param_expand_len.right12
-rwxr-xr-xshell/ash_test/ash-vars/param_expand_len.tests24
-rw-r--r--shell/ash_test/ash-vars/param_glob.right4
-rwxr-xr-xshell/ash_test/ash-vars/param_glob.tests9
-rw-r--r--shell/ash_test/ash-vars/param_subshell.right7
-rwxr-xr-xshell/ash_test/ash-vars/param_subshell.tests15
-rw-r--r--shell/ash_test/ash-vars/readonly1.right2
-rwxr-xr-xshell/ash_test/ash-vars/readonly1.tests7
-rw-r--r--shell/ash_test/ash-vars/star.right (renamed from shell/msh_test/msh-vars/star.right)0
-rwxr-xr-xshell/ash_test/ash-vars/star.tests (renamed from shell/msh_test/msh-vars/star.tests)0
-rw-r--r--shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/ash_test/ash-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-runtime-quote-detection.tests1
-rw-r--r--shell/ash_test/ash-vars/var-utf8-length.right1
-rwxr-xr-xshell/ash_test/ash-vars/var-utf8-length.tests2
-rw-r--r--shell/ash_test/ash-vars/var1.right10
-rwxr-xr-xshell/ash_test/ash-vars/var1.tests19
-rw-r--r--shell/ash_test/ash-vars/var2.right3
-rwxr-xr-xshell/ash_test/ash-vars/var2.tests5
-rw-r--r--shell/ash_test/ash-vars/var3.right5
-rwxr-xr-xshell/ash_test/ash-vars/var3.tests1
-rw-r--r--shell/ash_test/ash-vars/var4.right1
-rwxr-xr-xshell/ash_test/ash-vars/var4.tests1
-rw-r--r--shell/ash_test/ash-vars/var5.right6
-rwxr-xr-xshell/ash_test/ash-vars/var5.tests14
-rw-r--r--shell/ash_test/ash-vars/var_bash1a.right6
-rwxr-xr-xshell/ash_test/ash-vars/var_bash1a.tests11
-rw-r--r--shell/ash_test/ash-vars/var_bash4.right25
-rwxr-xr-xshell/ash_test/ash-vars/var_bash4.tests52
-rw-r--r--shell/ash_test/ash-vars/var_bash5.right13
-rwxr-xr-xshell/ash_test/ash-vars/var_bash5.tests24
-rw-r--r--shell/ash_test/ash-vars/var_expand_in_assign.right (renamed from shell/msh_test/msh-bugs/var_expand_in_assign.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_in_assign.tests (renamed from shell/msh_test/msh-bugs/var_expand_in_assign.tests)0
-rw-r--r--shell/ash_test/ash-vars/var_expand_in_redir.right (renamed from shell/msh_test/msh-bugs/var_expand_in_redir.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_in_redir.tests (renamed from shell/msh_test/msh-bugs/var_expand_in_redir.tests)0
-rw-r--r--shell/ash_test/ash-vars/var_expand_on_ifs.right9
-rwxr-xr-xshell/ash_test/ash-vars/var_expand_on_ifs.tests11
-rw-r--r--shell/ash_test/ash-vars/var_in_pipes.right6
-rwxr-xr-xshell/ash_test/ash-vars/var_in_pipes.tests7
-rw-r--r--shell/ash_test/ash-vars/var_leaks.right (copied from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_leaks.tests14
-rw-r--r--shell/ash_test/ash-vars/var_posix1.right30
-rwxr-xr-xshell/ash_test/ash-vars/var_posix1.tests37
-rw-r--r--shell/ash_test/ash-vars/var_serial.right5
-rwxr-xr-xshell/ash_test/ash-vars/var_serial.tests22
-rw-r--r--shell/ash_test/ash-vars/var_subst_in_for.right (renamed from shell/msh_test/msh-vars/var_subst_in_for.right)0
-rwxr-xr-xshell/ash_test/ash-vars/var_subst_in_for.tests40
-rw-r--r--shell/ash_test/ash-vars/var_unbackslash.right11
-rwxr-xr-xshell/ash_test/ash-vars/var_unbackslash.tests23
-rw-r--r--shell/ash_test/ash-vars/var_unbackslash1.right7
-rwxr-xr-xshell/ash_test/ash-vars/var_unbackslash1.tests35
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs1.right41
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs1.tests42
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs2.right3
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs2.tests13
-rw-r--r--shell/ash_test/ash-vars/var_wordsplit_ifs3.right12
-rwxr-xr-xshell/ash_test/ash-vars/var_wordsplit_ifs3.tests5
-rw-r--r--shell/ash_test/printenv.c1
-rwxr-xr-xshell/ash_test/run-all6
-rw-r--r--shell/hush.c2076
-rwxr-xr-xshell/hush_test/hush-glob/bash_brace1.tests2
-rw-r--r--shell/hush_test/hush-glob/glob3.right2
-rwxr-xr-xshell/hush_test/hush-glob/glob3.tests2
-rw-r--r--shell/hush_test/hush-glob/glob_dir.right19
-rwxr-xr-xshell/hush_test/hush-glob/glob_dir.tests25
-rw-r--r--shell/hush_test/hush-heredoc/heredoc1.right (renamed from shell/hush_test/hush-misc/heredoc1.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc1.tests (renamed from shell/hush_test/hush-misc/heredoc1.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc2.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc2.tests7
-rw-r--r--shell/hush_test/hush-heredoc/heredoc3.right (renamed from shell/hush_test/hush-misc/heredoc3.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc3.tests (renamed from shell/hush_test/hush-misc/heredoc3.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc4.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc4.tests3
-rw-r--r--shell/hush_test/hush-heredoc/heredoc5.right (renamed from shell/hush_test/hush-misc/heredoc2.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc5.tests (renamed from shell/hush_test/hush-misc/heredoc2.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc6.right2
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc6.tests4
-rw-r--r--shell/hush_test/hush-heredoc/heredoc7.right1
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc7.tests3
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_backslash1.right (renamed from shell/hush_test/hush-misc/heredoc_backslash1.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_backslash1.tests (renamed from shell/hush_test/hush-misc/heredoc_backslash1.tests)0
-rw-r--r--shell/hush_test/hush-heredoc/heredoc_huge.right (renamed from shell/hush_test/hush-misc/heredoc_huge.right)0
-rwxr-xr-xshell/hush_test/hush-heredoc/heredoc_huge.tests (renamed from shell/hush_test/hush-misc/heredoc_huge.tests)0
-rw-r--r--shell/hush_test/hush-invert/invert.right10
-rwxr-xr-xshell/hush_test/hush-invert/invert.tests19
-rw-r--r--shell/hush_test/hush-misc/assignment2.right2
-rw-r--r--shell/hush_test/hush-misc/assignment2.rigth2
-rwxr-xr-xshell/hush_test/hush-misc/assignment2.tests1
-rw-r--r--shell/hush_test/hush-misc/eval1.right1
-rwxr-xr-xshell/hush_test/hush-misc/eval1.tests4
-rw-r--r--shell/hush_test/hush-misc/eval2.right3
-rwxr-xr-xshell/hush_test/hush-misc/eval2.tests4
-rw-r--r--shell/hush_test/hush-misc/exitcode1.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode1.tests2
-rw-r--r--shell/hush_test/hush-misc/exitcode2.right4
-rwxr-xr-xshell/hush_test/hush-misc/exitcode2.tests12
-rw-r--r--shell/hush_test/hush-misc/exitcode_EACCES.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_EACCES.tests (renamed from shell/msh_test/msh-execution/exitcode_EACCES.tests)0
-rw-r--r--shell/hush_test/hush-misc/exitcode_ENOENT.right2
-rwxr-xr-xshell/hush_test/hush-misc/exitcode_ENOENT.tests (renamed from shell/msh_test/msh-execution/exitcode_ENOENT.tests)0
-rw-r--r--shell/hush_test/hush-misc/for.right (copied from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/hush_test/hush-misc/for.tests5
-rw-r--r--shell/hush_test/hush-misc/func6.right2
-rwxr-xr-xshell/hush_test/hush-misc/func6.tests11
-rwxr-xr-xshell/hush_test/hush-misc/func_args1.tests2
-rw-r--r--shell/hush_test/hush-misc/group_in_braces.right5
-rwxr-xr-xshell/hush_test/hush-misc/group_in_braces.tests11
-rw-r--r--shell/hush_test/hush-misc/last_amp.right2
-rwxr-xr-xshell/hush_test/hush-misc/last_amp.tests8
-rw-r--r--shell/hush_test/hush-misc/local1.right4
-rwxr-xr-xshell/hush_test/hush-misc/local1.tests11
-rw-r--r--shell/hush_test/hush-misc/nommu3.right2
-rwxr-xr-xshell/hush_test/hush-misc/nommu3.tests15
-rw-r--r--shell/hush_test/hush-misc/nulltick1.right3
-rwxr-xr-xshell/hush_test/hush-misc/nulltick1.tests3
-rw-r--r--shell/hush_test/hush-misc/source1.right7
-rwxr-xr-xshell/hush_test/hush-misc/source1.tests15
-rw-r--r--shell/hush_test/hush-misc/source2.right5
-rwxr-xr-xshell/hush_test/hush-misc/source2.tests11
-rw-r--r--shell/hush_test/hush-misc/source3.right2
-rwxr-xr-xshell/hush_test/hush-misc/source3.tests6
-rw-r--r--shell/hush_test/hush-misc/source4.right5
-rwxr-xr-xshell/hush_test/hush-misc/source4.tests10
-rw-r--r--shell/hush_test/hush-misc/source5.right4
-rwxr-xr-xshell/hush_test/hush-misc/source5.tests8
-rw-r--r--shell/hush_test/hush-misc/unicode1.right3
-rwxr-xr-xshell/hush_test/hush-misc/unicode1.tests13
-rw-r--r--shell/hush_test/hush-misc/wait1.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rw-r--r--shell/hush_test/hush-misc/wait2.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests4
-rw-r--r--shell/hush_test/hush-misc/wait3.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
-rw-r--r--shell/hush_test/hush-misc/wait4.right1
-rwxr-xr-xshell/hush_test/hush-misc/wait4.tests2
-rw-r--r--shell/hush_test/hush-misc/wait5.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait5.tests5
-rw-r--r--shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right10
-rwxr-xr-xshell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests21
-rwxr-xr-xshell/hush_test/hush-read/read_r.tests6
-rw-r--r--shell/hush_test/hush-redir/redir1.right (renamed from shell/hush_test/hush-misc/redir1.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir1.tests (renamed from shell/hush_test/hush-misc/redir1.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir2.right (copied from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir2.tests5
-rw-r--r--shell/hush_test/hush-redir/redir3.right (renamed from shell/hush_test/hush-misc/redir3.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir3.tests (renamed from shell/hush_test/hush-misc/redir3.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir4.right (renamed from shell/msh_test/msh-execution/many_continues.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir4.tests72
-rw-r--r--shell/hush_test/hush-redir/redir5.right (renamed from shell/hush_test/hush-misc/redir5.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir5.tests (renamed from shell/hush_test/hush-misc/redir5.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir6.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir6.tests3
-rw-r--r--shell/hush_test/hush-redir/redir7.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir7.tests12
-rw-r--r--shell/hush_test/hush-redir/redir8.right3
-rwxr-xr-xshell/hush_test/hush-redir/redir8.tests15
-rw-r--r--shell/hush_test/hush-redir/redir9.right2
-rwxr-xr-xshell/hush_test/hush-redir/redir9.tests4
-rw-r--r--shell/hush_test/hush-redir/redirA.right2
-rwxr-xr-xshell/hush_test/hush-redir/redirA.tests11
-rw-r--r--shell/hush_test/hush-redir/redir_escapednum.right (copied from shell/hush_test/hush-misc/redir2.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir_escapednum.tests (renamed from shell/hush_test/hush-misc/redir2.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir_expand.right (renamed from shell/hush_test/hush-misc/redir4.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir_expand.tests (renamed from shell/hush_test/hush-misc/redir4.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir_leak.right6
-rwxr-xr-xshell/hush_test/hush-redir/redir_leak.tests10
-rw-r--r--shell/hush_test/hush-redir/redir_multi.right (renamed from shell/hush_test/hush-misc/redir6.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir_multi.tests (renamed from shell/hush_test/hush-misc/redir6.tests)0
-rw-r--r--shell/hush_test/hush-redir/redir_script.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_script.tests29
-rw-r--r--shell/hush_test/hush-redir/redir_space.right (renamed from shell/hush_test/hush-parsing/redir_space.right)0
-rwxr-xr-xshell/hush_test/hush-redir/redir_space.tests (renamed from shell/hush_test/hush-parsing/redir_space.tests)0
-rw-r--r--shell/hush_test/hush-signals/catch.right (renamed from shell/hush_test/hush-trap/catch.right)0
-rwxr-xr-xshell/hush_test/hush-signals/catch.tests (renamed from shell/hush_test/hush-trap/catch.tests)0
-rw-r--r--shell/hush_test/hush-signals/continue_and_trap1.right1
-rwxr-xr-xshell/hush_test/hush-signals/continue_and_trap1.tests7
-rw-r--r--shell/hush_test/hush-signals/exit.right (renamed from shell/hush_test/hush-trap/exit.right)0
-rwxr-xr-xshell/hush_test/hush-signals/exit.tests (renamed from shell/hush_test/hush-trap/exit.tests)0
-rw-r--r--shell/hush_test/hush-signals/reap1.right (renamed from shell/hush_test/hush-misc/redir2.right)0
-rwxr-xr-xshell/hush_test/hush-signals/reap1.tests14
-rw-r--r--shell/hush_test/hush-signals/return_in_trap1.right4
-rwxr-xr-xshell/hush_test/hush-signals/return_in_trap1.tests18
-rw-r--r--shell/hush_test/hush-signals/save-ret.right (renamed from shell/hush_test/hush-trap/save-ret.right)0
-rwxr-xr-xshell/hush_test/hush-signals/save-ret.tests (renamed from shell/hush_test/hush-trap/save-ret.tests)0
-rw-r--r--shell/hush_test/hush-signals/savetrap.right (renamed from shell/hush_test/hush-trap/savetrap.right)0
-rwxr-xr-xshell/hush_test/hush-signals/savetrap.tests (renamed from shell/hush_test/hush-trap/savetrap.tests)0
-rw-r--r--shell/hush_test/hush-signals/sigint1.right1
-rwxr-xr-xshell/hush_test/hush-signals/sigint1.tests41
-rw-r--r--shell/hush_test/hush-signals/signal2.right3
-rwxr-xr-xshell/hush_test/hush-signals/signal2.tests18
-rw-r--r--shell/hush_test/hush-signals/signal3.right4
-rwxr-xr-xshell/hush_test/hush-signals/signal3.tests17
-rw-r--r--shell/hush_test/hush-signals/signal5.right12
-rwxr-xr-xshell/hush_test/hush-signals/signal5.tests14
-rw-r--r--shell/hush_test/hush-signals/signal6.right2
-rwxr-xr-xshell/hush_test/hush-signals/signal6.tests2
-rw-r--r--shell/hush_test/hush-signals/signal7.right (renamed from shell/hush_test/hush-trap/signal7.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal7.tests (renamed from shell/hush_test/hush-trap/signal7.tests)0
-rw-r--r--shell/hush_test/hush-signals/signal_read1.right (renamed from shell/hush_test/hush-trap/signal_read1.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal_read1.tests (renamed from shell/hush_test/hush-trap/signal_read1.tests)0
-rw-r--r--shell/hush_test/hush-signals/signal_read2.right (renamed from shell/hush_test/hush-trap/signal_read2.right)0
-rwxr-xr-xshell/hush_test/hush-signals/signal_read2.tests (renamed from shell/hush_test/hush-trap/signal_read2.tests)0
-rw-r--r--shell/hush_test/hush-signals/sigquit_exec.right2
-rwxr-xr-xshell/hush_test/hush-signals/sigquit_exec.tests4
-rw-r--r--shell/hush_test/hush-signals/subshell.right (renamed from shell/hush_test/hush-trap/subshell.right)0
-rwxr-xr-xshell/hush_test/hush-signals/subshell.tests (renamed from shell/hush_test/hush-trap/subshell.tests)0
-rw-r--r--shell/hush_test/hush-signals/usage.right (renamed from shell/hush_test/hush-trap/usage.right)0
-rwxr-xr-xshell/hush_test/hush-signals/usage.tests (renamed from shell/hush_test/hush-trap/usage.tests)0
-rw-r--r--shell/hush_test/hush-standalone/noexec_gets_no_env.right4
-rwxr-xr-xshell/hush_test/hush-standalone/noexec_gets_no_env.tests5
-rw-r--r--shell/hush_test/hush-standalone/nofork_trashes_getopt.right1
-rwxr-xr-xshell/hush_test/hush-standalone/nofork_trashes_getopt.tests6
-rw-r--r--shell/hush_test/hush-standalone/var_standalone1.right1
-rwxr-xr-xshell/hush_test/hush-standalone/var_standalone1.tests2
-rw-r--r--shell/hush_test/hush-vars/param_expand_len.right3
-rwxr-xr-xshell/hush_test/hush-vars/param_expand_len.tests7
-rw-r--r--shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right2
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests3
-rw-r--r--shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests2
-rw-r--r--shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests3
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests2
-rw-r--r--shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests2
-rw-r--r--shell/hush_test/hush-vars/var-runtime-quote-detection.right1
-rwxr-xr-xshell/hush_test/hush-vars/var-runtime-quote-detection.tests1
-rw-r--r--shell/hush_test/hush-vars/var3.right7
-rwxr-xr-xshell/hush_test/hush-vars/var3.tests5
-rw-r--r--shell/hush_test/hush-vars/var4.right1
-rwxr-xr-xshell/hush_test/hush-vars/var4.tests1
-rw-r--r--shell/hush_test/hush-vars/var5.right6
-rwxr-xr-xshell/hush_test/hush-vars/var5.tests14
-rw-r--r--shell/hush_test/hush-vars/var6.right2
-rwxr-xr-xshell/hush_test/hush-vars/var6.tests4
-rw-r--r--shell/hush_test/hush-vars/var_unbackslash1.right7
-rwxr-xr-xshell/hush_test/hush-vars/var_unbackslash1.tests35
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs1.right41
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs1.tests42
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs2.right3
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs2.tests13
-rw-r--r--shell/hush_test/hush-vars/var_wordsplit_ifs3.right12
-rwxr-xr-xshell/hush_test/hush-vars/var_wordsplit_ifs3.tests5
-rwxr-xr-xshell/hush_test/run-all3
-rw-r--r--shell/math.c27
-rw-r--r--shell/math.h2
-rw-r--r--shell/msh_test/msh-bugs/noeol3.right1
-rwxr-xr-xshell/msh_test/msh-bugs/noeol3.tests2
-rw-r--r--shell/msh_test/msh-bugs/process_subst.right3
-rwxr-xr-xshell/msh_test/msh-bugs/process_subst.tests3
-rw-r--r--shell/msh_test/msh-bugs/starquoted.right8
-rwxr-xr-xshell/msh_test/msh-bugs/starquoted.tests8
-rw-r--r--shell/msh_test/msh-bugs/syntax_err.right2
-rwxr-xr-xshell/msh_test/msh-bugs/syntax_err.tests3
-rw-r--r--shell/msh_test/msh-execution/exitcode_EACCES.right2
-rw-r--r--shell/msh_test/msh-execution/exitcode_ENOENT.right2
-rwxr-xr-xshell/msh_test/msh-execution/many_continues.tests15
-rw-r--r--shell/msh_test/msh-execution/nested_break.right8
-rwxr-xr-xshell/msh_test/msh-execution/nested_break.tests17
-rw-r--r--shell/msh_test/msh-misc/tick.right2
-rwxr-xr-xshell/msh_test/msh-misc/tick.tests4
-rwxr-xr-xshell/msh_test/msh-parsing/argv0.tests4
-rw-r--r--shell/msh_test/msh-parsing/noeol.right1
-rwxr-xr-xshell/msh_test/msh-parsing/noeol.tests2
-rw-r--r--shell/msh_test/msh-parsing/noeol2.right1
-rwxr-xr-xshell/msh_test/msh-parsing/noeol2.tests7
-rw-r--r--shell/msh_test/msh-parsing/quote1.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote1.tests2
-rw-r--r--shell/msh_test/msh-parsing/quote2.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote2.tests2
-rw-r--r--shell/msh_test/msh-parsing/quote3.right3
-rwxr-xr-xshell/msh_test/msh-parsing/quote3.tests8
-rw-r--r--shell/msh_test/msh-parsing/quote4.right1
-rwxr-xr-xshell/msh_test/msh-parsing/quote4.tests2
-rw-r--r--shell/msh_test/msh-vars/var.right4
-rwxr-xr-xshell/msh_test/msh-vars/var.tests9
-rwxr-xr-xshell/msh_test/msh-vars/var_subst_in_for.tests40
-rwxr-xr-xshell/msh_test/run-all64
-rw-r--r--shell/random.c142
-rw-r--r--shell/random.h16
-rw-r--r--shell/shell_common.c6
-rw-r--r--shell/shell_common.h2
-rw-r--r--sysklogd/Config.src159
-rw-r--r--sysklogd/Kbuild.src4
-rw-r--r--sysklogd/klogd.c39
-rw-r--r--sysklogd/logger.c15
-rw-r--r--sysklogd/logread.c46
-rw-r--r--sysklogd/syslogd.c248
-rw-r--r--sysklogd/syslogd_and_logger.c1
-rwxr-xr-xtestsuite/ar.tests2
-rwxr-xr-xtestsuite/awk.tests24
-rwxr-xr-xtestsuite/busybox.tests16
-rwxr-xr-xtestsuite/bzcat.tests88
-rw-r--r--testsuite/bzcat/bzcat-does-not-remove-compressed-file (renamed from testsuite/bunzip2/bzcat-does-not-remove-compressed-file)0
-rwxr-xr-xtestsuite/cpio.tests27
-rwxr-xr-xtestsuite/dc.tests56
-rwxr-xr-xtestsuite/diff.tests23
-rw-r--r--testsuite/du/du-m-works2
-rwxr-xr-xtestsuite/find.tests76
-rwxr-xr-xtestsuite/grep.tests34
-rw-r--r--testsuite/gzip/gzip-compression-levels5
-rwxr-xr-xtestsuite/makedevs.tests2
-rwxr-xr-xtestsuite/md5sum.tests22
-rwxr-xr-xtestsuite/mdev.tests2
-rwxr-xr-xtestsuite/patch.tests45
-rw-r--r--testsuite/pwd/pwd-prints-working-directory5
-rwxr-xr-xtestsuite/readlink.tests11
-rwxr-xr-xtestsuite/sed.tests72
-rwxr-xr-xtestsuite/sha3sum.tests2
-rwxr-xr-xtestsuite/sort.tests55
-rwxr-xr-xtestsuite/tar.tests77
-rw-r--r--testsuite/tar.utf8.tar.bz23
-rwxr-xr-xtestsuite/test.tests20
-rwxr-xr-xtestsuite/unzip.tests24
-rw-r--r--testsuite/which/which-uses-default-path2
-rw-r--r--util-linux/Config.src644
-rw-r--r--util-linux/Kbuild.src37
-rw-r--r--util-linux/acpid.c36
-rw-r--r--util-linux/blkdiscard.c82
-rw-r--r--util-linux/blkid.c20
-rw-r--r--util-linux/blockdev.c9
-rw-r--r--util-linux/dmesg.c95
-rw-r--r--util-linux/fatattr.c105
-rw-r--r--util-linux/fbset.c63
-rw-r--r--util-linux/fdformat.c14
-rw-r--r--util-linux/fdisk.c351
-rw-r--r--util-linux/fdisk_gpt.c58
-rw-r--r--util-linux/fdisk_osf.c5
-rw-r--r--util-linux/fdisk_sgi.c29
-rw-r--r--util-linux/findfs.c16
-rw-r--r--util-linux/flock.c30
-rw-r--r--util-linux/freeramdisk.c30
-rw-r--r--util-linux/fsck_minix.c30
-rw-r--r--util-linux/fstrim.c27
-rw-r--r--util-linux/getopt.c31
-rw-r--r--util-linux/hexdump.c32
-rw-r--r--util-linux/hwclock.c50
-rw-r--r--util-linux/ipcrm.c13
-rw-r--r--util-linux/ipcs.c11
-rw-r--r--util-linux/losetup.c12
-rw-r--r--util-linux/lspci.c13
-rw-r--r--util-linux/lsusb.c13
-rw-r--r--util-linux/mdev.c141
-rw-r--r--util-linux/minix.h5
-rw-r--r--util-linux/mkfs_ext2.c33
-rw-r--r--util-linux/mkfs_minix.c34
-rw-r--r--util-linux/mkfs_reiser.c21
-rw-r--r--util-linux/mkfs_vfat.c23
-rw-r--r--util-linux/mkswap.c27
-rw-r--r--util-linux/more.c27
-rw-r--r--util-linux/mount.c187
-rw-r--r--util-linux/nsenter.c291
-rw-r--r--util-linux/pivot_root.c16
-rw-r--r--util-linux/rdate.c24
-rw-r--r--util-linux/rdev.c9
-rw-r--r--util-linux/readprofile.c20
-rw-r--r--util-linux/rev.c9
-rw-r--r--util-linux/rtcwake.c79
-rw-r--r--util-linux/script.c26
-rw-r--r--util-linux/scriptreplay.c10
-rw-r--r--util-linux/setarch.c79
-rw-r--r--util-linux/swaponoff.c311
-rw-r--r--util-linux/switch_root.c25
-rw-r--r--util-linux/uevent.c130
-rw-r--r--util-linux/umount.c43
-rw-r--r--util-linux/unshare.c379
-rw-r--r--util-linux/volume_id/bcache.c110
-rw-r--r--util-linux/volume_id/get_devname.c10
-rw-r--r--util-linux/volume_id/luks.c2
-rw-r--r--util-linux/volume_id/ubifs.c125
-rw-r--r--util-linux/volume_id/volume_id.c6
-rw-r--r--util-linux/volume_id/volume_id_internal.h4
1303 files changed, 32187 insertions, 57138 deletions
diff --git a/AUTHORS b/AUTHORS
index 0908fc7..fa58697 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -146,6 +146,10 @@ Rob Sullivan <cogito.ergo.cogito@gmail.com>
Linus Torvalds
mkswap, fsck.minix, mkfs.minix
+Linus Walleij
+ fbset and fbsplash config RGBA parsing
+ rewrite of mdev helper to create devices from /sys/dev
+
Mark Whitley <markw@codepoet.org>
grep, sed, cut, xargs(previous),
style-guide, new-applet-HOWTO, bug fixes, etc.
diff --git a/Config.in b/Config.in
index 2c4be2e..924a197 100644
--- a/Config.in
+++ b/Config.in
@@ -11,15 +11,18 @@ config HAVE_DOT_CONFIG
menu "Busybox Settings"
-menu "General Configuration"
-
config DESKTOP
bool "Enable options for full-blown desktop systems"
default y
help
Enable options and features which are not essential.
- Select this only if you plan to use busybox on full-blown
- desktop machine with common Linux distro, not on an embedded box.
+ Select this if you plan to use busybox on full-blown desktop machine
+ with common Linux distro, which needs higher level of command-line
+ compatibility.
+
+ If you are preparing your build to be used on an embedded box
+ where you have tighter control over the entire set of userspace
+ tools, you can unselect this option for smaller code size.
config EXTRA_COMPAT
bool "Provide compatible behavior for rare corner cases (bigger code)"
@@ -58,30 +61,6 @@ config PLATFORM_LINUX
Answering 'N' here will disable such applets and hide the
corresponding configuration options.
-choice
- prompt "Buffer allocation policy"
- default FEATURE_BUFFERS_USE_MALLOC
- help
- There are 3 ways BusyBox can handle buffer allocations:
- - Use malloc. This costs code size for the call to xmalloc.
- - Put them on stack. For some very small machines with limited stack
- space, this can be deadly. For most folks, this works just fine.
- - Put them in BSS. This works beautifully for computers with a real
- MMU (and OS support), but wastes runtime RAM for uCLinux. This
- behavior was the only one available for BusyBox versions 0.48 and
- earlier.
-
-config FEATURE_BUFFERS_USE_MALLOC
- bool "Allocate with Malloc"
-
-config FEATURE_BUFFERS_GO_ON_STACK
- bool "Allocate on the Stack"
-
-config FEATURE_BUFFERS_GO_IN_BSS
- bool "Allocate in the .bss section"
-
-endchoice
-
config SHOW_USAGE
bool "Show applet usage messages"
default y
@@ -116,9 +95,21 @@ config FEATURE_COMPRESS_USAGE
and have very little memory, this might not be a win. Otherwise,
you probably want this.
+config BUSYBOX
+ bool "Include busybox applet"
+ default y
+ help
+ The busybox applet provides general help regarding busybox and
+ allows the included applets to be listed. It's also required
+ if applet links are to be installed at runtime.
+
+ If you can live without these features disabling this will save
+ some space.
+
config FEATURE_INSTALLER
bool "Support --install [-s] to install applet links at runtime"
default y
+ depends on BUSYBOX
help
Enable 'busybox --install [-s]' support. This will allow you to use
busybox at runtime to create hard links or symlinks for all the
@@ -132,130 +123,12 @@ config INSTALL_NO_USR
will install applets only to /bin and /sbin,
never to /usr/bin or /usr/sbin.
-config LOCALE_SUPPORT
- bool "Enable locale support (system needs locale for this to work)"
+config PAM
+ bool "Support for PAM (Pluggable Authentication Modules)"
default n
help
- Enable this if your system has locale support and you would like
- busybox to support locale settings.
-
-config UNICODE_SUPPORT
- bool "Support Unicode"
- default y
- help
- This makes various applets aware that one byte is not
- one character on screen.
-
- Busybox aims to eventually work correctly with Unicode displays.
- Any older encodings are not guaranteed to work.
- Probably by the time when busybox will be fully Unicode-clean,
- other encodings will be mainly of historic interest.
-
-config UNICODE_USING_LOCALE
- bool "Use libc routines for Unicode (else uses internal ones)"
- default n
- depends on UNICODE_SUPPORT && LOCALE_SUPPORT
- help
- With this option on, Unicode support is implemented using libc
- routines. Otherwise, internal implementation is used.
- Internal implementation is smaller.
-
-config FEATURE_CHECK_UNICODE_IN_ENV
- bool "Check $LC_ALL, $LC_CTYPE and $LANG environment variables"
- default n
- depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
- help
- With this option on, Unicode support is activated
- only if locale-related variables have the value of the form
- "xxxx.utf8"
-
- Otherwise, Unicode support will be always enabled and active.
-
-config SUBST_WCHAR
- int "Character code to substitute unprintable characters with"
- depends on UNICODE_SUPPORT
- default 63
- help
- Typical values are 63 for '?' (works with any output device),
- 30 for ASCII substitute control code,
- 65533 (0xfffd) for Unicode replacement character.
-
-config LAST_SUPPORTED_WCHAR
- int "Range of supported Unicode characters"
- depends on UNICODE_SUPPORT
- default 767
- help
- Any character with Unicode value bigger than this is assumed
- to be non-printable on output device. Many applets replace
- such chars with substitution character.
-
- The idea is that many valid printable Unicode chars are
- nevertheless are not displayed correctly. Think about
- combining charachers, double-wide hieroglyphs, obscure
- characters in dozens of ancient scripts...
- Many terminals, terminal emulators, xterms etc will fail
- to handle them correctly. Choose the smallest value
- which suits your needs.
-
- Typical values are:
- 126 - ASCII only
- 767 (0x2ff) - there are no combining chars in [0..767] range
- (the range includes Latin 1, Latin Ext. A and B),
- code is ~700 bytes smaller for this case.
- 4351 (0x10ff) - there are no double-wide chars in [0..4351] range,
- code is ~300 bytes smaller for this case.
- 12799 (0x31ff) - nearly all non-ideographic characters are
- available in [0..12799] range, including
- East Asian scripts like katakana, hiragana, hangul,
- bopomofo...
- 0 - off, any valid printable Unicode character will be printed.
-
-config UNICODE_COMBINING_WCHARS
- bool "Allow zero-width Unicode characters on output"
- default n
- depends on UNICODE_SUPPORT
- help
- With this option off, any Unicode char with width of 0
- is substituted on output.
-
-config UNICODE_WIDE_WCHARS
- bool "Allow wide Unicode characters on output"
- default n
- depends on UNICODE_SUPPORT
- help
- With this option off, any Unicode char with width > 1
- is substituted on output.
-
-config UNICODE_BIDI_SUPPORT
- bool "Bidirectional character-aware line input"
- default n
- depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
- help
- With this option on, right-to-left Unicode characters
- are treated differently on input (e.g. cursor movement).
-
-config UNICODE_NEUTRAL_TABLE
- bool "In bidi input, support non-ASCII neutral chars too"
- default n
- depends on UNICODE_BIDI_SUPPORT
- help
- In most cases it's enough to treat only ASCII non-letters
- (i.e. punctuation, numbers and space) as characters
- with neutral directionality.
- With this option on, more extensive (and bigger) table
- of neutral chars will be used.
-
-config UNICODE_PRESERVE_BROKEN
- bool "Make it possible to enter sequences of chars which are not Unicode"
- default n
- depends on UNICODE_SUPPORT
- help
- With this option on, on line-editing input (such as used by shells)
- invalid UTF-8 bytes are not substituted with the selected
- substitution character.
- For example, this means that entering 'l', 's', ' ', 0xff, [Enter]
- at shell prompt will list file named 0xff (single char name
- with char value 255), not file named '?'.
+ Use PAM in some busybox applets (currently login and httpd) instead
+ of direct access to password database.
config LONG_OPTS
bool "Support for --long-options"
@@ -416,10 +289,7 @@ config SELINUX
the option of compiling in SELinux applets.
If you do not have a complete SELinux userland installed, this stuff
- will not compile. Go visit
- http://www.nsa.gov/selinux/index.html
- to download the necessary stuff to allow busybox to compile with
- this option enabled. Specifially, libselinux 1.28 or better is
+ will not compile. Specifially, libselinux 1.28 or better is
directly required by busybox. If the installation is located in a
non-standard directory, provide it by invoking make as follows:
CFLAGS=-I<libselinux-include-path> \
@@ -468,9 +338,7 @@ config FEATURE_HAVE_RPC
# This is automatically selected if any of enabled applets need it.
# You do not need to select it manually.
-endmenu
-
-menu 'Build Options'
+comment 'Build Options'
config STATIC
bool "Build BusyBox as a static binary (no shared libs)"
@@ -645,9 +513,73 @@ config EXTRA_LDLIBS
help
Additional LDLIBS to pass to the linker with -l.
-endmenu
+comment 'Installation Options ("make install" behavior)'
+
+choice
+ prompt "What kind of applet links to install"
+ default INSTALL_APPLET_SYMLINKS
+ help
+ Choose what kind of links to applets are created by "make install".
+
+config INSTALL_APPLET_SYMLINKS
+ bool "as soft-links"
+ help
+ Install applets as soft-links to the busybox binary. This needs some
+ free inodes on the filesystem, but might help with filesystem
+ generators that can't cope with hard-links.
+
+config INSTALL_APPLET_HARDLINKS
+ bool "as hard-links"
+ help
+ Install applets as hard-links to the busybox binary. This might
+ count on a filesystem with few inodes.
+
+config INSTALL_APPLET_SCRIPT_WRAPPERS
+ bool "as script wrappers"
+ help
+ Install applets as script wrappers that call the busybox binary.
+
+config INSTALL_APPLET_DONT
+ bool "not installed"
+ help
+ Do not install applet links. Useful when you plan to use
+ busybox --install for installing links, or plan to use
+ a standalone shell and thus don't need applet links.
+
+endchoice
+
+choice
+ prompt "/bin/sh applet link"
+ default INSTALL_SH_APPLET_SYMLINK
+ depends on INSTALL_APPLET_SCRIPT_WRAPPERS
+ help
+ Choose how you install /bin/sh applet link.
+
+config INSTALL_SH_APPLET_SYMLINK
+ bool "as soft-link"
+ help
+ Install /bin/sh applet as soft-link to the busybox binary.
+
+config INSTALL_SH_APPLET_HARDLINK
+ bool "as hard-link"
+ help
+ Install /bin/sh applet as hard-link to the busybox binary.
-menu 'Debugging Options'
+config INSTALL_SH_APPLET_SCRIPT_WRAPPER
+ bool "as script wrapper"
+ help
+ Install /bin/sh applet as script wrapper that calls
+ the busybox binary.
+
+endchoice
+
+config PREFIX
+ string "BusyBox installation prefix"
+ default "./_install"
+ help
+ Define your directory to install BusyBox files/subdirs in.
+
+comment 'Debugging Options'
config DEBUG
bool "Build BusyBox with extra Debugging symbols"
@@ -671,6 +603,24 @@ config DEBUG_PESSIMIZE
in a much bigger executable that more closely matches the source
code.
+config DEBUG_SANITIZE
+ bool "Enable runtime sanitizers (ASAN/LSAN/USAN/etc...)"
+ default n
+ help
+ Say Y here if you want to enable runtime sanitizers. These help
+ catch bad memory accesses (e.g. buffer overflows), but will make
+ the executable larger and slow down runtime a bit.
+
+ If you aren't developing/testing busybox, say N here.
+
+config UNIT_TEST
+ bool "Build unit tests"
+ default n
+ help
+ Say Y here if you want to build unit tests (both the framework and
+ test cases) as a Busybox applet. This results in bigger code, so you
+ probably don't want this option in production builds.
+
config WERROR
bool "Abort compilation on any warning"
default n
@@ -723,78 +673,8 @@ endchoice
endmenu
-menu 'Installation Options ("make install" behavior)'
-
-choice
- prompt "What kind of applet links to install"
- default INSTALL_APPLET_SYMLINKS
- help
- Choose what kind of links to applets are created by "make install".
-
-config INSTALL_APPLET_SYMLINKS
- bool "as soft-links"
- help
- Install applets as soft-links to the busybox binary. This needs some
- free inodes on the filesystem, but might help with filesystem
- generators that can't cope with hard-links.
-
-config INSTALL_APPLET_HARDLINKS
- bool "as hard-links"
- help
- Install applets as hard-links to the busybox binary. This might
- count on a filesystem with few inodes.
-
-config INSTALL_APPLET_SCRIPT_WRAPPERS
- bool "as script wrappers"
- help
- Install applets as script wrappers that call the busybox binary.
-
-config INSTALL_APPLET_DONT
- bool "not installed"
- help
- Do not install applet links. Useful when you plan to use
- busybox --install for installing links, or plan to use
- a standalone shell and thus don't need applet links.
-
-endchoice
-
-choice
- prompt "/bin/sh applet link"
- default INSTALL_SH_APPLET_SYMLINK
- depends on INSTALL_APPLET_SCRIPT_WRAPPERS
- help
- Choose how you install /bin/sh applet link.
-
-config INSTALL_SH_APPLET_SYMLINK
- bool "as soft-link"
- help
- Install /bin/sh applet as soft-link to the busybox binary.
-
-config INSTALL_SH_APPLET_HARDLINK
- bool "as hard-link"
- help
- Install /bin/sh applet as hard-link to the busybox binary.
-
-config INSTALL_SH_APPLET_SCRIPT_WRAPPER
- bool "as script wrapper"
- help
- Install /bin/sh applet as script wrapper that calls
- the busybox binary.
-
-endchoice
-
-config PREFIX
- string "BusyBox installation prefix"
- default "./_install"
- help
- Define your directory to install BusyBox files/subdirs in.
-
-endmenu
-
source libbb/Config.in
-endmenu
-
comment "Applets"
source archival/Config.in
diff --git a/Makefile b/Makefile
index 111adc0..971e68e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,14 +1,8 @@
VERSION = 1
-PATCHLEVEL = 22
-SUBLEVEL = 1
+PATCHLEVEL = 26
+SUBLEVEL = 2
EXTRAVERSION =
-NAME = bionic
-
-# prevent local tree builds in bionic,
-# but allow initial version check (SUBMAKE)
-ifeq (,$(filter s, $(MAKEFLAGS)))
- KBUILD_OUTPUT ?= $(OUT)/obj/busybox
-endif
+NAME = Unnamed
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
@@ -110,8 +104,7 @@ ifneq ($(KBUILD_OUTPUT),)
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
$(if $(KBUILD_OUTPUT),, \
- $(warning output directory "$(saved-output)" does not exist) \
- $(error On AOSP repo, type 'mma' to build or set O=out/<folder> ))
+ $(error output directory "$(saved-output)" does not exist))
PHONY += $(MAKECMDGOALS)
@@ -559,7 +552,7 @@ export INSTALL_PATH ?= /boot
#
# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
# relocations required by build roots. This is not defined in the
-# makefile but the arguement can be passed to make if needed.
+# makefile but the argument can be passed to make if needed.
#
MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
@@ -617,7 +610,8 @@ quiet_cmd_busybox__ ?= LINK $@
"$(LDFLAGS) $(EXTRA_LDFLAGS)" \
"$(core-y)" \
"$(libs-y)" \
- "$(LDLIBS)"
+ "$(LDLIBS)" \
+ && $(srctree)/scripts/generate_BUFSIZ.sh --post include/common_bufsiz.h
# Generate System.map
quiet_cmd_sysmap = SYSMAP
@@ -851,12 +845,15 @@ export CPPFLAGS_busybox.lds += -P -C -U$(ARCH)
# Split autoconf.h into include/linux/config/*
quiet_cmd_gen_bbconfigopts = GEN include/bbconfigopts.h
cmd_gen_bbconfigopts = $(srctree)/scripts/mkconfigs include/bbconfigopts.h include/bbconfigopts_bz2.h
+quiet_cmd_gen_common_bufsiz = GEN include/common_bufsiz.h
+ cmd_gen_common_bufsiz = $(srctree)/scripts/generate_BUFSIZ.sh include/common_bufsiz.h
quiet_cmd_split_autoconf = SPLIT include/autoconf.h -> include/config/*
cmd_split_autoconf = scripts/basic/split-include include/autoconf.h include/config
#bbox# piggybacked generation of few .h files
include/config/MARKER: scripts/basic/split-include include/autoconf.h
$(call cmd,split_autoconf)
$(call cmd,gen_bbconfigopts)
+ $(call cmd,gen_common_bufsiz)
@touch $@
# Generate some files
@@ -972,6 +969,7 @@ CLEAN_FILES += busybox busybox_unstripped* busybox.links \
MRPROPER_DIRS += include/config include2
MRPROPER_FILES += .config .config.old include/asm .version .old_version \
include/NUM_APPLETS.h \
+ include/common_bufsiz.h \
include/autoconf.h \
include/bbconfigopts.h \
include/bbconfigopts_bz2.h \
@@ -983,9 +981,6 @@ MRPROPER_FILES += .config .config.old include/asm .version .old_version \
.kernelrelease Module.symvers tags TAGS cscope* \
busybox_old
-MRPROPER_FILES += include-full/*.h \
- include-minimal/*.h
-
# clean - Delete most, but leave enough to build external modules
#
clean: rm-dirs := $(CLEAN_DIRS)
@@ -1175,24 +1170,7 @@ endif
ALLSOURCE_ARCHS := $(ARCH)
define all-sources
- ( find $(__srctree) $(RCS_FIND_IGNORE) \
- \( -name include -o -name arch \) -prune -o \
- -name '*.[chS]' -print; \
- for ARCH in $(ALLSOURCE_ARCHS) ; do \
- find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \
- -name '*.[chS]' -print; \
- done ; \
- find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \
- -name '*.[chS]' -print; \
- find $(__srctree)include $(RCS_FIND_IGNORE) \
- \( -name config -o -name 'asm-*' \) -prune \
- -o -name '*.[chS]' -print; \
- for ARCH in $(ALLINCLUDE_ARCHS) ; do \
- find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \
- -name '*.[chS]' -print; \
- done ; \
- find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
- -name '*.[chS]' -print )
+ ( find -regex '.*\.[ch]$$' )
endef
quiet_cmd_cscope-file = FILELST cscope.files
@@ -1345,11 +1323,6 @@ endif # skip-makefile
PHONY += FORCE
FORCE:
-show-sources:
- @for f in $(busybox-dirs) ; do \
- $(MAKE) $(build)=$$f show-src ; \
- done
-
-include $(srctree)/Makefile.custom
# Declare the contents of the .PHONY variable as phony. We keep that
diff --git a/Makefile.custom b/Makefile.custom
index 8c95ef2..891c9ce 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -28,6 +28,10 @@ ifeq ($(CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER),y)
INSTALL_OPTS:= --scriptwrapper
endif
endif
+ifeq ($(CONFIG_FEATURE_INDIVIDUAL),y)
+INSTALL_OPTS:= --binaries
+LIBBUSYBOX_SONAME:= 0_lib/libbusybox.so.$(BB_VER)
+endif
install: $(srctree)/applets/install.sh busybox busybox.links
$(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \
$(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)
@@ -55,7 +59,11 @@ endif
# (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be))
.PHONY: check
.PHONY: test
+ifeq ($(CONFIG_UNIT_TEST),y)
+UNIT_CMD = ./busybox unit
+endif
check test: busybox busybox.links
+ $(UNIT_CMD)
test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree)
bindir=$(objtree) srcdir=$(srctree)/testsuite \
$(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)"
diff --git a/Makefile.flags b/Makefile.flags
index 640eeec..89d599c 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -56,6 +56,9 @@ CFLAGS += $(call cc-option,-falign-functions=1 -falign-jumps=1 -falign-labels=1
# Defeat .eh_frame bloat (gcc 4.6.3 x86-32 defconfig: 20% smaller busybox binary):
CFLAGS += $(call cc-option,-fno-unwind-tables,)
CFLAGS += $(call cc-option,-fno-asynchronous-unwind-tables,)
+# No automatic printf->puts,putchar conversions
+# (try disabling this and comparing assembly, it's instructive)
+CFLAGS += $(call cc-option,-fno-builtin-printf,)
# FIXME: These warnings are at least partially to be concerned about and should
# be fixed..
@@ -72,6 +75,11 @@ else
CFLAGS += $(call cc-option,-Os,$(call cc-option,-O2,))
endif
endif
+ifeq ($(CONFIG_DEBUG_SANITIZE),y)
+CFLAGS += $(call cc-option,-fsanitize=address,)
+CFLAGS += $(call cc-option,-fsanitize=leak,)
+CFLAGS += $(call cc-option,-fsanitize=undefined,)
+endif
# If arch/$(ARCH)/Makefile did not override it (with, say, -fPIC)...
ARCH_FPIC ?= -fpic
@@ -126,15 +134,19 @@ else
LDLIBS += m
endif
+# libpam may use libpthread, libdl and/or libaudit.
+# On some platforms that requires an explicit -lpthread, -ldl, -laudit.
+# However, on *other platforms* it fails when some of those flags
+# given needlessly. On some systems, crypt needs pthread.
+#
+# I even had a system where a runtime test for pthread
+# (similar to CRYPT_AVAILABLE test above) was not reliable.
+#
+# Do not propagate this mess by adding libraries to CONFIG_PAM/CRYPT_AVAILABLE blocks.
+# Add libraries you need to CONFIG_EXTRA_LDLIBS instead.
+
ifeq ($(CONFIG_PAM),y)
-# libpam uses libpthread, so for static builds busybox must be linked to
-# libpthread. On some platforms that requires an explicit -lpthread, so
-# it should be in LDLIBS. For non-static builds, scripts/trylink will
-# take care of removing -lpthread if possible. (Not bothering to check
-# CONFIG_STATIC because even in a non-static build it could be that the
-# only libpam available is libpam.a, so -lpthread could still be
-# needed.)
-LDLIBS += pam pam_misc pthread
+LDLIBS += pam pam_misc
endif
ifeq ($(CONFIG_SELINUX),y)
@@ -165,7 +177,7 @@ SKIP_STRIP = y
endif
ifneq ($(CONFIG_EXTRA_LDFLAGS),)
-EXTRA_LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS)))
+LDFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_LDFLAGS)))
#"))
endif
diff --git a/Makefile.help b/Makefile.help
index 119dd6f..6a23e2a 100644
--- a/Makefile.help
+++ b/Makefile.help
@@ -21,10 +21,6 @@ help:
@echo ' defconfig - set .config to largest generic configuration'
@echo ' menuconfig - interactive curses-based configurator'
@echo ' oldconfig - resolve any unresolved symbols in .config'
- @echo ' hosttools - build sed for the host.'
- @echo ' You can use these commands if the commands on the host'
- @echo ' is unusable. Afterwards use it like:'
- @echo ' make SED="$(objtree)/sed"'
@$(if $(boards), \
$(foreach b, $(boards), \
printf " %-21s - Build for %s\\n" $(b) $(subst _defconfig,,$(b));) \
diff --git a/README b/README
index 4814372..ebdda73 100644
--- a/README
+++ b/README
@@ -211,7 +211,7 @@ Supported hardware:
Under 2.4 Linux kernels, kernel module loading was implemented in a
platform-specific manner. Busybox's insmod utility has been reported to
work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390,
- SH3/4/5, Sparc, v850e, and x86_64. Anything else probably won't work.
+ SH3/4/5, Sparc, and v850e. Anything else probably won't work.
The module loading mechanism for the 2.6 kernel is much more generic, and
we believe 2.6.x kernel module loading support should work on all
diff --git a/TODO b/TODO
index dcf48c2..8904b21 100644
--- a/TODO
+++ b/TODO
@@ -258,5 +258,3 @@ vdprintf() -> similar sized functionality
* more support for advanced linux 2.6.x features, see: iotop
most likely there is more
-
-* even more support for statistics: mpstat, iostat, powertop....
diff --git a/android/librpc/libintl.h b/android/librpc/libintl.h
deleted file mode 100644
index e69de29..0000000
--- a/android/librpc/libintl.h
+++ b/dev/null
diff --git a/applets/Kbuild.src b/applets/Kbuild.src
index b612399..3aedbbf 100644
--- a/applets/Kbuild.src
+++ b/applets/Kbuild.src
@@ -29,7 +29,7 @@ applets/applets.o: include/usage_compressed.h include/applet_tables.h
applets/applet_tables: .config include/applets.h
applets/usage: .config include/applets.h
-applets/usage_pod: .config include/applet_tables.h include/applets.h
+applets/usage_pod: .config include/applets.h include/applet_tables.h
quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets
@@ -37,11 +37,21 @@ quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed
$(call cmd,gen_usage_compressed)
-quiet_cmd_gen_applet_tables = GEN include/applet_tables.h
+quiet_cmd_gen_applet_tables = GEN include/applet_tables.h include/NUM_APPLETS.h
cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.h
-include/applet_tables.h: applets/applet_tables
+include/NUM_APPLETS.h: applets/applet_tables
$(call cmd,gen_applet_tables)
-include/NUM_APPLETS.h: applets/applet_tables
+# In fact, include/applet_tables.h depends only on applets/applet_tables,
+# and is generated by it. But specifying only it can run
+# applets/applet_tables twice, possibly in parallel.
+# We say that it also needs NUM_APPLETS.h
+#
+# Unfortunately, we need to list the same command,
+# and it can be executed twice (sequentially).
+# The alternative is to not list any command,
+# and then if include/applet_tables.h is deleted, it won't be rebuilt.
+#
+include/applet_tables.h: include/NUM_APPLETS.h applets/applet_tables
$(call cmd,gen_applet_tables)
diff --git a/applets/applet_tables.c b/applets/applet_tables.c
index 94b974e..ef911a4 100644
--- a/applets/applet_tables.c
+++ b/applets/applet_tables.c
@@ -14,6 +14,7 @@
#include <string.h>
#include <stdio.h>
#include <unistd.h>
+#include <ctype.h>
#undef ARRAY_SIZE
#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
@@ -40,8 +41,6 @@ struct bb_applet {
enum { NUM_APPLETS = ARRAY_SIZE(applets) };
-static int offset[NUM_APPLETS];
-
static int cmp_name(const void *a, const void *b)
{
const struct bb_applet *aa = a;
@@ -49,26 +48,42 @@ static int cmp_name(const void *a, const void *b)
return strcmp(aa->name, bb->name);
}
+static int str_isalnum_(const char *s)
+{
+ while (*s) {
+ if (!isalnum(*s) && *s != '_')
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
int main(int argc, char **argv)
{
- int i;
- int ofs;
-// unsigned MAX_APPLET_NAME_LEN = 1;
+ int i, j;
+
+ // In find_applet_by_name(), before linear search, narrow it down
+ // by looking at N "equidistant" names. With ~350 applets:
+ // KNOWN_APPNAME_OFFSETS cycles
+ // 0 9057
+ // 2 4604 + ~100 bytes of code
+ // 4 2407 + 4 bytes
+ // 8 1342 + 8 bytes
+ // 16 908 + 16 bytes
+ // 32 884 + 32 bytes
+ // With 8, int16_t applet_nameofs[] table has 7 elements.
+ int KNOWN_APPNAME_OFFSETS = 8;
+ // With 128 applets we do two linear searches, with 1..7 strcmp's in the first one
+ // and 1..16 strcmp's in the second. With 256 apps, second search does 1..32 strcmp's.
+ if (NUM_APPLETS < 128)
+ KNOWN_APPNAME_OFFSETS = 4;
+ if (NUM_APPLETS < 32)
+ KNOWN_APPNAME_OFFSETS = 0;
qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name);
- ofs = 0;
- for (i = 0; i < NUM_APPLETS; i++) {
- offset[i] = ofs;
- ofs += strlen(applets[i].name) + 1;
- }
- /* We reuse 4 high-order bits of offset array for other purposes,
- * so if they are indeed needed, refuse to proceed */
- if (ofs > 0xfff)
- return 1;
if (!argv[1])
return 1;
-
i = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (i < 0)
return 1;
@@ -83,7 +98,27 @@ int main(int argc, char **argv)
printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name);
printf("#define SINGLE_APPLET_MAIN %s_main\n", applets[0].main);
}
- printf("\n");
+
+ printf("#define KNOWN_APPNAME_OFFSETS %u\n\n", KNOWN_APPNAME_OFFSETS);
+ if (KNOWN_APPNAME_OFFSETS > 0) {
+ int ofs, offset[KNOWN_APPNAME_OFFSETS], index[KNOWN_APPNAME_OFFSETS];
+ for (i = 0; i < KNOWN_APPNAME_OFFSETS; i++)
+ index[i] = i * NUM_APPLETS / KNOWN_APPNAME_OFFSETS;
+ ofs = 0;
+ for (i = 0; i < NUM_APPLETS; i++) {
+ for (j = 0; j < KNOWN_APPNAME_OFFSETS; j++)
+ if (i == index[j])
+ offset[j] = ofs;
+ ofs += strlen(applets[i].name) + 1;
+ }
+ /* If the list of names is too long refuse to proceed */
+ if (ofs > 0xffff)
+ return 1;
+ printf("const uint16_t applet_nameofs[] ALIGN2 = {\n");
+ for (i = 1; i < KNOWN_APPNAME_OFFSETS; i++)
+ printf("%d,\n", offset[i]);
+ printf("};\n\n");
+ }
//printf("#ifndef SKIP_definitions\n");
printf("const char applet_names[] ALIGN1 = \"\"\n");
@@ -94,6 +129,12 @@ int main(int argc, char **argv)
}
printf(";\n\n");
+ for (i = 0; i < NUM_APPLETS; i++) {
+ if (str_isalnum_(applets[i].name))
+ printf("#define APPLET_NO_%s %d\n", applets[i].name, i);
+ }
+ printf("\n");
+
printf("#ifndef SKIP_applet_main\n");
printf("int (*const applet_main[])(int argc, char **argv) = {\n");
for (i = 0; i < NUM_APPLETS; i++) {
@@ -102,20 +143,41 @@ int main(int argc, char **argv)
printf("};\n");
printf("#endif\n\n");
- printf("const uint16_t applet_nameofs[] ALIGN2 = {\n");
- for (i = 0; i < NUM_APPLETS; i++) {
- printf("0x%04x,\n",
- offset[i]
-#if ENABLE_FEATURE_PREFER_APPLETS
- + (applets[i].nofork << 12)
- + (applets[i].noexec << 13)
+#if ENABLE_FEATURE_PREFER_APPLETS \
+ || ENABLE_FEATURE_SH_STANDALONE \
+ || ENABLE_FEATURE_SH_NOFORK
+ printf("const uint8_t applet_flags[] ALIGN1 = {\n");
+ i = 0;
+ while (i < NUM_APPLETS) {
+ int v = applets[i].nofork + (applets[i].noexec << 1);
+ if (++i < NUM_APPLETS)
+ v |= (applets[i].nofork + (applets[i].noexec << 1)) << 2;
+ if (++i < NUM_APPLETS)
+ v |= (applets[i].nofork + (applets[i].noexec << 1)) << 4;
+ if (++i < NUM_APPLETS)
+ v |= (applets[i].nofork + (applets[i].noexec << 1)) << 6;
+ printf("0x%02x,\n", v);
+ i++;
+ }
+ printf("};\n\n");
#endif
+
#if ENABLE_FEATURE_SUID
- + (applets[i].need_suid << 14) /* 2 bits */
-#endif
- );
+ printf("const uint8_t applet_suid[] ALIGN1 = {\n");
+ i = 0;
+ while (i < NUM_APPLETS) {
+ int v = applets[i].need_suid; /* 2 bits */
+ if (++i < NUM_APPLETS)
+ v |= applets[i].need_suid << 2;
+ if (++i < NUM_APPLETS)
+ v |= applets[i].need_suid << 4;
+ if (++i < NUM_APPLETS)
+ v |= applets[i].need_suid << 6;
+ printf("0x%02x,\n", v);
+ i++;
}
printf("};\n\n");
+#endif
#if ENABLE_FEATURE_INSTALLER
printf("const uint8_t applet_install_loc[] ALIGN1 = {\n");
@@ -130,27 +192,28 @@ int main(int argc, char **argv)
printf("};\n");
#endif
//printf("#endif /* SKIP_definitions */\n");
+
// printf("\n");
// printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN);
if (argv[2]) {
- char line_old[80];
- char line_new[80];
FILE *fp;
+ char line_new[80];
+// char line_old[80];
- line_old[0] = 0;
- fp = fopen(argv[2], "r");
- if (fp) {
- fgets(line_old, sizeof(line_old), fp);
- fclose(fp);
- }
sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS);
- if (strcmp(line_old, line_new) != 0) {
+// line_old[0] = 0;
+// fp = fopen(argv[2], "r");
+// if (fp) {
+// fgets(line_old, sizeof(line_old), fp);
+// fclose(fp);
+// }
+// if (strcmp(line_old, line_new) != 0) {
fp = fopen(argv[2], "w");
if (!fp)
return 1;
fputs(line_new, fp);
- }
+// }
}
return 0;
diff --git a/applets/install.sh b/applets/install.sh
index 95b4719..f6c097e 100755
--- a/applets/install.sh
+++ b/applets/install.sh
@@ -5,19 +5,26 @@ export LC_CTYPE=POSIX
prefix=$1
if [ -z "$prefix" ]; then
- echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--scriptwrapper]"
+ echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--binaries/--scriptwrapper]"
exit 1
fi
+# Source the configuration
+. ./.config
+
h=`sort busybox.links | uniq`
+sharedlib_dir="0_lib"
+
linkopts=""
scriptwrapper="n"
+binaries="n"
cleanup="0"
noclobber="0"
case "$2" in
--hardlinks) linkopts="-f";;
--symlinks) linkopts="-fs";;
+ --binaries) binaries="y";;
--scriptwrapper) scriptwrapper="y";swrapall="y";;
--sw-sh-hard) scriptwrapper="y";linkopts="-f";;
--sw-sh-sym) scriptwrapper="y";linkopts="-fs";;
@@ -40,8 +47,9 @@ if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then
for i in $DO_INSTALL_LIBS; do
rm -f "$prefix/$libdir/$i" || exit 1
if [ -f "$i" ]; then
+ echo " Installing $i to the target at $prefix/$libdir/"
cp -pPR "$i" "$prefix/$libdir/" || exit 1
- chmod 0644 "$prefix/$libdir/$i" || exit 1
+ chmod 0644 "$prefix/$libdir/`basename $i`" || exit 1
fi
done
fi
@@ -68,6 +76,7 @@ install -m 755 busybox "$prefix/bin/busybox" || exit 1
for i in $h; do
appdir=`dirname "$i"`
+ app=`basename "$i"`
mkdir -p "$prefix/$appdir" || exit 1
if [ "$scriptwrapper" = "y" ]; then
if [ "$swrapall" != "y" ] && [ "$i" = "/bin/sh" ]; then
@@ -78,6 +87,19 @@ for i in $h; do
chmod +x "$prefix/$i"
fi
echo " $prefix/$i"
+ elif [ "$binaries" = "y" ]; then
+ # Copy the binary over rather
+ if [ -e $sharedlib_dir/$app ]; then
+ if [ "$noclobber" = "0" ] || [ ! -e "$prefix/$i" ]; then
+ echo " Copying $sharedlib_dir/$app to $prefix/$i"
+ cp -pPR $sharedlib_dir/$app $prefix/$i || exit 1
+ else
+ echo " $prefix/$i already exists"
+ fi
+ else
+ echo "Error: Could not find $sharedlib_dir/$app"
+ exit 1
+ fi
else
if [ "$2" = "--hardlinks" ]; then
bb_path="$prefix/bin/busybox"
diff --git a/applets/usage_compressed b/applets/usage_compressed
index fb6e1c2..186fcde 100755
--- a/applets/usage_compressed
+++ b/applets/usage_compressed
@@ -20,6 +20,7 @@ exec >"$target.$$"
echo '#define UNPACKED_USAGE "" \'
"$loc/usage" | od -v -b \
+| grep -v '^ ' \
| $SED -e 's/^[^ ]*//' \
-e 's/ //g' \
-e '/^$/d' \
@@ -27,6 +28,13 @@ echo '#define UNPACKED_USAGE "" \'
-e 's/^/"/' \
-e 's/$/" \\/'
echo ''
+# "grep -v '^ '" is for toybox's od bug: od -b prints some extra lines:
+#0000000 010 000 010 000 133 055 144 146 135 040 133 055 143 040 103 117
+# 000010 000010 026533 063144 020135 026533 020143 047503
+#0000020 116 106 104 111 122 135 040 133 055 154 040 114 117 107 106 111
+# 043116 044504 056522 055440 066055 046040 043517 044506
+#0000040 114 105 135 040 133 055 141 040 101 103 124 111 117 116 106 111
+# 042514 020135 026533 020141 041501 044524 047117 044506
echo '#define PACKED_USAGE \'
## Breaks on big-endian systems!
@@ -40,6 +48,7 @@ echo '#define PACKED_USAGE \'
## -e 's/\(..\)\(..\)/0x\2,0x\1,/g'
## -e 's/$/ \\/'
"$loc/usage" | bzip2 -1 | $DD bs=2 skip=1 2>/dev/null | od -v -b \
+| grep -v '^ ' \
| $SED -e 's/^[^ ]*//' \
-e 's/ //g' \
-e '/^$/d' \
diff --git a/applets/usage_pod.c b/applets/usage_pod.c
index a67e8b4..9e6d3f0 100644
--- a/applets/usage_pod.c
+++ b/applets/usage_pod.c
@@ -90,6 +90,8 @@ int main(void)
printf("%s\n\n", usage_array[i].aname);
}
}
+ printf("=back\n\n");
+
return 0;
}
diff --git a/archival/Config.src b/archival/Config.src
index 76635ba..5e7cfc0 100644
--- a/archival/Config.src
+++ b/archival/Config.src
@@ -30,8 +30,8 @@ config FEATURE_SEAMLESS_GZ
Make tar, rpm, modprobe etc understand .gz data.
config FEATURE_SEAMLESS_Z
- bool "tar, rpm, modprobe etc understand .Z data"
- default n
+ bool "Make tar, rpm, modprobe etc understand .Z data"
+ default n # it is ancient
help
Make tar, rpm, modprobe etc understand .Z data.
diff --git a/archival/Kbuild.src b/archival/Kbuild.src
index a6fd2ea..b3a7d53 100644
--- a/archival/Kbuild.src
+++ b/archival/Kbuild.src
@@ -4,7 +4,7 @@
#
# Licensed under GPLv2, see file LICENSE in this source tree.
-libs-y += libarchive/
+libs-y += libarchive/
lib-y:=
diff --git a/archival/ar.c b/archival/ar.c
index f86c52d..e49d5cb 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -22,23 +22,13 @@
//config: default n # needs to be improved to be able to replace binutils ar
//config: help
//config: ar is an archival utility program used to create, modify, and
-//config: extract contents from archives. An archive is a single file holding
-//config: a collection of other files in a structure that makes it possible to
-//config: retrieve the original individual files (called archive members).
-//config: The original files' contents, mode (permissions), timestamp, owner,
-//config: and group are preserved in the archive, and can be restored on
-//config: extraction.
+//config: extract contents from archives. In practice, it is used exclusively
+//config: for object module archives used by compilers.
//config:
-//config: The stored filename is limited to 15 characters. (for more information
-//config: see long filename support).
-//config: ar has 60 bytes of overheads for every stored file.
-//config:
-//config: This implementation of ar can extract archives, it cannot create or
-//config: modify them.
//config: On an x86 system, the ar applet adds about 1K.
//config:
//config: Unless you have a specific application which requires ar, you should
-//config: probably say N here.
+//config: probably say N here: most compilers come with their own ar utility.
//config:
//config:config FEATURE_AR_LONG_FILENAMES
//config: bool "Support for long filenames (not needed for debs)"
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index fce5ab9..60a837e 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -9,6 +9,12 @@
/* lzop_main() uses bbunpack(), need this: */
//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o
+//kbuild:lib-$(CONFIG_LZOPCAT) += bbunzip.o
+//kbuild:lib-$(CONFIG_UNLZOP) += bbunzip.o
+/* bzip2_main() too: */
+//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
+/* gzip_main() too: */
+//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
/* Note: must be kept in sync with archival/lzop.c */
enum {
@@ -39,7 +45,7 @@ char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
}
int FAST_FUNC bbunpack(char **argv,
- IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
+ IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_state_t *xstate),
char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
const char *expected_ext
)
@@ -48,7 +54,7 @@ int FAST_FUNC bbunpack(char **argv,
IF_DESKTOP(long long) int status = 0;
char *filename, *new_name;
smallint exitcode = 0;
- transformer_aux_data_t aux;
+ transformer_state_t xstate;
do {
/* NB: new_name is *maybe* malloc'ed! */
@@ -120,9 +126,11 @@ int FAST_FUNC bbunpack(char **argv,
}
if (!(option_mask32 & SEAMLESS_MAGIC)) {
- init_transformer_aux_data(&aux);
- aux.check_signature = 1;
- status = unpacker(&aux);
+ init_transformer_state(&xstate);
+ xstate.signature_skipped = 0;
+ /*xstate.src_fd = STDIN_FILENO; - already is */
+ xstate.dst_fd = STDOUT_FILENO;
+ status = unpacker(&xstate);
if (status < 0)
exitcode = 1;
} else {
@@ -141,10 +149,10 @@ int FAST_FUNC bbunpack(char **argv,
unsigned new_name_len;
/* TODO: restore other things? */
- if (aux.mtime != 0) {
+ if (xstate.mtime != 0) {
struct timeval times[2];
- times[1].tv_sec = times[0].tv_sec = aux.mtime;
+ times[1].tv_sec = times[0].tv_sec = xstate.mtime;
times[1].tv_usec = times[0].tv_usec = 0;
/* Note: we closed it first.
* On some systems calling utimes
@@ -188,7 +196,10 @@ int FAST_FUNC bbunpack(char **argv,
return exitcode;
}
-#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
+#if ENABLE_UNCOMPRESS \
+ || ENABLE_BUNZIP2 || ENABLE_BZCAT \
+ || ENABLE_UNLZMA || ENABLE_LZCAT || ENABLE_LZMA \
+ || ENABLE_UNXZ || ENABLE_XZCAT || ENABLE_XZ
static
char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
{
@@ -218,7 +229,7 @@ char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
//config:config UNCOMPRESS
//config: bool "uncompress"
-//config: default n
+//config: default n # ancient
//config: help
//config: uncompress is used to decompress archives created by compress.
//config: Not much used anymore, replaced by gzip/gunzip.
@@ -226,18 +237,13 @@ char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
//applet:IF_UNCOMPRESS(APPLET(uncompress, BB_DIR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_UNCOMPRESS) += bbunzip.o
#if ENABLE_UNCOMPRESS
-static
-IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(transformer_aux_data_t *aux)
-{
- return unpack_Z_stream(aux, STDIN_FILENO, STDOUT_FILENO);
-}
int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int uncompress_main(int argc UNUSED_PARAM, char **argv)
{
getopt32(argv, "cf");
argv += optind;
- return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
+ return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z");
}
#endif
@@ -293,12 +299,25 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
//config: gunzip is used to decompress archives created by gzip.
//config: You can use the `-t' option to test the integrity of
//config: an archive, without decompressing it.
+//config:
+//config:config ZCAT
+//config: bool "zcat"
+//config: default y
+//config: help
+//config: Alias to "gunzip -c".
+//config:
+//config:config FEATURE_GUNZIP_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on (GUNZIP || ZCAT) && LONG_OPTS
+//config: help
+//config: Enable use of long options.
//applet:IF_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_GUNZIP(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat))
-//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
+//applet:IF_ZCAT(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat))
//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o
-#if ENABLE_GUNZIP
+//kbuild:lib-$(CONFIG_ZCAT) += bbunzip.o
+#if ENABLE_GUNZIP || ENABLE_ZCAT
static
char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
{
@@ -324,11 +343,17 @@ char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UN
}
return filename;
}
-static
-IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
-{
- return unpack_gz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
-}
+
+#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
+static const char gunzip_longopts[] ALIGN1 =
+ "stdout\0" No_argument "c"
+ "to-stdout\0" No_argument "c"
+ "force\0" No_argument "f"
+ "test\0" No_argument "t"
+ "no-name\0" No_argument "n"
+ ;
+#endif
+
/*
* Linux kernel build uses gzip -d -n. We accept and ignore it.
* Man page says:
@@ -345,6 +370,9 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int gunzip_main(int argc UNUSED_PARAM, char **argv)
{
+#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
+ applet_long_options = gunzip_longopts;
+#endif
getopt32(argv, "cfvqdtn");
argv += optind;
@@ -352,10 +380,10 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
* Normally, "zcat" is just "gunzip -c".
* But if seamless magic is enabled, then we are much more clever.
*/
- if (applet_name[1] == 'c')
+ if (ENABLE_ZCAT && applet_name[1] == 'c')
option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
- return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
+ return bbunpack(argv, unpack_gz_stream, make_new_name_gunzip, /*unused:*/ NULL);
}
#endif
@@ -389,26 +417,27 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
//config:
//config: Unless you have a specific application which requires bunzip2, you
//config: should probably say N here.
+//config:
+//config:config BZCAT
+//config: bool "bzcat"
+//config: default y
+//config: help
+//config: Alias to "bunzip2 -c".
//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
-//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
-//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
+//applet:IF_BZCAT(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o
-#if ENABLE_BUNZIP2
-static
-IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
-{
- return unpack_bz2_stream(aux, STDIN_FILENO, STDOUT_FILENO);
-}
+//kbuild:lib-$(CONFIG_BZCAT) += bbunzip.o
+#if ENABLE_BUNZIP2 || ENABLE_BZCAT
int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int bunzip2_main(int argc UNUSED_PARAM, char **argv)
{
getopt32(argv, "cfvqdt");
argv += optind;
- if (applet_name[2] == 'c') /* bzcat */
+ if (ENABLE_BZCAT && applet_name[2] == 'c') /* bzcat */
option_mask32 |= OPT_STDOUT;
- return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
+ return bbunpack(argv, unpack_bz2_stream, make_new_name_generic, "bz2");
}
#endif
@@ -473,32 +502,40 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
//config: The BusyBox unlzma applet is limited to decompression only.
//config: On an x86 system, this applet adds about 4K.
//config:
-//config:config FEATURE_LZMA_FAST
-//config: bool "Optimize unlzma for speed"
-//config: default n
-//config: depends on UNLZMA
+//config:config LZCAT
+//config: bool "lzcat"
+//config: default y
//config: help
-//config: This option reduces decompression time by about 25% at the cost of
-//config: a 1K bigger binary.
+//config: unlzma is a compression utility using the Lempel-Ziv-Markov chain
+//config: compression algorithm, and range coding. Compression
+//config: is generally considerably better than that achieved by the bzip2
+//config: compressors.
+//config:
+//config: The BusyBox unlzma applet is limited to decompression only.
+//config: On an x86 system, this applet adds about 4K.
//config:
//config:config LZMA
-//config: bool "Provide lzma alias which supports only unpacking"
+//config: bool "lzma -d"
//config: default y
-//config: depends on UNLZMA
//config: help
//config: Enable this option if you want commands like "lzma -d" to work.
//config: IOW: you'll get lzma applet, but it will always require -d option.
+//config:
+//config:config FEATURE_LZMA_FAST
+//config: bool "Optimize unlzma for speed"
+//config: default n
+//config: depends on UNLZMA || LZCAT || LZMA
+//config: help
+//config: This option reduces decompression time by about 25% at the cost of
+//config: a 1K bigger binary.
//applet:IF_UNLZMA(APPLET(unlzma, BB_DIR_USR_BIN, BB_SUID_DROP))
-//applet:IF_UNLZMA(APPLET_ODDNAME(lzcat, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzcat))
+//applet:IF_LZCAT(APPLET_ODDNAME(lzcat, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzcat))
//applet:IF_LZMA(APPLET_ODDNAME(lzma, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzma))
//kbuild:lib-$(CONFIG_UNLZMA) += bbunzip.o
-#if ENABLE_UNLZMA
-static
-IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(transformer_aux_data_t *aux)
-{
- return unpack_lzma_stream(aux, STDIN_FILENO, STDOUT_FILENO);
-}
+//kbuild:lib-$(CONFIG_LZCAT) += bbunzip.o
+//kbuild:lib-$(CONFIG_LZMA) += bbunzip.o
+#if ENABLE_UNLZMA || ENABLE_LZCAT || ENABLE_LZMA
int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int unlzma_main(int argc UNUSED_PARAM, char **argv)
{
@@ -509,11 +546,11 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
bb_show_usage();
# endif
/* lzcat? */
- if (applet_name[2] == 'c')
+ if (ENABLE_LZCAT && applet_name[2] == 'c')
option_mask32 |= OPT_STDOUT;
argv += optind;
- return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
+ return bbunpack(argv, unpack_lzma_stream, make_new_name_generic, "lzma");
}
#endif
@@ -524,24 +561,26 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
//config: help
//config: unxz is a unlzma successor.
//config:
+//config:config XZCAT
+//config: bool "xzcat"
+//config: default y
+//config: help
+//config: Alias to "unxz -c".
+//config:
//config:config XZ
-//config: bool "Provide xz alias which supports only unpacking"
+//config: bool "xz -d"
//config: default y
-//config: depends on UNXZ
//config: help
//config: Enable this option if you want commands like "xz -d" to work.
//config: IOW: you'll get xz applet, but it will always require -d option.
//applet:IF_UNXZ(APPLET(unxz, BB_DIR_USR_BIN, BB_SUID_DROP))
-//applet:IF_UNXZ(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat))
+//applet:IF_XZCAT(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat))
//applet:IF_XZ(APPLET_ODDNAME(xz, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xz))
//kbuild:lib-$(CONFIG_UNXZ) += bbunzip.o
-#if ENABLE_UNXZ
-static
-IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(transformer_aux_data_t *aux)
-{
- return unpack_xz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
-}
+//kbuild:lib-$(CONFIG_XZCAT) += bbunzip.o
+//kbuild:lib-$(CONFIG_XZ) += bbunzip.o
+#if ENABLE_UNXZ || ENABLE_XZCAT || ENABLE_XZ
int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int unxz_main(int argc UNUSED_PARAM, char **argv)
{
@@ -552,10 +591,10 @@ int unxz_main(int argc UNUSED_PARAM, char **argv)
bb_show_usage();
# endif
/* xzcat? */
- if (applet_name[2] == 'c')
+ if (ENABLE_XZCAT && applet_name[2] == 'c')
option_mask32 |= OPT_STDOUT;
argv += optind;
- return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
+ return bbunpack(argv, unpack_xz_stream, make_new_name_generic, "xz");
}
#endif
diff --git a/archival/bzip2.c b/archival/bzip2.c
index f7718b4..47fa29a 100644
--- a/archival/bzip2.c
+++ b/archival/bzip2.c
@@ -127,7 +127,7 @@ IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, vo
}
static
-IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_aux_data_t *aux UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_state_t *xstate UNUSED_PARAM)
{
IF_DESKTOP(long long) int total;
ssize_t count;
diff --git a/archival/cpio.c b/archival/cpio.c
index 5fb5327..be98b42 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -11,6 +11,7 @@
* Only supports new ASCII and CRC formats
*/
#include "libbb.h"
+#include "common_bufsiz.h"
#include "bb_archive.h"
//config:config CPIO
@@ -22,7 +23,7 @@
//config: cpio has 110 bytes of overheads for every stored file.
//config:
//config: This implementation of cpio can extract cpio archives created in the
-//config: "newc" or "crc" format, it cannot create or modify them.
+//config: "newc" or "crc" format.
//config:
//config: Unless you have a specific application which requires cpio, you
//config: should probably say N here.
@@ -46,14 +47,14 @@
//kbuild:lib-$(CONFIG_CPIO) += cpio.o
//usage:#define cpio_trivial_usage
-//usage: "[-dmvu] [-F FILE]" IF_FEATURE_CPIO_O(" [-H newc]")
+//usage: "[-dmvu] [-F FILE] [-R USER[:GRP]]" IF_FEATURE_CPIO_O(" [-H newc]")
//usage: " [-ti"IF_FEATURE_CPIO_O("o")"]" IF_FEATURE_CPIO_P(" [-p DIR]")
//usage: " [EXTR_FILE]..."
//usage:#define cpio_full_usage "\n\n"
-//usage: "Extract or list files from a cpio archive"
+//usage: "Extract (-i) or list (-t) files from a cpio archive"
//usage: IF_FEATURE_CPIO_O(", or"
-//usage: "\ncreate an archive" IF_FEATURE_CPIO_P(" (-o) or copy files (-p)")
-//usage: " using file list on stdin"
+//usage: "\ntake file list from stdin and create an archive (-o)"
+//usage: IF_FEATURE_CPIO_P(" or copy files (-p)")
//usage: )
//usage: "\n"
//usage: "\nMain operation mode:"
@@ -65,11 +66,13 @@
//usage: IF_FEATURE_CPIO_P(
//usage: "\n -p DIR Copy files to DIR"
//usage: )
+//usage: "\nOptions:"
//usage: "\n -d Make leading directories"
//usage: "\n -m Preserve mtime"
//usage: "\n -v Verbose"
//usage: "\n -u Overwrite"
//usage: "\n -F FILE Input (-t,-i,-p) or output (-o) file"
+//usage: "\n -R USER[:GRP] Set owner of created files"
//usage: IF_FEATURE_CPIO_O(
//usage: "\n -H newc Archive format"
//usage: )
@@ -129,7 +132,7 @@
-I FILE File to use instead of standard input
-L, --dereference Dereference symbolic links (copy the files
that they point to instead of copying the links)
- -R, --owner=[USER][:.][GROUP] Set owner of created files
+ -R, --owner=[USER][:.][GRP] Set owner of created files
Options valid in --extract and --pass-through modes:
-d, --make-directories Create leading directories where needed
@@ -149,7 +152,8 @@ enum {
OPT_PRESERVE_MTIME = (1 << 6),
OPT_DEREF = (1 << 7),
OPT_FILE = (1 << 8),
- OPTBIT_FILE = 8,
+ OPT_OWNER = (1 << 9),
+ OPTBIT_OWNER = 9,
IF_FEATURE_CPIO_O(OPTBIT_CREATE ,)
IF_FEATURE_CPIO_O(OPTBIT_FORMAT ,)
IF_FEATURE_CPIO_P(OPTBIT_PASSTHROUGH,)
@@ -162,7 +166,18 @@ enum {
OPT_2STDOUT = IF_LONG_OPTS( (1 << OPTBIT_2STDOUT )) + 0,
};
-#define OPTION_STR "it0uvdmLF:"
+#define OPTION_STR "it0uvdmLF:R:"
+
+struct globals {
+ struct bb_uidgid_t owner_ugid;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+void BUG_cpio_globals_too_big(void);
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ G.owner_ugid.uid = -1L; \
+ G.owner_ugid.gid = -1L; \
+} while (0)
#if ENABLE_FEATURE_CPIO_O
static off_t cpio_pad4(off_t size)
@@ -180,7 +195,6 @@ static off_t cpio_pad4(off_t size)
* It's ok to exit instead of return. */
static NOINLINE int cpio_o(void)
{
- static const char trailer[] ALIGN1 = "TRAILER!!!";
struct name_s {
struct name_s *next;
char name[1];
@@ -222,6 +236,11 @@ static NOINLINE int cpio_o(void)
bb_simple_perror_msg_and_die(name);
}
+ if (G.owner_ugid.uid != (uid_t)-1L)
+ st.st_uid = G.owner_ugid.uid;
+ if (G.owner_ugid.gid != (gid_t)-1L)
+ st.st_gid = G.owner_ugid.gid;
+
if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
st.st_size = 0; /* paranoia */
@@ -256,7 +275,6 @@ static NOINLINE int cpio_o(void)
free(line);
continue;
}
-
} else { /* line == NULL: EOF */
next_link:
if (links) {
@@ -275,7 +293,7 @@ static NOINLINE int cpio_o(void)
} else {
/* If no (more) hardlinks to output,
* output "trailer" entry */
- name = trailer;
+ name = cpio_TRAILER;
/* st.st_size == 0 is a must, but for uniformity
* in the output, we zero out everything */
memset(&st, 0, sizeof(st));
@@ -323,7 +341,7 @@ static NOINLINE int cpio_o(void)
}
if (!line) {
- if (name != trailer)
+ if (name != cpio_TRAILER)
goto next_link;
/* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
return EXIT_SUCCESS;
@@ -339,6 +357,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
{
archive_handle_t *archive_handle;
char *cpio_filename;
+ char *cpio_owner;
IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
unsigned opt;
@@ -353,12 +372,14 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
"pass-through\0" No_argument "p"
#endif
#endif
+ "owner\0" Required_argument "R"
"verbose\0" No_argument "v"
"quiet\0" No_argument "\xff"
"to-stdout\0" No_argument "\xfe"
;
#endif
+ INIT_G();
archive_handle = init_handle();
/* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */
archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
@@ -369,14 +390,21 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
/* -L makes sense only with -o or -p */
#if !ENABLE_FEATURE_CPIO_O
- opt = getopt32(argv, OPTION_STR, &cpio_filename);
+ opt = getopt32(argv, OPTION_STR, &cpio_filename, &cpio_owner);
+#else
+ opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"),
+ &cpio_filename, &cpio_owner, &cpio_fmt);
+#endif
argv += optind;
+ if (opt & OPT_OWNER) { /* -R */
+ parse_chown_usergroup_or_die(&G.owner_ugid, cpio_owner);
+ archive_handle->cpio__owner = G.owner_ugid;
+ }
+#if !ENABLE_FEATURE_CPIO_O
if (opt & OPT_FILE) { /* -F */
xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
}
#else
- opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt);
- argv += optind;
if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */
xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
}
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 2893cfc..f133299 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -28,7 +28,7 @@
//config:config DPKG
//config: bool "dpkg"
-//config: default n
+//config: default y
//config: select FEATURE_SEAMLESS_GZ
//config: help
//config: dpkg is a medium-level tool to install, build, remove and manage
@@ -1151,13 +1151,13 @@ static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count
if (result && number_of_alternatives == 0) {
if (root_of_alternatives)
bb_error_msg_and_die(
- "package %s %sdepends on %s, "
- "which cannot be satisfied",
+ "package %s %sdepends on %s, which %s",
name_hashtable[package_node->name],
package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
- name_hashtable[root_of_alternatives->name]);
+ name_hashtable[root_of_alternatives->name],
+ "cannot be satisfied");
bb_error_msg_and_die(
- "package %s %sdepends on %s, which %s\n",
+ "package %s %sdepends on %s, which %s",
name_hashtable[package_node->name],
package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
name_hashtable[package_edge->name],
@@ -1472,12 +1472,16 @@ static void init_archive_deb_control(archive_handle_t *ar_handle)
tar_handle->src_fd = ar_handle->src_fd;
/* We don't care about data.tar.* or debian-binary, just control.tar.* */
+ llist_add_to(&(ar_handle->accept), (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
#endif
+#if ENABLE_FEATURE_SEAMLESS_XZ
+ llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
+#endif
/* Assign the tar handle as a subarchive of the ar handle */
ar_handle->dpkg__sub_archive = tar_handle;
@@ -1492,12 +1496,19 @@ static void init_archive_deb_data(archive_handle_t *ar_handle)
tar_handle->src_fd = ar_handle->src_fd;
/* We don't care about control.tar.* or debian-binary, just data.tar.* */
+ llist_add_to(&(ar_handle->accept), (char*)"data.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
#endif
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+ llist_add_to(&(ar_handle->accept), (char*)"data.tar.lzma");
+#endif
+#if ENABLE_FEATURE_SEAMLESS_XZ
+ llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
+#endif
/* Assign the tar handle as a subarchive of the ar handle */
ar_handle->dpkg__sub_archive = tar_handle;
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index 13f9db9..ebbc7f0 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -7,7 +7,7 @@
//config:config DPKG_DEB
//config: bool "dpkg_deb"
-//config: default n
+//config: default y
//config: select FEATURE_SEAMLESS_GZ
//config: help
//config: dpkg-deb unpacks and provides information about Debian archives.
@@ -16,29 +16,20 @@
//config:
//config: Unless you have a specific application which requires dpkg-deb,
//config: say N here.
-//config:
-//config:config FEATURE_DPKG_DEB_EXTRACT_ONLY
-//config: bool "Extract only (-x)"
-//config: default n
-//config: depends on DPKG_DEB
-//config: help
-//config: This reduces dpkg-deb to the equivalent of
-//config: "ar -p <deb> data.tar.gz | tar -zx". However it saves space as none
-//config: of the extra dpkg-deb, ar or tar options are needed, they are linked
-//config: to internally.
//applet:IF_DPKG_DEB(APPLET_ODDNAME(dpkg-deb, dpkg_deb, BB_DIR_USR_BIN, BB_SUID_DROP, dpkg_deb))
+
//kbuild:lib-$(CONFIG_DPKG_DEB) += dpkg_deb.o
//usage:#define dpkg_deb_trivial_usage
-//usage: "[-cefxX] FILE [argument]"
+//usage: "[-cefxX] FILE [DIR]"
//usage:#define dpkg_deb_full_usage "\n\n"
-//usage: "Perform actions on Debian packages (.debs)\n"
-//usage: "\n -c List contents of filesystem tree"
-//usage: "\n -e Extract control files to [argument] directory"
-//usage: "\n -f Display control field name starting with [argument]"
-//usage: "\n -x Extract packages filesystem tree to directory"
-//usage: "\n -X Verbose extract"
+//usage: "Perform actions on Debian packages (.deb)\n"
+//usage: "\n -c List files"
+//usage: "\n -f Print control fields"
+//usage: "\n -e Extract control files to DIR (default: ./DEBIAN)"
+//usage: "\n -x Extract files to DIR (no default)"
+//usage: "\n -X Verbose -x"
//usage:
//usage:#define dpkg_deb_example_usage
//usage: "$ dpkg-deb -X ./busybox_0.48-1_i386.deb /tmp\n"
@@ -49,18 +40,17 @@
#define DPKG_DEB_OPT_CONTENTS 1
#define DPKG_DEB_OPT_CONTROL 2
#define DPKG_DEB_OPT_FIELD 4
-#define DPKG_DEB_OPT_EXTRACT 8
-#define DPKG_DEB_OPT_EXTRACT_VERBOSE 16
+#define DPKG_DEB_OPT_EXTRACT_VERBOSE 8
+#define DPKG_DEB_OPT_EXTRACT 16
int dpkg_deb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int dpkg_deb_main(int argc, char **argv)
+int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
{
archive_handle_t *ar_archive;
archive_handle_t *tar_archive;
llist_t *control_tar_llist = NULL;
unsigned opt;
const char *extract_dir;
- int need_args;
/* Setup the tar archive handle */
tar_archive = init_handle();
@@ -70,6 +60,8 @@ int dpkg_deb_main(int argc, char **argv)
ar_archive->dpkg__sub_archive = tar_archive;
ar_archive->filter = filter_accept_list_reassign;
+ llist_add_to(&ar_archive->accept, (char*)"data.tar");
+ llist_add_to(&control_tar_llist, (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
@@ -82,54 +74,50 @@ int dpkg_deb_main(int argc, char **argv)
llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
#endif
+#if ENABLE_FEATURE_SEAMLESS_XZ
+ llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
+ llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
+#endif
- opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
+ /* Must have 1 or 2 args */
+ opt_complementary = "-1:?2:c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
opt = getopt32(argv, "cefXx");
argv += optind;
- argc -= optind;
+ //argc -= optind;
- if (opt & DPKG_DEB_OPT_CONTENTS) {
+ extract_dir = argv[1];
+ if (opt & DPKG_DEB_OPT_CONTENTS) { // -c
tar_archive->action_header = header_verbose_list;
+ if (extract_dir)
+ bb_show_usage();
}
- extract_dir = NULL;
- need_args = 1;
- if (opt & DPKG_DEB_OPT_CONTROL) {
- ar_archive->accept = control_tar_llist;
- tar_archive->action_data = data_extract_all;
- if (1 == argc) {
- extract_dir = "./DEBIAN";
- } else {
- need_args++;
- }
- }
- if (opt & DPKG_DEB_OPT_FIELD) {
- /* Print the entire control file
- * it should accept a second argument which specifies a
- * specific field to print */
+ if (opt & DPKG_DEB_OPT_FIELD) { // -f
+ /* Print the entire control file */
+//TODO: standard tool accepts an optional list of fields to print
ar_archive->accept = control_tar_llist;
llist_add_to(&(tar_archive->accept), (char*)"./control");
tar_archive->filter = filter_accept_list;
tar_archive->action_data = data_extract_to_stdout;
+ if (extract_dir)
+ bb_show_usage();
}
- if (opt & DPKG_DEB_OPT_EXTRACT) {
- tar_archive->action_header = header_list;
- }
- if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
+ if (opt & DPKG_DEB_OPT_CONTROL) { // -e
+ ar_archive->accept = control_tar_llist;
tar_archive->action_data = data_extract_all;
- need_args = 2;
+ if (!extract_dir)
+ extract_dir = "./DEBIAN";
}
-
- if (need_args != argc) {
- bb_show_usage();
+ if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) { // -Xx
+ if (opt & DPKG_DEB_OPT_EXTRACT_VERBOSE)
+ tar_archive->action_header = header_list;
+ tar_archive->action_data = data_extract_all;
+ if (!extract_dir)
+ bb_show_usage();
}
- tar_archive->src_fd = ar_archive->src_fd = xopen(argv[0], O_RDONLY);
+ /* Standard tool supports "-" */
+ tar_archive->src_fd = ar_archive->src_fd = xopen_stdin(argv[0]);
- /* Work out where to extract the files */
- /* 2nd argument is a dir name */
- if (argv[1]) {
- extract_dir = argv[1];
- }
if (extract_dir) {
mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
xchdir(extract_dir);
diff --git a/archival/gzip.c b/archival/gzip.c
index 1e779c9..1f0b70f 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -62,15 +62,30 @@ aa: 85.1% -- replaced with aa.gz
//config: 1: larger buffers, larger hash-tables
//config: 2: larger buffers, largest hash-tables
//config: Larger models may give slightly better compression
+//config:
+//config:config FEATURE_GZIP_LEVELS
+//config: bool "Enable compression levels"
+//config: default n
+//config: depends on GZIP
+//config: help
+//config: Enable support for compression levels 4-9. The default level
+//config: is 6. If levels 1-3 are specified, 4 is used.
+//config: If this option is not selected, -N options are ignored and -9
+//config: is used.
//applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_GZIP) += gzip.o
//usage:#define gzip_trivial_usage
-//usage: "[-cfd] [FILE]..."
+//usage: "[-cf" IF_GUNZIP("d") IF_FEATURE_GZIP_LEVELS("123456789") "] [FILE]..."
//usage:#define gzip_full_usage "\n\n"
//usage: "Compress FILEs (or stdin)\n"
+//usage: IF_FEATURE_GZIP_LEVELS(
+//usage: "\n -1..9 Compression level"
+//usage: )
+//usage: IF_GUNZIP(
//usage: "\n -d Decompress"
+//usage: )
//usage: "\n -c Write to stdout"
//usage: "\n -f Force"
//usage:
@@ -83,6 +98,7 @@ aa: 85.1% -- replaced with aa.gz
#include "libbb.h"
#include "bb_archive.h"
+#include <strings.h>
/* ===========================================================================
@@ -252,6 +268,8 @@ enum {
* input file length plus MIN_LOOKAHEAD.
*/
+#ifndef ENABLE_FEATURE_GZIP_LEVELS
+
max_chain_length = 4096,
/* To speed up deflation, hash chains are never searched beyond this length.
* A higher limit improves compression ratio but degrades the speed.
@@ -283,11 +301,23 @@ enum {
* For deflate_fast() (levels <= 3) good is ignored and lazy has a different
* meaning.
*/
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
};
struct globals {
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+ unsigned max_chain_length;
+ unsigned max_lazy_match;
+ unsigned good_match;
+ unsigned nice_match;
+#define max_chain_length (G1.max_chain_length)
+#define max_lazy_match (G1.max_lazy_match)
+#define good_match (G1.good_match)
+#define nice_match (G1.nice_match)
+#endif
+
lng block_start;
/* window position at the beginning of the current output block. Gets
@@ -417,19 +447,46 @@ static void flush_outbuf(void)
#define put_8bit(c) \
do { \
G1.outbuf[G1.outcnt++] = (c); \
- if (G1.outcnt == OUTBUFSIZ) flush_outbuf(); \
+ if (G1.outcnt == OUTBUFSIZ) \
+ flush_outbuf(); \
} while (0)
/* Output a 16 bit value, lsb first */
static void put_16bit(ush w)
{
- if (G1.outcnt < OUTBUFSIZ - 2) {
- G1.outbuf[G1.outcnt++] = w;
- G1.outbuf[G1.outcnt++] = w >> 8;
- } else {
- put_8bit(w);
- put_8bit(w >> 8);
+ /* GCC 4.2.1 won't optimize out redundant loads of G1.outcnt
+ * (probably because of fear of aliasing with G1.outbuf[]
+ * stores), do it explicitly:
+ */
+ unsigned outcnt = G1.outcnt;
+ uch *dst = &G1.outbuf[outcnt];
+
+#if BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN
+ if (outcnt < OUTBUFSIZ-2) {
+ /* Common case */
+ ush *dst16 = (void*) dst;
+ *dst16 = w; /* unalinged LSB 16-bit store */
+ G1.outcnt = outcnt + 2;
+ return;
}
+ *dst = (uch)w;
+ w >>= 8;
+#else
+ *dst = (uch)w;
+ w >>= 8;
+ if (outcnt < OUTBUFSIZ-2) {
+ /* Common case */
+ dst[1] = w;
+ G1.outcnt = outcnt + 2;
+ return;
+ }
+#endif
+
+ /* Slowpath: we will need to do flush_outbuf() */
+ G1.outcnt = ++outcnt;
+ if (outcnt == OUTBUFSIZ)
+ flush_outbuf();
+ put_8bit(w);
}
static void put_32bit(ulg n)
@@ -1318,7 +1375,6 @@ static void build_tree(tree_desc * desc)
/* and insert the new node in the heap */
G2.heap[SMALLEST] = node++;
pqdownheap(tree, SMALLEST);
-
} while (G2.heap_len >= 2);
G2.heap[--G2.heap_max] = G2.heap[SMALLEST];
@@ -1666,7 +1722,6 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
copy_block(buf, (unsigned) stored_len, 0); /* without header */
G2.compressed_len = stored_len << 3;
-
} else if (stored_len + 4 <= opt_lenb && buf != NULL) {
/* 4: two words for the lengths */
/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
@@ -1680,7 +1735,6 @@ static ulg flush_block(char *buf, ulg stored_len, int eof)
G2.compressed_len += (stored_len + 4) << 3;
copy_block(buf, (unsigned) stored_len, 1); /* with header */
-
} else if (static_lenb == opt_lenb) {
send_bits((STATIC_TREES << 1) + eof, 3);
compress_block((ct_data *) G2.static_ltree, (ct_data *) G2.static_dtree);
@@ -2007,7 +2061,7 @@ static void ct_init(void)
* IN assertions: the input and output buffers are cleared.
*/
-static void zip(ulg time_stamp)
+static void zip(void)
{
ush deflate_flags = 0; /* pkzip -es, -en or -ex equivalent */
@@ -2018,7 +2072,7 @@ static void zip(ulg time_stamp)
/* compression method: 8 (DEFLATED) */
/* general flags: 0 */
put_32bit(0x00088b1f);
- put_32bit(time_stamp);
+ put_32bit(0); /* Unix timestamp */
/* Write deflated file to zip file */
G1.crc = ~0;
@@ -2042,10 +2096,8 @@ static void zip(ulg time_stamp)
/* ======================================================================== */
static
-IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_aux_data_t *aux UNUSED_PARAM)
+IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_state_t *xstate UNUSED_PARAM)
{
- struct stat s;
-
/* Clear input and output buffers */
G1.outcnt = 0;
#ifdef DEBUG
@@ -2077,9 +2129,23 @@ IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_aux_data_t *aux UNUSED
G2.bl_desc.max_length = MAX_BL_BITS;
//G2.bl_desc.max_code = 0;
+#if 0
+ /* Saving of timestamp is disabled. Why?
+ * - it is not Y2038-safe.
+ * - some people want deterministic results
+ * (normally they'd use -n, but our -n is a nop).
+ * - it's bloat.
+ * Per RFC 1952, gzfile.time=0 is "no timestamp".
+ * If users will demand this to be reinstated,
+ * implement -n "don't save timestamp".
+ */
+ struct stat s;
s.st_ctime = 0;
fstat(STDIN_FILENO, &s);
zip(s.st_ctime);
+#else
+ zip();
+#endif
return 0;
}
@@ -2097,6 +2163,7 @@ static const char gzip_longopts[] ALIGN1 =
"quiet\0" No_argument "q"
"fast\0" No_argument "1"
"best\0" No_argument "9"
+ "no-name\0" No_argument "n"
;
#endif
@@ -2122,24 +2189,45 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
#endif
{
unsigned opt;
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+ static const struct {
+ uint8_t good;
+ uint8_t chain_shift;
+ uint8_t lazy2;
+ uint8_t nice2;
+ } gzip_level_config[6] = {
+ {4, 4, 4/2, 16/2}, /* Level 4 */
+ {8, 5, 16/2, 32/2}, /* Level 5 */
+ {8, 7, 16/2, 128/2}, /* Level 6 */
+ {8, 8, 32/2, 128/2}, /* Level 7 */
+ {32, 10, 128/2, 258/2}, /* Level 8 */
+ {32, 12, 258/2, 258/2}, /* Level 9 */
+ };
+#endif
+
+ SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2))
+ + sizeof(struct globals));
#if ENABLE_FEATURE_GZIP_LONG_OPTIONS
applet_long_options = gzip_longopts;
#endif
/* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
- opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "q123456789n");
+ opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "qn123456789");
#if ENABLE_GUNZIP /* gunzip_main may not be visible... */
if (opt & 0x18) // -d and/or -t
return gunzip_main(argc, argv);
#endif
- option_mask32 &= 0x7; /* ignore -q, -0..9 */
- //if (opt & 0x1) // -c
- //if (opt & 0x2) // -f
- //if (opt & 0x4) // -v
- argv += optind;
-
- SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2))
- + sizeof(struct globals));
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+ opt >>= ENABLE_GUNZIP ? 7 : 5; /* drop cfv[dt]qn bits */
+ if (opt == 0)
+ opt = 1 << 6; /* default: 6 */
+ opt = ffs(opt >> 4); /* Maps -1..-4 to [0], -5 to [1] ... -9 to [5] */
+ max_chain_length = 1 << gzip_level_config[opt].chain_shift;
+ good_match = gzip_level_config[opt].good;
+ max_lazy_match = gzip_level_config[opt].lazy2 * 2;
+ nice_match = gzip_level_config[opt].nice2 * 2;
+#endif
+ option_mask32 &= 0x7; /* retain only -cfv */
/* Allocate all global buffers (for DYN_ALLOC option) */
ALLOC(uch, G1.l_buf, INBUFSIZ);
@@ -2151,5 +2239,6 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
/* Initialize the CRC32 table */
global_crc32_table = crc32_filltable(NULL, 0);
+ argv += optind;
return bbunpack(argv, pack_gzip, append_ext, "gz");
}
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index 968fdf8..ad5c5c4 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -4,7 +4,7 @@
#
# Licensed under GPLv2 or later, see file LICENSE in this source tree.
-lib-y:=
+lib-y:= common.o
COMMON_FILES:= \
\
@@ -30,11 +30,13 @@ COMMON_FILES:= \
DPKG_FILES:= \
unpack_ar_archive.o \
filter_accept_list_reassign.o \
+ unsafe_prefix.o \
get_header_ar.o \
get_header_tar.o \
get_header_tar_gz.o \
get_header_tar_bz2.o \
get_header_tar_lzma.o \
+ get_header_tar_xz.o \
INSERT
@@ -43,19 +45,26 @@ lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES)
lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o
lib-$(CONFIG_CPIO) += get_header_cpio.o
-lib-$(CONFIG_TAR) += get_header_tar.o
+lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o
lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o
lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
+lib-$(CONFIG_UNLZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
+lib-$(CONFIG_LZOPCAT) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
lib-$(CONFIG_LZOP_COMPR_HIGH) += lzo1x_9x.o
lib-$(CONFIG_BUNZIP2) += open_transformer.o decompress_bunzip2.o
+lib-$(CONFIG_BZCAT) += open_transformer.o decompress_bunzip2.o
lib-$(CONFIG_UNLZMA) += open_transformer.o decompress_unlzma.o
+lib-$(CONFIG_LZCAT) += open_transformer.o decompress_unlzma.o
+lib-$(CONFIG_LZMA) += open_transformer.o decompress_unlzma.o
lib-$(CONFIG_UNXZ) += open_transformer.o decompress_unxz.o
+lib-$(CONFIG_XZCAT) += open_transformer.o decompress_unxz.o
+lib-$(CONFIG_XZ) += open_transformer.o decompress_unxz.o
lib-$(CONFIG_GUNZIP) += open_transformer.o decompress_gunzip.o
+lib-$(CONFIG_ZCAT) += open_transformer.o decompress_gunzip.o
lib-$(CONFIG_UNCOMPRESS) += open_transformer.o decompress_uncompress.o
-lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o
+lib-$(CONFIG_UNZIP) += open_transformer.o decompress_gunzip.o unsafe_prefix.o
lib-$(CONFIG_RPM2CPIO) += open_transformer.o decompress_gunzip.o get_header_cpio.o
lib-$(CONFIG_RPM) += open_transformer.o decompress_gunzip.o get_header_cpio.o
-
lib-$(CONFIG_GZIP) += open_transformer.o
lib-$(CONFIG_BZIP2) += open_transformer.o
lib-$(CONFIG_LZOP) += open_transformer.o
diff --git a/archival/libarchive/bz/compress.c b/archival/libarchive/bz/compress.c
index 23de9d3..2d99468 100644
--- a/archival/libarchive/bz/compress.c
+++ b/archival/libarchive/bz/compress.c
@@ -385,7 +385,6 @@ void sendMTFValues(EState* s)
cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
-
} else
#endif
{
diff --git a/archival/libarchive/common.c b/archival/libarchive/common.c
new file mode 100644
index 0000000..389cb78
--- a/dev/null
+++ b/archival/libarchive/common.c
@@ -0,0 +1,9 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+const char cpio_TRAILER[] ALIGN1 = "TRAILER!!!";
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c
index 2e18ffb..5d8e57a 100644
--- a/archival/libarchive/data_extract_all.c
+++ b/archival/libarchive/data_extract_all.c
@@ -11,6 +11,12 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
file_header_t *file_header = archive_handle->file_header;
int dst_fd;
int res;
+ char *hard_link;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ char *dst_name;
+#else
+# define dst_name (file_header->name)
+#endif
#if ENABLE_FEATURE_TAR_SELINUX
char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
@@ -26,11 +32,49 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
}
#endif
+ /* Hard links are encoded as regular files of size 0
+ * with a nonempty link field */
+ hard_link = NULL;
+ if (S_ISREG(file_header->mode) && file_header->size == 0)
+ hard_link = file_header->link_target;
+
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ dst_name = file_header->name;
+ if (archive_handle->tar__strip_components) {
+ unsigned n = archive_handle->tar__strip_components;
+ do {
+ dst_name = strchr(dst_name, '/');
+ if (!dst_name || dst_name[1] == '\0') {
+ data_skip(archive_handle);
+ goto ret;
+ }
+ dst_name++;
+ /*
+ * Link target is shortened only for hardlinks:
+ * softlinks restored unchanged.
+ */
+ if (hard_link) {
+// GNU tar 1.26 does not check that we reached end of link name:
+// if "dir/hardlink" is hardlinked to "file",
+// tar xvf a.tar --strip-components=1 says:
+// tar: hardlink: Cannot hard link to '': No such file or directory
+// and continues processing. We silently skip such entries.
+ hard_link = strchr(hard_link, '/');
+ if (!hard_link || hard_link[1] == '\0') {
+ data_skip(archive_handle);
+ goto ret;
+ }
+ hard_link++;
+ }
+ } while (--n != 0);
+ }
+#endif
+
if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
- char *slash = strrchr(file_header->name, '/');
+ char *slash = strrchr(dst_name, '/');
if (slash) {
*slash = '\0';
- bb_make_directory(file_header->name, -1, FILEUTILS_RECUR);
+ bb_make_directory(dst_name, -1, FILEUTILS_RECUR);
*slash = '/';
}
}
@@ -38,12 +82,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
/* Remove the entry if it exists */
if (!S_ISDIR(file_header->mode)) {
- /* Is it hardlink?
- * We encode hard links as regular files of size 0 with a symlink */
- if (S_ISREG(file_header->mode)
- && file_header->link_target
- && file_header->size == 0
- ) {
+ if (hard_link) {
/* Ugly special case:
* tar cf t.tar hardlink1 hardlink2 hardlink1
* results in this tarball structure:
@@ -51,22 +90,22 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
* hardlink2 -> hardlink1
* hardlink1 -> hardlink1 <== !!!
*/
- if (strcmp(file_header->link_target, file_header->name) == 0)
+ if (strcmp(hard_link, dst_name) == 0)
goto ret;
}
/* Proceed with deleting */
- if (unlink(file_header->name) == -1
+ if (unlink(dst_name) == -1
&& errno != ENOENT
) {
bb_perror_msg_and_die("can't remove old file %s",
- file_header->name);
+ dst_name);
}
}
}
else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
/* Remove the existing entry if its older than the extracted entry */
struct stat existing_sb;
- if (lstat(file_header->name, &existing_sb) == -1) {
+ if (lstat(dst_name, &existing_sb) == -1) {
if (errno != ENOENT) {
bb_perror_msg_and_die("can't stat old file");
}
@@ -76,30 +115,25 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
&& !S_ISDIR(file_header->mode)
) {
bb_error_msg("%s not created: newer or "
- "same age file exists", file_header->name);
+ "same age file exists", dst_name);
}
data_skip(archive_handle);
goto ret;
}
- else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
+ else if ((unlink(dst_name) == -1) && (errno != EISDIR)) {
bb_perror_msg_and_die("can't remove old file %s",
- file_header->name);
+ dst_name);
}
}
- /* Handle hard links separately
- * We encode hard links as regular files of size 0 with a symlink */
- if (S_ISREG(file_header->mode)
- && file_header->link_target
- && file_header->size == 0
- ) {
- /* hard link */
- res = link(file_header->link_target, file_header->name);
- if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
+ /* Handle hard links separately */
+ if (hard_link) {
+ res = link(hard_link, dst_name);
+ if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
bb_perror_msg("can't create %slink "
"from %s to %s", "hard",
- file_header->name,
- file_header->link_target);
+ dst_name,
+ hard_link);
}
/* Hardlinks have no separate mode/ownership, skip chown/chmod */
goto ret;
@@ -109,17 +143,17 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
switch (file_header->mode & S_IFMT) {
case S_IFREG: {
/* Regular file */
- char *dst_name;
+ char *dst_nameN;
int flags = O_WRONLY | O_CREAT | O_EXCL;
if (archive_handle->ah_flags & ARCHIVE_O_TRUNC)
flags = O_WRONLY | O_CREAT | O_TRUNC;
- dst_name = file_header->name;
+ dst_nameN = dst_name;
#ifdef ARCHIVE_REPLACE_VIA_RENAME
if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME)
/* rpm-style temp file name */
- dst_name = xasprintf("%s;%x", dst_name, (int)getpid());
+ dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid());
#endif
- dst_fd = xopen3(dst_name,
+ dst_fd = xopen3(dst_nameN,
flags,
file_header->mode
);
@@ -127,32 +161,32 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
close(dst_fd);
#ifdef ARCHIVE_REPLACE_VIA_RENAME
if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) {
- xrename(dst_name, file_header->name);
- free(dst_name);
+ xrename(dst_nameN, dst_name);
+ free(dst_nameN);
}
#endif
break;
}
case S_IFDIR:
- res = mkdir(file_header->name, file_header->mode);
+ res = mkdir(dst_name, file_header->mode);
if ((res == -1)
&& (errno != EISDIR) /* btw, Linux doesn't return this */
&& (errno != EEXIST)
&& !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
) {
- bb_perror_msg("can't make dir %s", file_header->name);
+ bb_perror_msg("can't make dir %s", dst_name);
}
break;
case S_IFLNK:
/* Symlink */
//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
- res = symlink(file_header->link_target, file_header->name);
- if ((res == -1)
+ res = symlink(file_header->link_target, dst_name);
+ if (res != 0
&& !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
) {
bb_perror_msg("can't create %slink "
"from %s to %s", "sym",
- file_header->name,
+ dst_name,
file_header->link_target);
}
break;
@@ -160,11 +194,11 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
- res = mknod(file_header->name, file_header->mode, file_header->device);
+ res = mknod(dst_name, file_header->mode, file_header->device);
if ((res == -1)
&& !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
) {
- bb_perror_msg("can't create node %s", file_header->name);
+ bb_perror_msg("can't create node %s", dst_name);
}
break;
default:
@@ -189,20 +223,20 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
}
#endif
/* GNU tar 1.15.1 uses chown, not lchown */
- chown(file_header->name, uid, gid);
+ chown(dst_name, uid, gid);
}
/* uclibc has no lchmod, glibc is even stranger -
* it has lchmod which seems to do nothing!
* so we use chmod... */
if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
- chmod(file_header->name, file_header->mode);
+ chmod(dst_name, file_header->mode);
}
if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
struct timeval t[2];
t[1].tv_sec = t[0].tv_sec = file_header->mtime;
t[1].tv_usec = t[0].tv_usec = 0;
- utimes(file_header->name, t);
+ utimes(dst_name, t);
}
}
diff --git a/archival/libarchive/data_extract_to_command.c b/archival/libarchive/data_extract_to_command.c
index 599288d..3b7d1ea 100644
--- a/archival/libarchive/data_extract_to_command.c
+++ b/archival/libarchive/data_extract_to_command.c
@@ -112,13 +112,12 @@ void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size);
close(p[1]);
- if (safe_waitpid(pid, &status, 0) == -1)
- bb_perror_msg_and_die("waitpid");
+ status = wait_for_exitstatus(pid);
if (WIFEXITED(status) && WEXITSTATUS(status))
bb_error_msg_and_die("'%s' returned status %d",
archive_handle->tar__to_command, WEXITSTATUS(status));
if (WIFSIGNALED(status))
- bb_error_msg_and_die("'%s' terminated on signal %d",
+ bb_error_msg_and_die("'%s' terminated by signal %d",
archive_handle->tar__to_command, WTERMSIG(status));
if (!BB_MMU) {
diff --git a/archival/libarchive/decompress_bunzip2.c b/archival/libarchive/decompress_bunzip2.c
index 0e3ab43..fe5953d 100644
--- a/archival/libarchive/decompress_bunzip2.c
+++ b/archival/libarchive/decompress_bunzip2.c
@@ -159,7 +159,7 @@ static int get_next_block(bunzip_data *bd)
struct group_data *hufGroup;
int dbufCount, dbufSize, groupCount, *base, *limit, selector,
i, j, t, runPos, symCount, symTotal, nSelectors, byteCount[256];
- int runCnt = 0;
+ int runCnt = runCnt; /* for compiler */
uint8_t uc, symToByte[256], mtfSymbol[256], *selectors;
uint32_t *dbuf;
unsigned origPtr;
@@ -731,7 +731,7 @@ void FAST_FUNC dealloc_bunzip(bunzip_data *bd)
/* Decompress src_fd to dst_fd. Stops at end of bzip data, not end of file. */
IF_DESKTOP(long long) int FAST_FUNC
-unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+unpack_bz2_stream(transformer_state_t *xstate)
{
IF_DESKTOP(long long total_written = 0;)
bunzip_data *bd;
@@ -739,14 +739,14 @@ unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
int i;
unsigned len;
- if (check_signature16(aux, src_fd, BZIP2_MAGIC))
+ if (check_signature16(xstate, BZIP2_MAGIC))
return -1;
outbuf = xmalloc(IOBUF_SIZE);
len = 0;
while (1) { /* "Process one BZ... stream" loop */
- i = start_bunzip(&bd, src_fd, outbuf + 2, len);
+ i = start_bunzip(&bd, xstate->src_fd, outbuf + 2, len);
if (i == 0) {
while (1) { /* "Produce some output bytes" loop */
@@ -756,8 +756,7 @@ unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
i = IOBUF_SIZE - i; /* number of bytes produced */
if (i == 0) /* EOF? */
break;
- if (i != full_write(dst_fd, outbuf, i)) {
- bb_error_msg("short write");
+ if (i != transformer_write(xstate, outbuf, i)) {
i = RETVAL_SHORT_WRITE;
goto release_mem;
}
@@ -790,7 +789,7 @@ unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
len = bd->inbufCount - bd->inbufPos;
memcpy(outbuf, &bd->inbuf[bd->inbufPos], len);
if (len < 2) {
- if (safe_read(src_fd, outbuf + len, 2 - len) != (int)(2 - len))
+ if (safe_read(xstate->src_fd, outbuf + len, 2 - len) != 2 - len)
break;
len = 2;
}
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index d27f428..b1d4989 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -305,11 +305,11 @@ static int huft_build(const unsigned *b, const unsigned n,
unsigned i; /* counter, current code */
unsigned j; /* counter */
int k; /* number of bits in current code */
- unsigned *p; /* pointer into c[], b[], or v[] */
+ const unsigned *p; /* pointer into c[], b[], or v[] */
huft_t *q; /* points to current table */
huft_t r; /* table entry for structure assignment */
huft_t *u[BMAX]; /* table stack */
- unsigned v[N_MAX]; /* values in order of bit length */
+ unsigned v[N_MAX + 1]; /* values in order of bit length. last v[] is never used */
int ws[BMAX + 1]; /* bits decoded stack */
int w; /* bits decoded */
unsigned x[BMAX + 1]; /* bit offsets, then code stack */
@@ -324,7 +324,7 @@ static int huft_build(const unsigned *b, const unsigned n,
/* Generate counts for each bit length */
memset(c, 0, sizeof(c));
- p = (unsigned *) b; /* cast allows us to reuse p for pointing to b */
+ p = b;
i = n;
do {
c[*p]++; /* assume all entries <= BMAX */
@@ -364,8 +364,12 @@ static int huft_build(const unsigned *b, const unsigned n,
*xp++ = j;
}
- /* Make a table of values in order of bit lengths */
- p = (unsigned *) b;
+ /* Make a table of values in order of bit lengths.
+ * To detect bad input, unused v[i]'s are set to invalid value UINT_MAX.
+ * In particular, last v[i] is never filled and must not be accessed.
+ */
+ memset(v, 0xff, sizeof(v));
+ p = b;
i = 0;
do {
j = *p++;
@@ -432,7 +436,9 @@ static int huft_build(const unsigned *b, const unsigned n,
/* set up table entry in r */
r.b = (unsigned char) (k - w);
- if (p >= v + n) {
+ if (/*p >= v + n || -- redundant, caught by the second check: */
+ *p == UINT_MAX /* do we access uninited v[i]? (see memset(v))*/
+ ) {
r.e = 99; /* out of values--invalid code */
} else if (*p < s) {
r.e = (unsigned char) (*p < 256 ? 16 : 15); /* 256 is EOB code */
@@ -517,8 +523,9 @@ static NOINLINE int inflate_codes(STATE_PARAM_ONLY)
e = t->e;
if (e > 16)
do {
- if (e == 99)
- abort_unzip(PASS_STATE_ONLY);;
+ if (e == 99) {
+ abort_unzip(PASS_STATE_ONLY);
+ }
bb >>= t->b;
k -= t->b;
e -= 16;
@@ -554,8 +561,9 @@ static NOINLINE int inflate_codes(STATE_PARAM_ONLY)
e = t->e;
if (e > 16)
do {
- if (e == 99)
+ if (e == 99) {
abort_unzip(PASS_STATE_ONLY);
+ }
bb >>= t->b;
k -= t->b;
e -= 16;
@@ -821,8 +829,9 @@ static int inflate_block(STATE_PARAM smallint *e)
b_dynamic >>= 4;
k_dynamic -= 4;
- if (nl > 286 || nd > 30)
+ if (nl > 286 || nd > 30) {
abort_unzip(PASS_STATE_ONLY); /* bad lengths */
+ }
/* read in bit-length-code lengths */
for (j = 0; j < nb; j++) {
@@ -903,12 +912,14 @@ static int inflate_block(STATE_PARAM smallint *e)
bl = lbits;
i = huft_build(ll, nl, 257, cplens, cplext, &inflate_codes_tl, &bl);
- if (i != 0)
+ if (i != 0) {
abort_unzip(PASS_STATE_ONLY);
+ }
bd = dbits;
i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &inflate_codes_td, &bd);
- if (i != 0)
+ if (i != 0) {
abort_unzip(PASS_STATE_ONLY);
+ }
/* set up data for inflate_codes() */
inflate_codes_setup(PASS_STATE bl, bd);
@@ -971,7 +982,7 @@ static int inflate_get_next_window(STATE_PARAM_ONLY)
/* Called from unpack_gz_stream() and inflate_unzip() */
static IF_DESKTOP(long long) int
-inflate_unzip_internal(STATE_PARAM int in, int out)
+inflate_unzip_internal(STATE_PARAM transformer_state_t *xstate)
{
IF_DESKTOP(long long) int n = 0;
ssize_t nwrote;
@@ -980,7 +991,7 @@ inflate_unzip_internal(STATE_PARAM int in, int out)
gunzip_window = xmalloc(GUNZIP_WSIZE);
gunzip_outbuf_count = 0;
gunzip_bytes_out = 0;
- gunzip_src_fd = in;
+ gunzip_src_fd = xstate->src_fd;
/* (re) initialize state */
method = -1;
@@ -1002,9 +1013,8 @@ inflate_unzip_internal(STATE_PARAM int in, int out)
while (1) {
int r = inflate_get_next_window(PASS_STATE_ONLY);
- nwrote = full_write(out, gunzip_window, gunzip_outbuf_count);
- if (nwrote != (ssize_t)gunzip_outbuf_count) {
- bb_perror_msg("write");
+ nwrote = transformer_write(xstate, gunzip_window, gunzip_outbuf_count);
+ if (nwrote == (ssize_t)-1) {
n = -1;
goto ret;
}
@@ -1034,22 +1044,22 @@ inflate_unzip_internal(STATE_PARAM int in, int out)
/* For unzip */
IF_DESKTOP(long long) int FAST_FUNC
-inflate_unzip(transformer_aux_data_t *aux, int in, int out)
+inflate_unzip(transformer_state_t *xstate)
{
IF_DESKTOP(long long) int n;
DECLARE_STATE;
ALLOC_STATE;
- to_read = aux->bytes_in;
+ to_read = xstate->bytes_in;
// bytebuffer_max = 0x8000;
bytebuffer_offset = 4;
bytebuffer = xmalloc(bytebuffer_max);
- n = inflate_unzip_internal(PASS_STATE in, out);
+ n = inflate_unzip_internal(PASS_STATE xstate);
free(bytebuffer);
- aux->crc32 = gunzip_crc;
- aux->bytes_out = gunzip_bytes_out;
+ xstate->crc32 = gunzip_crc;
+ xstate->bytes_out = gunzip_bytes_out;
DEALLOC_STATE;
return n;
}
@@ -1107,7 +1117,7 @@ static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY)
return res;
}
-static int check_header_gzip(STATE_PARAM transformer_aux_data_t *aux)
+static int check_header_gzip(STATE_PARAM transformer_state_t *xstate)
{
union {
unsigned char raw[8];
@@ -1119,9 +1129,8 @@ static int check_header_gzip(STATE_PARAM transformer_aux_data_t *aux)
uint8_t os_flags_UNUSED;
} PACKED formatted;
} header;
- struct BUG_header {
- char BUG_header[sizeof(header) == 8 ? 1 : -1];
- };
+
+ BUILD_BUG_ON(sizeof(header) != 8);
/*
* Rewind bytebuffer. We use the beginning because the header has 8
@@ -1169,8 +1178,7 @@ static int check_header_gzip(STATE_PARAM transformer_aux_data_t *aux)
}
}
- if (aux)
- aux->mtime = SWAP_LE32(header.formatted.mtime);
+ xstate->mtime = SWAP_LE32(header.formatted.mtime);
/* Read the header checksum */
if (header.formatted.flags & 0x02) {
@@ -1182,27 +1190,27 @@ static int check_header_gzip(STATE_PARAM transformer_aux_data_t *aux)
}
IF_DESKTOP(long long) int FAST_FUNC
-unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+unpack_gz_stream(transformer_state_t *xstate)
{
uint32_t v32;
IF_DESKTOP(long long) int total, n;
DECLARE_STATE;
#if !ENABLE_FEATURE_SEAMLESS_Z
- if (check_signature16(aux, src_fd, GZIP_MAGIC))
+ if (check_signature16(xstate, GZIP_MAGIC))
return -1;
#else
- if (aux && aux->check_signature) {
+ if (!xstate->signature_skipped) {
uint16_t magic2;
- if (full_read(src_fd, &magic2, 2) != 2) {
+ if (full_read(xstate->src_fd, &magic2, 2) != 2) {
bad_magic:
bb_error_msg("invalid magic");
return -1;
}
if (magic2 == COMPRESS_MAGIC) {
- aux->check_signature = 0;
- return unpack_Z_stream(aux, src_fd, dst_fd);
+ xstate->signature_skipped = 2;
+ return unpack_Z_stream(xstate);
}
if (magic2 != GZIP_MAGIC)
goto bad_magic;
@@ -1215,16 +1223,16 @@ unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
to_read = -1;
// bytebuffer_max = 0x8000;
bytebuffer = xmalloc(bytebuffer_max);
- gunzip_src_fd = src_fd;
+ gunzip_src_fd = xstate->src_fd;
again:
- if (!check_header_gzip(PASS_STATE aux)) {
+ if (!check_header_gzip(PASS_STATE xstate)) {
bb_error_msg("corrupted data");
total = -1;
goto ret;
}
- n = inflate_unzip_internal(PASS_STATE src_fd, dst_fd);
+ n = inflate_unzip_internal(PASS_STATE xstate);
if (n < 0) {
total = -1;
goto ret;
diff --git a/archival/libarchive/decompress_uncompress.c b/archival/libarchive/decompress_uncompress.c
index 53c2708..034ed50 100644
--- a/archival/libarchive/decompress_uncompress.c
+++ b/archival/libarchive/decompress_uncompress.c
@@ -73,7 +73,7 @@
*/
IF_DESKTOP(long long) int FAST_FUNC
-unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+unpack_Z_stream(transformer_state_t *xstate)
{
IF_DESKTOP(long long total_written = 0;)
IF_DESKTOP(long long) int retval = -1;
@@ -102,7 +102,7 @@ unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
/* block compress mode -C compatible with 2.0 */
int block_mode; /* = BLOCK_MODE; */
- if (check_signature16(aux, src_fd, COMPRESS_MAGIC))
+ if (check_signature16(xstate, COMPRESS_MAGIC))
return -1;
inbuf = xzalloc(IBUFSIZ + 64);
@@ -114,7 +114,7 @@ unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
/* xread isn't good here, we have to return - caller may want
* to do some cleanup (e.g. delete incomplete unpacked file etc) */
- if (full_read(src_fd, inbuf, 1) != 1) {
+ if (full_read(xstate->src_fd, inbuf, 1) != 1) {
bb_error_msg("short read");
goto err;
}
@@ -166,7 +166,7 @@ unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
}
if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) {
- rsize = safe_read(src_fd, inbuf + insize, IBUFSIZ);
+ rsize = safe_read(xstate->src_fd, inbuf + insize, IBUFSIZ);
if (rsize < 0)
bb_error_msg_and_die(bb_msg_read_error);
insize += rsize;
@@ -274,7 +274,7 @@ unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
}
if (outpos >= OBUFSIZ) {
- xwrite(dst_fd, outbuf, outpos);
+ xtransformer_write(xstate, outbuf, outpos);
IF_DESKTOP(total_written += outpos;)
outpos = 0;
}
@@ -297,11 +297,10 @@ unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
/* Remember previous code. */
oldcode = incode;
}
-
} while (rsize > 0);
if (outpos > 0) {
- xwrite(dst_fd, outbuf, outpos);
+ xtransformer_write(xstate, outbuf, outpos);
IF_DESKTOP(total_written += outpos;)
}
diff --git a/archival/libarchive/decompress_unlzma.c b/archival/libarchive/decompress_unlzma.c
index ca32bd8..c8622f9 100644
--- a/archival/libarchive/decompress_unlzma.c
+++ b/archival/libarchive/decompress_unlzma.c
@@ -206,7 +206,7 @@ enum {
IF_DESKTOP(long long) int FAST_FUNC
-unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst_fd)
+unpack_lzma_stream(transformer_state_t *xstate)
{
IF_DESKTOP(long long total_written = 0;)
lzma_header_t header;
@@ -214,8 +214,6 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
uint32_t pos_state_mask;
uint32_t literal_pos_mask;
uint16_t *p;
- int num_bits;
- int num_probs;
rc_t *rc;
int i;
uint8_t *buffer;
@@ -225,7 +223,7 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
int state = 0;
uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
- if (full_read(src_fd, &header, sizeof(header)) != sizeof(header)
+ if (full_read(xstate->src_fd, &header, sizeof(header)) != sizeof(header)
|| header.pos >= (9 * 5 * 5)
) {
bb_error_msg("bad lzma header");
@@ -239,6 +237,9 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
pos_state_mask = (1 << pb) - 1;
literal_pos_mask = (1 << lp) - 1;
+ /* Example values from linux-3.3.4.tar.lzma:
+ * dict_size: 64M, dst_size: 2^64-1
+ */
header.dict_size = SWAP_LE32(header.dict_size);
header.dst_size = SWAP_LE64(header.dst_size);
@@ -247,13 +248,17 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
buffer = xmalloc(MIN(header.dst_size, header.dict_size));
- num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
- p = xmalloc(num_probs * sizeof(*p));
- num_probs += LZMA_LITERAL - LZMA_BASE_SIZE;
- for (i = 0; i < num_probs; i++)
- p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
+ {
+ int num_probs;
+
+ num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
+ p = xmalloc(num_probs * sizeof(*p));
+ num_probs += LZMA_LITERAL - LZMA_BASE_SIZE;
+ for (i = 0; i < num_probs; i++)
+ p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
+ }
- rc = rc_init(src_fd); /*, RC_BUFFER_SIZE); */
+ rc = rc_init(xstate->src_fd); /*, RC_BUFFER_SIZE); */
while (global_pos + buffer_pos < header.dst_size) {
int pos_state = (buffer_pos + global_pos) & pos_state_mask;
@@ -301,7 +306,7 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
if (buffer_pos == header.dict_size) {
buffer_pos = 0;
global_pos += header.dict_size;
- if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size)
+ if (transformer_write(xstate, buffer, header.dict_size) != (ssize_t)header.dict_size)
goto bad;
IF_DESKTOP(total_written += header.dict_size;)
}
@@ -310,6 +315,7 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
goto one_byte2;
#endif
} else {
+ int num_bits;
int offset;
uint16_t *prob2;
#define prob_len prob2
@@ -434,19 +440,22 @@ unpack_lzma_stream(transformer_aux_data_t *aux UNUSED_PARAM, int src_fd, int dst
if (buffer_pos == header.dict_size) {
buffer_pos = 0;
global_pos += header.dict_size;
- if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size)
+ if (transformer_write(xstate, buffer, header.dict_size) != (ssize_t)header.dict_size)
goto bad;
IF_DESKTOP(total_written += header.dict_size;)
}
len--;
} while (len != 0 && buffer_pos < header.dst_size);
+ /* FIXME: ...........^^^^^
+ * shouldn't it be "global_pos + buffer_pos < header.dst_size"?
+ */
}
}
{
IF_NOT_DESKTOP(int total_written = 0; /* success */)
IF_DESKTOP(total_written += buffer_pos;)
- if (full_write(dst_fd, buffer, buffer_pos) != (ssize_t)buffer_pos) {
+ if (transformer_write(xstate, buffer, buffer_pos) != (ssize_t)buffer_pos) {
bad:
total_written = -1; /* failure */
}
diff --git a/archival/libarchive/decompress_unxz.c b/archival/libarchive/decompress_unxz.c
index 986b7b1..cd32cc7 100644
--- a/archival/libarchive/decompress_unxz.c
+++ b/archival/libarchive/decompress_unxz.c
@@ -38,7 +38,7 @@ static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
#include "unxz/xz_dec_stream.c"
IF_DESKTOP(long long) int FAST_FUNC
-unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+unpack_xz_stream(transformer_state_t *xstate)
{
enum xz_ret xz_result;
struct xz_buf iobuf;
@@ -55,7 +55,7 @@ unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
iobuf.out = membuf + BUFSIZ;
iobuf.out_size = BUFSIZ;
- if (!aux || aux->check_signature == 0) {
+ if (!xstate || xstate->signature_skipped) {
/* Preload XZ file signature */
strcpy((char*)membuf, HEADER_MAGIC);
iobuf.in_size = HEADER_MAGIC_SIZE;
@@ -67,7 +67,7 @@ unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
xz_result = X_OK;
while (1) {
if (iobuf.in_pos == iobuf.in_size) {
- int rd = safe_read(src_fd, membuf, BUFSIZ);
+ int rd = safe_read(xstate->src_fd, membuf, BUFSIZ);
if (rd < 0) {
bb_error_msg(bb_msg_read_error);
total = -1;
@@ -104,7 +104,7 @@ unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd)
// bb_error_msg("<in pos:%d size:%d out pos:%d size:%d r:%d",
// iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, xz_result);
if (iobuf.out_pos) {
- xwrite(dst_fd, iobuf.out, iobuf.out_pos);
+ xtransformer_write(xstate, iobuf.out, iobuf.out_pos);
IF_DESKTOP(total += iobuf.out_pos;)
iobuf.out_pos = 0;
}
diff --git a/archival/libarchive/filter_accept_list_reassign.c b/archival/libarchive/filter_accept_list_reassign.c
index 3d19abe..b9acfbc 100644
--- a/archival/libarchive/filter_accept_list_reassign.c
+++ b/archival/libarchive/filter_accept_list_reassign.c
@@ -28,6 +28,10 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
name_ptr++;
/* Modify the subarchive handler based on the extension */
+ if (strcmp(name_ptr, "tar") == 0) {
+ archive_handle->dpkg__action_data_subarchive = get_header_tar;
+ return EXIT_SUCCESS;
+ }
if (ENABLE_FEATURE_SEAMLESS_GZ
&& strcmp(name_ptr, "gz") == 0
) {
@@ -46,6 +50,12 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma;
return EXIT_SUCCESS;
}
+ if (ENABLE_FEATURE_SEAMLESS_XZ
+ && strcmp(name_ptr, "xz") == 0
+ ) {
+ archive_handle->dpkg__action_data_subarchive = get_header_tar_xz;
+ return EXIT_SUCCESS;
+ }
}
return EXIT_FAILURE;
}
diff --git a/archival/libarchive/get_header_cpio.c b/archival/libarchive/get_header_cpio.c
index 1a0058b..badd4a8 100644
--- a/archival/libarchive/get_header_cpio.c
+++ b/archival/libarchive/get_header_cpio.c
@@ -37,7 +37,7 @@ char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
}
archive_handle->offset += 110;
- if (strncmp(&cpio_header[0], "07070", 5) != 0
+ if (!is_prefixed_with(&cpio_header[0], "07070")
|| (cpio_header[5] != '1' && cpio_header[5] != '2')
) {
bb_error_msg_and_die("unsupported cpio format, use newc or crc");
@@ -52,6 +52,11 @@ char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
&major, &minor, &namesize) != 10)
bb_error_msg_and_die("damaged cpio file");
file_header->mode = mode;
+ /* "cpio -R USER:GRP" support: */
+ if (archive_handle->cpio__owner.uid != (uid_t)-1L)
+ uid = archive_handle->cpio__owner.uid;
+ if (archive_handle->cpio__owner.gid != (gid_t)-1L)
+ gid = archive_handle->cpio__owner.gid;
file_header->uid = uid;
file_header->gid = gid;
file_header->mtime = mtime;
@@ -75,7 +80,7 @@ char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
/* Update offset amount and skip padding before file contents */
data_align(archive_handle, 4);
- if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+ if (strcmp(file_header->name, cpio_TRAILER) == 0) {
/* Always round up. ">> 9" divides by 512 */
archive_handle->cpio__blocks = (uoff_t)(archive_handle->offset + 511) >> 9;
goto create_hardlinks;
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
index 278c8fb..c7e3bc1 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -17,36 +17,6 @@
typedef uint32_t aliased_uint32_t FIX_ALIASING;
typedef off_t aliased_off_t FIX_ALIASING;
-
-const char* FAST_FUNC strip_unsafe_prefix(const char *str)
-{
- const char *cp = str;
- while (1) {
- char *cp2;
- if (*cp == '/') {
- cp++;
- continue;
- }
- if (strncmp(cp, "/../"+1, 3) == 0) {
- cp += 3;
- continue;
- }
- cp2 = strstr(cp, "/../");
- if (!cp2)
- break;
- cp = cp2 + 4;
- }
- if (cp != str) {
- static smallint warned = 0;
- if (!warned) {
- warned = 1;
- bb_error_msg("removing leading '%.*s' from member names",
- (int)(cp - str), str);
- }
- }
- return cp;
-}
-
/* NB: _DESTROYS_ str[len] character! */
static unsigned long long getOctal(char *str, int len)
{
@@ -90,13 +60,21 @@ static unsigned long long getOctal(char *str, int len)
}
#define GET_OCTAL(a) getOctal((a), sizeof(a))
+#define TAR_EXTD (ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX)
+#if !TAR_EXTD
+#define process_pax_hdr(archive_handle, sz, global) \
+ process_pax_hdr(archive_handle, sz)
+#endif
/* "global" is 0 or 1 */
static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int global)
{
+#if !TAR_EXTD
+ unsigned blk_sz = (sz + 511) & (~511);
+ seek_by_read(archive_handle->src_fd, blk_sz);
+#else
+ unsigned blk_sz = (sz + 511) & (~511);
char *buf, *p;
- unsigned blk_sz;
- blk_sz = (sz + 511) & (~511);
p = buf = xmalloc(blk_sz + 1);
xread(archive_handle->src_fd, buf, blk_sz);
archive_handle->offset += blk_sz;
@@ -115,7 +93,9 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g
*/
p += len;
sz -= len;
- if ((int)sz < 0
+ if (
+ /** (int)sz < 0 - not good enough for huge malicious VALUE of 2^32-1 */
+ (int)(sz|len) < 0 /* this works */
|| len == 0
|| errno != EINVAL
|| *end != ' '
@@ -132,30 +112,39 @@ static void process_pax_hdr(archive_handle_t *archive_handle, unsigned sz, int g
p[-1] = '\0';
value = end + 1;
-#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
- if (!global && strncmp(value, "path=", sizeof("path=") - 1) == 0) {
- value += sizeof("path=") - 1;
- free(archive_handle->tar__longname);
- archive_handle->tar__longname = xstrdup(value);
- continue;
+# if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+ if (!global) {
+ if (is_prefixed_with(value, "path=")) {
+ value += sizeof("path=") - 1;
+ free(archive_handle->tar__longname);
+ archive_handle->tar__longname = xstrdup(value);
+ continue;
+ }
+ if (is_prefixed_with(value, "linkpath=")) {
+ value += sizeof("linkpath=") - 1;
+ free(archive_handle->tar__linkname);
+ archive_handle->tar__linkname = xstrdup(value);
+ continue;
+ }
}
-#endif
+# endif
-#if ENABLE_FEATURE_TAR_SELINUX
+# if ENABLE_FEATURE_TAR_SELINUX
/* Scan for SELinux contexts, via "RHT.security.selinux" keyword.
* This is what Red Hat's patched version of tar uses.
*/
-# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
- if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) {
+# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
+ if (is_prefixed_with(value, SELINUX_CONTEXT_KEYWORD"=")) {
value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1;
free(archive_handle->tar__sctx[global]);
archive_handle->tar__sctx[global] = xstrdup(value);
continue;
}
-#endif
+# endif
}
free(buf);
+#endif
}
char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
@@ -198,7 +187,13 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
* the message and we don't check whether we indeed
* saw zero block directly before this. */
if (i == 0) {
- bb_error_msg("short read");
+ /* GNU tar 1.29 will be silent if tar archive ends abruptly
+ * (if there are no zero blocks at all, and last read returns zero,
+ * not short read 0 < len < 512). Complain only if
+ * the very first read fails. Grrr.
+ */
+ if (archive_handle->offset == 0)
+ bb_error_msg("short read");
/* this merely signals end of archive, not exit(1): */
return EXIT_FAILURE;
}
@@ -214,7 +209,11 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
archive_handle->offset += i;
/* If there is no filename its an empty header */
- if (tar.name[0] == 0 && tar.prefix[0] == 0) {
+ if (tar.name[0] == 0 && tar.prefix[0] == 0
+ /* Have seen a tar archive with pax 'x' header supplying UTF8 filename,
+ * with actual file having all name fields NUL-filled. Check this: */
+ && !p_longname
+ ) {
if (archive_handle->tar__end) {
/* Second consecutive empty header - end of archive.
* Read until the end to empty the pipe from gz or bz2
@@ -230,7 +229,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
/* Check header has valid magic, "ustar" is for the proper tar,
* five NULs are for the old tar format */
- if (strncmp(tar.magic, "ustar", 5) != 0
+ if (!is_prefixed_with(tar.magic, "ustar")
&& (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
|| memcmp(tar.magic, "\0\0\0\0", 5) != 0)
) {
@@ -378,7 +377,14 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
case '6':
file_header->mode |= S_IFIFO;
goto size0;
+ case 'g': /* pax global header */
+ case 'x': { /* pax extended header */
+ if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
+ goto skip_ext_hdr;
+ process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g'));
+ goto again_after_align;
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+/* See http://www.gnu.org/software/tar/manual/html_node/Extensions.html */
case 'L':
/* free: paranoia: tar with several consecutive longnames */
free(p_longname);
@@ -398,18 +404,17 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
archive_handle->offset += file_header->size;
/* return get_header_tar(archive_handle); */
goto again;
- case 'D': /* GNU dump dir */
- case 'M': /* Continuation of multi volume archive */
- case 'N': /* Old GNU for names > 100 characters */
- case 'S': /* Sparse file */
- case 'V': /* Volume header */
+/*
+ * case 'S': // Sparse file
+ * Was seen in the wild. Not supported (yet?).
+ * See https://www.gnu.org/software/tar/manual/html_section/tar_92.html
+ * for the format. (An "Old GNU Format" was seen, not PAX formats).
+ */
+// case 'D': /* GNU dump dir */
+// case 'M': /* Continuation of multi volume archive */
+// case 'N': /* Old GNU for names > 100 characters */
+// case 'V': /* Volume header */
#endif
- case 'g': /* pax global header */
- case 'x': { /* pax extended header */
- if ((uoff_t)file_header->size > 0xfffff) /* paranoia */
- goto skip_ext_hdr;
- process_pax_hdr(archive_handle, file_header->size, (tar.typeflag == 'g'));
- goto again_after_align;
}
skip_ext_hdr:
{
@@ -440,6 +445,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
/* Everything up to and including last ".." component is stripped */
overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name));
+//TODO: do the same for file_header->link_target?
/* Strip trailing '/' in directories */
/* Must be done after mode is set as '/' is used to check if it's a directory */
diff --git a/archival/libarchive/get_header_tar_bz2.c b/archival/libarchive/get_header_tar_bz2.c
index 0ee00df..78f78a8 100644
--- a/archival/libarchive/get_header_tar_bz2.c
+++ b/archival/libarchive/get_header_tar_bz2.c
@@ -11,7 +11,7 @@ char FAST_FUNC get_header_tar_bz2(archive_handle_t *archive_handle)
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
- open_transformer_with_sig(archive_handle->src_fd, unpack_bz2_stream, "bunzip2");
+ fork_transformer_with_sig(archive_handle->src_fd, unpack_bz2_stream, "bunzip2");
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS)
continue;
diff --git a/archival/libarchive/get_header_tar_gz.c b/archival/libarchive/get_header_tar_gz.c
index 0328434..b11f503 100644
--- a/archival/libarchive/get_header_tar_gz.c
+++ b/archival/libarchive/get_header_tar_gz.c
@@ -11,7 +11,7 @@ char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
- open_transformer_with_sig(archive_handle->src_fd, unpack_gz_stream, "gunzip");
+ fork_transformer_with_sig(archive_handle->src_fd, unpack_gz_stream, "gunzip");
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS)
continue;
diff --git a/archival/libarchive/get_header_tar_lzma.c b/archival/libarchive/get_header_tar_lzma.c
index d565a21..d228cbc 100644
--- a/archival/libarchive/get_header_tar_lzma.c
+++ b/archival/libarchive/get_header_tar_lzma.c
@@ -14,7 +14,7 @@ char FAST_FUNC get_header_tar_lzma(archive_handle_t *archive_handle)
/* Can't lseek over pipes */
archive_handle->seek = seek_by_read;
- open_transformer_with_sig(archive_handle->src_fd, unpack_lzma_stream, "unlzma");
+ fork_transformer_with_sig(archive_handle->src_fd, unpack_lzma_stream, "unlzma");
archive_handle->offset = 0;
while (get_header_tar(archive_handle) == EXIT_SUCCESS)
continue;
diff --git a/archival/libarchive/get_header_tar_xz.c b/archival/libarchive/get_header_tar_xz.c
new file mode 100644
index 0000000..7bf3b3b
--- a/dev/null
+++ b/archival/libarchive/get_header_tar_xz.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+char FAST_FUNC get_header_tar_xz(archive_handle_t *archive_handle)
+{
+ /* Can't lseek over pipes */
+ archive_handle->seek = seek_by_read;
+
+ fork_transformer_with_sig(archive_handle->src_fd, unpack_xz_stream, "unxz");
+ archive_handle->offset = 0;
+ while (get_header_tar(archive_handle) == EXIT_SUCCESS)
+ continue;
+
+ /* Can only do one file at a time */
+ return EXIT_FAILURE;
+}
diff --git a/archival/libarchive/init_handle.c b/archival/libarchive/init_handle.c
index cbae06a..dcba666 100644
--- a/archival/libarchive/init_handle.c
+++ b/archival/libarchive/init_handle.c
@@ -17,6 +17,10 @@ archive_handle_t* FAST_FUNC init_handle(void)
archive_handle->action_data = data_skip;
archive_handle->filter = filter_accept_all;
archive_handle->seek = seek_by_jump;
+#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
+ archive_handle->cpio__owner.uid = (uid_t)-1L;
+ archive_handle->cpio__owner.gid = (gid_t)-1L;
+#endif
return archive_handle;
}
diff --git a/archival/libarchive/lzo1x_9x.c b/archival/libarchive/lzo1x_9x.c
index 2b490ae..09ee4ba 100644
--- a/archival/libarchive/lzo1x_9x.c
+++ b/archival/libarchive/lzo1x_9x.c
@@ -71,7 +71,6 @@ typedef struct {
uint8_t *out;
unsigned r1_lit;
-
} lzo1x_999_t;
#define getbyte(c) ((c).ip < (c).in_end ? *((c).ip)++ : (-1))
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 1986630..ac7e5db 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -6,27 +6,65 @@
#include "libbb.h"
#include "bb_archive.h"
-void FAST_FUNC init_transformer_aux_data(transformer_aux_data_t *aux)
+void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
{
- memset(aux, 0, sizeof(*aux));
+ memset(xstate, 0, sizeof(*xstate));
}
-int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16)
+int FAST_FUNC check_signature16(transformer_state_t *xstate, unsigned magic16)
{
- if (aux && aux->check_signature) {
+ if (!xstate->signature_skipped) {
uint16_t magic2;
- if (full_read(src_fd, &magic2, 2) != 2 || magic2 != magic16) {
+ if (full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) {
bb_error_msg("invalid magic");
-#if 0 /* possible future extension */
- if (aux->check_signature > 1)
- xfunc_die();
-#endif
return -1;
}
+ xstate->signature_skipped = 2;
}
return 0;
}
+ssize_t FAST_FUNC transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
+{
+ ssize_t nwrote;
+
+ if (xstate->mem_output_size_max != 0) {
+ size_t pos = xstate->mem_output_size;
+ size_t size;
+
+ size = (xstate->mem_output_size += bufsize);
+ if (size > xstate->mem_output_size_max) {
+ free(xstate->mem_output_buf);
+ xstate->mem_output_buf = NULL;
+ bb_perror_msg("buffer %u too small", (unsigned)xstate->mem_output_size_max);
+ nwrote = -1;
+ goto ret;
+ }
+ xstate->mem_output_buf = xrealloc(xstate->mem_output_buf, size + 1);
+ memcpy(xstate->mem_output_buf + pos, buf, bufsize);
+ xstate->mem_output_buf[size] = '\0';
+ nwrote = bufsize;
+ } else {
+ nwrote = full_write(xstate->dst_fd, buf, bufsize);
+ if (nwrote != (ssize_t)bufsize) {
+ bb_perror_msg("write");
+ nwrote = -1;
+ goto ret;
+ }
+ }
+ ret:
+ return nwrote;
+}
+
+ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
+{
+ ssize_t nwrote = transformer_write(xstate, buf, bufsize);
+ if (nwrote != (ssize_t)bufsize) {
+ xfunc_die();
+ }
+ return nwrote;
+}
+
void check_errors_in_children(int signo)
{
int status;
@@ -60,12 +98,12 @@ void check_errors_in_children(int signo)
/* transformer(), more than meets the eye */
#if BB_MMU
-void FAST_FUNC open_transformer(int fd,
- int check_signature,
- IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+void FAST_FUNC fork_transformer(int fd,
+ int signature_skipped,
+ IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate)
)
#else
-void FAST_FUNC open_transformer(int fd, const char *transform_prog)
+void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
#endif
{
struct fd_pair fd_pipe;
@@ -80,10 +118,12 @@ void FAST_FUNC open_transformer(int fd, const char *transform_prog)
#if BB_MMU
{
IF_DESKTOP(long long) int r;
- transformer_aux_data_t aux;
- init_transformer_aux_data(&aux);
- aux.check_signature = check_signature;
- r = transformer(&aux, fd, fd_pipe.wr);
+ transformer_state_t xstate;
+ init_transformer_state(&xstate);
+ xstate.signature_skipped = signature_skipped;
+ xstate.src_fd = fd;
+ xstate.dst_fd = fd_pipe.wr;
+ r = transformer(&xstate);
if (ENABLE_FEATURE_CLEAN_UP) {
close(fd_pipe.wr); /* send EOF */
close(fd);
@@ -118,16 +158,18 @@ void FAST_FUNC open_transformer(int fd, const char *transform_prog)
/* Used by e.g. rpm which gives us a fd without filename,
* thus we can't guess the format from filename's extension.
*/
-int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
+static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed)
{
union {
uint8_t b[4];
uint16_t b16[2];
uint32_t b32[1];
} magic;
- int offset = -2;
- USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd);)
- USE_FOR_NOMMU(const char *xformer_prog;)
+ transformer_state_t *xstate;
+
+ xstate = xzalloc(sizeof(*xstate));
+ xstate->src_fd = fd;
+ xstate->signature_skipped = 2;
/* .gz and .bz2 both have 2-byte signature, and their
* unpack_XXX_stream wants this header skipped. */
@@ -135,25 +177,32 @@ int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
if (ENABLE_FEATURE_SEAMLESS_GZ
&& magic.b16[0] == GZIP_MAGIC
) {
- USE_FOR_MMU(xformer = unpack_gz_stream;)
- USE_FOR_NOMMU(xformer_prog = "gunzip";)
+ xstate->xformer = unpack_gz_stream;
+ USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";)
+ goto found_magic;
+ }
+ if (ENABLE_FEATURE_SEAMLESS_Z
+ && magic.b16[0] == COMPRESS_MAGIC
+ ) {
+ xstate->xformer = unpack_Z_stream;
+ USE_FOR_NOMMU(xstate->xformer_prog = "uncompress";)
goto found_magic;
}
if (ENABLE_FEATURE_SEAMLESS_BZ2
&& magic.b16[0] == BZIP2_MAGIC
) {
- USE_FOR_MMU(xformer = unpack_bz2_stream;)
- USE_FOR_NOMMU(xformer_prog = "bunzip2";)
+ xstate->xformer = unpack_bz2_stream;
+ USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";)
goto found_magic;
}
if (ENABLE_FEATURE_SEAMLESS_XZ
&& magic.b16[0] == XZ_MAGIC1
) {
- offset = -6;
+ xstate->signature_skipped = 6;
xread(fd, magic.b32, sizeof(magic.b32[0]));
if (magic.b32[0] == XZ_MAGIC2) {
- USE_FOR_MMU(xformer = unpack_xz_stream;)
- USE_FOR_NOMMU(xformer_prog = "unxz";)
+ xstate->xformer = unpack_xz_stream;
+ USE_FOR_NOMMU(xstate->xformer_prog = "unxz";)
goto found_magic;
}
}
@@ -164,52 +213,137 @@ int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
IF_FEATURE_SEAMLESS_BZ2("/bzip2")
IF_FEATURE_SEAMLESS_XZ("/xz")
" magic");
- xlseek(fd, offset, SEEK_CUR);
- return 1;
+
+ /* Some callers expect this function to "consume" fd
+ * even if data is not compressed. In this case,
+ * we return a state with trivial transformer.
+ */
+// USE_FOR_MMU(xstate->xformer = copy_stream;)
+// USE_FOR_NOMMU(xstate->xformer_prog = "cat";)
found_magic:
+ return xstate;
+}
+
+/* Used by e.g. rpm which gives us a fd without filename,
+ * thus we can't guess the format from filename's extension.
+ */
+int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
+{
+ transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
+
+ if (!xstate || !xstate->xformer) {
+ free(xstate);
+ return 1;
+ }
+
# if BB_MMU
- open_transformer_with_no_sig(fd, xformer);
+ fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
# else
- /* NOMMU version of open_transformer execs
+ /* NOMMU version of fork_transformer execs
* an external unzipper that wants
- * file position at the start of the file */
- xlseek(fd, offset, SEEK_CUR);
- open_transformer_with_sig(fd, xformer, xformer_prog);
+ * file position at the start of the file.
+ */
+ xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
+ xstate->signature_skipped = 0;
+ fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
# endif
+ free(xstate);
return 0;
}
-int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
+static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
{
+ transformer_state_t *xstate;
int fd;
fd = open(fname, O_RDONLY);
if (fd < 0)
- return fd;
+ return NULL;
if (ENABLE_FEATURE_SEAMLESS_LZMA) {
/* .lzma has no header/signature, can only detect it by extension */
char *sfx = strrchr(fname, '.');
if (sfx && strcmp(sfx+1, "lzma") == 0) {
- open_transformer_with_sig(fd, unpack_lzma_stream, "unlzma");
- return fd;
+ xstate = xzalloc(sizeof(*xstate));
+ xstate->src_fd = fd;
+ xstate->xformer = unpack_lzma_stream;
+ USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
+ return xstate;
}
}
- if ((ENABLE_FEATURE_SEAMLESS_GZ)
- || (ENABLE_FEATURE_SEAMLESS_BZ2)
- || (ENABLE_FEATURE_SEAMLESS_XZ)
- ) {
- setup_unzip_on_fd(fd, fail_if_not_compressed);
+
+ xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
+
+ return xstate;
+}
+
+int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
+{
+ int fd;
+ transformer_state_t *xstate;
+
+ xstate = open_transformer(fname, fail_if_not_compressed);
+ if (!xstate)
+ return -1;
+
+ fd = xstate->src_fd;
+# if BB_MMU
+ if (xstate->xformer) {
+ fork_transformer_with_no_sig(fd, xstate->xformer);
+ } else {
+ /* the file is not compressed */
+ xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
+ xstate->signature_skipped = 0;
}
+# else
+ /* NOMMU can't avoid the seek :( */
+ xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
+ xstate->signature_skipped = 0;
+ if (xstate->xformer) {
+ fork_transformer_with_sig(fd, xstate->xformer, xstate->xformer_prog);
+ } /* else: the file is not compressed */
+# endif
+ free(xstate);
return fd;
}
-#endif /* SEAMLESS_COMPRESSION */
-
void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
{
+# if 1
+ transformer_state_t *xstate;
+ char *image;
+
+ xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0);
+ if (!xstate) /* file open error */
+ return NULL;
+
+ image = NULL;
+ if (xstate->xformer) {
+ /* In-memory decompression */
+ xstate->mem_output_size_max = maxsz_p ? *maxsz_p : (size_t)(INT_MAX - 4095);
+ xstate->xformer(xstate);
+ if (xstate->mem_output_buf) {
+ image = xstate->mem_output_buf;
+ if (maxsz_p)
+ *maxsz_p = xstate->mem_output_size;
+ }
+ } else {
+ /* File is not compressed */
+//FIXME: avoid seek
+ xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
+ xstate->signature_skipped = 0;
+ image = xmalloc_read(xstate->src_fd, maxsz_p);
+ }
+
+ if (!image)
+ bb_perror_msg("read error from '%s'", fname);
+ close(xstate->src_fd);
+ free(xstate);
+ return image;
+# else
+ /* This version forks a subprocess - much more expensive */
int fd;
char *image;
@@ -221,6 +355,8 @@ void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_
if (!image)
bb_perror_msg("read error from '%s'", fname);
close(fd);
-
return image;
+# endif
}
+
+#endif /* SEAMLESS_COMPRESSION */
diff --git a/archival/libarchive/unpack_ar_archive.c b/archival/libarchive/unpack_ar_archive.c
index 214d17e..0bc0303 100644
--- a/archival/libarchive/unpack_ar_archive.c
+++ b/archival/libarchive/unpack_ar_archive.c
@@ -12,7 +12,7 @@ void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive)
char magic[7];
xread(ar_archive->src_fd, magic, AR_MAGIC_LEN);
- if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) {
+ if (!is_prefixed_with(magic, AR_MAGIC)) {
bb_error_msg_and_die("invalid ar magic");
}
ar_archive->offset += AR_MAGIC_LEN;
diff --git a/archival/libarchive/unsafe_prefix.c b/archival/libarchive/unsafe_prefix.c
new file mode 100644
index 0000000..9994f4d
--- a/dev/null
+++ b/archival/libarchive/unsafe_prefix.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "bb_archive.h"
+
+const char* FAST_FUNC strip_unsafe_prefix(const char *str)
+{
+ const char *cp = str;
+ while (1) {
+ char *cp2;
+ if (*cp == '/') {
+ cp++;
+ continue;
+ }
+ if (is_prefixed_with(cp, "/../"+1)) {
+ cp += 3;
+ continue;
+ }
+ cp2 = strstr(cp, "/../");
+ if (!cp2)
+ break;
+ cp = cp2 + 4;
+ }
+ if (cp != str) {
+ static smallint warned = 0;
+ if (!warned) {
+ warned = 1;
+ bb_error_msg("removing leading '%.*s' from member names",
+ (int)(cp - str), str);
+ }
+ }
+ return cp;
+}
diff --git a/archival/libarchive/unxz/xz_dec_lzma2.c b/archival/libarchive/unxz/xz_dec_lzma2.c
index 3c2dc88..351251f 100644
--- a/archival/libarchive/unxz/xz_dec_lzma2.c
+++ b/archival/libarchive/unxz/xz_dec_lzma2.c
@@ -995,10 +995,8 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
s->lzma2.need_props = false;
s->lzma2.next_sequence
= SEQ_PROPERTIES;
-
} else if (s->lzma2.need_props) {
return XZ_DATA_ERROR;
-
} else {
s->lzma2.next_sequence
= SEQ_LZMA_PREPARE;
@@ -1080,7 +1078,6 @@ XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
rc_reset(&s->rc);
s->lzma2.sequence = SEQ_CONTROL;
-
} else if (b->out_pos == b->out_size
|| (b->in_pos == b->in_size
&& s->temp.size
diff --git a/archival/libarchive/unxz/xz_dec_stream.c b/archival/libarchive/unxz/xz_dec_stream.c
index bdcbf1b..bf79105 100644
--- a/archival/libarchive/unxz/xz_dec_stream.c
+++ b/archival/libarchive/unxz/xz_dec_stream.c
@@ -353,7 +353,6 @@ static enum xz_ret XZ_FUNC crc32_validate(struct xz_dec *s, struct xz_buf *b)
return XZ_DATA_ERROR;
s->pos += 8;
-
} while (s->pos < 32);
s->crc32 = 0;
@@ -753,7 +752,6 @@ XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b)
b->in_pos = in_start;
b->out_pos = out_start;
}
-
} else if (ret == XZ_OK && in_start == b->in_pos
&& out_start == b->out_pos) {
if (s->allow_buf_error)
diff --git a/archival/lzop.c b/archival/lzop.c
index 5062d93..e0e90ac 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -31,19 +31,33 @@
//config: help
//config: Lzop compression/decompresion.
//config:
+//config:config UNLZOP
+//config: bool "unlzop"
+//config: default y
+//config: help
+//config: Lzop decompresion.
+//config:
+//config:config LZOPCAT
+//config: bool "lzopcat"
+//config: default y
+//config: help
+//config: Alias to "unlzop -c".
+//config:
//config:config LZOP_COMPR_HIGH
//config: bool "lzop compression levels 7,8,9 (not very useful)"
//config: default n
-//config: depends on LZOP
+//config: depends on LZOP || UNLZOP || LZOPCAT
//config: help
//config: High levels (7,8,9) of lzop compression. These levels
//config: are actually slower than gzip at equivalent compression ratios
//config: and take up 3.2K of code.
//applet:IF_LZOP(APPLET(lzop, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_LZOP(APPLET_ODDNAME(lzopcat, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, lzopcat))
-//applet:IF_LZOP(APPLET_ODDNAME(unlzop, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, unlzop))
+//applet:IF_UNLZOP(APPLET_ODDNAME(unlzop, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, unlzop))
+//applet:IF_LZOPCAT(APPLET_ODDNAME(lzopcat, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, lzopcat))
//kbuild:lib-$(CONFIG_LZOP) += lzop.o
+//kbuild:lib-$(CONFIG_UNLZOP) += lzop.o
+//kbuild:lib-$(CONFIG_LZOPCAT) += lzop.o
//usage:#define lzop_trivial_usage
//usage: "[-cfvd123456789CF] [FILE]..."
@@ -57,20 +71,21 @@
//usage: "\n -C Also write checksum of compressed block"
//usage:
//usage:#define lzopcat_trivial_usage
-//usage: "[-vCF] [FILE]..."
+//usage: "[-vF] [FILE]..."
//usage:#define lzopcat_full_usage "\n\n"
//usage: " -v Verbose"
-//usage: "\n -F Don't store or verify checksum"
+//usage: "\n -F Don't verify checksum"
//usage:
//usage:#define unlzop_trivial_usage
-//usage: "[-cfvCF] [FILE]..."
+//usage: "[-cfvF] [FILE]..."
//usage:#define unlzop_full_usage "\n\n"
//usage: " -c Write to stdout"
//usage: "\n -f Force"
//usage: "\n -v Verbose"
-//usage: "\n -F Don't store or verify checksum"
+//usage: "\n -F Don't verify checksum"
#include "libbb.h"
+#include "common_bufsiz.h"
#include "bb_archive.h"
#include "liblzo_interface.h"
@@ -443,8 +458,8 @@ struct globals {
chksum_t chksum_in;
chksum_t chksum_out;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
//#define G (*ptr_to_globals)
//#define INIT_G() do {
// SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
@@ -640,7 +655,7 @@ static int lzo_get_method(header_t *h)
/**********************************************************************/
// compress a file
/**********************************************************************/
-static NOINLINE smallint lzo_compress(const header_t *h)
+static NOINLINE int lzo_compress(const header_t *h)
{
unsigned block_size = LZO_BLOCK_SIZE;
int r = 0; /* LZO_E_OK */
@@ -650,7 +665,6 @@ static NOINLINE smallint lzo_compress(const header_t *h)
uint32_t d_adler32 = ADLER32_INIT_VALUE;
uint32_t d_crc32 = CRC32_INIT_VALUE;
int l;
- smallint ok = 1;
uint8_t *wrk_mem = NULL;
if (h->method == M_LZO1X_1)
@@ -732,7 +746,7 @@ static NOINLINE smallint lzo_compress(const header_t *h)
free(wrk_mem);
free(b1);
free(b2);
- return ok;
+ return 1;
}
static FAST_FUNC void lzo_check(
@@ -753,7 +767,7 @@ static FAST_FUNC void lzo_check(
/**********************************************************************/
// decompress a file
/**********************************************************************/
-static NOINLINE smallint lzo_decompress(const header_t *h)
+static NOINLINE int lzo_decompress(const header_t *h)
{
unsigned block_size = LZO_BLOCK_SIZE;
int r;
@@ -761,7 +775,6 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
uint32_t c_adler32 = ADLER32_INIT_VALUE;
uint32_t d_adler32 = ADLER32_INIT_VALUE;
uint32_t c_crc32 = CRC32_INIT_VALUE, d_crc32 = CRC32_INIT_VALUE;
- smallint ok = 1;
uint8_t *b1;
uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
uint8_t *b2 = NULL;
@@ -865,7 +878,7 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
}
free(b2);
- return ok;
+ return 1;
}
/**********************************************************************/
@@ -897,7 +910,7 @@ static NOINLINE smallint lzo_decompress(const header_t *h)
* chksum_out
* The rest is identical.
*/
-static const unsigned char lzop_magic[9] = {
+static const unsigned char lzop_magic[9] ALIGN1 = {
0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a
};
@@ -1050,7 +1063,7 @@ static void lzo_set_method(header_t *h)
h->level = level;
}
-static smallint do_lzo_compress(void)
+static int do_lzo_compress(void)
{
header_t header;
@@ -1078,7 +1091,7 @@ static smallint do_lzo_compress(void)
/**********************************************************************/
// decompress
/**********************************************************************/
-static smallint do_lzo_decompress(void)
+static int do_lzo_decompress(void)
{
header_t header;
@@ -1099,7 +1112,7 @@ static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_e
return xasprintf("%s.lzo", filename);
}
-static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_aux_data_t *aux UNUSED_PARAM)
+static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_state_t *xstate UNUSED_PARAM)
{
if (option_mask32 & OPT_DECOMPRESS)
return do_lzo_decompress();
@@ -1112,10 +1125,10 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
getopt32(argv, OPTION_STRING);
argv += optind;
/* lzopcat? */
- if (applet_name[4] == 'c')
+ if (ENABLE_LZOPCAT && applet_name[4] == 'c')
option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
/* unlzop? */
- if (applet_name[4] == 'o')
+ if (ENABLE_UNLZOP && applet_name[4] == 'o')
option_mask32 |= OPT_DECOMPRESS;
global_crc32_table = crc32_filltable(NULL, 0);
diff --git a/archival/rpm.c b/archival/rpm.c
index 1053944..83160f9 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -29,6 +29,7 @@
//usage: "\n -qpc List config files"
#include "libbb.h"
+#include "common_bufsiz.h"
#include "bb_archive.h"
#include "rpm.h"
@@ -93,8 +94,8 @@ struct globals {
rpm_index **mytags;
int tagcount;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void extract_cpio(int fd, const char *source_rpm)
{
diff --git a/archival/tar.c b/archival/tar.c
index cdc6067..8e315c6 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -146,25 +146,23 @@
#include <fnmatch.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#include "bb_archive.h"
/* FIXME: Stop using this non-standard feature */
#ifndef FNM_LEADING_DIR
# define FNM_LEADING_DIR 0
#endif
-
-//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
-#define DBG(...) ((void)0)
+#if 0
+# define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
+#else
+# define DBG(...) ((void)0)
+#endif
+#define DBG_OPTION_PARSING 0
#define block_buf bb_common_bufsiz1
-
-
-#if !ENABLE_FEATURE_SEAMLESS_GZ && !ENABLE_FEATURE_SEAMLESS_BZ2
-/* Do not pass gzip flag to writeTarFile() */
-#define writeTarFile(tar_fd, verboseFlag, optFlags, recurseFlags, include, exclude, gzip) \
- writeTarFile(tar_fd, verboseFlag, optFlags, recurseFlags, include, exclude)
-#endif
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#if ENABLE_FEATURE_TAR_CREATE
@@ -187,8 +185,6 @@ typedef struct TarBallInfo {
int tarFd; /* Open-for-write file descriptor
* for the tarball */
int verboseFlag; /* Whether to print extra stuff or not */
- unsigned optFlags; /* all command line flags */
-
const llist_t *excludeList; /* List of files to not include */
HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
@@ -212,7 +208,6 @@ enum {
CONTTYPE = '7', /* reserved */
GNULONGLINK = 'K', /* GNU long (>100 chars) link name */
GNULONGNAME = 'L', /* GNU long (>100 chars) file name */
- EXTTYPE = 'x', /* ext metadata for next file, store selinux_context */
};
/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
@@ -354,34 +349,6 @@ static void writeLongname(int fd, int type, const char *name, int dir)
}
#endif
-#if ENABLE_FEATURE_TAR_SELINUX
-# define SELINUX_CONTEXT_KEYWORD "RHT.security.selinux"
-/* Write 2 blocks : extended file header + selinux context */
-static int writeSeHeader(int fd, const char *con, struct tar_header_t *header)
-{
- char block[TAR_BLOCK_SIZE];
- struct tar_header_t hd;
-
- int sz = sizeof(SELINUX_CONTEXT_KEYWORD) + 4 + strlen(con);
- if (sz >= 100) sz++; /* another ascii digit for size */
- if (sz > TAR_BLOCK_SIZE)
- return FALSE;
-
- memset(&block, 0, TAR_BLOCK_SIZE);
- sprintf(block, "%d %s=%s\n", sz, SELINUX_CONTEXT_KEYWORD, con);
-
- /* write duplicated file entry */
- memcpy(&hd, header, sizeof(hd));
- hd.typeflag = EXTTYPE;
- PUT_OCTAL(hd.size, sz);
- chksum_and_xwrite(fd, &hd);
-
- /* write selinux context */
- xwrite(fd, &block, TAR_BLOCK_SIZE);
- return TRUE;
-}
-#endif
-
/* Write out a tar header for the specified file/directory/whatever */
static int writeTarHeader(struct TarBallInfo *tbInfo,
const char *header_name, const char *fileName, struct stat *statbuf)
@@ -397,7 +364,8 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
PUT_OCTAL(header.uid, statbuf->st_uid);
PUT_OCTAL(header.gid, statbuf->st_gid);
memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
- PUT_OCTAL(header.mtime, statbuf->st_mtime);
+ /* users report that files with negative st_mtime cause trouble, so: */
+ PUT_OCTAL(header.mtime, statbuf->st_mtime >= 0 ? statbuf->st_mtime : 0);
/* Enter the user and group names */
safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
@@ -481,7 +449,7 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
*p8 |= 0x80;
} else {
bb_error_msg_and_die("can't store file '%s' "
- "of size %"FILESIZE_FMT"u, aborting",
+ "of size %"OFF_FMT"u, aborting",
fileName, statbuf->st_size);
}
header.typeflag = REGTYPE;
@@ -498,18 +466,6 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
header_name, S_ISDIR(statbuf->st_mode));
#endif
-#if ENABLE_FEATURE_TAR_SELINUX
- if (is_selinux_enabled() && (tbInfo->optFlags & ARCHIVE_STORE_SELINUX)) {
- security_context_t sid;
- lgetfilecon(fileName, &sid);
- if (sid) {
- // optional extended block
- writeSeHeader(tbInfo->tarFd, sid, &header);
- freecon(sid);
- }
- }
-#endif
-
/* Now write the header out to disk */
chksum_and_xwrite(tbInfo->tarFd, &header);
@@ -663,21 +619,12 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
return TRUE;
}
-#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
-# if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2)
-# define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd)
-# endif
+#if SEAMLESS_COMPRESSION
/* Don't inline: vfork scares gcc and pessimizes code */
-static void NOINLINE vfork_compressor(int tar_fd, int gzip)
+static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
{
pid_t gzipPid;
-# if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2
- const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
-# elif ENABLE_FEATURE_SEAMLESS_GZ
- const char *zip_exec = "gzip";
-# else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */
- const char *zip_exec = "bzip2";
-# endif
+
// On Linux, vfork never unpauses parent early, although standard
// allows for that. Do we want to waste bytes checking for it?
# define WAIT_FOR_CHILD 0
@@ -691,11 +638,6 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip)
signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
-# if defined(__GNUC__) && __GNUC__
- /* Avoid vfork clobbering */
- (void) &zip_exec;
-# endif
-
gzipPid = xvfork();
if (gzipPid == 0) {
@@ -711,7 +653,7 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip)
xmove_fd(gzipDataPipe.rd, 0);
xmove_fd(tar_fd, 1);
/* exec gzip/bzip2 program/applet */
- BB_EXECLP(zip_exec, zip_exec, "-f", (char *)0);
+ BB_EXECLP(gzip, gzip, "-f", (char *)0);
vfork_exec_errno = errno;
_exit(EXIT_FAILURE);
}
@@ -734,17 +676,21 @@ static void NOINLINE vfork_compressor(int tar_fd, int gzip)
# endif
if (vfork_exec_errno) {
errno = vfork_exec_errno;
- bb_perror_msg_and_die("can't execute '%s'", zip_exec);
+ bb_perror_msg_and_die("can't execute '%s'", gzip);
}
}
-#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */
+#endif /* SEAMLESS_COMPRESSION */
+#if !SEAMLESS_COMPRESSION
+/* Do not pass gzip flag to writeTarFile() */
+#define writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude, gzip) \
+ writeTarFile(tar_fd, verboseFlag, recurseFlags, include, exclude)
+#endif
/* gcc 4.2.1 inlines it, making code bigger */
static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
- unsigned optFlags,
int recurseFlags, const llist_t *include,
- const llist_t *exclude, int gzip)
+ const llist_t *exclude, const char *gzip)
{
int errorFlag = FALSE;
struct TarBallInfo tbInfo;
@@ -752,13 +698,12 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
tbInfo.hlInfoHead = NULL;
tbInfo.tarFd = tar_fd;
tbInfo.verboseFlag = verboseFlag;
- tbInfo.optFlags = optFlags;
/* Store the stat info for the tarball's file, so
* can avoid including the tarball into itself.... */
xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file");
-#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+#if SEAMLESS_COMPRESSION
if (gzip)
vfork_compressor(tbInfo.tarFd, gzip);
#endif
@@ -793,7 +738,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
if (errorFlag)
bb_error_msg("error exit delayed from previous errors");
-#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+#if SEAMLESS_COMPRESSION
if (gzip) {
int status;
if (safe_waitpid(-1, &status, 0) == -1)
@@ -805,12 +750,9 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
#endif
return errorFlag;
}
-#else
-int writeTarFile(int tar_fd, int verboseFlag,
- unsigned optFlags,
- int recurseFlags, const llist_t *include,
- const llist_t *exclude, int gzip);
-#endif /* FEATURE_TAR_CREATE */
+#else /* !FEATURE_TAR_CREATE */
+# define writeTarFile(...) 0
+#endif
#if ENABLE_FEATURE_TAR_FROM
static llist_t *append_file_list_to_list(llist_t *list)
@@ -843,7 +785,6 @@ static llist_t *append_file_list_to_list(llist_t *list)
//usage: IF_FEATURE_SEAMLESS_LZMA("a")
//usage: IF_FEATURE_TAR_CREATE("h")
//usage: IF_FEATURE_TAR_NOPRESERVE_TIME("m")
-//usage: IF_FEATURE_TAR_SELINUX("p")
//usage: "vO] "
//usage: IF_FEATURE_TAR_FROM("[-X FILE] [-T FILE] ")
//usage: "[-f TARFILE] [-C DIR] [FILE]..."
@@ -889,9 +830,6 @@ static llist_t *append_file_list_to_list(llist_t *list)
//usage: "\n X File with names to exclude"
//usage: "\n T File with names to include"
//usage: )
-//usage: IF_FEATURE_TAR_SELINUX(
-//usage: "\n p Store SELinux contexts"
-//usage: )
//usage:
//usage:#define tar_example_usage
//usage: "$ zcat /tmp/tarball.tar.gz | tar -xf -\n"
@@ -922,6 +860,7 @@ enum {
IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,)
IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ OPTBIT_STRIP_COMPONENTS,
OPTBIT_NORECURSION,
IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,)
OPTBIT_NUMERIC_OWNER,
@@ -946,12 +885,13 @@ enum {
OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z
OPT_XZ = IF_FEATURE_SEAMLESS_XZ( (1 << OPTBIT_XZ )) + 0, // J
OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z
- OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
- OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion
- OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command
- OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner
- OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions
- OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite
+ OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
+ OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components
+ OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion
+ OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command
+ OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner
+ OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions
+ OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite
OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS),
};
@@ -995,6 +935,7 @@ static const char tar_longopts[] ALIGN1 =
# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
"touch\0" No_argument "m"
# endif
+ "strip-components\0" Required_argument "\xf9"
"no-recursion\0" No_argument "\xfa"
# if ENABLE_FEATURE_TAR_TO_COMMAND
"to-command\0" Required_argument "\xfb"
@@ -1024,6 +965,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
llist_t *excludes = NULL;
#endif
+ INIT_G();
/* Initialise default values */
tar_handle = init_handle();
@@ -1038,17 +980,29 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
/* Prepend '-' to the first argument if required */
opt_complementary = "--:" // first arg is options
"tt:vv:" // count -t,-v
- IF_FEATURE_TAR_FROM("X::T::") // cumulative lists
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
- "\xff::" // cumulative lists for --exclude
+ "\xff::" // --exclude=PATTERN is a list
#endif
IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
- IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
+ IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ ":\xf9+" // --strip-components=NUM
+#endif
+ ;
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
applet_long_options = tar_longopts;
#endif
#if ENABLE_DESKTOP
+ /* Lie to buildroot when it starts asking stupid questions. */
+ if (argv[1] && strcmp(argv[1], "--version") == 0) {
+ // Output of 'tar --version' examples:
+ // tar (GNU tar) 1.15.1
+ // tar (GNU tar) 1.25
+ // bsdtar 2.8.3 - libarchive 2.8.3
+ puts("tar (busybox) " BB_VER);
+ return 0;
+ }
if (argv[1] && argv[1][0] != '-') {
/* Compat:
* 1st argument without dash handles options with parameters
@@ -1077,18 +1031,22 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
#endif
opt = getopt32(argv,
"txC:f:Oopvk"
- IF_FEATURE_TAR_CREATE( "ch" )
- IF_FEATURE_SEAMLESS_BZ2( "j" )
- IF_FEATURE_SEAMLESS_LZMA("a" )
- IF_FEATURE_TAR_FROM( "T:X:")
- IF_FEATURE_SEAMLESS_GZ( "z" )
- IF_FEATURE_SEAMLESS_XZ( "J" )
- IF_FEATURE_SEAMLESS_Z( "Z" )
+ IF_FEATURE_TAR_CREATE( "ch" )
+ IF_FEATURE_SEAMLESS_BZ2( "j" )
+ IF_FEATURE_SEAMLESS_LZMA("a" )
+ IF_FEATURE_TAR_FROM( "T:*X:*")
+ IF_FEATURE_SEAMLESS_GZ( "z" )
+ IF_FEATURE_SEAMLESS_XZ( "J" )
+ IF_FEATURE_SEAMLESS_Z( "Z" )
IF_FEATURE_TAR_NOPRESERVE_TIME("m")
+ IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components
, &base_dir // -C dir
, &tar_filename // -f filename
IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ , &tar_handle->tar__strip_components // --strip-components
+#endif
IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
, &excludes // --exclude
@@ -1096,11 +1054,49 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
, &verboseFlag // combined count for -t and -v
, &verboseFlag // combined count for -t and -v
);
- //bb_error_msg("opt:%08x", opt);
+#if DBG_OPTION_PARSING
+ bb_error_msg("opt: 0x%08x", opt);
+# define showopt(o) bb_error_msg("opt & %s(%x): %x", #o, o, opt & o);
+ showopt(OPT_TEST );
+ showopt(OPT_EXTRACT );
+ showopt(OPT_BASEDIR );
+ showopt(OPT_TARNAME );
+ showopt(OPT_2STDOUT );
+ showopt(OPT_NOPRESERVE_OWNER);
+ showopt(OPT_P );
+ showopt(OPT_VERBOSE );
+ showopt(OPT_KEEP_OLD );
+ showopt(OPT_CREATE );
+ showopt(OPT_DEREFERENCE );
+ showopt(OPT_BZIP2 );
+ showopt(OPT_LZMA );
+ showopt(OPT_INCLUDE_FROM );
+ showopt(OPT_EXCLUDE_FROM );
+ showopt(OPT_GZIP );
+ showopt(OPT_XZ );
+ showopt(OPT_COMPRESS );
+ showopt(OPT_NOPRESERVE_TIME );
+ showopt(OPT_STRIP_COMPONENTS);
+ showopt(OPT_NORECURSION );
+ showopt(OPT_2COMMAND );
+ showopt(OPT_NUMERIC_OWNER );
+ showopt(OPT_NOPRESERVE_PERM );
+ showopt(OPT_OVERWRITE );
+ showopt(OPT_ANY_COMPRESS );
+ bb_error_msg("base_dir:'%s'", base_dir);
+ bb_error_msg("tar_filename:'%s'", tar_filename);
+ bb_error_msg("verboseFlag:%d", verboseFlag);
+ bb_error_msg("tar_handle->tar__to_command:'%s'", tar_handle->tar__to_command);
+ bb_error_msg("tar_handle->tar__strip_components:%u", tar_handle->tar__strip_components);
+ return 0;
+# undef showopt
+#endif
argv += optind;
- if (verboseFlag) tar_handle->action_header = header_verbose_list;
- if (verboseFlag == 1) tar_handle->action_header = header_list;
+ if (verboseFlag)
+ tar_handle->action_header = header_verbose_list;
+ if (verboseFlag == 1)
+ tar_handle->action_header = header_list;
if (opt & OPT_EXTRACT)
tar_handle->action_data = data_extract_all;
@@ -1127,11 +1123,6 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
if (opt & OPT_NOPRESERVE_PERM)
tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
-#if ENABLE_FEATURE_TAR_SELINUX
- if (opt & OPT_P)
- tar_handle->ah_flags |= ARCHIVE_STORE_SELINUX;
-#endif
-
if (opt & OPT_OVERWRITE) {
tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD;
tar_handle->ah_flags |= ARCHIVE_O_TRUNC;
@@ -1203,30 +1194,37 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
if (base_dir)
xchdir(base_dir);
- //if (SEAMLESS_COMPRESSION || OPT_COMPRESS)
+ //if (SEAMLESS_COMPRESSION)
// /* We need to know whether child (gzip/bzip/etc) exits abnormally */
// signal(SIGCHLD, check_errors_in_children);
+#if ENABLE_FEATURE_TAR_CREATE
/* Create an archive */
if (opt & OPT_CREATE) {
-#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
- int zipMode = 0;
- if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP))
- zipMode = 1;
- if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2))
- zipMode = 2;
-#endif
+# if SEAMLESS_COMPRESSION
+ const char *zipMode = NULL;
+ if (opt & OPT_COMPRESS)
+ zipMode = "compress";
+ if (opt & OPT_GZIP)
+ zipMode = "gzip";
+ if (opt & OPT_BZIP2)
+ zipMode = "bzip2";
+ if (opt & OPT_LZMA)
+ zipMode = "lzma";
+ if (opt & OPT_XZ)
+ zipMode = "xz";
+# endif
/* NB: writeTarFile() closes tar_handle->src_fd */
return writeTarFile(tar_handle->src_fd, verboseFlag,
- tar_handle->ah_flags,
(opt & OPT_DEREFERENCE ? ACTION_FOLLOWLINKS : 0)
| (opt & OPT_NORECURSION ? 0 : ACTION_RECURSE),
tar_handle->accept,
tar_handle->reject, zipMode);
}
+#endif
if (opt & OPT_ANY_COMPRESS) {
- USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd);)
+ USE_FOR_MMU(IF_DESKTOP(long long) int FAST_FUNC (*xformer)(transformer_state_t *xstate);)
USE_FOR_NOMMU(const char *xformer_prog;)
if (opt & OPT_COMPRESS)
@@ -1245,7 +1243,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
USE_FOR_MMU(xformer = unpack_xz_stream;)
USE_FOR_NOMMU(xformer_prog = "unxz";)
- open_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog);
+ fork_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog);
/* Can't lseek over pipes */
tar_handle->seek = seek_by_read;
/*tar_handle->offset = 0; - already is */
diff --git a/archival/unzip.c b/archival/unzip.c
index 1cde28d..c540485 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -45,10 +45,10 @@
#include "libbb.h"
#include "bb_archive.h"
-#if defined(ANDROID) || defined(__ANDROID__)
-/* Needed to ensure we can extract signed Android OTA packages */
-#undef ENABLE_DESKTOP
-#define ENABLE_DESKTOP 1
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
#endif
enum {
@@ -199,15 +199,17 @@ static uint32_t find_cdf_offset(void)
unsigned char *p;
off_t end;
unsigned char *buf = xzalloc(PEEK_FROM_END);
+ uint32_t found;
end = xlseek(zip_fd, 0, SEEK_END);
end -= PEEK_FROM_END;
if (end < 0)
end = 0;
- xlseek(zip_fd, end, SEEK_SET);
+ dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end);
+ xlseek(zip_fd, end, SEEK_SET);
full_read(zip_fd, buf, PEEK_FROM_END);
- cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
+ found = BAD_CDF_OFFSET;
p = buf;
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
if (*p != 'P') {
@@ -226,14 +228,25 @@ static uint32_t find_cdf_offset(void)
/*
* I've seen .ZIP files with seemingly valid CDEs
* where cdf_offset points past EOF - ??
- * Ignore such CDEs:
+ * This check ignores such CDEs:
*/
- if ((int64_t) cde_header.formatted.cdf_offset < end + (p - buf))
- break;
- cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
+ if (cde_header.formatted.cdf_offset < end + (p - buf)) {
+ found = cde_header.formatted.cdf_offset;
+ dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x",
+ (unsigned)found, end + (p-3 - buf));
+ dbg(" cdf_offset+cdf_size:0x%x",
+ (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size)));
+ /*
+ * We do not "break" here because only the last CDE is valid.
+ * I've seen a .zip archive which contained a .zip file,
+ * uncompressed, and taking the first CDE was using
+ * the CDE inside that file!
+ */
+ }
}
free(buf);
- return cde_header.formatted.cdf_offset;
+ dbg("Found cdf_offset:0x%x", (unsigned)found);
+ return found;
};
static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
@@ -246,15 +259,22 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
cdf_offset = find_cdf_offset();
if (cdf_offset != BAD_CDF_OFFSET) {
+ dbg("Reading CDF at 0x%x", (unsigned)cdf_offset);
xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
FIX_ENDIANNESS_CDF(*cdf_ptr);
+ dbg(" file_name_length:%u extra_field_length:%u file_comment_length:%u",
+ (unsigned)cdf_ptr->formatted.file_name_length,
+ (unsigned)cdf_ptr->formatted.extra_field_length,
+ (unsigned)cdf_ptr->formatted.file_comment_length
+ );
cdf_offset += 4 + CDF_HEADER_LEN
+ cdf_ptr->formatted.file_name_length
+ cdf_ptr->formatted.extra_field_length
+ cdf_ptr->formatted.file_comment_length;
}
+ dbg("Returning file position to 0x%"OFF_FMT"x", org);
xlseek(zip_fd, org, SEEK_SET);
return cdf_offset;
};
@@ -286,17 +306,19 @@ static void unzip_extract(zip_header_t *zip_header, int dst_fd)
bb_copyfd_exact_size(zip_fd, dst_fd, size);
} else {
/* Method 8 - inflate */
- transformer_aux_data_t aux;
- init_transformer_aux_data(&aux);
- aux.bytes_in = zip_header->formatted.cmpsize;
- if (inflate_unzip(&aux, zip_fd, dst_fd) < 0)
+ transformer_state_t xstate;
+ init_transformer_state(&xstate);
+ xstate.bytes_in = zip_header->formatted.cmpsize;
+ xstate.src_fd = zip_fd;
+ xstate.dst_fd = dst_fd;
+ if (inflate_unzip(&xstate) < 0)
bb_error_msg_and_die("inflate error");
/* Validate decompression - crc */
- if (zip_header->formatted.crc32 != (aux.crc32 ^ 0xffffffffL)) {
+ if (zip_header->formatted.crc32 != (xstate.crc32 ^ 0xffffffffL)) {
bb_error_msg_and_die("crc error");
}
/* Validate decompression - size */
- if (zip_header->formatted.ucmpsize != (uint32_t) aux.bytes_out) {
+ if (zip_header->formatted.ucmpsize != xstate.bytes_out) {
/* Don't die. Who knows, maybe len calculation
* was botched somewhere. After all, crc matched! */
bb_error_msg("bad length");
@@ -465,7 +487,7 @@ int unzip_main(int argc, char **argv)
if (overwrite == O_PROMPT)
overwrite = O_NEVER;
} else {
- static const char extn[][5] = { ".zip", ".ZIP" };
+ static const char extn[][5] ALIGN1 = { ".zip", ".ZIP" };
char *ext = src_fn + strlen(src_fn);
int src_fd;
@@ -492,11 +514,11 @@ int unzip_main(int argc, char **argv)
printf("Archive: %s\n", src_fn);
if (listing) {
puts(verbose ?
- " Length Method Size Ratio Date Time CRC-32 Name\n"
- "-------- ------ ------- ----- ---- ---- ------ ----"
+ " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ "-------- ------ ------- ---- ---------- ----- -------- ----"
:
- " Length Date Time Name\n"
- " -------- ---- ---- ----"
+ " Length Date Time Name\n"
+ "--------- ---------- ----- ----"
);
}
}
@@ -536,11 +558,14 @@ int unzip_main(int argc, char **argv)
/* Check magic number */
xread(zip_fd, &magic, 4);
/* Central directory? It's at the end, so exit */
- if (magic == ZIP_CDF_MAGIC)
+ if (magic == ZIP_CDF_MAGIC) {
+ dbg("got ZIP_CDF_MAGIC");
break;
+ }
#if ENABLE_DESKTOP
/* Data descriptor? It was a streaming file, go on */
if (magic == ZIP_DD_MAGIC) {
+ dbg("got ZIP_DD_MAGIC");
/* skip over duplicate crc32, cmpsize and ucmpsize */
unzip_skip(3 * 4);
continue;
@@ -548,6 +573,7 @@ int unzip_main(int argc, char **argv)
#endif
if (magic != ZIP_FILEHEADER_MAGIC)
bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
+ dbg("got ZIP_FILEHEADER_MAGIC");
/* Read the file header */
xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
@@ -591,6 +617,11 @@ int unzip_main(int argc, char **argv)
bb_error_msg_and_die("can't find file table");
}
#endif
+ dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x",
+ (unsigned)zip_header.formatted.cmpsize,
+ (unsigned)zip_header.formatted.extra_len,
+ (unsigned)zip_header.formatted.ucmpsize
+ );
/* Read filename */
free(dst_fn);
@@ -600,52 +631,72 @@ int unzip_main(int argc, char **argv)
/* Skip extra header bytes */
unzip_skip(zip_header.formatted.extra_len);
+ /* Guard against "/abspath", "/../" and similar attacks */
+ overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn));
+
/* Filter zip entries */
if (find_list_entry(zreject, dst_fn)
|| (zaccept && !find_list_entry(zaccept, dst_fn))
) { /* Skip entry */
i = 'n';
-
- } else { /* Extract entry */
- if (listing) { /* List entry */
- unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
+ } else {
+ if (listing) {
+ /* List entry */
+ char dtbuf[sizeof("mm-dd-yyyy hh:mm")];
+ sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u",
+ (zip_header.formatted.moddate >> 5) & 0xf, // mm: 0x01e0
+ (zip_header.formatted.moddate) & 0x1f, // dd: 0x001f
+ (zip_header.formatted.moddate >> 9) + 1980, // yy: 0xfe00
+ (zip_header.formatted.modtime >> 11), // hh: 0xf800
+ (zip_header.formatted.modtime >> 5) & 0x3f // mm: 0x07e0
+ // seconds/2 are not shown, encoded in ----------- 0x001f
+ );
if (!verbose) {
- // " Length Date Time Name\n"
- // " -------- ---- ---- ----"
- printf( "%9u %02u-%02u-%02u %02u:%02u %s\n",
+ // " Length Date Time Name\n"
+ // "--------- ---------- ----- ----"
+ printf( "%9u " "%s " "%s\n",
(unsigned)zip_header.formatted.ucmpsize,
- (dostime & 0x01e00000) >> 21,
- (dostime & 0x001f0000) >> 16,
- (((dostime & 0xfe000000) >> 25) + 1980) % 100,
- (dostime & 0x0000f800) >> 11,
- (dostime & 0x000007e0) >> 5,
+ dtbuf,
dst_fn);
- total_usize += zip_header.formatted.ucmpsize;
} else {
unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
+ if ((int32_t)percents < 0)
+ percents = 0; /* happens if ucmpsize < cmpsize */
percents = percents * 100;
if (zip_header.formatted.ucmpsize)
percents /= zip_header.formatted.ucmpsize;
- // " Length Method Size Ratio Date Time CRC-32 Name\n"
- // "-------- ------ ------- ----- ---- ---- ------ ----"
- printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n",
+ // " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ // "-------- ------ ------- ---- ---------- ----- -------- ----"
+ printf( "%8u %s" "%9u%4u%% " "%s " "%08x " "%s\n",
(unsigned)zip_header.formatted.ucmpsize,
+ zip_header.formatted.method == 0 ? "Stored" : "Defl:N", /* Defl is method 8 */
+/* TODO: show other methods?
+ * 1 - Shrunk
+ * 2 - Reduced with compression factor 1
+ * 3 - Reduced with compression factor 2
+ * 4 - Reduced with compression factor 3
+ * 5 - Reduced with compression factor 4
+ * 6 - Imploded
+ * 7 - Reserved for Tokenizing compression algorithm
+ * 9 - Deflate64
+ * 10 - PKWARE Data Compression Library Imploding
+ * 11 - Reserved by PKWARE
+ * 12 - BZIP2
+ */
(unsigned)zip_header.formatted.cmpsize,
(unsigned)percents,
- (dostime & 0x01e00000) >> 21,
- (dostime & 0x001f0000) >> 16,
- (((dostime & 0xfe000000) >> 25) + 1980) % 100,
- (dostime & 0x0000f800) >> 11,
- (dostime & 0x000007e0) >> 5,
+ dtbuf,
zip_header.formatted.crc32,
dst_fn);
- total_usize += zip_header.formatted.ucmpsize;
total_size += zip_header.formatted.cmpsize;
}
+ total_usize += zip_header.formatted.ucmpsize;
i = 'n';
- } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
+ } else if (dst_fd == STDOUT_FILENO) {
+ /* Extracting to STDOUT */
i = -1;
- } else if (last_char_is(dst_fn, '/')) { /* Extract directory */
+ } else if (last_char_is(dst_fn, '/')) {
+ /* Extract directory */
if (stat(dst_fn, &stat_buf) == -1) {
if (errno != ENOENT) {
bb_perror_msg_and_die("can't stat '%s'", dst_fn);
@@ -659,22 +710,26 @@ int unzip_main(int argc, char **argv)
}
} else {
if (!S_ISDIR(stat_buf.st_mode)) {
- bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
+ bb_error_msg_and_die("'%s' exists but is not a %s",
+ dst_fn, "directory");
}
}
i = 'n';
-
- } else { /* Extract file */
+ } else {
+ /* Extract file */
check_file:
- if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
+ if (stat(dst_fn, &stat_buf) == -1) {
+ /* File does not exist */
if (errno != ENOENT) {
bb_perror_msg_and_die("can't stat '%s'", dst_fn);
}
i = 'y';
- } else { /* File already exists */
+ } else {
+ /* File already exists */
if (overwrite == O_NEVER) {
i = 'n';
- } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
+ } else if (S_ISREG(stat_buf.st_mode)) {
+ /* File is regular file */
if (overwrite == O_ALWAYS) {
i = 'y';
} else {
@@ -682,8 +737,10 @@ int unzip_main(int argc, char **argv)
my_fgets80(key_buf);
i = key_buf[0];
}
- } else { /* File is not regular file */
- bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn);
+ } else {
+ /* File is not regular file */
+ bb_error_msg_and_die("'%s' exists but is not a %s",
+ dst_fn, "regular file");
}
}
}
@@ -736,21 +793,25 @@ int unzip_main(int argc, char **argv)
if (listing && quiet <= 1) {
if (!verbose) {
- // " Length Date Time Name\n"
- // " -------- ---- ---- ----"
- printf( " -------- -------\n"
- "%9lu" " %u files\n",
- total_usize, total_entries);
+ // " Length Date Time Name\n"
+ // "--------- ---------- ----- ----"
+ printf( " --------%21s" "-------\n"
+ "%9lu%21s" "%u files\n",
+ "",
+ total_usize, "", total_entries);
} else {
unsigned long percents = total_usize - total_size;
+ if ((long)percents < 0)
+ percents = 0; /* happens if usize < size */
percents = percents * 100;
if (total_usize)
percents /= total_usize;
- // " Length Method Size Ratio Date Time CRC-32 Name\n"
- // "-------- ------ ------- ----- ---- ---- ------ ----"
- printf( "-------- ------- --- -------\n"
- "%8lu" "%17lu%4u%% %u files\n",
- total_usize, total_size, (unsigned)percents,
+ // " Length Method Size Cmpr Date Time CRC-32 Name\n"
+ // "-------- ------ ------- ---- ---------- ----- -------- ----"
+ printf( "-------- ------- ----%28s" "----\n"
+ "%8lu" "%17lu%4u%%%28s" "%u files\n",
+ "",
+ total_usize, total_size, (unsigned)percents, "",
total_entries);
}
}
diff --git a/busybox-full.config b/busybox-full.config
index d64b351..8668d6d 100644
--- a/busybox-full.config
+++ b/busybox-full.config
@@ -1,41 +1,25 @@
#
# Automatically generated make config: don't edit
-# Busybox version: 1.22.1
-# Wed Aug 6 13:27:12 2014
+# Busybox version: 1.26.2
+# Thu May 18 16:36:09 2017
#
CONFIG_HAVE_DOT_CONFIG=y
#
# Busybox Settings
#
-
-#
-# General Configuration
-#
CONFIG_DESKTOP=y
# CONFIG_EXTRA_COMPAT is not set
# CONFIG_INCLUDE_SUSv2 is not set
# CONFIG_USE_PORTABLE_CODE is not set
CONFIG_PLATFORM_LINUX=y
-CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
-# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
-# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
CONFIG_SHOW_USAGE=y
CONFIG_FEATURE_VERBOSE_USAGE=y
CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_BUSYBOX=y
CONFIG_FEATURE_INSTALLER=y
CONFIG_INSTALL_NO_USR=y
-# CONFIG_LOCALE_SUPPORT is not set
-CONFIG_UNICODE_SUPPORT=y
-# CONFIG_UNICODE_USING_LOCALE is not set
-# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
-CONFIG_SUBST_WCHAR=63
-CONFIG_LAST_SUPPORTED_WCHAR=0
-# CONFIG_UNICODE_COMBINING_WCHARS is not set
-CONFIG_UNICODE_WIDE_WCHARS=y
-# CONFIG_UNICODE_BIDI_SUPPORT is not set
-# CONFIG_UNICODE_NEUTRAL_TABLE is not set
-CONFIG_UNICODE_PRESERVE_BROKEN=y
+# CONFIG_PAM is not set
CONFIG_LONG_OPTS=y
CONFIG_FEATURE_DEVPTS=y
# CONFIG_FEATURE_CLEAN_UP is not set
@@ -62,22 +46,13 @@ CONFIG_FEATURE_HAVE_RPC=y
# CONFIG_FEATURE_INDIVIDUAL is not set
# CONFIG_FEATURE_SHARED_BUSYBOX is not set
# CONFIG_LFS is not set
+CONFIG_CROSS_COMPILER_PREFIX=""
CONFIG_SYSROOT=""
CONFIG_EXTRA_CFLAGS="-Os -fno-short-enums -fgcse-after-reload -frerun-cse-after-loop -frename-registers"
CONFIG_EXTRA_LDFLAGS=""
CONFIG_EXTRA_LDLIBS=""
#
-# Debugging Options
-#
-# CONFIG_DEBUG is not set
-# CONFIG_DEBUG_PESSIMIZE is not set
-# CONFIG_WERROR is not set
-CONFIG_NO_DEBUG_LIB=y
-# CONFIG_DMALLOC is not set
-# CONFIG_EFENCE is not set
-
-#
# Installation Options ("make install" behavior)
#
CONFIG_INSTALL_APPLET_SYMLINKS=y
@@ -90,34 +65,61 @@ CONFIG_INSTALL_APPLET_SYMLINKS=y
CONFIG_PREFIX="./_install"
#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_DEBUG_PESSIMIZE is not set
+# CONFIG_DEBUG_SANITIZE is not set
+# CONFIG_UNIT_TEST is not set
+# CONFIG_WERROR is not set
+CONFIG_NO_DEBUG_LIB=y
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+
+#
# Busybox Library Tuning
#
-# CONFIG_FEATURE_SYSTEMD is not set
+# CONFIG_FEATURE_USE_BSS_TAIL is not set
CONFIG_FEATURE_RTMINMAX=y
+CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
CONFIG_PASSWORD_MINLEN=6
CONFIG_MD5_SMALL=1
CONFIG_SHA3_SMALL=1
-CONFIG_FEATURE_FAST_TOP=y
+# CONFIG_FEATURE_FAST_TOP is not set
# CONFIG_FEATURE_ETC_NETWORKS is not set
CONFIG_FEATURE_USE_TERMIOS=y
CONFIG_FEATURE_EDITING=y
CONFIG_FEATURE_EDITING_MAX_LEN=1024
-CONFIG_FEATURE_EDITING_VI=y
+# CONFIG_FEATURE_EDITING_VI is not set
CONFIG_FEATURE_EDITING_HISTORY=256
CONFIG_FEATURE_EDITING_SAVEHISTORY=y
# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
CONFIG_FEATURE_REVERSE_SEARCH=y
CONFIG_FEATURE_TAB_COMPLETION=y
-# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_USERNAME_COMPLETION=y
CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
-CONFIG_FEATURE_EDITING_ASK_TERMINAL=y
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_USING_LOCALE is not set
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
+# CONFIG_UNICODE_BIDI_SUPPORT is not set
+# CONFIG_UNICODE_NEUTRAL_TABLE is not set
+# CONFIG_UNICODE_PRESERVE_BROKEN is not set
CONFIG_FEATURE_NON_POSIX_CP=y
# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_USE_SENDFILE=y
CONFIG_FEATURE_COPYBUF_KB=4
CONFIG_FEATURE_SKIP_ROOTFS=y
CONFIG_MONOTONIC_SYSCALL=y
CONFIG_IOCTL_HEX2STR_ERROR=y
-# CONFIG_FEATURE_HWIB is not set
+CONFIG_FEATURE_HWIB=y
#
# Applets
@@ -136,11 +138,16 @@ CONFIG_FEATURE_SEAMLESS_GZ=y
# CONFIG_FEATURE_AR_CREATE is not set
CONFIG_UNCOMPRESS=y
CONFIG_GUNZIP=y
+CONFIG_ZCAT=y
+CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
CONFIG_BUNZIP2=y
+CONFIG_BZCAT=y
CONFIG_UNLZMA=y
-CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_LZCAT=y
CONFIG_LZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
CONFIG_UNXZ=y
+CONFIG_XZCAT=y
CONFIG_XZ=y
CONFIG_BZIP2=y
CONFIG_CPIO=y
@@ -148,11 +155,13 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GZIP=y
CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
CONFIG_GZIP_FAST=2
+# CONFIG_FEATURE_GZIP_LEVELS is not set
CONFIG_LZOP=y
+CONFIG_UNLZOP=y
+CONFIG_LZOPCAT=y
CONFIG_LZOP_COMPR_HIGH=y
# CONFIG_RPM is not set
# CONFIG_RPM2CPIO is not set
@@ -174,26 +183,8 @@ CONFIG_UNZIP=y
# Coreutils
#
CONFIG_BASENAME=y
-CONFIG_CAT=y
-CONFIG_DATE=y
-CONFIG_FEATURE_DATE_ISOFMT=y
-# CONFIG_FEATURE_DATE_NANO is not set
-CONFIG_FEATURE_DATE_COMPAT=y
-# CONFIG_HOSTID is not set
-CONFIG_ID=y
-CONFIG_GROUPS=y
-CONFIG_TEST=y
-# CONFIG_FEATURE_TEST_64 is not set
-CONFIG_TOUCH=y
-# CONFIG_FEATURE_TOUCH_NODEREF is not set
-CONFIG_FEATURE_TOUCH_SUSV3=y
-CONFIG_TR=y
-CONFIG_FEATURE_TR_CLASSES=y
-# CONFIG_FEATURE_TR_EQUIV is not set
-CONFIG_BASE64=y
-# CONFIG_WHO is not set
-# CONFIG_USERS is not set
CONFIG_CAL=y
+CONFIG_CAT=y
CONFIG_CATV=y
CONFIG_CHGRP=y
CONFIG_CHMOD=y
@@ -205,10 +196,15 @@ CONFIG_COMM=y
CONFIG_CP=y
CONFIG_FEATURE_CP_LONG_OPTIONS=y
CONFIG_CUT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+CONFIG_FEATURE_DATE_COMPAT=y
CONFIG_DD=y
CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_FEATURE_DD_STATUS=y
CONFIG_DF=y
CONFIG_FEATURE_DF_FANCY=y
CONFIG_DIRNAME=y
@@ -222,6 +218,8 @@ CONFIG_ENV=y
CONFIG_FEATURE_ENV_LONG_OPTIONS=y
CONFIG_EXPAND=y
CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+CONFIG_UNEXPAND=y
+CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
CONFIG_EXPR=y
CONFIG_EXPR_MATH_SUPPORT_64=y
CONFIG_FALSE=y
@@ -229,6 +227,9 @@ CONFIG_FOLD=y
CONFIG_FSYNC=y
CONFIG_HEAD=y
CONFIG_FEATURE_FANCY_HEAD=y
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+CONFIG_GROUPS=y
CONFIG_INSTALL=y
CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
CONFIG_LN=y
@@ -243,6 +244,15 @@ CONFIG_FEATURE_LS_USERNAME=y
CONFIG_FEATURE_LS_COLOR=y
# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set
CONFIG_MD5SUM=y
+CONFIG_SHA1SUM=y
+CONFIG_SHA256SUM=y
+CONFIG_SHA512SUM=y
+CONFIG_SHA3SUM=y
+
+#
+# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
CONFIG_MKDIR=y
CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
CONFIG_MKFIFO=y
@@ -262,10 +272,7 @@ CONFIG_RM=y
CONFIG_RMDIR=y
CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
CONFIG_SEQ=y
-CONFIG_SHA1SUM=y
-CONFIG_SHA256SUM=y
-CONFIG_SHA512SUM=y
-CONFIG_SHA3SUM=y
+CONFIG_SHUF=y
CONFIG_SLEEP=y
CONFIG_FEATURE_FANCY_SLEEP=y
CONFIG_FEATURE_FLOAT_SLEEP=y
@@ -275,29 +282,50 @@ CONFIG_SPLIT=y
CONFIG_FEATURE_SPLIT_FANCY=y
CONFIG_STAT=y
# CONFIG_FEATURE_STAT_FORMAT is not set
+CONFIG_FEATURE_STAT_FILESYSTEM=y
CONFIG_STTY=y
CONFIG_SUM=y
CONFIG_SYNC=y
+CONFIG_FEATURE_SYNC_FANCY=y
CONFIG_TAC=y
CONFIG_TAIL=y
CONFIG_FEATURE_FANCY_TAIL=y
CONFIG_TEE=y
CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TEST=y
+CONFIG_TEST1=y
+CONFIG_TEST2=y
+# CONFIG_FEATURE_TEST_64 is not set
+CONFIG_TOUCH=y
+# CONFIG_FEATURE_TOUCH_NODEREF is not set
+CONFIG_FEATURE_TOUCH_SUSV3=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+# CONFIG_FEATURE_TR_EQUIV is not set
CONFIG_TRUE=y
+CONFIG_TRUNCATE=y
# CONFIG_TTY is not set
CONFIG_UNAME=y
-CONFIG_UNEXPAND=y
-CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+CONFIG_UNAME_OSNAME="GNU/Linux"
CONFIG_UNIQ=y
+CONFIG_UNLINK=y
CONFIG_USLEEP=y
CONFIG_UUDECODE=y
+CONFIG_BASE64=y
CONFIG_UUENCODE=y
CONFIG_WC=y
CONFIG_FEATURE_WC_LARGE=y
+# CONFIG_WHO is not set
+# CONFIG_USERS is not set
CONFIG_WHOAMI=y
CONFIG_YES=y
#
+# Common options
+#
+CONFIG_FEATURE_VERBOSE=y
+
+#
# Common options for cp and mv
#
CONFIG_FEATURE_PRESERVE_HARDLINKS=y
@@ -313,20 +341,20 @@ CONFIG_FEATURE_AUTOWIDTH=y
CONFIG_FEATURE_HUMAN_READABLE=y
#
-# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
-#
-CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
-
-#
# Console Utilities
#
CONFIG_CHVT=y
-CONFIG_FGCONSOLE=y
CONFIG_CLEAR=y
CONFIG_DEALLOCVT=y
# CONFIG_DUMPKMAP is not set
+CONFIG_FGCONSOLE=y
# CONFIG_KBD_MODE is not set
# CONFIG_LOADFONT is not set
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+CONFIG_DEFAULT_SETFONT_DIR=""
+# CONFIG_FEATURE_LOADFONT_PSF2 is not set
+# CONFIG_FEATURE_LOADFONT_RAW is not set
# CONFIG_LOADKMAP is not set
CONFIG_OPENVT=y
CONFIG_RESET=y
@@ -334,14 +362,9 @@ CONFIG_RESIZE=y
# CONFIG_FEATURE_RESIZE_PRINT is not set
CONFIG_SETCONSOLE=y
# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
-# CONFIG_SETFONT is not set
-# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
-CONFIG_DEFAULT_SETFONT_DIR=""
CONFIG_SETKEYCODES=y
# CONFIG_SETLOGCONS is not set
# CONFIG_SHOWKEY is not set
-# CONFIG_FEATURE_LOADFONT_PSF2 is not set
-# CONFIG_FEATURE_LOADFONT_RAW is not set
#
# Debian Utilities
@@ -383,6 +406,9 @@ CONFIG_FEATURE_VI_SETOPTS=y
CONFIG_FEATURE_VI_SET=y
CONFIG_FEATURE_VI_WIN_RESIZE=y
CONFIG_FEATURE_VI_ASK_TERMINAL=y
+CONFIG_FEATURE_VI_UNDO=y
+CONFIG_FEATURE_VI_UNDO_QUEUE=y
+CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
CONFIG_FEATURE_ALLOW_EXEC=y
#
@@ -399,6 +425,7 @@ CONFIG_FEATURE_FIND_MAXDEPTH=y
CONFIG_FEATURE_FIND_NEWER=y
CONFIG_FEATURE_FIND_INUM=y
CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_EXEC_PLUS=y
CONFIG_FEATURE_FIND_USER=y
CONFIG_FEATURE_FIND_GROUP=y
CONFIG_FEATURE_FIND_NOT=y
@@ -412,14 +439,15 @@ CONFIG_FEATURE_FIND_REGEX=y
# CONFIG_FEATURE_FIND_CONTEXT is not set
CONFIG_FEATURE_FIND_LINKS=y
CONFIG_GREP=y
-CONFIG_FEATURE_GREP_EGREP_ALIAS=y
-CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_EGREP=y
+CONFIG_FGREP=y
CONFIG_FEATURE_GREP_CONTEXT=y
CONFIG_XARGS=y
CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
#
# Init Utilities
@@ -428,9 +456,12 @@ CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
CONFIG_HALT=y
+CONFIG_POWEROFF=y
+CONFIG_REBOOT=y
# CONFIG_FEATURE_CALL_TELINIT is not set
CONFIG_TELINIT_PATH=""
# CONFIG_INIT is not set
+CONFIG_LINUXRC=y
# CONFIG_FEATURE_USE_INITTAB is not set
# CONFIG_FEATURE_KILL_REMOVED is not set
CONFIG_FEATURE_KILL_DELAY=0
@@ -438,44 +469,45 @@ CONFIG_FEATURE_KILL_DELAY=0
# CONFIG_FEATURE_INIT_SYSLOG is not set
# CONFIG_FEATURE_EXTRA_QUIET is not set
# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
CONFIG_INIT_TERMINAL_TYPE=""
+CONFIG_FEATURE_INIT_MODIFY_CMDLINE=y
CONFIG_MESG=y
CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
#
# Login/Password Management Utilities
#
-# CONFIG_ADD_SHELL is not set
-# CONFIG_REMOVE_SHELL is not set
# CONFIG_FEATURE_SHADOWPASSWDS is not set
# CONFIG_USE_BB_PWD_GRP is not set
# CONFIG_USE_BB_SHADOW is not set
CONFIG_USE_BB_CRYPT=y
CONFIG_USE_BB_CRYPT_SHA=y
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+# CONFIG_ADDGROUP is not set
+# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
# CONFIG_ADDUSER is not set
# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_LAST_ID=0
CONFIG_FIRST_SYSTEM_ID=0
CONFIG_LAST_SYSTEM_ID=0
-# CONFIG_ADDGROUP is not set
-# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
-# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
+# CONFIG_CHPASSWD is not set
+CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
+# CONFIG_CRYPTPW is not set
+CONFIG_MKPASSWD=y
# CONFIG_DELUSER is not set
# CONFIG_DELGROUP is not set
# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
# CONFIG_GETTY is not set
# CONFIG_LOGIN is not set
# CONFIG_LOGIN_SESSION_AS_CHILD is not set
-# CONFIG_PAM is not set
# CONFIG_LOGIN_SCRIPTS is not set
# CONFIG_FEATURE_NOLOGIN is not set
# CONFIG_FEATURE_SECURETTY is not set
# CONFIG_PASSWD is not set
# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
-# CONFIG_CRYPTPW is not set
-# CONFIG_CHPASSWD is not set
-CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
# CONFIG_SU is not set
# CONFIG_FEATURE_SU_SYSLOG is not set
# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
@@ -493,17 +525,17 @@ CONFIG_TUNE2FS=y
#
# Linux Module Utilities
#
-CONFIG_MODINFO=y
# CONFIG_MODPROBE_SMALL is not set
-CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
-CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
+CONFIG_DEPMOD=y
CONFIG_INSMOD=y
-CONFIG_RMMOD=y
CONFIG_LSMOD=y
CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT=y
+CONFIG_MODINFO=y
+# CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE is not set
+# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
CONFIG_MODPROBE=y
CONFIG_FEATURE_MODPROBE_BLACKLIST=y
-CONFIG_DEPMOD=y
+CONFIG_RMMOD=y
#
# Options common to multiple modutils
@@ -518,31 +550,24 @@ CONFIG_FEATURE_INSMOD_TRY_MMAP=y
CONFIG_FEATURE_CHECK_TAINTED_MODULE=y
CONFIG_FEATURE_MODUTILS_ALIAS=y
CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
#
# Linux System Utilities
#
-CONFIG_BLOCKDEV=y
-CONFIG_FSTRIM=y
-# CONFIG_MDEV is not set
-# CONFIG_FEATURE_MDEV_CONF is not set
-# CONFIG_FEATURE_MDEV_RENAME is not set
-# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
-# CONFIG_FEATURE_MDEV_EXEC is not set
-# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
-CONFIG_REV=y
# CONFIG_ACPID is not set
# CONFIG_FEATURE_ACPID_COMPAT is not set
+CONFIG_BLKDISCARD=y
CONFIG_BLKID=y
CONFIG_FEATURE_BLKID_TYPE=y
+CONFIG_BLOCKDEV=y
CONFIG_DMESG=y
# CONFIG_FEATURE_DMESG_PRETTY is not set
+CONFIG_FATATTR=y
CONFIG_FBSET=y
CONFIG_FEATURE_FBSET_FANCY=y
-CONFIG_FEATURE_DMESG_COLOR=y
# CONFIG_FEATURE_FBSET_READMODE is not set
-# CONFIG_FDFLUSH is not set
# CONFIG_FDFORMAT is not set
CONFIG_FDISK=y
# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
@@ -555,13 +580,10 @@ CONFIG_FEATURE_GPT_LABEL=y
CONFIG_FEATURE_FDISK_ADVANCED=y
CONFIG_FINDFS=y
CONFIG_FLOCK=y
+# CONFIG_FDFLUSH is not set
CONFIG_FREERAMDISK=y
# CONFIG_FSCK_MINIX is not set
-CONFIG_MKFS_EXT2=y
-# CONFIG_MKFS_MINIX is not set
-# CONFIG_FEATURE_MINIX2 is not set
-# CONFIG_MKFS_REISER is not set
-CONFIG_MKFS_VFAT=y
+CONFIG_FSTRIM=y
CONFIG_GETOPT=y
CONFIG_FEATURE_GETOPT_LONG=y
CONFIG_HEXDUMP=y
@@ -575,6 +597,19 @@ CONFIG_HWCLOCK=y
CONFIG_LOSETUP=y
CONFIG_LSPCI=y
CONFIG_LSUSB=y
+# CONFIG_MDEV is not set
+# CONFIG_FEATURE_MDEV_CONF is not set
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
+# CONFIG_FEATURE_MDEV_EXEC is not set
+# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
+CONFIG_MKE2FS=y
+CONFIG_MKFS_EXT2=y
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
+# CONFIG_MKFS_REISER is not set
+CONFIG_MKDOSFS=y
+CONFIG_MKFS_VFAT=y
CONFIG_MKSWAP=y
# CONFIG_FEATURE_MKSWAP_UUID is not set
CONFIG_MORE=y
@@ -587,19 +622,29 @@ CONFIG_FEATURE_MOUNT_NFS=y
CONFIG_FEATURE_MOUNT_CIFS=y
CONFIG_FEATURE_MOUNT_FLAGS=y
CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_FEATURE_MOUNT_OTHERTAB=y
+CONFIG_NSENTER=y
+CONFIG_FEATURE_NSENTER_LONG_OPTS=y
# CONFIG_PIVOT_ROOT is not set
CONFIG_RDATE=y
CONFIG_RDEV=y
# CONFIG_READPROFILE is not set
+CONFIG_REV=y
# CONFIG_RTCWAKE is not set
# CONFIG_SCRIPT is not set
# CONFIG_SCRIPTREPLAY is not set
# CONFIG_SETARCH is not set
-CONFIG_SWAPONOFF=y
+CONFIG_LINUX32=y
+CONFIG_LINUX64=y
+CONFIG_SWAPON=y
+CONFIG_FEATURE_SWAPON_DISCARD=y
CONFIG_FEATURE_SWAPON_PRI=y
+CONFIG_SWAPOFF=y
CONFIG_SWITCH_ROOT=y
+CONFIG_UEVENT=y
CONFIG_UMOUNT=y
CONFIG_FEATURE_UMOUNT_ALL=y
+CONFIG_UNSHARE=y
#
# Common options for mount/umount
@@ -612,6 +657,7 @@ CONFIG_VOLUMEID=y
#
# Filesystem/Volume identification
#
+CONFIG_FEATURE_VOLUMEID_BCACHE=y
# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
CONFIG_FEATURE_VOLUMEID_EXFAT=y
@@ -631,34 +677,13 @@ CONFIG_FEATURE_VOLUMEID_NTFS=y
# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
CONFIG_FEATURE_VOLUMEID_SQUASHFS=y
# CONFIG_FEATURE_VOLUMEID_SYSV is not set
+CONFIG_FEATURE_VOLUMEID_UBIFS=y
# CONFIG_FEATURE_VOLUMEID_UDF is not set
# CONFIG_FEATURE_VOLUMEID_XFS is not set
#
# Miscellaneous Utilities
#
-# CONFIG_CONSPY is not set
-CONFIG_LESS=y
-CONFIG_FEATURE_LESS_MAXLINES=65536
-# CONFIG_FEATURE_LESS_BRACKETS is not set
-# CONFIG_FEATURE_LESS_FLAGS is not set
-CONFIG_FEATURE_LESS_MARKS=y
-CONFIG_FEATURE_LESS_REGEXP=y
-CONFIG_FEATURE_LESS_WINCH=y
-CONFIG_FEATURE_LESS_ASK_TERMINAL=y
-# CONFIG_FEATURE_LESS_DASHCMD is not set
-# CONFIG_FEATURE_LESS_LINENUMS is not set
-CONFIG_NANDWRITE=y
-CONFIG_NANDDUMP=y
-# CONFIG_RFKILL is not set
-CONFIG_SETSERIAL=y
-# CONFIG_UBIATTACH is not set
-# CONFIG_UBIDETACH is not set
-# CONFIG_UBIMKVOL is not set
-# CONFIG_UBIRMVOL is not set
-# CONFIG_UBIRSVOL is not set
-# CONFIG_UBIUPDATEVOL is not set
-# CONFIG_WALL is not set
CONFIG_ADJTIMEX=y
CONFIG_BBCONFIG=y
CONFIG_FEATURE_COMPRESS_BBCONFIG=y
@@ -674,6 +699,7 @@ CONFIG_FEATURE_BEEP_LENGTH_MS=0
# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
# CONFIG_CHRT is not set
+# CONFIG_CONSPY is not set
CONFIG_CROND=y
CONFIG_FEATURE_CROND_D=y
# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
@@ -690,15 +716,10 @@ CONFIG_DEVMEM=y
# CONFIG_EJECT is not set
# CONFIG_FEATURE_EJECT_SCSI is not set
CONFIG_FBSPLASH=y
-CONFIG_FLASHCP=y
+# CONFIG_FLASH_ERASEALL is not set
CONFIG_FLASH_LOCK=y
CONFIG_FLASH_UNLOCK=y
-# CONFIG_FLASH_ERASEALL is not set
-CONFIG_IONICE=y
-# CONFIG_INOTIFYD is not set
-# CONFIG_LAST is not set
-# CONFIG_FEATURE_LAST_SMALL is not set
-# CONFIG_FEATURE_LAST_FANCY is not set
+CONFIG_FLASHCP=y
# CONFIG_HDPARM is not set
# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
@@ -706,6 +727,25 @@ CONFIG_IONICE=y
# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
+CONFIG_I2CGET=y
+CONFIG_I2CSET=y
+CONFIG_I2CDUMP=y
+CONFIG_I2CDETECT=y
+# CONFIG_INOTIFYD is not set
+CONFIG_IONICE=y
+# CONFIG_LAST is not set
+# CONFIG_FEATURE_LAST_FANCY is not set
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=65536
+# CONFIG_FEATURE_LESS_BRACKETS is not set
+# CONFIG_FEATURE_LESS_FLAGS is not set
+CONFIG_FEATURE_LESS_TRUNCATE=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_ASK_TERMINAL=y
+# CONFIG_FEATURE_LESS_DASHCMD is not set
+# CONFIG_FEATURE_LESS_LINENUMS is not set
# CONFIG_MAKEDEVS is not set
# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
@@ -713,10 +753,14 @@ CONFIG_MAN=y
# CONFIG_MICROCOM is not set
CONFIG_MOUNTPOINT=y
# CONFIG_MT is not set
+CONFIG_NANDWRITE=y
+CONFIG_NANDDUMP=y
# CONFIG_RAIDAUTORUN is not set
# CONFIG_READAHEAD is not set
+# CONFIG_RFKILL is not set
# CONFIG_RUNLEVEL is not set
CONFIG_RX=y
+CONFIG_SETSERIAL=y
CONFIG_SETSID=y
CONFIG_STRINGS=y
CONFIG_TASKSET=y
@@ -724,23 +768,20 @@ CONFIG_FEATURE_TASKSET_FANCY=y
CONFIG_TIME=y
CONFIG_TIMEOUT=y
CONFIG_TTYSIZE=y
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
+CONFIG_UBIRENAME=y
# CONFIG_VOLNAME is not set
+# CONFIG_WALL is not set
# CONFIG_WATCHDOG is not set
#
# Networking Utilities
#
-# CONFIG_NAMEIF is not set
-# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
-CONFIG_NBDCLIENT=y
-CONFIG_NC=y
-CONFIG_NC_SERVER=y
-CONFIG_NC_EXTRA=y
-# CONFIG_NC_110_COMPAT is not set
-CONFIG_PING=y
-# CONFIG_PING6 is not set
-CONFIG_FEATURE_FANCY_PING=y
-# CONFIG_WHOIS is not set
CONFIG_FEATURE_IPV6=y
# CONFIG_FEATURE_UNIX_LOCAL is not set
CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
@@ -752,17 +793,17 @@ CONFIG_FEATURE_BRCTL_FANCY=y
CONFIG_FEATURE_BRCTL_SHOW=y
CONFIG_DNSD=y
# CONFIG_ETHER_WAKE is not set
-# CONFIG_FAKEIDENTD is not set
# CONFIG_FTPD is not set
-# CONFIG_FEATURE_FTP_WRITE is not set
+# CONFIG_FEATURE_FTPD_WRITE is not set
# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
+# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
CONFIG_FTPGET=y
CONFIG_FTPPUT=y
CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
# CONFIG_HOSTNAME is not set
+CONFIG_DNSDOMAINNAME=y
# CONFIG_HTTPD is not set
# CONFIG_FEATURE_HTTPD_RANGES is not set
-# CONFIG_FEATURE_HTTPD_USE_SENDFILE is not set
# CONFIG_FEATURE_HTTPD_SETUID is not set
# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
@@ -781,11 +822,10 @@ CONFIG_FEATURE_IFCONFIG_HW=y
# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
# CONFIG_IFENSLAVE is not set
# CONFIG_IFPLUGD is not set
-# CONFIG_IFUPDOWN is not set
+CONFIG_IFUP=y
+CONFIG_IFDOWN=y
CONFIG_IFUPDOWN_IFSTATE_PATH=""
# CONFIG_FEATURE_IFUPDOWN_IP is not set
-# CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN is not set
-# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
@@ -798,31 +838,46 @@ CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
# CONFIG_FEATURE_INETD_RPC is not set
CONFIG_IP=y
+# CONFIG_IPADDR is not set
+# CONFIG_IPLINK is not set
+# CONFIG_IPROUTE is not set
+# CONFIG_IPTUNNEL is not set
+# CONFIG_IPRULE is not set
+CONFIG_IPNEIGH=y
CONFIG_FEATURE_IP_ADDRESS=y
CONFIG_FEATURE_IP_LINK=y
CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_ROUTE_DIR="/etc/iproute2"
# CONFIG_FEATURE_IP_TUNNEL is not set
CONFIG_FEATURE_IP_RULE=y
-# CONFIG_FEATURE_IP_SHORT_FORMS is not set
+CONFIG_FEATURE_IP_NEIGH=y
# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
-# CONFIG_IPADDR is not set
-# CONFIG_IPLINK is not set
-# CONFIG_IPROUTE is not set
-# CONFIG_IPTUNNEL is not set
-# CONFIG_IPRULE is not set
# CONFIG_IPCALC is not set
# CONFIG_FEATURE_IPCALC_FANCY is not set
# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set
+# CONFIG_FAKEIDENTD is not set
+# CONFIG_NAMEIF is not set
+# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
+CONFIG_NBDCLIENT=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+# CONFIG_NC_110_COMPAT is not set
CONFIG_NETSTAT=y
CONFIG_FEATURE_NETSTAT_WIDE=y
CONFIG_FEATURE_NETSTAT_PRG=y
CONFIG_NSLOOKUP=y
CONFIG_NTPD=y
# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_FEATURE_NTPD_CONF=y
+CONFIG_PING=y
+# CONFIG_PING6 is not set
+CONFIG_FEATURE_FANCY_PING=y
# CONFIG_PSCAN is not set
CONFIG_ROUTE=y
# CONFIG_SLATTACH is not set
# CONFIG_TCPSVD is not set
+# CONFIG_UDPSVD is not set
CONFIG_TELNET=y
CONFIG_FEATURE_TELNET_TTYPE=y
# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
@@ -843,10 +898,19 @@ CONFIG_FEATURE_TFTP_PUT=y
CONFIG_TRACEROUTE=y
# CONFIG_TRACEROUTE6 is not set
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
# CONFIG_TUNCTL is not set
# CONFIG_FEATURE_TUNCTL_UG is not set
+# CONFIG_VCONFIG is not set
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
+CONFIG_FEATURE_WGET_OPENSSL=y
+CONFIG_FEATURE_WGET_SSL_HELPER=y
+# CONFIG_WHOIS is not set
+# CONFIG_ZCIP is not set
# CONFIG_UDHCPC6 is not set
# CONFIG_UDHCPD is not set
# CONFIG_DHCPRELAY is not set
@@ -856,6 +920,7 @@ CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
CONFIG_DHCPD_LEASES_FILE=""
# CONFIG_UDHCPC is not set
# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
# CONFIG_FEATURE_UDHCP_PORT is not set
CONFIG_UDHCP_DEBUG=0
# CONFIG_FEATURE_UDHCP_RFC3397 is not set
@@ -863,14 +928,6 @@ CONFIG_UDHCP_DEBUG=0
CONFIG_UDHCPC_DEFAULT_SCRIPT=""
CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
-# CONFIG_UDPSVD is not set
-# CONFIG_VCONFIG is not set
-CONFIG_WGET=y
-CONFIG_FEATURE_WGET_STATUSBAR=y
-CONFIG_FEATURE_WGET_AUTHENTICATION=y
-CONFIG_FEATURE_WGET_LONG_OPTIONS=y
-CONFIG_FEATURE_WGET_TIMEOUT=y
-# CONFIG_ZCIP is not set
#
# Print Utilities
@@ -883,25 +940,43 @@ CONFIG_FEATURE_WGET_TIMEOUT=y
# Mail Utilities
#
# CONFIG_MAKEMIME is not set
-CONFIG_FEATURE_MIME_CHARSET=""
# CONFIG_POPMAILDIR is not set
# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
# CONFIG_REFORMIME is not set
# CONFIG_FEATURE_REFORMIME_COMPAT is not set
# CONFIG_SENDMAIL is not set
+CONFIG_FEATURE_MIME_CHARSET=""
#
# Process Utilities
#
+CONFIG_FREE=y
+CONFIG_FUSER=y
CONFIG_IOSTAT=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
CONFIG_LSOF=y
CONFIG_MPSTAT=y
CONFIG_NMETER=y
+CONFIG_PGREP=y
+CONFIG_PKILL=y
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
CONFIG_PMAP=y
# CONFIG_POWERTOP is not set
+CONFIG_PS=y
+# CONFIG_FEATURE_PS_WIDE is not set
+# CONFIG_FEATURE_PS_LONG is not set
+CONFIG_FEATURE_PS_TIME=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
CONFIG_PSTREE=y
CONFIG_PWDX=y
+CONFIG_RENICE=y
CONFIG_SMEMCAP=y
+CONFIG_BB_SYSCTL=y
CONFIG_TOP=y
CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
@@ -911,41 +986,24 @@ CONFIG_FEATURE_TOP_SMP_PROCESS=y
CONFIG_FEATURE_TOPMEM=y
CONFIG_UPTIME=y
# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
-CONFIG_FREE=y
-CONFIG_FUSER=y
-CONFIG_KILL=y
-CONFIG_KILLALL=y
-CONFIG_KILLALL5=y
-CONFIG_PGREP=y
-CONFIG_PIDOF=y
-CONFIG_FEATURE_PIDOF_SINGLE=y
-CONFIG_FEATURE_PIDOF_OMIT=y
-CONFIG_PKILL=y
-CONFIG_PS=y
-# CONFIG_FEATURE_PS_WIDE is not set
-# CONFIG_FEATURE_PS_LONG is not set
-CONFIG_FEATURE_PS_TIME=y
-CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
-# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
-CONFIG_RENICE=y
-CONFIG_BB_SYSCTL=y
-CONFIG_FEATURE_SHOW_THREADS=y
CONFIG_WATCH=y
+CONFIG_FEATURE_SHOW_THREADS=y
#
# Runit Utilities
#
+# CONFIG_CHPST is not set
+# CONFIG_SETUIDGID is not set
+# CONFIG_ENVUIDGID is not set
+# CONFIG_ENVDIR is not set
+# CONFIG_SOFTLIMIT is not set
# CONFIG_RUNSV is not set
# CONFIG_RUNSVDIR is not set
# CONFIG_FEATURE_RUNSVDIR_LOG is not set
# CONFIG_SV is not set
CONFIG_SV_DEFAULT_SERVICE_DIR=""
+CONFIG_SVC=y
# CONFIG_SVLOGD is not set
-# CONFIG_CHPST is not set
-# CONFIG_SETUIDGID is not set
-# CONFIG_ENVUIDGID is not set
-# CONFIG_ENVDIR is not set
-# CONFIG_SOFTLIMIT is not set
#
# SELinux Utilities
@@ -956,20 +1014,30 @@ CONFIG_GETENFORCE=y
CONFIG_GETSEBOOL=y
# CONFIG_LOAD_POLICY is not set
CONFIG_MATCHPATHCON=y
-CONFIG_RESTORECON=y
CONFIG_RUNCON=y
CONFIG_FEATURE_RUNCON_LONG_OPTIONS=y
CONFIG_SELINUXENABLED=y
+CONFIG_SESTATUS=y
CONFIG_SETENFORCE=y
CONFIG_SETFILES=y
# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+CONFIG_RESTORECON=y
CONFIG_SETSEBOOL=y
-CONFIG_SESTATUS=y
#
# Shells
#
+CONFIG_SH_IS_ASH=y
+# CONFIG_SH_IS_HUSH is not set
+# CONFIG_SH_IS_NONE is not set
+# CONFIG_BASH_IS_ASH is not set
+# CONFIG_BASH_IS_HUSH is not set
+CONFIG_BASH_IS_NONE=y
CONFIG_ASH=y
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_INTERNAL_GLOB=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+CONFIG_ASH_EXPAND_PRMT=y
CONFIG_ASH_BASH_COMPAT=y
# CONFIG_ASH_IDLE_TIMEOUT is not set
CONFIG_ASH_JOB_CONTROL=y
@@ -978,11 +1046,9 @@ CONFIG_ASH_ALIAS=y
CONFIG_ASH_BUILTIN_ECHO=y
CONFIG_ASH_BUILTIN_PRINTF=y
CONFIG_ASH_BUILTIN_TEST=y
+CONFIG_ASH_HELP=y
CONFIG_ASH_CMDCMD=y
# CONFIG_ASH_MAIL is not set
-CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
-CONFIG_ASH_RANDOM_SUPPORT=y
-CONFIG_ASH_EXPAND_PRMT=y
# CONFIG_CTTYHACK is not set
# CONFIG_HUSH is not set
# CONFIG_HUSH_BASH_COMPAT is not set
@@ -1001,14 +1067,8 @@ CONFIG_ASH_EXPAND_PRMT=y
# CONFIG_HUSH_EXPORT_N is not set
# CONFIG_HUSH_MODE_X is not set
# CONFIG_MSH is not set
-CONFIG_FEATURE_SH_IS_ASH=y
-# CONFIG_FEATURE_SH_IS_HUSH is not set
-# CONFIG_FEATURE_SH_IS_NONE is not set
-# CONFIG_FEATURE_BASH_IS_ASH is not set
-# CONFIG_FEATURE_BASH_IS_HUSH is not set
-CONFIG_FEATURE_BASH_IS_NONE=y
-CONFIG_SH_MATH_SUPPORT=y
-CONFIG_SH_MATH_SUPPORT_64=y
+CONFIG_FEATURE_SH_MATH=y
+CONFIG_FEATURE_SH_MATH_64=y
CONFIG_FEATURE_SH_EXTRA_QUIET=y
# CONFIG_FEATURE_SH_STANDALONE is not set
# CONFIG_FEATURE_SH_NOFORK is not set
@@ -1017,6 +1077,11 @@ CONFIG_FEATURE_SH_EXTRA_QUIET=y
#
# System Logging Utilities
#
+# CONFIG_KLOGD is not set
+# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
+# CONFIG_LOGGER is not set
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
# CONFIG_SYSLOGD is not set
# CONFIG_FEATURE_ROTATE_LOGFILE is not set
# CONFIG_FEATURE_REMOTE_LOG is not set
@@ -1025,9 +1090,4 @@ CONFIG_FEATURE_SH_EXTRA_QUIET=y
CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
# CONFIG_FEATURE_IPC_SYSLOG is not set
CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
-# CONFIG_LOGREAD is not set
-# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
# CONFIG_FEATURE_KMSG_SYSLOG is not set
-# CONFIG_KLOGD is not set
-# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
-# CONFIG_LOGGER is not set
diff --git a/busybox-full.sources b/busybox-full.sources
index 64e80cc..307e78d 100644
--- a/busybox-full.sources
+++ b/busybox-full.sources
@@ -11,6 +11,7 @@ archival/libarchive/find_list_entry.c archival/libarchive/get_header_cpio.c arch
archival/libarchive/get_header_tar_bz2.c archival/libarchive/get_header_tar_gz.c archival/libarchive/get_header_tar_lzma.c
archival/libarchive/header_list.c archival/libarchive/header_skip.c archival/libarchive/header_verbose_list.c
archival/libarchive/init_handle.c archival/libarchive/open_transformer.c archival/libarchive/seek_by_jump.c archival/libarchive/seek_by_read.c
+archival/libarchive/common.c archival/libarchive/get_header_tar_xz.c archival/libarchive/unsafe_prefix.c
console-tools/chvt.c console-tools/clear.c console-tools/deallocvt.c console-tools/fgconsole.c console-tools/openvt.c
console-tools/reset.c console-tools/resize.c console-tools/setconsole.c console-tools/setkeycodes.c
@@ -18,6 +19,7 @@ console-tools/reset.c console-tools/resize.c console-tools/setconsole.c console-
coreutils/basename.c coreutils/cal.c coreutils/cat.c coreutils/catv.c coreutils/chgrp.c coreutils/chmod.c coreutils/chown.c coreutils/chroot.c coreutils/cp.c coreutils/cut.c coreutils/date.c coreutils/dd.c coreutils/df.c coreutils/dirname.c coreutils/dos2unix.c coreutils/du.c coreutils/echo.c coreutils/env.c coreutils/expr.c coreutils/false.c coreutils/fold.c coreutils/head.c coreutils/id.c coreutils/install.c coreutils/ln.c coreutils/ls.c coreutils/md5_sha1_sum.c coreutils/mkdir.c coreutils/mkfifo.c coreutils/mknod.c coreutils/mv.c coreutils/nice.c coreutils/nohup.c coreutils/od.c coreutils/printenv.c coreutils/printf.c coreutils/pwd.c coreutils/readlink.c coreutils/realpath.c coreutils/rm.c coreutils/rmdir.c coreutils/seq.c coreutils/sleep.c coreutils/sort.c coreutils/split.c coreutils/stat.c coreutils/stty.c coreutils/sync.c coreutils/tac.c coreutils/tail.c coreutils/tee.c coreutils/test.c coreutils/test_ptr_hack.c coreutils/touch.c coreutils/tr.c coreutils/true.c coreutils/uname.c coreutils/uniq.c coreutils/usleep.c coreutils/uudecode.c coreutils/uuencode.c coreutils/wc.c coreutils/whoami.c coreutils/yes.c
coreutils/libcoreutils/cp_mv_stat.c coreutils/libcoreutils/getopt_mk_fifo_nod.c
coreutils/comm.c coreutils/expand.c coreutils/sum.c coreutils/fsync.c
+coreutils/shuf.c coreutils/truncate.c coreutils/unlink.c
debianutils/mktemp.c debianutils/run_parts.c debianutils/which.c debianutils/pipe_progress.c
@@ -25,16 +27,16 @@ editors/awk.c editors/cmp.c editors/diff.c editors/ed.c editors/patch.c editors/
e2fsprogs/e2fs_lib.c e2fsprogs/chattr.c e2fsprogs/lsattr.c e2fsprogs/tune2fs.c
findutils/find.c findutils/grep.c findutils/xargs.c
-init/halt.c init/mesg.c
+init/halt.c init/mesg.c init/init.c
libbb/missing_syscalls.c
libbb/appletlib.c libbb/ask_confirmation.c libbb/bb_askpass.c libbb/bb_do_delay.c libbb/bb_pwd.c libbb/bb_qsort.c libbb/bb_strtonum.c
libbb/change_identity.c libbb/chomp.c libbb/compare_string_array.c libbb/concat_path_file.c libbb/concat_subpath_file.c libbb/copy_file.c libbb/copyfd.c
-libbb/crc32.c libbb/percent_decode.c libbb/default_error_retval.c libbb/device_open.c libbb/dump.c libbb/execable.c libbb/fclose_nonstdin.c
+libbb/crc32.c libbb/percent_decode.c libbb/default_error_retval.c libbb/device_open.c libbb/dump.c libbb/executable.c libbb/fclose_nonstdin.c
libbb/fflush_stdout_and_exit.c libbb/fgets_str.c libbb/find_mount_point.c libbb/find_pid_by_name.c libbb/find_root_device.c libbb/full_write.c
libbb/get_console.c libbb/get_cpu_count.c libbb/get_last_path_component.c libbb/get_line_from_file.c libbb/get_volsize.c
libbb/getopt32.c libbb/getpty.c libbb/get_shell_name.c
-libbb/herror_msg.c libbb/human_readable.c libbb/inet_cksum.c libbb/inet_common.c libbb/info_msg.c libbb/inode_hash.c libbb/isdirectory.c
+libbb/herror_msg.c libbb/human_readable.c libbb/inet_cksum.c libbb/inet_common.c libbb/inode_hash.c libbb/isdirectory.c
libbb/kernel_version.c libbb/last_char_is.c libbb/lineedit.c libbb/lineedit_ptr_hack.c libbb/llist.c libbb/login.c libbb/loop.c
libbb/make_directory.c libbb/makedev.c libbb/match_fstype.c libbb/hash_md5_sha.c libbb/bb_bswap_64.c libbb/messages.c libbb/mode_string.c libbb/mtab.c
libbb/parse_config.c libbb/parse_mode.c libbb/perror_msg.c libbb/perror_nomsg.c libbb/perror_nomsg_and_die.c libbb/pidfile.c libbb/platform.c
@@ -47,15 +49,21 @@ libbb/vdprintf.c libbb/verror_msg.c libbb/vfork_daemon_rexec.c libbb/warn_ignori
libbb/write.c libbb/xatonum.c libbb/xconnect.c libbb/xfunc_die.c libbb/xfuncs.c libbb/xfuncs_printf.c
libbb/xgetcwd.c libbb/xgethostbyname.c libbb/xreadlink.c libbb/xrealloc_vector.c libbb/xregcomp.c
libbb/endofname.c libbb/in_ether.c libbb/nuke_str.c
+libbb/auto_string.c libbb/bbunit.c libbb/common_bufsiz.c libbb/logenv.c libbb/replace.c libbb/sysconf.c libbb/ubi.c
+libbb/pw_encrypt.c
libpwdgrp/uidgid_get.c
+loginutils/cryptpw.c
+
miscutils/adjtimex.c miscutils/bbconfig.c miscutils/crond.c miscutils/crontab.c miscutils/dc.c miscutils/devmem.c miscutils/fbsplash.c miscutils/flash_lock_unlock.c miscutils/flashcp.c miscutils/ionice.c miscutils/less.c miscutils/man.c miscutils/mountpoint.c miscutils/nandwrite.c miscutils/rx.c miscutils/setserial.c miscutils/setsid.c miscutils/strings.c miscutils/taskset.c miscutils/time.c miscutils/timeout.c miscutils/ttysize.c
+miscutils/i2c_tools.c miscutils/ubirename.c
modutils/depmod.c modutils/insmod.c modutils/lsmod.c modutils/modinfo.c modutils/modprobe.c modutils/modutils.c modutils/rmmod.c
networking/arp.c networking/brctl.c networking/dnsd.c networking/ftpgetput.c networking/ifconfig.c networking/inetd.c networking/interface.c networking/ip.c networking/nbd-client.c networking/nc.c networking/netstat.c networking/nslookup.c networking/ntpd.c networking/ping.c networking/route.c networking/telnet.c networking/telnetd.c networking/tftp.c networking/traceroute.c networking/wget.c
networking/libiproute/ip_parse_common_args.c networking/libiproute/ipaddress.c networking/libiproute/iplink.c networking/libiproute/iproute.c networking/libiproute/iprule.c networking/libiproute/libnetlink.c networking/libiproute/ll_addr.c networking/libiproute/ll_map.c networking/libiproute/ll_proto.c networking/libiproute/ll_types.c networking/libiproute/rt_names.c networking/libiproute/rtm_map.c networking/libiproute/utils.c
-
+networking/libiproute/ipneigh.c
+networking/hostname.c networking/ifupdown.c
procps/free.c procps/fuser.c procps/kill.c procps/pgrep.c procps/pidof.c procps/ps.c procps/renice.c procps/sysctl.c procps/top.c procps/uptime.c procps/watch.c
procps/pmap.c procps/iostat.c procps/mpstat.c
@@ -68,6 +76,8 @@ selinux/chcon.c selinux/selinuxenabled.c
selinux/getenforce.c selinux/sestatus.c selinux/setsebool.c
selinux/getsebool.c selinux/runcon.c selinux/setenforce.c selinux/setfiles.c selinux/matchpathcon.c
+runit/sv.c
+
util-linux/blkid.c util-linux/blockdev.c util-linux/dmesg.c util-linux/fdisk.c util-linux/findfs.c util-linux/flock.c
util-linux/freeramdisk.c util-linux/fstrim.c util-linux/getopt.c
util-linux/hexdump.c util-linux/hwclock.c util-linux/losetup.c util-linux/lspci.c util-linux/lsusb.c
@@ -78,5 +88,7 @@ util-linux/swaponoff.c util-linux/switch_root.c util-linux/umount.c
util-linux/volume_id/get_devname.c util-linux/volume_id/volume_id.c util-linux/volume_id/util.c util-linux/volume_id/ext.c
util-linux/volume_id/fat.c util-linux/volume_id/iso9660.c util-linux/volume_id/ntfs.c util-linux/volume_id/linux_swap.c
util-linux/volume_id/exfat.c util-linux/volume_id/squashfs.c util-linux/volume_id/f2fs.c
+util-linux/volume_id/bcache.c util-linux/volume_id/ubifs.c
-util-linux/fbset.c
+util-linux/fbset.c util-linux/setarch.c util-linux/nsenter.c util-linux/unshare.c
+util-linux/blkdiscard.c util-linux/fatattr.c util-linux/uevent.c \ No newline at end of file
diff --git a/busybox-minimal.sources b/busybox-minimal.sources
index 3ea4129..01da3e5 100644
--- a/busybox-minimal.sources
+++ b/busybox-minimal.sources
@@ -13,10 +13,10 @@ findutils/find.c findutils/grep.c findutils/xargs.c
libbb/missing_syscalls.c
libbb/appletlib.c libbb/ask_confirmation.c libbb/bb_askpass.c libbb/bb_do_delay.c libbb/bb_pwd.c libbb/bb_qsort.c libbb/bb_strtonum.c
libbb/change_identity.c libbb/chomp.c libbb/compare_string_array.c libbb/concat_path_file.c libbb/concat_subpath_file.c libbb/copy_file.c libbb/copyfd.c
-libbb/crc32.c libbb/default_error_retval.c libbb/device_open.c libbb/dump.c libbb/execable.c libbb/fclose_nonstdin.c
+libbb/crc32.c libbb/default_error_retval.c libbb/device_open.c libbb/dump.c libbb/executable.c libbb/fclose_nonstdin.c
libbb/fflush_stdout_and_exit.c libbb/fgets_str.c libbb/find_mount_point.c libbb/find_pid_by_name.c libbb/find_root_device.c libbb/full_write.c
libbb/get_console.c libbb/get_last_path_component.c libbb/get_line_from_file.c libbb/get_shell_name.c libbb/endofname.c libbb/in_ether.c libbb/get_volsize.c
-libbb/getopt32.c libbb/getpty.c libbb/herror_msg.c libbb/human_readable.c libbb/inet_common.c libbb/info_msg.c libbb/inode_hash.c libbb/isdirectory.c
+libbb/getopt32.c libbb/getpty.c libbb/herror_msg.c libbb/human_readable.c libbb/inet_common.c libbb/inode_hash.c libbb/isdirectory.c
libbb/kernel_version.c libbb/last_char_is.c libbb/lineedit.c libbb/lineedit_ptr_hack.c libbb/llist.c libbb/login.c libbb/loop.c
libbb/make_directory.c libbb/makedev.c libbb/match_fstype.c libbb/hash_md5_sha.c libbb/bb_bswap_64.c libbb/messages.c libbb/mode_string.c libbb/mtab.c
libbb/parse_config.c libbb/parse_mode.c libbb/perror_msg.c libbb/perror_nomsg.c libbb/perror_nomsg_and_die.c libbb/pidfile.c libbb/platform.c
diff --git a/configs/TEST_nommu_defconfig b/configs/TEST_nommu_defconfig
index b45afd9..08351a2 100644
--- a/configs/TEST_nommu_defconfig
+++ b/configs/TEST_nommu_defconfig
@@ -118,7 +118,6 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
CONFIG_DPKG=y
CONFIG_DPKG_DEB=y
-CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY=y
CONFIG_GUNZIP=y
CONFIG_GZIP=y
CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
@@ -390,7 +389,7 @@ CONFIG_FEATURE_INIT_SCTTY=y
CONFIG_FEATURE_INIT_SYSLOG=y
CONFIG_FEATURE_EXTRA_QUIET=y
CONFIG_FEATURE_INIT_COREDUMPS=y
-CONFIG_FEATURE_INITRD=y
+CONFIG_LINUXRC=y
CONFIG_HALT=y
# CONFIG_FEATURE_CALL_TELINIT is not set
CONFIG_TELINIT_PATH=""
@@ -760,7 +759,6 @@ CONFIG_TFTP_DEBUG=y
CONFIG_TRACEROUTE=y
CONFIG_TRACEROUTE6=y
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
CONFIG_FEATURE_TRACEROUTE_USE_ICMP=y
CONFIG_UDHCPD=y
CONFIG_DHCPRELAY=y
diff --git a/configs/TEST_noprintf_defconfig b/configs/TEST_noprintf_defconfig
index 809b60c..70dacec 100644
--- a/configs/TEST_noprintf_defconfig
+++ b/configs/TEST_noprintf_defconfig
@@ -130,7 +130,6 @@ CONFIG_FEATURE_SEAMLESS_Z=y
# CONFIG_FEATURE_CPIO_P is not set
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
# CONFIG_GUNZIP is not set
# CONFIG_GZIP is not set
# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
@@ -395,7 +394,7 @@ CONFIG_FEATURE_KILL_DELAY=0
# CONFIG_FEATURE_INIT_SYSLOG is not set
# CONFIG_FEATURE_EXTRA_QUIET is not set
# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
+# CONFIG_LINUXRC is not set
# CONFIG_HALT is not set
# CONFIG_FEATURE_CALL_TELINIT is not set
CONFIG_TELINIT_PATH=""
@@ -762,7 +761,6 @@ CONFIG_IFUPDOWN_IFSTATE_PATH=""
# CONFIG_TRACEROUTE is not set
# CONFIG_TRACEROUTE6 is not set
# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
# CONFIG_TUNCTL is not set
# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/configs/TEST_rh9_defconfig b/configs/TEST_rh9_defconfig
index 565b826..4a5fad3 100644
--- a/configs/TEST_rh9_defconfig
+++ b/configs/TEST_rh9_defconfig
@@ -128,7 +128,6 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GUNZIP=y
CONFIG_GZIP=y
CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
@@ -407,7 +406,7 @@ CONFIG_FEATURE_INIT_SCTTY=y
CONFIG_FEATURE_INIT_SYSLOG=y
CONFIG_FEATURE_EXTRA_QUIET=y
CONFIG_FEATURE_INIT_COREDUMPS=y
-CONFIG_FEATURE_INITRD=y
+CONFIG_LINUXRC=y
CONFIG_HALT=y
# CONFIG_FEATURE_CALL_TELINIT is not set
CONFIG_TELINIT_PATH=""
@@ -778,7 +777,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
CONFIG_TRACEROUTE=y
CONFIG_TRACEROUTE6=y
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
CONFIG_TUNCTL=y
CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android2_defconfig b/configs/android2_defconfig
index 4dfbdb5..754f612 100644
--- a/configs/android2_defconfig
+++ b/configs/android2_defconfig
@@ -89,7 +89,6 @@ CONFIG_PREFIX="./_install"
#
# Busybox Library Tuning
#
-# CONFIG_FEATURE_SYSTEMD is not set
# CONFIG_FEATURE_RTMINMAX is not set
CONFIG_PASSWORD_MINLEN=6
CONFIG_MD5_SMALL=1
@@ -135,7 +134,6 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
CONFIG_DPKG=y
CONFIG_DPKG_DEB=y
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GUNZIP=y
CONFIG_GZIP=y
# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
@@ -426,7 +424,7 @@ CONFIG_FEATURE_INIT_SCTTY=y
CONFIG_FEATURE_INIT_SYSLOG=y
CONFIG_FEATURE_EXTRA_QUIET=y
CONFIG_FEATURE_INIT_COREDUMPS=y
-CONFIG_FEATURE_INITRD=y
+CONFIG_LINUXRC=y
CONFIG_INIT_TERMINAL_TYPE="linux"
CONFIG_MESG=y
CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
@@ -815,7 +813,6 @@ CONFIG_TCPSVD=y
# CONFIG_TRACEROUTE is not set
# CONFIG_TRACEROUTE6 is not set
# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
CONFIG_TUNCTL=y
CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android_502_defconfig b/configs/android_502_defconfig
new file mode 100644
index 0000000..8ea6c29
--- a/dev/null
+++ b/configs/android_502_defconfig
@@ -0,0 +1,1140 @@
+## This config was successfully used to build busybox on
+## Samsung SM-T700 tablet
+## Android 5.0.2
+## gcc/toolchain from https://termux.com/
+## binutils 2.26.20160125
+## gcc 4.9.3
+## bionic ANDROID_API 21
+##
+## Static build did not work (static libraries not installed?):
+## # CONFIG_STATIC is not set
+## syslog() requires an additional library:
+## CONFIG_EXTRA_LDLIBS="log"
+## Bionic's botched off_t:
+## # CONFIG_LFS is not set
+##
+## Incompatible password database API:
+## # CONFIG_FEATURE_SHADOWPASSWDS is not set
+## # CONFIG_USE_BB_PWD_GRP is not set
+## # CONFIG_USE_BB_SHADOW is not set
+## # CONFIG_ADDUSER is not set
+## # CONFIG_ADDGROUP is not set
+## # CONFIG_DELUSER is not set
+## # CONFIG_DELGROUP is not set
+##
+## No utmp/wtmp:
+## # CONFIG_FEATURE_UTMP is not set
+## # CONFIG_FEATURE_WTMP is not set
+## # CONFIG_WHO is not set
+## # CONFIG_LAST is not set
+## # CONFIG_USERS is not set
+## # CONFIG_WALL is not set
+##
+## Assorted header problems:
+## # CONFIG_HOSTID is not set
+## # CONFIG_FEATURE_SYNC_FANCY is not set - syncfs()
+## # CONFIG_FEATURE_TOUCH_NODEREF is not set - lutimes()
+## # CONFIG_LOGNAME is not set - getlogin_r()
+## # CONFIG_LOADFONT is not set
+## # CONFIG_SETFONT is not set
+## # CONFIG_MDEV is not set
+## # CONFIG_FSCK_MINIX is not set
+## # CONFIG_MKFS_MINIX is not set
+## # CONFIG_IPCRM is not set
+## # CONFIG_IPCS is not set
+## # CONFIG_SWAPONOFF is not set
+## # CONFIG_CONSPY is not set
+## # CONFIG_NANDWRITE is not set
+## # CONFIG_NANDDUMP is not set
+## # CONFIG_RFKILL is not set
+## # CONFIG_UBIATTACH is not set
+## # CONFIG_UBIDETACH is not set
+## # CONFIG_UBIMKVOL is not set
+## # CONFIG_UBIRMVOL is not set
+## # CONFIG_UBIRSVOL is not set
+## # CONFIG_UBIUPDATEVOL is not set
+## # CONFIG_FEATURE_EJECT_SCSI is not set - scsi headers
+## # CONFIG_ARP is not set
+## # CONFIG_ARPING is not set
+## # CONFIG_ETHER_WAKE is not set
+## # CONFIG_IFCONFIG is not set
+## # CONFIG_IFENSLAVE is not set
+## # CONFIG_NSLOOKUP is not set
+## # CONFIG_ROUTE is not set
+## # CONFIG_ZCIP is not set
+## # CONFIG_HUSH is not set - glob.h
+## # CONFIG_KLOGD is not set
+## # CONFIG_LOGGER is not set
+## # CONFIG_LOGREAD is not set
+## # CONFIG_SYSLOGD is not set
+##-----------------------------------------------
+
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.25.0.git
+# Mon Mar 14 20:43:42 2016
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+CONFIG_DESKTOP=y
+# CONFIG_EXTRA_COMPAT is not set
+CONFIG_INCLUDE_SUSv2=y
+# CONFIG_USE_PORTABLE_CODE is not set
+CONFIG_PLATFORM_LINUX=y
+CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
+# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
+# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
+CONFIG_SHOW_USAGE=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+# CONFIG_INSTALL_NO_USR is not set
+# CONFIG_LOCALE_SUPPORT is not set
+CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_USING_LOCALE is not set
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# CONFIG_UNICODE_COMBINING_WCHARS is not set
+# CONFIG_UNICODE_WIDE_WCHARS is not set
+# CONFIG_UNICODE_BIDI_SUPPORT is not set
+# CONFIG_UNICODE_NEUTRAL_TABLE is not set
+# CONFIG_UNICODE_PRESERVE_BROKEN is not set
+# CONFIG_PAM is not set
+CONFIG_FEATURE_USE_SENDFILE=y
+CONFIG_LONG_OPTS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+# CONFIG_FEATURE_UTMP is not set
+# CONFIG_FEATURE_WTMP is not set
+CONFIG_FEATURE_PIDFILE=y
+CONFIG_PID_FILE_PATH="/var/run"
+CONFIG_FEATURE_SUID=y
+CONFIG_FEATURE_SUID_CONFIG=y
+CONFIG_FEATURE_SUID_CONFIG_QUIET=y
+# CONFIG_SELINUX is not set
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+CONFIG_FEATURE_SYSLOG=y
+# CONFIG_FEATURE_HAVE_RPC is not set
+
+#
+# Build Options
+#
+# CONFIG_STATIC is not set
+# CONFIG_PIE is not set
+# CONFIG_NOMMU is not set
+# CONFIG_BUILD_LIBBUSYBOX is not set
+# CONFIG_FEATURE_INDIVIDUAL is not set
+# CONFIG_FEATURE_SHARED_BUSYBOX is not set
+# CONFIG_LFS is not set
+CONFIG_CROSS_COMPILER_PREFIX=""
+CONFIG_SYSROOT=""
+CONFIG_EXTRA_CFLAGS=""
+CONFIG_EXTRA_LDFLAGS=""
+CONFIG_EXTRA_LDLIBS="log"
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_DEBUG_PESSIMIZE is not set
+# CONFIG_DEBUG_SANITIZE is not set
+# CONFIG_UNIT_TEST is not set
+# CONFIG_WERROR is not set
+CONFIG_NO_DEBUG_LIB=y
+# CONFIG_DMALLOC is not set
+# CONFIG_EFENCE is not set
+
+#
+# Installation Options ("make install" behavior)
+#
+CONFIG_INSTALL_APPLET_SYMLINKS=y
+# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
+# CONFIG_INSTALL_APPLET_DONT is not set
+# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
+# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
+# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
+CONFIG_PREFIX="./_install"
+
+#
+# Busybox Library Tuning
+#
+CONFIG_FEATURE_RTMINMAX=y
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SMALL=1
+CONFIG_SHA3_SMALL=1
+# CONFIG_FEATURE_FAST_TOP is not set
+# CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_FEATURE_EDITING=y
+CONFIG_FEATURE_EDITING_MAX_LEN=1024
+# CONFIG_FEATURE_EDITING_VI is not set
+CONFIG_FEATURE_EDITING_HISTORY=255
+CONFIG_FEATURE_EDITING_SAVEHISTORY=y
+# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
+CONFIG_FEATURE_REVERSE_SEARCH=y
+CONFIG_FEATURE_TAB_COMPLETION=y
+# CONFIG_FEATURE_USERNAME_COMPLETION is not set
+CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
+# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
+CONFIG_FEATURE_NON_POSIX_CP=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_COPYBUF_KB=4
+CONFIG_FEATURE_SKIP_ROOTFS=y
+CONFIG_MONOTONIC_SYSCALL=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+CONFIG_FEATURE_HWIB=y
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+CONFIG_FEATURE_SEAMLESS_XZ=y
+CONFIG_FEATURE_SEAMLESS_LZMA=y
+CONFIG_FEATURE_SEAMLESS_BZ2=y
+CONFIG_FEATURE_SEAMLESS_GZ=y
+# CONFIG_FEATURE_SEAMLESS_Z is not set
+# CONFIG_AR is not set
+# CONFIG_FEATURE_AR_LONG_FILENAMES is not set
+# CONFIG_FEATURE_AR_CREATE is not set
+# CONFIG_UNCOMPRESS is not set
+CONFIG_GUNZIP=y
+CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
+CONFIG_BUNZIP2=y
+CONFIG_UNLZMA=y
+# CONFIG_FEATURE_LZMA_FAST is not set
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
+CONFIG_BZIP2=y
+CONFIG_CPIO=y
+CONFIG_FEATURE_CPIO_O=y
+CONFIG_FEATURE_CPIO_P=y
+# CONFIG_DPKG is not set
+# CONFIG_DPKG_DEB is not set
+CONFIG_GZIP=y
+CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
+CONFIG_GZIP_FAST=0
+# CONFIG_FEATURE_GZIP_LEVELS is not set
+CONFIG_LZOP=y
+# CONFIG_LZOP_COMPR_HIGH is not set
+CONFIG_RPM=y
+CONFIG_RPM2CPIO=y
+CONFIG_TAR=y
+CONFIG_FEATURE_TAR_CREATE=y
+CONFIG_FEATURE_TAR_AUTODETECT=y
+CONFIG_FEATURE_TAR_FROM=y
+CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY=y
+CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+CONFIG_FEATURE_TAR_TO_COMMAND=y
+CONFIG_FEATURE_TAR_UNAME_GNAME=y
+CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
+# CONFIG_FEATURE_TAR_SELINUX is not set
+CONFIG_UNZIP=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAT=y
+CONFIG_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+CONFIG_FEATURE_DATE_COMPAT=y
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_FEATURE_DD_STATUS=y
+# CONFIG_HOSTID is not set
+CONFIG_ID=y
+CONFIG_GROUPS=y
+CONFIG_SHUF=y
+CONFIG_STAT=y
+CONFIG_FEATURE_STAT_FORMAT=y
+CONFIG_FEATURE_STAT_FILESYSTEM=y
+CONFIG_SYNC=y
+# CONFIG_FEATURE_SYNC_FANCY is not set
+CONFIG_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+# CONFIG_FEATURE_TOUCH_NODEREF is not set
+CONFIG_FEATURE_TOUCH_SUSV3=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_TRUNCATE=y
+CONFIG_UNLINK=y
+CONFIG_BASE64=y
+# CONFIG_WHO is not set
+# CONFIG_USERS is not set
+CONFIG_CAL=y
+CONFIG_CATV=y
+CONFIG_CHGRP=y
+CONFIG_CHMOD=y
+CONFIG_CHOWN=y
+CONFIG_FEATURE_CHOWN_LONG_OPTIONS=y
+CONFIG_CHROOT=y
+CONFIG_CKSUM=y
+CONFIG_COMM=y
+CONFIG_CP=y
+CONFIG_FEATURE_CP_LONG_OPTIONS=y
+CONFIG_CUT=y
+CONFIG_DF=y
+CONFIG_FEATURE_DF_FANCY=y
+CONFIG_DIRNAME=y
+CONFIG_DOS2UNIX=y
+CONFIG_UNIX2DOS=y
+CONFIG_DU=y
+CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
+CONFIG_ECHO=y
+CONFIG_FEATURE_FANCY_ECHO=y
+CONFIG_ENV=y
+CONFIG_FEATURE_ENV_LONG_OPTIONS=y
+CONFIG_EXPAND=y
+CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y
+CONFIG_EXPR=y
+CONFIG_EXPR_MATH_SUPPORT_64=y
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
+CONFIG_LN=y
+# CONFIG_LOGNAME is not set
+CONFIG_LS=y
+CONFIG_FEATURE_LS_FILETYPES=y
+CONFIG_FEATURE_LS_FOLLOWLINKS=y
+CONFIG_FEATURE_LS_RECURSIVE=y
+CONFIG_FEATURE_LS_SORTFILES=y
+CONFIG_FEATURE_LS_TIMESTAMPS=y
+CONFIG_FEATURE_LS_USERNAME=y
+CONFIG_FEATURE_LS_COLOR=y
+CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
+CONFIG_MD5SUM=y
+CONFIG_MKDIR=y
+CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y
+CONFIG_MKFIFO=y
+CONFIG_MKNOD=y
+CONFIG_MV=y
+CONFIG_FEATURE_MV_LONG_OPTIONS=y
+CONFIG_NICE=y
+CONFIG_NOHUP=y
+CONFIG_OD=y
+CONFIG_PRINTENV=y
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+CONFIG_REALPATH=y
+CONFIG_RM=y
+CONFIG_RMDIR=y
+CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y
+CONFIG_SEQ=y
+CONFIG_SHA1SUM=y
+CONFIG_SHA256SUM=y
+CONFIG_SHA512SUM=y
+CONFIG_SHA3SUM=y
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+CONFIG_FEATURE_FLOAT_SLEEP=y
+CONFIG_SORT=y
+CONFIG_FEATURE_SORT_BIG=y
+CONFIG_SPLIT=y
+CONFIG_FEATURE_SPLIT_FANCY=y
+CONFIG_STTY=y
+CONFIG_SUM=y
+CONFIG_TAC=y
+CONFIG_TAIL=y
+CONFIG_FEATURE_FANCY_TAIL=y
+CONFIG_TEE=y
+CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
+CONFIG_TRUE=y
+CONFIG_TTY=y
+CONFIG_UNAME=y
+CONFIG_UNAME_OSNAME="GNU/Linux"
+CONFIG_UNEXPAND=y
+CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+CONFIG_UUDECODE=y
+CONFIG_UUENCODE=y
+CONFIG_WC=y
+CONFIG_FEATURE_WC_LARGE=y
+CONFIG_WHOAMI=y
+CONFIG_YES=y
+
+#
+# Common options
+#
+CONFIG_FEATURE_VERBOSE=y
+
+#
+# Common options for cp and mv
+#
+CONFIG_FEATURE_PRESERVE_HARDLINKS=y
+
+#
+# Common options for ls, more and telnet
+#
+CONFIG_FEATURE_AUTOWIDTH=y
+
+#
+# Common options for df, du, ls
+#
+CONFIG_FEATURE_HUMAN_READABLE=y
+
+#
+# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
+#
+CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
+
+#
+# Console Utilities
+#
+CONFIG_CHVT=y
+CONFIG_FGCONSOLE=y
+CONFIG_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_KBD_MODE=y
+# CONFIG_LOADFONT is not set
+CONFIG_LOADKMAP=y
+CONFIG_OPENVT=y
+CONFIG_RESET=y
+CONFIG_RESIZE=y
+CONFIG_FEATURE_RESIZE_PRINT=y
+CONFIG_SETCONSOLE=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+CONFIG_DEFAULT_SETFONT_DIR=""
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
+# CONFIG_FEATURE_LOADFONT_PSF2 is not set
+# CONFIG_FEATURE_LOADFONT_RAW is not set
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+CONFIG_PIPE_PROGRESS=y
+CONFIG_RUN_PARTS=y
+CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS=y
+CONFIG_FEATURE_RUN_PARTS_FANCY=y
+CONFIG_START_STOP_DAEMON=y
+CONFIG_FEATURE_START_STOP_DAEMON_FANCY=y
+CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS=y
+CONFIG_WHICH=y
+
+#
+# Editors
+#
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_LIBM=y
+CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
+CONFIG_CMP=y
+CONFIG_DIFF=y
+CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
+CONFIG_PATCH=y
+CONFIG_SED=y
+CONFIG_VI=y
+CONFIG_FEATURE_VI_MAX_LEN=4096
+# CONFIG_FEATURE_VI_8BIT is not set
+CONFIG_FEATURE_VI_COLON=y
+CONFIG_FEATURE_VI_YANKMARK=y
+CONFIG_FEATURE_VI_SEARCH=y
+# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
+CONFIG_FEATURE_VI_USE_SIGNALS=y
+CONFIG_FEATURE_VI_DOT_CMD=y
+CONFIG_FEATURE_VI_READONLY=y
+CONFIG_FEATURE_VI_SETOPTS=y
+CONFIG_FEATURE_VI_SET=y
+CONFIG_FEATURE_VI_WIN_RESIZE=y
+CONFIG_FEATURE_VI_ASK_TERMINAL=y
+CONFIG_FEATURE_VI_UNDO=y
+CONFIG_FEATURE_VI_UNDO_QUEUE=y
+CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
+CONFIG_FEATURE_ALLOW_EXEC=y
+
+#
+# Finding Utilities
+#
+CONFIG_FIND=y
+CONFIG_FEATURE_FIND_PRINT0=y
+CONFIG_FEATURE_FIND_MTIME=y
+CONFIG_FEATURE_FIND_MMIN=y
+CONFIG_FEATURE_FIND_PERM=y
+CONFIG_FEATURE_FIND_TYPE=y
+CONFIG_FEATURE_FIND_XDEV=y
+CONFIG_FEATURE_FIND_MAXDEPTH=y
+CONFIG_FEATURE_FIND_NEWER=y
+CONFIG_FEATURE_FIND_INUM=y
+CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_EXEC_PLUS=y
+CONFIG_FEATURE_FIND_USER=y
+CONFIG_FEATURE_FIND_GROUP=y
+CONFIG_FEATURE_FIND_NOT=y
+CONFIG_FEATURE_FIND_DEPTH=y
+CONFIG_FEATURE_FIND_PAREN=y
+CONFIG_FEATURE_FIND_SIZE=y
+CONFIG_FEATURE_FIND_PRUNE=y
+CONFIG_FEATURE_FIND_DELETE=y
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+CONFIG_FEATURE_FIND_LINKS=y
+CONFIG_GREP=y
+CONFIG_FEATURE_GREP_EGREP_ALIAS=y
+CONFIG_FEATURE_GREP_FGREP_ALIAS=y
+CONFIG_FEATURE_GREP_CONTEXT=y
+CONFIG_XARGS=y
+CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
+CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
+CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
+CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
+
+#
+# Init Utilities
+#
+CONFIG_BOOTCHARTD=y
+CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER=y
+CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE=y
+CONFIG_HALT=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+# CONFIG_FEATURE_KILL_REMOVED is not set
+CONFIG_FEATURE_KILL_DELAY=0
+CONFIG_FEATURE_INIT_SCTTY=y
+CONFIG_FEATURE_INIT_SYSLOG=y
+CONFIG_FEATURE_EXTRA_QUIET=y
+CONFIG_FEATURE_INIT_COREDUMPS=y
+CONFIG_LINUXRC=y
+CONFIG_INIT_TERMINAL_TYPE="linux"
+CONFIG_FEATURE_INIT_MODIFY_CMDLINE=y
+CONFIG_MESG=y
+CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_FEATURE_SHADOWPASSWDS is not set
+# CONFIG_USE_BB_PWD_GRP is not set
+# CONFIG_USE_BB_SHADOW is not set
+CONFIG_USE_BB_CRYPT=y
+CONFIG_USE_BB_CRYPT_SHA=y
+CONFIG_ADD_SHELL=y
+CONFIG_REMOVE_SHELL=y
+# CONFIG_ADDGROUP is not set
+# CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set
+# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
+# CONFIG_ADDUSER is not set
+# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_LAST_ID=0
+CONFIG_FIRST_SYSTEM_ID=0
+CONFIG_LAST_SYSTEM_ID=0
+CONFIG_CHPASSWD=y
+CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="des"
+CONFIG_CRYPTPW=y
+# CONFIG_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_LOGIN_SESSION_AS_CHILD is not set
+CONFIG_LOGIN_SCRIPTS=y
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+CONFIG_VLOCK=y
+
+#
+# Linux Ext2 FS Progs
+#
+CONFIG_CHATTR=y
+CONFIG_FSCK=y
+CONFIG_LSATTR=y
+# CONFIG_TUNE2FS is not set
+
+#
+# Linux Module Utilities
+#
+CONFIG_MODINFO=y
+CONFIG_MODPROBE_SMALL=y
+CONFIG_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE=y
+CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
+# CONFIG_INSMOD is not set
+# CONFIG_RMMOD is not set
+# CONFIG_LSMOD is not set
+# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
+# CONFIG_MODPROBE is not set
+# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
+# CONFIG_DEPMOD is not set
+
+#
+# Options common to multiple modutils
+#
+# CONFIG_FEATURE_2_4_MODULES is not set
+# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
+# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
+# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
+# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
+# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
+# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
+# CONFIG_FEATURE_MODUTILS_ALIAS is not set
+# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# Linux System Utilities
+#
+CONFIG_BLKDISCARD=y
+CONFIG_BLOCKDEV=y
+CONFIG_FATATTR=y
+CONFIG_FSTRIM=y
+# CONFIG_MDEV is not set
+# CONFIG_FEATURE_MDEV_CONF is not set
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
+# CONFIG_FEATURE_MDEV_EXEC is not set
+# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
+CONFIG_MOUNT=y
+CONFIG_FEATURE_MOUNT_FAKE=y
+CONFIG_FEATURE_MOUNT_VERBOSE=y
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+CONFIG_FEATURE_MOUNT_LABEL=y
+# CONFIG_FEATURE_MOUNT_NFS is not set
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_FEATURE_MOUNT_OTHERTAB=y
+CONFIG_REV=y
+CONFIG_SETARCH=y
+CONFIG_UEVENT=y
+CONFIG_ACPID=y
+CONFIG_FEATURE_ACPID_COMPAT=y
+CONFIG_BLKID=y
+# CONFIG_FEATURE_BLKID_TYPE is not set
+CONFIG_DMESG=y
+CONFIG_FEATURE_DMESG_PRETTY=y
+CONFIG_FBSET=y
+CONFIG_FEATURE_FBSET_FANCY=y
+CONFIG_FEATURE_FBSET_READMODE=y
+CONFIG_FDFLUSH=y
+CONFIG_FDFORMAT=y
+CONFIG_FDISK=y
+CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+CONFIG_FEATURE_FDISK_WRITABLE=y
+# CONFIG_FEATURE_AIX_LABEL is not set
+# CONFIG_FEATURE_SGI_LABEL is not set
+# CONFIG_FEATURE_SUN_LABEL is not set
+# CONFIG_FEATURE_OSF_LABEL is not set
+# CONFIG_FEATURE_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+CONFIG_FINDFS=y
+CONFIG_FLOCK=y
+CONFIG_FREERAMDISK=y
+# CONFIG_FSCK_MINIX is not set
+CONFIG_MKFS_EXT2=y
+# CONFIG_MKFS_MINIX is not set
+# CONFIG_FEATURE_MINIX2 is not set
+# CONFIG_MKFS_REISER is not set
+CONFIG_MKFS_VFAT=y
+CONFIG_GETOPT=y
+CONFIG_FEATURE_GETOPT_LONG=y
+CONFIG_HEXDUMP=y
+CONFIG_FEATURE_HEXDUMP_REVERSE=y
+CONFIG_HD=y
+CONFIG_HWCLOCK=y
+CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS=y
+# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
+# CONFIG_IPCRM is not set
+# CONFIG_IPCS is not set
+CONFIG_LOSETUP=y
+CONFIG_LSPCI=y
+CONFIG_LSUSB=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_UUID=y
+CONFIG_MORE=y
+CONFIG_PIVOT_ROOT=y
+CONFIG_RDATE=y
+CONFIG_RDEV=y
+CONFIG_READPROFILE=y
+CONFIG_RTCWAKE=y
+CONFIG_SCRIPT=y
+CONFIG_SCRIPTREPLAY=y
+# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_DISCARD is not set
+# CONFIG_FEATURE_SWAPON_PRI is not set
+CONFIG_SWITCH_ROOT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_UMOUNT_ALL=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+CONFIG_FEATURE_MOUNT_LOOP_CREATE=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+CONFIG_VOLUMEID=y
+
+#
+# Filesystem/Volume identification
+#
+CONFIG_FEATURE_VOLUMEID_BCACHE=y
+CONFIG_FEATURE_VOLUMEID_BTRFS=y
+CONFIG_FEATURE_VOLUMEID_CRAMFS=y
+CONFIG_FEATURE_VOLUMEID_EXFAT=y
+CONFIG_FEATURE_VOLUMEID_EXT=y
+CONFIG_FEATURE_VOLUMEID_F2FS=y
+CONFIG_FEATURE_VOLUMEID_FAT=y
+CONFIG_FEATURE_VOLUMEID_HFS=y
+CONFIG_FEATURE_VOLUMEID_ISO9660=y
+CONFIG_FEATURE_VOLUMEID_JFS=y
+CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
+CONFIG_FEATURE_VOLUMEID_LUKS=y
+CONFIG_FEATURE_VOLUMEID_NILFS=y
+CONFIG_FEATURE_VOLUMEID_NTFS=y
+CONFIG_FEATURE_VOLUMEID_OCFS2=y
+CONFIG_FEATURE_VOLUMEID_REISERFS=y
+CONFIG_FEATURE_VOLUMEID_ROMFS=y
+# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
+CONFIG_FEATURE_VOLUMEID_SYSV=y
+CONFIG_FEATURE_VOLUMEID_UDF=y
+CONFIG_FEATURE_VOLUMEID_XFS=y
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_CONSPY is not set
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_I2CGET=y
+CONFIG_I2CSET=y
+CONFIG_I2CDUMP=y
+CONFIG_I2CDETECT=y
+CONFIG_LESS=y
+CONFIG_FEATURE_LESS_MAXLINES=9999999
+CONFIG_FEATURE_LESS_BRACKETS=y
+CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_TRUNCATE=y
+CONFIG_FEATURE_LESS_MARKS=y
+CONFIG_FEATURE_LESS_REGEXP=y
+CONFIG_FEATURE_LESS_WINCH=y
+CONFIG_FEATURE_LESS_ASK_TERMINAL=y
+CONFIG_FEATURE_LESS_DASHCMD=y
+CONFIG_FEATURE_LESS_LINENUMS=y
+# CONFIG_NANDWRITE is not set
+# CONFIG_NANDDUMP is not set
+# CONFIG_RFKILL is not set
+CONFIG_SETSERIAL=y
+CONFIG_TASKSET=y
+CONFIG_FEATURE_TASKSET_FANCY=y
+# CONFIG_UBIATTACH is not set
+# CONFIG_UBIDETACH is not set
+# CONFIG_UBIMKVOL is not set
+# CONFIG_UBIRMVOL is not set
+# CONFIG_UBIRSVOL is not set
+# CONFIG_UBIUPDATEVOL is not set
+# CONFIG_WALL is not set
+CONFIG_ADJTIMEX=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+CONFIG_BEEP=y
+CONFIG_FEATURE_BEEP_FREQ=4000
+CONFIG_FEATURE_BEEP_LENGTH_MS=30
+CONFIG_CHAT=y
+CONFIG_FEATURE_CHAT_NOFAIL=y
+# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
+CONFIG_FEATURE_CHAT_IMPLICIT_CR=y
+CONFIG_FEATURE_CHAT_SWALLOW_OPTS=y
+CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
+CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
+CONFIG_FEATURE_CHAT_CLR_ABORT=y
+CONFIG_CHRT=y
+CONFIG_CRONTAB=y
+CONFIG_DC=y
+CONFIG_FEATURE_DC_LIBM=y
+# CONFIG_DEVFSD is not set
+# CONFIG_DEVFSD_MODLOAD is not set
+# CONFIG_DEVFSD_FG_NP is not set
+# CONFIG_DEVFSD_VERBOSE is not set
+# CONFIG_FEATURE_DEVFS is not set
+CONFIG_DEVMEM=y
+CONFIG_EJECT=y
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# CONFIG_FLASHCP is not set
+# CONFIG_FLASH_LOCK is not set
+# CONFIG_FLASH_UNLOCK is not set
+# CONFIG_FLASH_ERASEALL is not set
+CONFIG_IONICE=y
+# CONFIG_INOTIFYD is not set
+# CONFIG_LAST is not set
+# CONFIG_FEATURE_LAST_FANCY is not set
+CONFIG_HDPARM=y
+CONFIG_FEATURE_HDPARM_GET_IDENTITY=y
+CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET=y
+CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF=y
+CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA=y
+CONFIG_MAKEDEVS=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+CONFIG_MAN=y
+CONFIG_MICROCOM=y
+CONFIG_MOUNTPOINT=y
+# CONFIG_MT is not set
+CONFIG_RAIDAUTORUN=y
+# CONFIG_READAHEAD is not set
+# CONFIG_RUNLEVEL is not set
+CONFIG_RX=y
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
+CONFIG_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+CONFIG_WATCHDOG=y
+
+#
+# Networking Utilities
+#
+CONFIG_NAMEIF=y
+CONFIG_FEATURE_NAMEIF_EXTENDED=y
+CONFIG_NBDCLIENT=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+# CONFIG_NC_110_COMPAT is not set
+CONFIG_PING=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+CONFIG_WGET=y
+CONFIG_FEATURE_WGET_STATUSBAR=y
+CONFIG_FEATURE_WGET_AUTHENTICATION=y
+CONFIG_FEATURE_WGET_LONG_OPTIONS=y
+CONFIG_FEATURE_WGET_TIMEOUT=y
+CONFIG_FEATURE_WGET_OPENSSL=y
+CONFIG_FEATURE_WGET_SSL_HELPER=y
+CONFIG_WHOIS=y
+CONFIG_FEATURE_IPV6=y
+# CONFIG_FEATURE_UNIX_LOCAL is not set
+CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
+# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
+# CONFIG_ARP is not set
+# CONFIG_ARPING is not set
+CONFIG_BRCTL=y
+CONFIG_FEATURE_BRCTL_FANCY=y
+CONFIG_FEATURE_BRCTL_SHOW=y
+CONFIG_DNSD=y
+# CONFIG_ETHER_WAKE is not set
+CONFIG_FAKEIDENTD=y
+CONFIG_FTPD=y
+CONFIG_FEATURE_FTP_WRITE=y
+CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
+CONFIG_FEATURE_FTP_AUTHENTICATION=y
+CONFIG_FTPGET=y
+CONFIG_FTPPUT=y
+CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
+CONFIG_HOSTNAME=y
+CONFIG_HTTPD=y
+CONFIG_FEATURE_HTTPD_RANGES=y
+CONFIG_FEATURE_HTTPD_SETUID=y
+CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
+CONFIG_FEATURE_HTTPD_AUTH_MD5=y
+CONFIG_FEATURE_HTTPD_CGI=y
+CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR=y
+CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
+CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
+CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
+CONFIG_FEATURE_HTTPD_GZIP=y
+# CONFIG_IFCONFIG is not set
+# CONFIG_FEATURE_IFCONFIG_STATUS is not set
+# CONFIG_FEATURE_IFCONFIG_SLIP is not set
+# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
+# CONFIG_FEATURE_IFCONFIG_HW is not set
+# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
+# CONFIG_IFENSLAVE is not set
+CONFIG_IFPLUGD=y
+CONFIG_IFUPDOWN=y
+CONFIG_IFUPDOWN_IFSTATE_PATH="/var/run/ifstate"
+CONFIG_FEATURE_IFUPDOWN_IP=y
+CONFIG_FEATURE_IFUPDOWN_IP_BUILTIN=y
+# CONFIG_FEATURE_IFUPDOWN_IFCONFIG_BUILTIN is not set
+CONFIG_FEATURE_IFUPDOWN_IPV4=y
+CONFIG_FEATURE_IFUPDOWN_IPV6=y
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
+CONFIG_INETD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME=y
+CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN=y
+# CONFIG_FEATURE_INETD_RPC is not set
+CONFIG_IP=y
+CONFIG_FEATURE_IP_ADDRESS=y
+CONFIG_FEATURE_IP_LINK=y
+CONFIG_FEATURE_IP_ROUTE=y
+CONFIG_FEATURE_IP_ROUTE_DIR="/etc/iproute2"
+CONFIG_FEATURE_IP_TUNNEL=y
+CONFIG_FEATURE_IP_RULE=y
+CONFIG_FEATURE_IP_NEIGH=y
+CONFIG_FEATURE_IP_SHORT_FORMS=y
+# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
+CONFIG_IPADDR=y
+CONFIG_IPLINK=y
+CONFIG_IPROUTE=y
+CONFIG_IPTUNNEL=y
+CONFIG_IPRULE=y
+CONFIG_IPNEIGH=y
+CONFIG_IPCALC=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+# CONFIG_NSLOOKUP is not set
+CONFIG_NTPD=y
+CONFIG_FEATURE_NTPD_SERVER=y
+CONFIG_FEATURE_NTPD_CONF=y
+CONFIG_PSCAN=y
+# CONFIG_ROUTE is not set
+CONFIG_SLATTACH=y
+CONFIG_TCPSVD=y
+CONFIG_TELNET=y
+CONFIG_FEATURE_TELNET_TTYPE=y
+CONFIG_FEATURE_TELNET_AUTOLOGIN=y
+CONFIG_TELNETD=y
+CONFIG_FEATURE_TELNETD_STANDALONE=y
+CONFIG_FEATURE_TELNETD_INETD_WAIT=y
+CONFIG_TFTP=y
+CONFIG_TFTPD=y
+
+#
+# Common options for tftp/tftpd
+#
+CONFIG_FEATURE_TFTP_GET=y
+CONFIG_FEATURE_TFTP_PUT=y
+CONFIG_FEATURE_TFTP_BLOCKSIZE=y
+CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
+# CONFIG_TFTP_DEBUG is not set
+CONFIG_TRACEROUTE=y
+CONFIG_TRACEROUTE6=y
+CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
+# CONFIG_UDHCPC6 is not set
+CONFIG_UDHCPD=y
+CONFIG_DHCPRELAY=y
+CONFIG_DUMPLEASES=y
+CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY=y
+# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
+CONFIG_DHCPD_LEASES_FILE="/var/lib/misc/udhcpd.leases"
+CONFIG_UDHCPC=y
+CONFIG_FEATURE_UDHCPC_ARPING=y
+CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y
+# CONFIG_FEATURE_UDHCP_PORT is not set
+CONFIG_UDHCP_DEBUG=9
+CONFIG_FEATURE_UDHCP_RFC3397=y
+CONFIG_FEATURE_UDHCP_8021Q=y
+CONFIG_UDHCPC_DEFAULT_SCRIPT="/usr/share/udhcpc/default.script"
+CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=80
+CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="-R -n"
+CONFIG_UDPSVD=y
+CONFIG_VCONFIG=y
+# CONFIG_ZCIP is not set
+
+#
+# Print Utilities
+#
+CONFIG_LPD=y
+CONFIG_LPR=y
+CONFIG_LPQ=y
+
+#
+# Mail Utilities
+#
+CONFIG_MAKEMIME=y
+CONFIG_FEATURE_MIME_CHARSET="us-ascii"
+CONFIG_POPMAILDIR=y
+CONFIG_FEATURE_POPMAILDIR_DELIVERY=y
+CONFIG_REFORMIME=y
+CONFIG_FEATURE_REFORMIME_COMPAT=y
+CONFIG_SENDMAIL=y
+
+#
+# Process Utilities
+#
+CONFIG_IOSTAT=y
+CONFIG_LSOF=y
+CONFIG_MPSTAT=y
+CONFIG_NMETER=y
+CONFIG_PMAP=y
+CONFIG_POWERTOP=y
+CONFIG_PSTREE=y
+CONFIG_PWDX=y
+CONFIG_SMEMCAP=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_PGREP=y
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=y
+CONFIG_PS=y
+# CONFIG_FEATURE_PS_WIDE is not set
+# CONFIG_FEATURE_PS_LONG is not set
+CONFIG_FEATURE_PS_TIME=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=y
+CONFIG_FEATURE_SHOW_THREADS=y
+CONFIG_WATCH=y
+
+#
+# Runit Utilities
+#
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
+CONFIG_RUNSV=y
+CONFIG_RUNSVDIR=y
+# CONFIG_FEATURE_RUNSVDIR_LOG is not set
+CONFIG_SV=y
+CONFIG_SV_DEFAULT_SERVICE_DIR="/var/service"
+CONFIG_SVLOGD=y
+# CONFIG_CHCON is not set
+# CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set
+# CONFIG_GETENFORCE is not set
+# CONFIG_GETSEBOOL is not set
+# CONFIG_LOAD_POLICY is not set
+# CONFIG_MATCHPATHCON is not set
+# CONFIG_RESTORECON is not set
+# CONFIG_RUNCON is not set
+# CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set
+# CONFIG_SELINUXENABLED is not set
+# CONFIG_SETENFORCE is not set
+# CONFIG_SETFILES is not set
+# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+# CONFIG_SETSEBOOL is not set
+# CONFIG_SESTATUS is not set
+
+#
+# Shells
+#
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+CONFIG_ASH_JOB_CONTROL=y
+CONFIG_ASH_ALIAS=y
+CONFIG_ASH_GETOPTS=y
+CONFIG_ASH_BUILTIN_ECHO=y
+CONFIG_ASH_BUILTIN_PRINTF=y
+CONFIG_ASH_BUILTIN_TEST=y
+CONFIG_ASH_HELP=y
+CONFIG_ASH_CMDCMD=y
+# CONFIG_ASH_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+CONFIG_ASH_EXPAND_PRMT=y
+CONFIG_CTTYHACK=y
+# CONFIG_HUSH is not set
+# CONFIG_HUSH_BASH_COMPAT is not set
+# CONFIG_HUSH_BRACE_EXPANSION is not set
+# CONFIG_HUSH_HELP is not set
+# CONFIG_HUSH_INTERACTIVE is not set
+# CONFIG_HUSH_SAVEHISTORY is not set
+# CONFIG_HUSH_JOB is not set
+# CONFIG_HUSH_TICK is not set
+# CONFIG_HUSH_IF is not set
+# CONFIG_HUSH_LOOPS is not set
+# CONFIG_HUSH_CASE is not set
+# CONFIG_HUSH_FUNCTIONS is not set
+# CONFIG_HUSH_LOCAL is not set
+# CONFIG_HUSH_RANDOM_SUPPORT is not set
+# CONFIG_HUSH_EXPORT_N is not set
+# CONFIG_HUSH_MODE_X is not set
+# CONFIG_MSH is not set
+CONFIG_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+# CONFIG_FEATURE_BASH_IS_ASH is not set
+# CONFIG_FEATURE_BASH_IS_HUSH is not set
+CONFIG_FEATURE_BASH_IS_NONE=y
+CONFIG_SH_MATH_SUPPORT=y
+CONFIG_SH_MATH_SUPPORT_64=y
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+# CONFIG_FEATURE_SH_STANDALONE is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+CONFIG_FEATURE_SH_HISTFILESIZE=y
+
+#
+# System Logging Utilities
+#
+# CONFIG_KLOGD is not set
+# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
+# CONFIG_LOGGER is not set
+# CONFIG_LOGREAD is not set
+# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
+# CONFIG_SYSLOGD is not set
+# CONFIG_FEATURE_ROTATE_LOGFILE is not set
+# CONFIG_FEATURE_REMOTE_LOG is not set
+# CONFIG_FEATURE_SYSLOGD_DUP is not set
+# CONFIG_FEATURE_SYSLOGD_CFG is not set
+CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
+# CONFIG_FEATURE_IPC_SYSLOG is not set
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
+# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/configs/android_defconfig b/configs/android_defconfig
index e35830e..30e888a 100644
--- a/configs/android_defconfig
+++ b/configs/android_defconfig
@@ -107,7 +107,6 @@ CONFIG_PREFIX="./_install"
#
# Busybox Library Tuning
#
-# CONFIG_FEATURE_SYSTEMD is not set
# CONFIG_FEATURE_RTMINMAX is not set
CONFIG_PASSWORD_MINLEN=6
CONFIG_MD5_SMALL=1
@@ -155,7 +154,6 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
CONFIG_DPKG=y
CONFIG_DPKG_DEB=y
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GUNZIP=y
CONFIG_GZIP=y
# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
@@ -449,7 +447,7 @@ CONFIG_FEATURE_INIT_SCTTY=y
CONFIG_FEATURE_INIT_SYSLOG=y
CONFIG_FEATURE_EXTRA_QUIET=y
CONFIG_FEATURE_INIT_COREDUMPS=y
-CONFIG_FEATURE_INITRD=y
+CONFIG_LINUXRC=y
CONFIG_INIT_TERMINAL_TYPE="linux"
CONFIG_MESG=y
CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
@@ -844,7 +842,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
CONFIG_TRACEROUTE=y
# CONFIG_TRACEROUTE6 is not set
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
CONFIG_TUNCTL=y
CONFIG_FEATURE_TUNCTL_UG=y
diff --git a/configs/android_ndk_defconfig b/configs/android_ndk_defconfig
index 01cc2dd..bdcadc7 100644
--- a/configs/android_ndk_defconfig
+++ b/configs/android_ndk_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Busybox version: 1.21.0.git
-# Mon May 28 21:51:18 2012
+# Busybox version: 1.24.0.git
+# Fri Sep 18 20:39:29 2015
#
CONFIG_HAVE_DOT_CONFIG=y
@@ -36,12 +36,15 @@ CONFIG_LAST_SUPPORTED_WCHAR=0
# CONFIG_UNICODE_BIDI_SUPPORT is not set
# CONFIG_UNICODE_NEUTRAL_TABLE is not set
# CONFIG_UNICODE_PRESERVE_BROKEN is not set
+# CONFIG_PAM is not set
+CONFIG_FEATURE_USE_SENDFILE=y
CONFIG_LONG_OPTS=y
# CONFIG_FEATURE_DEVPTS is not set
# CONFIG_FEATURE_CLEAN_UP is not set
# CONFIG_FEATURE_UTMP is not set
# CONFIG_FEATURE_WTMP is not set
# CONFIG_FEATURE_PIDFILE is not set
+CONFIG_PID_FILE_PATH=""
# CONFIG_FEATURE_SUID is not set
# CONFIG_FEATURE_SUID_CONFIG is not set
# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set
@@ -63,7 +66,7 @@ CONFIG_FEATURE_SYSLOG=y
# CONFIG_LFS is not set
CONFIG_CROSS_COMPILER_PREFIX="arm-linux-androideabi-"
CONFIG_SYSROOT="/opt/android-ndk/platforms/android-9/arch-arm"
-CONFIG_EXTRA_CFLAGS="-DANDROID -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers"
+CONFIG_EXTRA_CFLAGS="-DANDROID -D__ANDROID__ -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frename-registers -fuse-ld=bfd"
CONFIG_EXTRA_LDFLAGS="-Xlinker -z -Xlinker muldefs -nostdlib -Bdynamic -Xlinker -dynamic-linker -Xlinker /system/bin/linker -Xlinker -z -Xlinker nocopyreloc -Xlinker --no-undefined ${SYSROOT}/usr/lib/crtbegin_dynamic.o ${SYSROOT}/usr/lib/crtend_android.o"
CONFIG_EXTRA_LDLIBS="dl m c gcc"
@@ -72,6 +75,7 @@ CONFIG_EXTRA_LDLIBS="dl m c gcc"
#
# CONFIG_DEBUG is not set
# CONFIG_DEBUG_PESSIMIZE is not set
+# CONFIG_UNIT_TEST is not set
# CONFIG_WERROR is not set
CONFIG_NO_DEBUG_LIB=y
# CONFIG_DMALLOC is not set
@@ -92,10 +96,10 @@ CONFIG_PREFIX="./_install"
#
# Busybox Library Tuning
#
-# CONFIG_FEATURE_SYSTEMD is not set
# CONFIG_FEATURE_RTMINMAX is not set
CONFIG_PASSWORD_MINLEN=6
CONFIG_MD5_SMALL=1
+CONFIG_SHA3_SMALL=1
# CONFIG_FEATURE_FAST_TOP is not set
# CONFIG_FEATURE_ETC_NETWORKS is not set
CONFIG_FEATURE_USE_TERMIOS=y
@@ -133,22 +137,28 @@ CONFIG_FEATURE_SEAMLESS_Z=y
CONFIG_AR=y
CONFIG_FEATURE_AR_LONG_FILENAMES=y
CONFIG_FEATURE_AR_CREATE=y
+CONFIG_UNCOMPRESS=y
+CONFIG_GUNZIP=y
CONFIG_BUNZIP2=y
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_LZMA=y
+CONFIG_UNXZ=y
+CONFIG_XZ=y
CONFIG_BZIP2=y
CONFIG_CPIO=y
CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
CONFIG_DPKG=y
CONFIG_DPKG_DEB=y
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
-CONFIG_GUNZIP=y
CONFIG_GZIP=y
# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set
CONFIG_GZIP_FAST=0
+# CONFIG_FEATURE_GZIP_LEVELS is not set
CONFIG_LZOP=y
CONFIG_LZOP_COMPR_HIGH=y
-CONFIG_RPM2CPIO=y
CONFIG_RPM=y
+CONFIG_RPM2CPIO=y
CONFIG_TAR=y
CONFIG_FEATURE_TAR_CREATE=y
CONFIG_FEATURE_TAR_AUTODETECT=y
@@ -161,12 +171,6 @@ CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
CONFIG_FEATURE_TAR_UNAME_GNAME=y
CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
# CONFIG_FEATURE_TAR_SELINUX is not set
-CONFIG_UNCOMPRESS=y
-CONFIG_UNLZMA=y
-CONFIG_FEATURE_LZMA_FAST=y
-CONFIG_LZMA=y
-CONFIG_UNXZ=y
-CONFIG_XZ=y
CONFIG_UNZIP=y
#
@@ -178,16 +182,27 @@ CONFIG_CAT=y
# CONFIG_FEATURE_DATE_ISOFMT is not set
# CONFIG_FEATURE_DATE_NANO is not set
# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_DD=y
+CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
+CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
+CONFIG_FEATURE_DD_IBS_OBS=y
+CONFIG_FEATURE_DD_STATUS=y
# CONFIG_HOSTID is not set
# CONFIG_ID is not set
# CONFIG_GROUPS is not set
+CONFIG_SHUF=y
+CONFIG_SYNC=y
+# CONFIG_FEATURE_SYNC_FANCY is not set
CONFIG_TEST=y
CONFIG_FEATURE_TEST_64=y
CONFIG_TOUCH=y
+# CONFIG_FEATURE_TOUCH_NODEREF is not set
CONFIG_FEATURE_TOUCH_SUSV3=y
CONFIG_TR=y
CONFIG_FEATURE_TR_CLASSES=y
CONFIG_FEATURE_TR_EQUIV=y
+CONFIG_TRUNCATE=y
+CONFIG_UNLINK=y
CONFIG_BASE64=y
# CONFIG_WHO is not set
# CONFIG_USERS is not set
@@ -203,10 +218,6 @@ CONFIG_COMM=y
CONFIG_CP=y
# CONFIG_FEATURE_CP_LONG_OPTIONS is not set
CONFIG_CUT=y
-CONFIG_DD=y
-CONFIG_FEATURE_DD_SIGNAL_HANDLING=y
-CONFIG_FEATURE_DD_THIRD_STATUS_LINE=y
-CONFIG_FEATURE_DD_IBS_OBS=y
# CONFIG_DF is not set
# CONFIG_FEATURE_DF_FANCY is not set
CONFIG_DIRNAME=y
@@ -263,6 +274,7 @@ CONFIG_SEQ=y
CONFIG_SHA1SUM=y
CONFIG_SHA256SUM=y
CONFIG_SHA512SUM=y
+CONFIG_SHA3SUM=y
CONFIG_SLEEP=y
CONFIG_FEATURE_FANCY_SLEEP=y
CONFIG_FEATURE_FLOAT_SLEEP=y
@@ -274,7 +286,6 @@ CONFIG_FEATURE_SPLIT_FANCY=y
# CONFIG_FEATURE_STAT_FORMAT is not set
CONFIG_STTY=y
CONFIG_SUM=y
-CONFIG_SYNC=y
CONFIG_TAC=y
CONFIG_TAIL=y
CONFIG_FEATURE_FANCY_TAIL=y
@@ -283,6 +294,7 @@ CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
CONFIG_TRUE=y
# CONFIG_TTY is not set
CONFIG_UNAME=y
+CONFIG_UNAME_OSNAME="GNU/Linux"
CONFIG_UNEXPAND=y
# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
CONFIG_UNIQ=y
@@ -295,6 +307,11 @@ CONFIG_WHOAMI=y
CONFIG_YES=y
#
+# Common options
+#
+CONFIG_FEATURE_VERBOSE=y
+
+#
# Common options for cp and mv
#
CONFIG_FEATURE_PRESERVE_HARDLINKS=y
@@ -310,7 +327,7 @@ CONFIG_FEATURE_AUTOWIDTH=y
CONFIG_FEATURE_HUMAN_READABLE=y
#
-# Common options for md5sum, sha1sum, sha256sum, sha512sum
+# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
#
CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
@@ -356,7 +373,16 @@ CONFIG_WHICH=y
#
# Editors
#
+CONFIG_AWK=y
+CONFIG_FEATURE_AWK_LIBM=y
+CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
+CONFIG_CMP=y
+CONFIG_DIFF=y
+# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
+CONFIG_FEATURE_DIFF_DIR=y
+CONFIG_ED=y
CONFIG_PATCH=y
+CONFIG_SED=y
CONFIG_VI=y
CONFIG_FEATURE_VI_MAX_LEN=4096
CONFIG_FEATURE_VI_8BIT=y
@@ -371,14 +397,9 @@ CONFIG_FEATURE_VI_SETOPTS=y
CONFIG_FEATURE_VI_SET=y
CONFIG_FEATURE_VI_WIN_RESIZE=y
CONFIG_FEATURE_VI_ASK_TERMINAL=y
-CONFIG_AWK=y
-CONFIG_FEATURE_AWK_LIBM=y
-CONFIG_CMP=y
-CONFIG_DIFF=y
-# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set
-CONFIG_FEATURE_DIFF_DIR=y
-CONFIG_ED=y
-CONFIG_SED=y
+CONFIG_FEATURE_VI_UNDO=y
+CONFIG_FEATURE_VI_UNDO_QUEUE=y
+CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
CONFIG_FEATURE_ALLOW_EXEC=y
#
@@ -395,6 +416,7 @@ CONFIG_FEATURE_FIND_MAXDEPTH=y
CONFIG_FEATURE_FIND_NEWER=y
CONFIG_FEATURE_FIND_INUM=y
CONFIG_FEATURE_FIND_EXEC=y
+CONFIG_FEATURE_FIND_EXEC_PLUS=y
CONFIG_FEATURE_FIND_USER=y
CONFIG_FEATURE_FIND_GROUP=y
CONFIG_FEATURE_FIND_NOT=y
@@ -416,6 +438,7 @@ CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
+CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
#
# Init Utilities
@@ -434,7 +457,7 @@ CONFIG_FEATURE_INIT_SCTTY=y
CONFIG_FEATURE_INIT_SYSLOG=y
CONFIG_FEATURE_EXTRA_QUIET=y
CONFIG_FEATURE_INIT_COREDUMPS=y
-CONFIG_FEATURE_INITRD=y
+CONFIG_LINUXRC=y
CONFIG_INIT_TERMINAL_TYPE="linux"
CONFIG_MESG=y
CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
@@ -447,11 +470,12 @@ CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
# CONFIG_FEATURE_SHADOWPASSWDS is not set
# CONFIG_USE_BB_PWD_GRP is not set
# CONFIG_USE_BB_SHADOW is not set
-# CONFIG_USE_BB_CRYPT is not set
-# CONFIG_USE_BB_CRYPT_SHA is not set
+CONFIG_USE_BB_CRYPT=y
+CONFIG_USE_BB_CRYPT_SHA=y
# CONFIG_ADDUSER is not set
# CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set
# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_LAST_ID=0
CONFIG_FIRST_SYSTEM_ID=0
CONFIG_LAST_SYSTEM_ID=0
# CONFIG_ADDGROUP is not set
@@ -463,7 +487,6 @@ CONFIG_LAST_SYSTEM_ID=0
# CONFIG_GETTY is not set
# CONFIG_LOGIN is not set
# CONFIG_LOGIN_SESSION_AS_CHILD is not set
-# CONFIG_PAM is not set
# CONFIG_LOGIN_SCRIPTS is not set
# CONFIG_FEATURE_NOLOGIN is not set
# CONFIG_FEATURE_SECURETTY is not set
@@ -514,20 +537,33 @@ CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED=y
# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
# CONFIG_FEATURE_MODUTILS_ALIAS is not set
# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
-CONFIG_DEFAULT_MODULES_DIR="/system/lib/modules"
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
#
# Linux System Utilities
#
CONFIG_BLOCKDEV=y
-CONFIG_MDEV=y
-CONFIG_FEATURE_MDEV_CONF=y
-CONFIG_FEATURE_MDEV_RENAME=y
-CONFIG_FEATURE_MDEV_RENAME_REGEXP=y
-CONFIG_FEATURE_MDEV_EXEC=y
-CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_FATATTR=y
+CONFIG_FSTRIM=y
+# CONFIG_MDEV is not set
+# CONFIG_FEATURE_MDEV_CONF is not set
+# CONFIG_FEATURE_MDEV_RENAME is not set
+# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
+# CONFIG_FEATURE_MDEV_EXEC is not set
+# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
+# CONFIG_MOUNT is not set
+# CONFIG_FEATURE_MOUNT_FAKE is not set
+# CONFIG_FEATURE_MOUNT_VERBOSE is not set
+# CONFIG_FEATURE_MOUNT_HELPERS is not set
+# CONFIG_FEATURE_MOUNT_LABEL is not set
+# CONFIG_FEATURE_MOUNT_NFS is not set
+# CONFIG_FEATURE_MOUNT_CIFS is not set
+# CONFIG_FEATURE_MOUNT_FLAGS is not set
+# CONFIG_FEATURE_MOUNT_FSTAB is not set
+# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
CONFIG_REV=y
+CONFIG_UEVENT=y
# CONFIG_ACPID is not set
# CONFIG_FEATURE_ACPID_COMPAT is not set
CONFIG_BLKID=y
@@ -573,15 +609,6 @@ CONFIG_LSUSB=y
CONFIG_MKSWAP=y
CONFIG_FEATURE_MKSWAP_UUID=y
CONFIG_MORE=y
-# CONFIG_MOUNT is not set
-# CONFIG_FEATURE_MOUNT_FAKE is not set
-# CONFIG_FEATURE_MOUNT_VERBOSE is not set
-# CONFIG_FEATURE_MOUNT_HELPERS is not set
-# CONFIG_FEATURE_MOUNT_LABEL is not set
-# CONFIG_FEATURE_MOUNT_NFS is not set
-# CONFIG_FEATURE_MOUNT_CIFS is not set
-# CONFIG_FEATURE_MOUNT_FLAGS is not set
-# CONFIG_FEATURE_MOUNT_FSTAB is not set
# CONFIG_PIVOT_ROOT is not set
# CONFIG_RDATE is not set
CONFIG_RDEV=y
@@ -591,6 +618,7 @@ CONFIG_SCRIPT=y
CONFIG_SCRIPTREPLAY=y
# CONFIG_SETARCH is not set
# CONFIG_SWAPONOFF is not set
+# CONFIG_FEATURE_SWAPON_DISCARD is not set
# CONFIG_FEATURE_SWAPON_PRI is not set
CONFIG_SWITCH_ROOT=y
# CONFIG_UMOUNT is not set
@@ -603,32 +631,45 @@ CONFIG_VOLUMEID=y
#
# Filesystem/Volume identification
#
-CONFIG_FEATURE_VOLUMEID_EXT=y
CONFIG_FEATURE_VOLUMEID_BTRFS=y
-CONFIG_FEATURE_VOLUMEID_REISERFS=y
+CONFIG_FEATURE_VOLUMEID_CRAMFS=y
+CONFIG_FEATURE_VOLUMEID_EXFAT=y
+CONFIG_FEATURE_VOLUMEID_EXT=y
+CONFIG_FEATURE_VOLUMEID_F2FS=y
CONFIG_FEATURE_VOLUMEID_FAT=y
CONFIG_FEATURE_VOLUMEID_HFS=y
-CONFIG_FEATURE_VOLUMEID_JFS=y
-CONFIG_FEATURE_VOLUMEID_XFS=y
-CONFIG_FEATURE_VOLUMEID_NTFS=y
CONFIG_FEATURE_VOLUMEID_ISO9660=y
-CONFIG_FEATURE_VOLUMEID_UDF=y
-CONFIG_FEATURE_VOLUMEID_LUKS=y
+CONFIG_FEATURE_VOLUMEID_JFS=y
+CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
CONFIG_FEATURE_VOLUMEID_LINUXSWAP=y
-CONFIG_FEATURE_VOLUMEID_CRAMFS=y
+CONFIG_FEATURE_VOLUMEID_LUKS=y
+CONFIG_FEATURE_VOLUMEID_NILFS=y
+CONFIG_FEATURE_VOLUMEID_NTFS=y
+CONFIG_FEATURE_VOLUMEID_OCFS2=y
+CONFIG_FEATURE_VOLUMEID_REISERFS=y
CONFIG_FEATURE_VOLUMEID_ROMFS=y
+CONFIG_FEATURE_VOLUMEID_SQUASHFS=y
CONFIG_FEATURE_VOLUMEID_SYSV=y
-CONFIG_FEATURE_VOLUMEID_OCFS2=y
-CONFIG_FEATURE_VOLUMEID_LINUXRAID=y
+CONFIG_FEATURE_VOLUMEID_UDF=y
+CONFIG_FEATURE_VOLUMEID_XFS=y
#
# Miscellaneous Utilities
#
# CONFIG_CONSPY is not set
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_I2CGET=y
+CONFIG_I2CSET=y
+CONFIG_I2CDUMP=y
+CONFIG_I2CDETECT=y
CONFIG_LESS=y
CONFIG_FEATURE_LESS_MAXLINES=9999999
CONFIG_FEATURE_LESS_BRACKETS=y
CONFIG_FEATURE_LESS_FLAGS=y
+CONFIG_FEATURE_LESS_TRUNCATE=y
CONFIG_FEATURE_LESS_MARKS=y
CONFIG_FEATURE_LESS_REGEXP=y
CONFIG_FEATURE_LESS_WINCH=y
@@ -637,13 +678,17 @@ CONFIG_FEATURE_LESS_DASHCMD=y
CONFIG_FEATURE_LESS_LINENUMS=y
# CONFIG_NANDWRITE is not set
CONFIG_NANDDUMP=y
+# CONFIG_RFKILL is not set
CONFIG_SETSERIAL=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
# CONFIG_UBIATTACH is not set
# CONFIG_UBIDETACH is not set
# CONFIG_UBIMKVOL is not set
# CONFIG_UBIRMVOL is not set
# CONFIG_UBIRSVOL is not set
# CONFIG_UBIUPDATEVOL is not set
+# CONFIG_WALL is not set
# CONFIG_ADJTIMEX is not set
CONFIG_BBCONFIG=y
CONFIG_FEATURE_COMPRESS_BBCONFIG=y
@@ -659,10 +704,6 @@ CONFIG_FEATURE_CHAT_SEND_ESCAPES=y
CONFIG_FEATURE_CHAT_VAR_ABORT_LEN=y
CONFIG_FEATURE_CHAT_CLR_ABORT=y
CONFIG_CHRT=y
-CONFIG_CROND=y
-CONFIG_FEATURE_CROND_D=y
-CONFIG_FEATURE_CROND_CALL_SENDMAIL=y
-CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
CONFIG_CRONTAB=y
CONFIG_DC=y
CONFIG_FEATURE_DC_LIBM=y
@@ -700,18 +741,14 @@ CONFIG_MAN=y
# CONFIG_MT is not set
CONFIG_RAIDAUTORUN=y
# CONFIG_READAHEAD is not set
-# CONFIG_RFKILL is not set
# CONFIG_RUNLEVEL is not set
CONFIG_RX=y
CONFIG_SETSID=y
CONFIG_STRINGS=y
-# CONFIG_TASKSET is not set
-# CONFIG_FEATURE_TASKSET_FANCY is not set
CONFIG_TIME=y
CONFIG_TIMEOUT=y
CONFIG_TTYSIZE=y
CONFIG_VOLNAME=y
-# CONFIG_WALL is not set
# CONFIG_WATCHDOG is not set
#
@@ -743,13 +780,13 @@ CONFIG_FAKEIDENTD=y
CONFIG_FTPD=y
CONFIG_FEATURE_FTP_WRITE=y
CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST=y
+CONFIG_FEATURE_FTP_AUTHENTICATION=y
CONFIG_FTPGET=y
CONFIG_FTPPUT=y
# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set
# CONFIG_HOSTNAME is not set
CONFIG_HTTPD=y
CONFIG_FEATURE_HTTPD_RANGES=y
-CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
CONFIG_FEATURE_HTTPD_SETUID=y
CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
@@ -806,6 +843,7 @@ CONFIG_FEATURE_NETSTAT_PRG=y
# CONFIG_NSLOOKUP is not set
# CONFIG_NTPD is not set
# CONFIG_FEATURE_NTPD_SERVER is not set
+# CONFIG_FEATURE_NTPD_CONF is not set
CONFIG_PSCAN=y
CONFIG_ROUTE=y
# CONFIG_SLATTACH is not set
@@ -830,7 +868,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
CONFIG_TRACEROUTE=y
# CONFIG_TRACEROUTE6 is not set
CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
CONFIG_TUNCTL=y
CONFIG_FEATURE_TUNCTL_UG=y
@@ -843,6 +880,7 @@ CONFIG_FEATURE_TUNCTL_UG=y
CONFIG_DHCPD_LEASES_FILE=""
CONFIG_UDHCPC=y
CONFIG_FEATURE_UDHCPC_ARPING=y
+CONFIG_FEATURE_UDHCPC_SANITIZEOPT=y
CONFIG_FEATURE_UDHCP_PORT=y
CONFIG_UDHCP_DEBUG=9
CONFIG_FEATURE_UDHCP_RFC3397=y
@@ -889,6 +927,13 @@ CONFIG_POWERTOP=y
CONFIG_PSTREE=y
CONFIG_PWDX=y
CONFIG_SMEMCAP=y
+CONFIG_TOP=y
+CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
+CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
+CONFIG_FEATURE_TOP_SMP_CPU=y
+CONFIG_FEATURE_TOP_DECIMALS=y
+CONFIG_FEATURE_TOP_SMP_PROCESS=y
+CONFIG_FEATURE_TOPMEM=y
CONFIG_UPTIME=y
# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
CONFIG_FREE=y
@@ -909,13 +954,6 @@ CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
CONFIG_RENICE=y
CONFIG_BB_SYSCTL=y
-CONFIG_TOP=y
-CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
-CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
-CONFIG_FEATURE_TOP_SMP_CPU=y
-CONFIG_FEATURE_TOP_DECIMALS=y
-CONFIG_FEATURE_TOP_SMP_PROCESS=y
-CONFIG_FEATURE_TOPMEM=y
CONFIG_FEATURE_SHOW_THREADS=y
CONFIG_WATCH=y
@@ -961,6 +999,7 @@ CONFIG_SOFTLIMIT=y
# CONFIG_ASH_BUILTIN_ECHO is not set
# CONFIG_ASH_BUILTIN_PRINTF is not set
# CONFIG_ASH_BUILTIN_TEST is not set
+# CONFIG_ASH_HELP is not set
# CONFIG_ASH_CMDCMD is not set
# CONFIG_ASH_MAIL is not set
# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
@@ -1010,6 +1049,7 @@ CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
# CONFIG_LOGREAD is not set
# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
+# CONFIG_FEATURE_KMSG_SYSLOG is not set
CONFIG_KLOGD=y
CONFIG_FEATURE_KLOGD_KLOGCTL=y
# CONFIG_LOGGER is not set
diff --git a/configs/cygwin_defconfig b/configs/cygwin_defconfig
index aa346e3..b856482 100644
--- a/configs/cygwin_defconfig
+++ b/configs/cygwin_defconfig
@@ -89,7 +89,6 @@ CONFIG_PREFIX="./_install"
#
# Busybox Library Tuning
#
-# CONFIG_FEATURE_SYSTEMD is not set
CONFIG_FEATURE_RTMINMAX=y
CONFIG_PASSWORD_MINLEN=6
CONFIG_MD5_SMALL=1
@@ -135,7 +134,6 @@ CONFIG_FEATURE_CPIO_O=y
CONFIG_FEATURE_CPIO_P=y
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GUNZIP=y
CONFIG_GZIP=y
CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
@@ -426,7 +424,7 @@ CONFIG_FEATURE_KILL_DELAY=0
# CONFIG_FEATURE_INIT_SYSLOG is not set
# CONFIG_FEATURE_EXTRA_QUIET is not set
# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
+# CONFIG_LINUXRC is not set
CONFIG_INIT_TERMINAL_TYPE=""
CONFIG_MESG=y
CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP=y
@@ -815,7 +813,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
# CONFIG_TRACEROUTE is not set
# CONFIG_TRACEROUTE6 is not set
# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
# CONFIG_TUNCTL is not set
# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/configs/freebsd_defconfig b/configs/freebsd_defconfig
index ec3ed03..4f14d3b 100644
--- a/configs/freebsd_defconfig
+++ b/configs/freebsd_defconfig
@@ -132,7 +132,6 @@ CONFIG_CPIO=y
# CONFIG_FEATURE_CPIO_P is not set
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
-# CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set
CONFIG_GUNZIP=y
CONFIG_GZIP=y
CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
@@ -422,7 +421,7 @@ CONFIG_FEATURE_KILL_DELAY=0
# CONFIG_FEATURE_INIT_SYSLOG is not set
# CONFIG_FEATURE_EXTRA_QUIET is not set
# CONFIG_FEATURE_INIT_COREDUMPS is not set
-# CONFIG_FEATURE_INITRD is not set
+# CONFIG_LINUXRC is not set
CONFIG_INIT_TERMINAL_TYPE=""
# CONFIG_MESG is not set
@@ -795,7 +794,6 @@ CONFIG_FEATURE_TFTP_PROGRESS_BAR=y
# CONFIG_TRACEROUTE is not set
# CONFIG_TRACEROUTE6 is not set
# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
-# CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE is not set
# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
# CONFIG_TUNCTL is not set
# CONFIG_FEATURE_TUNCTL_UG is not set
diff --git a/console-tools/Config.src b/console-tools/Config.src
index c657044..e6587ad 100644
--- a/console-tools/Config.src
+++ b/console-tools/Config.src
@@ -7,170 +7,4 @@ menu "Console Utilities"
INSERT
-config CHVT
- bool "chvt"
- default y
- select PLATFORM_LINUX
- help
- This program is used to change to another terminal.
- Example: chvt 4 (change to terminal /dev/tty4)
-
-config FGCONSOLE
- bool "fgconsole"
- default y
- select PLATFORM_LINUX
- help
- This program prints active (foreground) console number.
-
-config CLEAR
- bool "clear"
- default y
- help
- This program clears the terminal screen.
-
-config DEALLOCVT
- bool "deallocvt"
- default y
- select PLATFORM_LINUX
- help
- This program deallocates unused virtual consoles.
-
-config DUMPKMAP
- bool "dumpkmap"
- default y
- select PLATFORM_LINUX
- help
- This program dumps the kernel's keyboard translation table to
- stdout, in binary format. You can then use loadkmap to load it.
-
-config KBD_MODE
- bool "kbd_mode"
- default y
- select PLATFORM_LINUX
- help
- This program reports and sets keyboard mode.
-
-config LOADFONT
- bool "loadfont"
- default y
- select PLATFORM_LINUX
- help
- This program loads a console font from standard input.
-
-config LOADKMAP
- bool "loadkmap"
- default y
- select PLATFORM_LINUX
- help
- This program loads a keyboard translation table from
- standard input.
-
-config OPENVT
- bool "openvt"
- default y
- select PLATFORM_LINUX
- help
- This program is used to start a command on an unused
- virtual terminal.
-
-config RESET
- bool "reset"
- default y
- help
- This program is used to reset the terminal screen, if it
- gets messed up.
-
-config RESIZE
- bool "resize"
- default y
- help
- This program is used to (re)set the width and height of your current
- terminal.
-
-config FEATURE_RESIZE_PRINT
- bool "Print environment variables"
- default y
- depends on RESIZE
- help
- Prints the newly set size (number of columns and rows) of
- the terminal.
- E.g.:
- COLUMNS=80;LINES=44;export COLUMNS LINES;
-
-config SETCONSOLE
- bool "setconsole"
- default y
- select PLATFORM_LINUX
- help
- This program redirects the system console to another device,
- like the current tty while logged in via telnet.
-
-config FEATURE_SETCONSOLE_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on SETCONSOLE && LONG_OPTS
- help
- Support long options for the setconsole applet.
-
-config SETFONT
- bool "setfont"
- default y
- select PLATFORM_LINUX
- help
- Allows to load console screen map. Useful for i18n.
-
-config FEATURE_SETFONT_TEXTUAL_MAP
- bool "Support reading textual screen maps"
- default y
- depends on SETFONT
- help
- Support reading textual screen maps.
-
-config DEFAULT_SETFONT_DIR
- string "Default directory for console-tools files"
- default ""
- depends on SETFONT
- help
- Directory to use if setfont's params are simple filenames
- (not /path/to/file or ./file). Default is "" (no default directory).
-
-config SETKEYCODES
- bool "setkeycodes"
- default y
- select PLATFORM_LINUX
- help
- This program loads entries into the kernel's scancode-to-keycode
- map, allowing unusual keyboards to generate usable keycodes.
-
-config SETLOGCONS
- bool "setlogcons"
- default y
- select PLATFORM_LINUX
- help
- This program redirects the output console of kernel messages.
-
-config SHOWKEY
- bool "showkey"
- default y
- select PLATFORM_LINUX
- help
- Shows keys pressed.
-
-comment "Common options for loadfont and setfont"
- depends on LOADFONT || SETFONT
-
-config FEATURE_LOADFONT_PSF2
- bool "Support for PSF2 console fonts"
- default y
- depends on LOADFONT || SETFONT
- help
- Support PSF2 console fonts.
-
-config FEATURE_LOADFONT_RAW
- bool "Support for old (raw) console fonts"
- default y
- depends on LOADFONT || SETFONT
- help
- Support old (raw) console fonts.
-
endmenu
diff --git a/console-tools/Kbuild.src b/console-tools/Kbuild.src
index 94de9ad..6b4fb74 100644
--- a/console-tools/Kbuild.src
+++ b/console-tools/Kbuild.src
@@ -7,19 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_CHVT) += chvt.o
-lib-$(CONFIG_FGCONSOLE) += fgconsole.o
-lib-$(CONFIG_CLEAR) += clear.o
-lib-$(CONFIG_DEALLOCVT) += deallocvt.o
-lib-$(CONFIG_DUMPKMAP) += dumpkmap.o
-lib-$(CONFIG_SETCONSOLE) += setconsole.o
-lib-$(CONFIG_KBD_MODE) += kbd_mode.o
-lib-$(CONFIG_LOADFONT) += loadfont.o
-lib-$(CONFIG_LOADKMAP) += loadkmap.o
-lib-$(CONFIG_OPENVT) += openvt.o
-lib-$(CONFIG_RESET) += reset.o
-lib-$(CONFIG_RESIZE) += resize.o
-lib-$(CONFIG_SETFONT) += loadfont.o
-lib-$(CONFIG_SETKEYCODES) += setkeycodes.o
-lib-$(CONFIG_SETLOGCONS) += setlogcons.o
-lib-$(CONFIG_SHOWKEY) += showkey.o
diff --git a/console-tools/chvt.c b/console-tools/chvt.c
index b9c974f..2b993eb 100644
--- a/console-tools/chvt.c
+++ b/console-tools/chvt.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHVT
+//config: bool "chvt"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program is used to change to another terminal.
+//config: Example: chvt 4 (change to terminal /dev/tty4)
+
+//applet:IF_CHVT(APPLET(chvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHVT) += chvt.o
//usage:#define chvt_trivial_usage
//usage: "N"
diff --git a/console-tools/clear.c b/console-tools/clear.c
index ac22b78..b360d34 100644
--- a/console-tools/clear.c
+++ b/console-tools/clear.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CLEAR
+//config: bool "clear"
+//config: default y
+//config: help
+//config: This program clears the terminal screen.
+
+//applet:IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CLEAR) += clear.o
//usage:#define clear_trivial_usage
//usage: ""
diff --git a/console-tools/deallocvt.c b/console-tools/deallocvt.c
index b131c0a..37c966a 100644
--- a/console-tools/deallocvt.c
+++ b/console-tools/deallocvt.c
@@ -7,8 +7,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config DEALLOCVT
+//config: bool "deallocvt"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program deallocates unused virtual consoles.
-/* no options, no getopt */
+//applet:IF_DEALLOCVT(APPLET(deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DEALLOCVT) += deallocvt.o
//usage:#define deallocvt_trivial_usage
//usage: "[N]"
diff --git a/console-tools/dumpkmap.c b/console-tools/dumpkmap.c
index bf8d690..4a24986 100644
--- a/console-tools/dumpkmap.c
+++ b/console-tools/dumpkmap.c
@@ -7,7 +7,17 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
*/
-/* no options, no getopt */
+//config:config DUMPKMAP
+//config: bool "dumpkmap"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program dumps the kernel's keyboard translation table to
+//config: stdout, in binary format. You can then use loadkmap to load it.
+
+//applet:IF_DUMPKMAP(APPLET(dumpkmap, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DUMPKMAP) += dumpkmap.o
//usage:#define dumpkmap_trivial_usage
//usage: "> keymap"
@@ -18,6 +28,7 @@
//usage: "$ dumpkmap > keymap\n"
#include "libbb.h"
+#include "common_bufsiz.h"
/* From <linux/kd.h> */
struct kbentry {
@@ -37,6 +48,7 @@ int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
struct kbentry ke;
int i, j, fd;
#define flags bb_common_bufsiz1
+ setup_common_bufsiz();
/* When user accidentally runs "dumpkmap FILE"
* instead of "dumpkmap >FILE", we'd dump binary stuff to tty.
diff --git a/console-tools/fgconsole.c b/console-tools/fgconsole.c
index 54355be..0197617 100644
--- a/console-tools/fgconsole.c
+++ b/console-tools/fgconsole.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FGCONSOLE
+//config: bool "fgconsole"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program prints active (foreground) console number.
+
+//applet:IF_FGCONSOLE(APPLET(fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FGCONSOLE) += fgconsole.o
//usage:#define fgconsole_trivial_usage
//usage: ""
diff --git a/console-tools/kbd_mode.c b/console-tools/kbd_mode.c
index 1385367..f1238c6 100644
--- a/console-tools/kbd_mode.c
+++ b/console-tools/kbd_mode.c
@@ -8,6 +8,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config KBD_MODE
+//config: bool "kbd_mode"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program reports and sets keyboard mode.
+
+//applet:IF_KBD_MODE(APPLET(kbd_mode, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_KBD_MODE) += kbd_mode.o
//usage:#define kbd_mode_trivial_usage
//usage: "[-a|k|s|u] [-C TTY]"
diff --git a/console-tools/loadfont.c b/console-tools/loadfont.c
index 032506d..52605ba 100644
--- a/console-tools/loadfont.c
+++ b/console-tools/loadfont.c
@@ -9,6 +9,57 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config LOADFONT
+//config: bool "loadfont"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program loads a console font from standard input.
+//config:
+//config:config SETFONT
+//config: bool "setfont"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Allows to load console screen map. Useful for i18n.
+//config:
+//config:config FEATURE_SETFONT_TEXTUAL_MAP
+//config: bool "Support reading textual screen maps"
+//config: default y
+//config: depends on SETFONT
+//config: help
+//config: Support reading textual screen maps.
+//config:
+//config:config DEFAULT_SETFONT_DIR
+//config: string "Default directory for console-tools files"
+//config: default ""
+//config: depends on SETFONT
+//config: help
+//config: Directory to use if setfont's params are simple filenames
+//config: (not /path/to/file or ./file). Default is "" (no default directory).
+//config:
+//config:comment "Common options for loadfont and setfont"
+//config: depends on LOADFONT || SETFONT
+//config:
+//config:config FEATURE_LOADFONT_PSF2
+//config: bool "Support for PSF2 console fonts"
+//config: default y
+//config: depends on LOADFONT || SETFONT
+//config: help
+//config: Support PSF2 console fonts.
+//config:
+//config:config FEATURE_LOADFONT_RAW
+//config: bool "Support for old (raw) console fonts"
+//config: default y
+//config: depends on LOADFONT || SETFONT
+//config: help
+//config: Support old (raw) console fonts.
+
+//applet:IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LOADFONT) += loadfont.o
+//kbuild:lib-$(CONFIG_SETFONT) += loadfont.o
//usage:#define loadfont_trivial_usage
//usage: "< font"
@@ -319,8 +370,10 @@ int loadfont_main(int argc UNUSED_PARAM, char **argv)
* We used to look at the length of the input file
* with stat(); now that we accept compressed files,
* just read the entire file.
+ * Len was 32k, but latarcyrheb-sun32.psfu is 34377 bytes
+ * (it has largish Unicode map).
*/
- len = 32*1024; // can't be larger
+ len = 128*1024;
buffer = xmalloc_read(STDIN_FILENO, &len);
// xmalloc_open_zipped_read_close(filename, &len);
if (!buffer)
@@ -405,7 +458,7 @@ int setfont_main(int argc UNUSED_PARAM, char **argv)
}
}
// load font
- len = 32*1024; // can't be larger
+ len = 128*1024;
buffer = xmalloc_open_zipped_read_close(*argv, &len);
if (!buffer)
bb_simple_perror_msg_and_die(*argv);
diff --git a/console-tools/loadkmap.c b/console-tools/loadkmap.c
index 66ec3b0..70ab55a 100644
--- a/console-tools/loadkmap.c
+++ b/console-tools/loadkmap.c
@@ -6,12 +6,24 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LOADKMAP
+//config: bool "loadkmap"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program loads a keyboard translation table from
+//config: standard input.
+
+//applet:IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LOADKMAP) += loadkmap.o
//usage:#define loadkmap_trivial_usage
//usage: "< keymap"
//usage:#define loadkmap_full_usage "\n\n"
-//usage: "Load a binary keyboard translation table from stdin\n"
-/* //usage: "\n -C TTY Affect TTY instead of /dev/tty" */
+//usage: "Load a binary keyboard translation table from stdin"
+////usage: "\n"
+////usage: "\n -C TTY Affect TTY instead of /dev/tty"
//usage:
//usage:#define loadkmap_example_usage
//usage: "$ loadkmap < /etc/i18n/lang-keymap\n"
@@ -56,7 +68,7 @@ int loadkmap_main(int argc UNUSED_PARAM, char **argv)
*/
xread(STDIN_FILENO, flags, 7);
- if (strncmp(flags, BINARY_KEYMAP_MAGIC, 7))
+ if (!is_prefixed_with(flags, BINARY_KEYMAP_MAGIC))
bb_error_msg_and_die("not a valid binary keymap");
xread(STDIN_FILENO, flags, MAX_NR_KEYMAPS);
diff --git a/console-tools/openvt.c b/console-tools/openvt.c
index e523566..5cbc717 100644
--- a/console-tools/openvt.c
+++ b/console-tools/openvt.c
@@ -7,6 +7,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config OPENVT
+//config: bool "openvt"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program is used to start a command on an unused
+//config: virtual terminal.
+
+//applet:IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_OPENVT) += openvt.o
//usage:#define openvt_trivial_usage
//usage: "[-c N] [-sw] [PROG ARGS]"
diff --git a/console-tools/reset.c b/console-tools/reset.c
index 65940bd..57cebb4 100644
--- a/console-tools/reset.c
+++ b/console-tools/reset.c
@@ -7,9 +7,18 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+/* "Standard" version of this tool is in ncurses package */
-/* BTW, which "standard" package has this utility? It doesn't seem
- * to be ncurses, coreutils, console-tools... then what? */
+//config:config RESET
+//config: bool "reset"
+//config: default y
+//config: help
+//config: This program is used to reset the terminal screen, if it
+//config: gets messed up.
+
+//applet:IF_RESET(APPLET(reset, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RESET) += reset.o
//usage:#define reset_trivial_usage
//usage: ""
diff --git a/console-tools/resize.c b/console-tools/resize.c
index 4b0d63a..567086f 100644
--- a/console-tools/resize.c
+++ b/console-tools/resize.c
@@ -6,7 +6,26 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-/* no options, no getopt */
+//config:config RESIZE
+//config: bool "resize"
+//config: default y
+//config: help
+//config: This program is used to (re)set the width and height of your current
+//config: terminal.
+//config:
+//config:config FEATURE_RESIZE_PRINT
+//config: bool "Print environment variables"
+//config: default y
+//config: depends on RESIZE
+//config: help
+//config: Prints the newly set size (number of columns and rows) of
+//config: the terminal.
+//config: E.g.:
+//config: COLUMNS=80;LINES=44;export COLUMNS LINES;
+
+//applet:IF_RESIZE(APPLET(resize, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RESIZE) += resize.o
//usage:#define resize_trivial_usage
//usage: ""
@@ -14,10 +33,12 @@
//usage: "Resize the screen"
#include "libbb.h"
+#include "common_bufsiz.h"
#define ESC "\033"
-#define old_termios_p ((struct termios*)&bb_common_bufsiz1)
+#define old_termios_p ((struct termios*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void
onintr(int sig UNUSED_PARAM)
@@ -33,6 +54,8 @@ int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
struct winsize w = { 0, 0, 0, 0 };
int ret;
+ INIT_G();
+
/* We use _stderr_ in order to make resize usable
* in shell backticks (those redirect stdout away from tty).
* NB: other versions of resize open "/dev/tty"
diff --git a/console-tools/setconsole.c b/console-tools/setconsole.c
index c0051dc..5805726 100644
--- a/console-tools/setconsole.c
+++ b/console-tools/setconsole.c
@@ -7,6 +7,24 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SETCONSOLE
+//config: bool "setconsole"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program redirects the system console to another device,
+//config: like the current tty while logged in via telnet.
+//config:
+//config:config FEATURE_SETCONSOLE_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on SETCONSOLE && LONG_OPTS
+//config: help
+//config: Support long options for the setconsole applet.
+
+//applet:IF_SETCONSOLE(APPLET(setconsole, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETCONSOLE) += setconsole.o
//usage:#define setconsole_trivial_usage
//usage: "[-r" IF_FEATURE_SETCONSOLE_LONG_OPTIONS("|--reset") "] [DEVICE]"
diff --git a/console-tools/setkeycodes.c b/console-tools/setkeycodes.c
index a6a7c23..11fc37a 100644
--- a/console-tools/setkeycodes.c
+++ b/console-tools/setkeycodes.c
@@ -8,6 +8,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SETKEYCODES
+//config: bool "setkeycodes"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program loads entries into the kernel's scancode-to-keycode
+//config: map, allowing unusual keyboards to generate usable keycodes.
+
+//applet:IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETKEYCODES) += setkeycodes.o
//usage:#define setkeycodes_trivial_usage
//usage: "SCANCODE KEYCODE..."
diff --git a/console-tools/setlogcons.c b/console-tools/setlogcons.c
index c76a5a4..2ea36f0 100644
--- a/console-tools/setlogcons.c
+++ b/console-tools/setlogcons.c
@@ -8,11 +8,21 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SETLOGCONS
+//config: bool "setlogcons"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This program redirects the output console of kernel messages.
+
+//applet:IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETLOGCONS) += setlogcons.o
//usage:#define setlogcons_trivial_usage
-//usage: "N"
+//usage: "[N]"
//usage:#define setlogcons_full_usage "\n\n"
-//usage: "Redirect the kernel output to console N (0 for current)"
+//usage: "Redirect the kernel output to console N. Default:0 (current console)"
#include "libbb.h"
diff --git a/console-tools/showkey.c b/console-tools/showkey.c
index 69b785e..c2447b8 100644
--- a/console-tools/showkey.c
+++ b/console-tools/showkey.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SHOWKEY
+//config: bool "showkey"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Shows keys pressed.
+
+//applet:IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SHOWKEY) += showkey.o
//usage:#define showkey_trivial_usage
//usage: "[-a | -k | -s]"
@@ -83,7 +93,6 @@ int showkey_main(int argc UNUSED_PARAM, char **argv)
if (04 /*CTRL-D*/ == c)
break;
}
-
} else {
// we assume a PC keyboard
xioctl(STDIN_FILENO, KDGKBMODE, &kbmode);
diff --git a/coreutils/Config.src b/coreutils/Config.src
index 0c44c4b..c056320 100644
--- a/coreutils/Config.src
+++ b/coreutils/Config.src
@@ -7,736 +7,15 @@ menu "Coreutils"
INSERT
-config CAL
- bool "cal"
- default y
- help
- cal is used to display a monthly calender.
-
-config CATV
- bool "catv"
- default y
- help
- Display nonprinting characters as escape sequences (like some
- implementations' cat -v option).
-
-config CHGRP
- bool "chgrp"
- default y
- help
- chgrp is used to change the group ownership of files.
-
-config CHMOD
- bool "chmod"
- default y
- help
- chmod is used to change the access permission of files.
-
-config CHOWN
- bool "chown"
- default y
- help
- chown is used to change the user and/or group ownership
- of files.
-
-config FEATURE_CHOWN_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on CHOWN && LONG_OPTS
- help
- Enable use of long options
-
-config CHROOT
- bool "chroot"
- default y
- help
- chroot is used to change the root directory and run a command.
- The default command is `/bin/sh'.
-
-config CKSUM
- bool "cksum"
- default y
- help
- cksum is used to calculate the CRC32 checksum of a file.
-
-config COMM
- bool "comm"
- default y
- help
- comm is used to compare two files line by line and return
- a three-column output.
-
-config CP
- bool "cp"
- default y
- help
- cp is used to copy files and directories.
-
-config FEATURE_CP_LONG_OPTIONS
- bool "Enable long options for cp"
- default y
- depends on CP && LONG_OPTS
- help
- Enable long options for cp.
- Also add support for --parents option.
-
-config CUT
- bool "cut"
- default y
- help
- cut is used to print selected parts of lines from
- each file to stdout.
-
-config DD
- bool "dd"
- default y
- help
- dd copies a file (from standard input to standard output,
- by default) using specific input and output blocksizes,
- while optionally performing conversions on it.
-
-config FEATURE_DD_SIGNAL_HANDLING
- bool "Enable DD signal handling for status reporting"
- default y
- depends on DD
- help
- Sending a SIGUSR1 signal to a running `dd' process makes it
- print to standard error the number of records read and written
- so far, then to resume copying.
-
- $ dd if=/dev/zero of=/dev/null&
- $ pid=$! kill -USR1 $pid; sleep 1; kill $pid
- 10899206+0 records in
- 10899206+0 records out
-
-config FEATURE_DD_THIRD_STATUS_LINE
- bool "Enable the third status line upon signal"
- default y
- depends on DD && FEATURE_DD_SIGNAL_HANDLING
- help
- Displays a coreutils-like third status line with transferred bytes,
- elapsed time and speed.
-
-config FEATURE_DD_IBS_OBS
- bool "Enable ibs, obs and conv options"
- default y
- depends on DD
- help
- Enables support for writing a certain number of bytes in and out,
- at a time, and performing conversions on the data stream.
-
-config DF
- bool "df"
- default y
- help
- df reports the amount of disk space used and available
- on filesystems.
-
-config FEATURE_DF_FANCY
- bool "Enable -a, -i, -B"
- default y
- depends on DF
- help
- This option enables -a, -i and -B.
-
- -a Show all filesystems
- -i Inodes
- -B <SIZE> Blocksize
-
-config DIRNAME
- bool "dirname"
- default y
- help
- dirname is used to strip a non-directory suffix from
- a file name.
-
-config DOS2UNIX
- bool "dos2unix/unix2dos"
- default y
- help
- dos2unix is used to convert a text file from DOS format to
- UNIX format, and vice versa.
-
-config UNIX2DOS
- bool
- default y
- depends on DOS2UNIX
- help
- unix2dos is used to convert a text file from UNIX format to
- DOS format, and vice versa.
-
-config DU
- bool "du (default blocksize of 512 bytes)"
- default y
- help
- du is used to report the amount of disk space used
- for specified files.
-
-config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
- bool "Use a default blocksize of 1024 bytes (1K)"
- default y
- depends on DU
- help
- Use a blocksize of (1K) instead of the default 512b.
-
-config ECHO
- bool "echo (basic SuSv3 version taking no options)"
- default y
- help
- echo is used to print a specified string to stdout.
-
-# this entry also appears in shell/Config.in, next to the echo builtin
-config FEATURE_FANCY_ECHO
- bool "Enable echo options (-n and -e)"
- default y
- depends on ECHO || ASH_BUILTIN_ECHO || HUSH
- help
- This adds options (-n and -e) to echo.
-
-config ENV
- bool "env"
- default y
- help
- env is used to set an environment variable and run
- a command; without options it displays the current
- environment.
-
-config FEATURE_ENV_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on ENV && LONG_OPTS
- help
- Support long options for the env applet.
-
-config EXPAND
- bool "expand"
- default y
- help
- By default, convert all tabs to spaces.
-
-config FEATURE_EXPAND_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on EXPAND && LONG_OPTS
- help
- Support long options for the expand applet.
-
-config EXPR
- bool "expr"
- default y
- help
- expr is used to calculate numbers and print the result
- to standard output.
-
-config EXPR_MATH_SUPPORT_64
- bool "Extend Posix numbers support to 64 bit"
- default y
- depends on EXPR
- help
- Enable 64-bit math support in the expr applet. This will make
- the applet slightly larger, but will allow computation with very
- large numbers.
-
-config FALSE
- bool "false"
- default y
- help
- false returns an exit code of FALSE (1).
-
-config FOLD
- bool "fold"
- default y
- help
- Wrap text to fit a specific width.
-
-config FSYNC
- bool "fsync"
- default y
- help
- fsync is used to flush file-related cached blocks to disk.
-
-config HEAD
- bool "head"
- default y
- help
- head is used to print the first specified number of lines
- from files.
-
-config FEATURE_FANCY_HEAD
- bool "Enable head options (-c, -q, and -v)"
- default y
- depends on HEAD
- help
- This enables the head options (-c, -q, and -v).
-
-config INSTALL
- bool "install"
- default y
- help
- Copy files and set attributes.
-
-config FEATURE_INSTALL_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on INSTALL && LONG_OPTS
- help
- Support long options for the install applet.
-
-####config LENGTH
-#### bool "length"
-#### default y
-#### help
-#### length is used to print out the length of a specified string.
-
-config LN
- bool "ln"
- default y
- help
- ln is used to create hard or soft links between files.
-
-config LOGNAME
- bool "logname"
- default y
- help
- logname is used to print the current user's login name.
-
-config LS
- bool "ls"
- default y
- help
- ls is used to list the contents of directories.
-
-config FEATURE_LS_FILETYPES
- bool "Enable filetyping options (-p and -F)"
- default y
- depends on LS
- help
- Enable the ls options (-p and -F).
-
-config FEATURE_LS_FOLLOWLINKS
- bool "Enable symlinks dereferencing (-L)"
- default y
- depends on LS
- help
- Enable the ls option (-L).
-
-config FEATURE_LS_RECURSIVE
- bool "Enable recursion (-R)"
- default y
- depends on LS
- help
- Enable the ls option (-R).
-
-config FEATURE_LS_SORTFILES
- bool "Sort the file names"
- default y
- depends on LS
- help
- Allow ls to sort file names alphabetically.
-
-config FEATURE_LS_TIMESTAMPS
- bool "Show file timestamps"
- default y
- depends on LS
- help
- Allow ls to display timestamps for files.
-
-config FEATURE_LS_USERNAME
- bool "Show username/groupnames"
- default y
- depends on LS
- help
- Allow ls to display username/groupname for files.
-
-config FEATURE_LS_COLOR
- bool "Allow use of color to identify file types"
- default y
- depends on LS && LONG_OPTS
- help
- This enables the --color option to ls.
-
-config FEATURE_LS_COLOR_IS_DEFAULT
- bool "Produce colored ls output by default"
- default y
- depends on FEATURE_LS_COLOR
- help
- Saying yes here will turn coloring on by default,
- even if no "--color" option is given to the ls command.
- This is not recommended, since the colors are not
- configurable, and the output may not be legible on
- many output screens.
-
-config MD5SUM
- bool "md5sum"
- default y
- help
- md5sum is used to print or check MD5 checksums.
-
-config MKDIR
- bool "mkdir"
- default y
- help
- mkdir is used to create directories with the specified names.
-
-config FEATURE_MKDIR_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on MKDIR && LONG_OPTS
- help
- Support long options for the mkdir applet.
+comment "Common options"
-config MKFIFO
- bool "mkfifo"
+config FEATURE_VERBOSE
+ bool "Support verbose options (usually -v) for various applets"
default y
help
- mkfifo is used to create FIFOs (named pipes).
- The `mknod' program can also create FIFOs.
-
-config MKNOD
- bool "mknod"
- default y
- help
- mknod is used to create FIFOs or block/character special
- files with the specified names.
-
-config MV
- bool "mv"
- default y
- help
- mv is used to move or rename files or directories.
-
-config FEATURE_MV_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on MV && LONG_OPTS
- help
- Support long options for the mv applet.
-
-config NICE
- bool "nice"
- default y
- help
- nice runs a program with modified scheduling priority.
-
-config NOHUP
- bool "nohup"
- default y
- help
- run a command immune to hangups, with output to a non-tty.
-
-config OD
- bool "od"
- default y
- help
- od is used to dump binary files in octal and other formats.
-
-config PRINTENV
- bool "printenv"
- default y
- help
- printenv is used to print all or part of environment.
-
-config PRINTF
- bool "printf"
- default y
- help
- printf is used to format and print specified strings.
- It's similar to `echo' except it has more options.
-
-config PWD
- bool "pwd"
- default y
- help
- pwd is used to print the current directory.
-
-config READLINK
- bool "readlink"
- default y
- help
- This program reads a symbolic link and returns the name
- of the file it points to
-
-config FEATURE_READLINK_FOLLOW
- bool "Enable canonicalization by following all symlinks (-f)"
- default y
- depends on READLINK
- help
- Enable the readlink option (-f).
-
-config REALPATH
- bool "realpath"
- default y
- help
- Return the canonicalized absolute pathname.
- This isn't provided by GNU shellutils, but where else does it belong.
-
-config RM
- bool "rm"
- default y
- help
- rm is used to remove files or directories.
-
-config RMDIR
- bool "rmdir"
- default y
- help
- rmdir is used to remove empty directories.
-
-config FEATURE_RMDIR_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on RMDIR && LONG_OPTS
- help
- Support long options for the rmdir applet, including
- --ignore-fail-on-non-empty for compatibility with GNU rmdir.
-
-config SEQ
- bool "seq"
- default y
- help
- print a sequence of numbers
-
-config SHA1SUM
- bool "sha1sum"
- default y
- help
- Compute and check SHA1 message digest
-
-config SHA256SUM
- bool "sha256sum"
- default y
- help
- Compute and check SHA256 message digest
-
-config SHA512SUM
- bool "sha512sum"
- default y
- help
- Compute and check SHA512 message digest
-
-config SHA3SUM
- bool "sha3sum"
- default y
- help
- Compute and check SHA3 (512-bit) message digest
-
-config SLEEP
- bool "sleep"
- default y
- help
- sleep is used to pause for a specified number of seconds.
- It comes in 3 versions:
- - small: takes one integer parameter
- - fancy: takes multiple integer arguments with suffixes:
- sleep 1d 2h 3m 15s
- - fancy with fractional numbers:
- sleep 2.3s 4.5h sleeps for 16202.3 seconds
- Last one is "the most compatible" with coreutils sleep,
- but it adds around 1k of code.
-
-config FEATURE_FANCY_SLEEP
- bool "Enable multiple arguments and s/m/h/d suffixes"
- default y
- depends on SLEEP
- help
- Allow sleep to pause for specified minutes, hours, and days.
-
-config FEATURE_FLOAT_SLEEP
- bool "Enable fractional arguments"
- default y
- depends on FEATURE_FANCY_SLEEP
- help
- Allow for fractional numeric parameters.
-
-config SORT
- bool "sort"
- default y
- help
- sort is used to sort lines of text in specified files.
-
-config FEATURE_SORT_BIG
- bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)"
- default y
- depends on SORT
- help
- Without this, sort only supports -r, -u, and an integer version
- of -n. Selecting this adds sort keys, floating point support, and
- more. This adds a little over 3k to a nonstatic build on x86.
-
- The SuSv3 sort standard is available at:
- http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
-
-config SPLIT
- bool "split"
- default y
- help
- split a file into pieces.
-
-config FEATURE_SPLIT_FANCY
- bool "Fancy extensions"
- default y
- depends on SPLIT
- help
- Add support for features not required by SUSv3.
- Supports additional suffixes 'b' for 512 bytes,
- 'g' for 1GiB for the -b option.
-
-config STAT
- bool "stat"
- default y
- select PLATFORM_LINUX # statfs()
- help
- display file or filesystem status.
-
-config FEATURE_STAT_FORMAT
- bool "Enable custom formats (-c)"
- default y
- depends on STAT
- help
- Without this, stat will not support the '-c format' option where
- users can pass a custom format string for output. This adds about
- 7k to a nonstatic build on amd64.
-
-config STTY
- bool "stty"
- default y
- help
- stty is used to change and print terminal line settings.
-
-config SUM
- bool "sum"
- default y
- help
- checksum and count the blocks in a file
-
-config SYNC
- bool "sync"
- default y
- help
- sync is used to flush filesystem buffers.
-
-config TAC
- bool "tac"
- default y
- help
- tac is used to concatenate and print files in reverse.
-
-config TAIL
- bool "tail"
- default y
- help
- tail is used to print the last specified number of lines
- from files.
-
-config FEATURE_FANCY_TAIL
- bool "Enable extra tail options (-q, -s, -v, and -F)"
- default y
- depends on TAIL
- help
- The options (-q, -s, and -v) are provided by GNU tail, but
- are not specific in the SUSv3 standard.
-
- -q Never output headers giving file names
- -s SEC Wait SEC seconds between reads with -f
- -v Always output headers giving file names
-
-config TEE
- bool "tee"
- default y
- help
- tee is used to read from standard input and write
- to standard output and files.
-
-config FEATURE_TEE_USE_BLOCK_IO
- bool "Enable block I/O (larger/faster) instead of byte I/O"
- default y
- depends on TEE
- help
- Enable this option for a faster tee, at expense of size.
-
-config TRUE
- bool "true"
- default y
- help
- true returns an exit code of TRUE (0).
-
-config TTY
- bool "tty"
- default y
- help
- tty is used to print the name of the current terminal to
- standard output.
-
-config UNAME
- bool "uname"
- default y
- help
- uname is used to print system information.
-
-config UNEXPAND
- bool "unexpand"
- default y
- help
- By default, convert only leading sequences of blanks to tabs.
-
-config FEATURE_UNEXPAND_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on UNEXPAND && LONG_OPTS
- help
- Support long options for the unexpand applet.
-
-config UNIQ
- bool "uniq"
- default y
- help
- uniq is used to remove duplicate lines from a sorted file.
-
-config USLEEP
- bool "usleep"
- default y
- help
- usleep is used to pause for a specified number of microseconds.
-
-config UUDECODE
- bool "uudecode"
- default y
- help
- uudecode is used to decode a uuencoded file.
-
-config UUENCODE
- bool "uuencode"
- default y
- help
- uuencode is used to uuencode a file.
-
-config WC
- bool "wc"
- default y
- help
- wc is used to print the number of bytes, words, and lines,
- in specified files.
-
-config FEATURE_WC_LARGE
- bool "Support very large files in wc"
- default y
- depends on WC
- help
- Use "unsigned long long" in wc for counter variables.
-
-config WHOAMI
- bool "whoami"
- default y
- help
- whoami is used to print the username of the current
- user id (same as id -un).
-
-config YES
- bool "yes"
- default y
- help
- yes is used to repeatedly output a specific string, or
- the default string `y'.
+ Enable cp -v, rm -v and similar messages.
+ Also enables long option (--verbose) if it exists.
+ Without this option, -v is accepted but ignored.
comment "Common options for cp and mv"
depends on CP || MV
@@ -772,17 +51,4 @@ config FEATURE_HUMAN_READABLE
help
Allow df, du, and ls to have human readable output.
-comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum"
- depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
-
-config FEATURE_MD5_SHA1_SUM_CHECK
- bool "Enable -c, -s and -w options"
- default y
- depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
- help
- Enabling the -c options allows files to be checked
- against pre-calculated hash values.
-
- -s and -w are useful options when verifying checksums.
-
endmenu
diff --git a/coreutils/Kbuild.src b/coreutils/Kbuild.src
index 9970c8f..d9a4487 100644
--- a/coreutils/Kbuild.src
+++ b/coreutils/Kbuild.src
@@ -9,77 +9,17 @@ libs-y += libcoreutils/
lib-y:=
INSERT
-lib-$(CONFIG_CAL) += cal.o
-lib-$(CONFIG_CATV) += catv.o
-lib-$(CONFIG_CHGRP) += chgrp.o chown.o
-lib-$(CONFIG_CHMOD) += chmod.o
-lib-$(CONFIG_CHOWN) += chown.o
-lib-$(CONFIG_ADDUSER) += chown.o # used by adduser
-lib-$(CONFIG_ADDGROUP) += chown.o # used by adduser
-lib-$(CONFIG_CHROOT) += chroot.o
-lib-$(CONFIG_CKSUM) += cksum.o
-lib-$(CONFIG_COMM) += comm.o
-lib-$(CONFIG_CP) += cp.o
-lib-$(CONFIG_CUT) += cut.o
-lib-$(CONFIG_DD) += dd.o
-lib-$(CONFIG_DF) += df.o
-lib-$(CONFIG_DIRNAME) += dirname.o
-lib-$(CONFIG_DOS2UNIX) += dos2unix.o
-lib-$(CONFIG_DU) += du.o
-lib-$(CONFIG_ECHO) += echo.o
-lib-$(CONFIG_ASH) += echo.o # used by ash
-lib-$(CONFIG_HUSH) += echo.o # used by hush
-lib-$(CONFIG_ENV) += env.o
-lib-$(CONFIG_EXPR) += expr.o
-lib-$(CONFIG_EXPAND) += expand.o
-lib-$(CONFIG_FALSE) += false.o
-lib-$(CONFIG_FOLD) += fold.o
-lib-$(CONFIG_FSYNC) += fsync.o
-lib-$(CONFIG_INSTALL) += install.o
-#lib-$(CONFIG_LENGTH) += length.o
-lib-$(CONFIG_LN) += ln.o
-lib-$(CONFIG_LOGNAME) += logname.o
-lib-$(CONFIG_LS) += ls.o
-lib-$(CONFIG_FTPD) += ls.o
-lib-$(CONFIG_GROUPS) += id.o
-lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
-lib-$(CONFIG_MKDIR) += mkdir.o
-lib-$(CONFIG_MKFIFO) += mkfifo.o
-lib-$(CONFIG_MKNOD) += mknod.o
-lib-$(CONFIG_MV) += mv.o
-lib-$(CONFIG_NICE) += nice.o
-lib-$(CONFIG_NOHUP) += nohup.o
-lib-$(CONFIG_OD) += od.o
-lib-$(CONFIG_PRINTENV) += printenv.o
-lib-$(CONFIG_PRINTF) += printf.o
+
+lib-$(CONFIG_MORE) += cat.o # more uses it if stdout isn't a tty
+lib-$(CONFIG_LESS) += cat.o # less too
+lib-$(CONFIG_CRONTAB) += cat.o # crontab -l
+lib-$(CONFIG_ADDUSER) += chown.o # used by adduser
+lib-$(CONFIG_ADDGROUP) += chown.o # used by adduser
+lib-$(CONFIG_ASH) += echo.o # used by ash
+lib-$(CONFIG_SH_IS_ASH) += echo.o # used by ash
+lib-$(CONFIG_BASH_IS_ASH) += echo.o # used by ash
+lib-$(CONFIG_HUSH) += echo.o # used by hush
+lib-$(CONFIG_SH_IS_HUSH) += echo.o # used by hush
+lib-$(CONFIG_BASH_IS_HUSH) += echo.o # used by hush
+lib-$(CONFIG_FTPD) += ls.o # used by ftpd
lib-$(CONFIG_ASH_BUILTIN_PRINTF) += printf.o
-lib-$(CONFIG_PWD) += pwd.o
-lib-$(CONFIG_READLINK) += readlink.o
-lib-$(CONFIG_REALPATH) += realpath.o
-lib-$(CONFIG_RM) += rm.o
-lib-$(CONFIG_RMDIR) += rmdir.o
-lib-$(CONFIG_SEQ) += seq.o
-lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
-lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
-lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
-lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
-lib-$(CONFIG_SLEEP) += sleep.o
-lib-$(CONFIG_SPLIT) += split.o
-lib-$(CONFIG_SORT) += sort.o
-lib-$(CONFIG_STAT) += stat.o
-lib-$(CONFIG_STTY) += stty.o
-lib-$(CONFIG_SUM) += sum.o
-lib-$(CONFIG_SYNC) += sync.o
-lib-$(CONFIG_TAC) += tac.o
-lib-$(CONFIG_TEE) += tee.o
-lib-$(CONFIG_TRUE) += true.o
-lib-$(CONFIG_TTY) += tty.o
-lib-$(CONFIG_UNAME) += uname.o
-lib-$(CONFIG_UNEXPAND) += expand.o
-lib-$(CONFIG_UNIQ) += uniq.o
-lib-$(CONFIG_USLEEP) += usleep.o
-lib-$(CONFIG_UUDECODE) += uudecode.o
-lib-$(CONFIG_UUENCODE) += uuencode.o
-lib-$(CONFIG_WC) += wc.o
-lib-$(CONFIG_WHOAMI) += whoami.o
-lib-$(CONFIG_YES) += yes.o
diff --git a/coreutils/basename.c b/coreutils/basename.c
index 1f7a137..ace0148 100644
--- a/coreutils/basename.c
+++ b/coreutils/basename.c
@@ -14,12 +14,6 @@
* 2) Don't check for options, as per SUSv3.
* 3) Save some space by using strcmp(). Calling strncmp() here was silly.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
-
-//kbuild:lib-$(CONFIG_BASENAME) += basename.o
-
//config:config BASENAME
//config: bool "basename"
//config: default y
@@ -28,10 +22,17 @@
//config: leaving just the filename itself. Enable this option if you wish
//config: to enable the 'basename' utility.
+//applet:IF_BASENAME(APPLET_NOFORK(basename, basename, BB_DIR_USR_BIN, BB_SUID_DROP, basename))
+
+//kbuild:lib-$(CONFIG_BASENAME) += basename.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
+
//usage:#define basename_trivial_usage
//usage: "FILE [SUFFIX]"
//usage:#define basename_full_usage "\n\n"
-//usage: "Strip directory path and .SUFFIX from FILE\n"
+//usage: "Strip directory path and .SUFFIX from FILE"
//usage:
//usage:#define basename_example_usage
//usage: "$ basename /usr/local/bin/foo\n"
diff --git a/coreutils/cal.c b/coreutils/cal.c
index 5d97d61..4f5bbf7 100644
--- a/coreutils/cal.c
+++ b/coreutils/cal.c
@@ -6,17 +6,25 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
+ *
+ * Major size reduction... over 50% (>1.5k) on i386.
+ */
+//config:config CAL
+//config: bool "cal"
+//config: default y
+//config: help
+//config: cal is used to display a monthly calendar.
+
+//applet:IF_CAL(APPLET(cal, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CAL) += cal.o
/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect. The upstream
* BB_AUDIT BUG: version in util-linux seems to be broken as well. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
-/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
- *
- * Major size reduction... over 50% (>1.5k) on i386.
- */
-
//usage:#define cal_trivial_usage
//usage: "[-jy] [[MONTH] YEAR]"
//usage:#define cal_full_usage "\n\n"
diff --git a/coreutils/cat.c b/coreutils/cat.c
index 00c38d4..6597888 100644
--- a/coreutils/cat.c
+++ b/coreutils/cat.c
@@ -6,15 +6,6 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
-
-//kbuild:lib-$(CONFIG_CAT) += cat.o
-//kbuild:lib-$(CONFIG_MORE) += cat.o # more uses it if stdout isn't a tty
-//kbuild:lib-$(CONFIG_LESS) += cat.o # less too
-//kbuild:lib-$(CONFIG_CRONTAB) += cat.o # crontab -l
-
//config:config CAT
//config: bool "cat"
//config: default y
@@ -22,6 +13,13 @@
//config: cat is used to concatenate files and print them to the standard
//config: output. Enable this option if you wish to enable the 'cat' utility.
+//applet:IF_CAT(APPLET_NOFORK(cat, cat, BB_DIR_BIN, BB_SUID_DROP, cat))
+
+//kbuild:lib-$(CONFIG_CAT) += cat.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
+
//usage:#define cat_trivial_usage
//usage: "[FILE]..."
//usage:#define cat_full_usage "\n\n"
diff --git a/coreutils/catv.c b/coreutils/catv.c
index e3499c5..1aeebe1 100644
--- a/coreutils/catv.c
+++ b/coreutils/catv.c
@@ -10,6 +10,17 @@
/* See "Cat -v considered harmful" at
* http://cm.bell-labs.com/cm/cs/doc/84/kp.ps.gz */
+//config:config CATV
+//config: bool "catv"
+//config: default y
+//config: help
+//config: Display nonprinting characters as escape sequences (like some
+//config: implementations' cat -v option).
+
+//applet:IF_CATV(APPLET(catv, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CATV) += catv.o
+
//usage:#define catv_trivial_usage
//usage: "[-etv] [FILE]..."
//usage:#define catv_full_usage "\n\n"
@@ -19,21 +30,24 @@
//usage: "\n -v Don't use ^x or M-x escapes"
#include "libbb.h"
+#include "common_bufsiz.h"
-int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int catv_main(int argc UNUSED_PARAM, char **argv)
-{
- int retval = EXIT_SUCCESS;
- int fd;
- unsigned opts;
#define CATV_OPT_e (1<<0)
#define CATV_OPT_t (1<<1)
#define CATV_OPT_v (1<<2)
- typedef char BUG_const_mismatch[
+struct BUG_const_mismatch {
+ char BUG_const_mismatch[
CATV_OPT_e == VISIBLE_ENDLINE && CATV_OPT_t == VISIBLE_SHOW_TABS
? 1 : -1
];
+};
+int catv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int catv_main(int argc UNUSED_PARAM, char **argv)
+{
+ int retval = EXIT_SUCCESS;
+ int fd;
+ unsigned opts;
opts = getopt32(argv, "etv");
argv += optind;
#if 0 /* These consts match, we can just pass "opts" to visible() */
@@ -46,6 +60,9 @@ int catv_main(int argc UNUSED_PARAM, char **argv)
/* Read from stdin if there's nothing else to do. */
if (!argv[0])
*--argv = (char*)"-";
+
+#define read_buf bb_common_bufsiz1
+ setup_common_bufsiz();
do {
fd = open_or_warn_stdin(*argv);
if (fd < 0) {
@@ -55,7 +72,6 @@ int catv_main(int argc UNUSED_PARAM, char **argv)
for (;;) {
int i, res;
-#define read_buf bb_common_bufsiz1
res = read(fd, read_buf, COMMON_BUFSIZE);
if (res < 0)
retval = EXIT_FAILURE;
diff --git a/coreutils/chgrp.c b/coreutils/chgrp.c
index 7076db6..8dca63c 100644
--- a/coreutils/chgrp.c
+++ b/coreutils/chgrp.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHGRP
+//config: bool "chgrp"
+//config: default y
+//config: help
+//config: chgrp is used to change the group ownership of files.
+
+//applet:IF_CHGRP(APPLET_NOEXEC(chgrp, chgrp, BB_DIR_BIN, BB_SUID_DROP, chgrp))
+
+//kbuild:lib-$(CONFIG_CHGRP) += chgrp.o chown.o
/* BB_AUDIT SUSv3 defects - none? */
/* BB_AUDIT GNU defects - unsupported long options. */
diff --git a/coreutils/chmod.c b/coreutils/chmod.c
index 5ee45b9..80913f5 100644
--- a/coreutils/chmod.c
+++ b/coreutils/chmod.c
@@ -9,6 +9,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHMOD
+//config: bool "chmod"
+//config: default y
+//config: help
+//config: chmod is used to change the access permission of files.
+
+//applet:IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod))
+
+//kbuild:lib-$(CONFIG_CHMOD) += chmod.o
/* BB_AUDIT SUSv3 compliant */
/* BB_AUDIT GNU defects - unsupported long options. */
@@ -69,9 +78,9 @@ static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void
if (S_ISLNK(statbuf->st_mode))
return TRUE;
}
- newmode = statbuf->st_mode;
- if (!bb_parse_mode((char *)param, &newmode))
+ newmode = bb_parse_mode((char *)param, statbuf->st_mode);
+ if (newmode == (mode_t)-1)
bb_error_msg_and_die("invalid mode '%s'", (char *)param);
if (chmod(fileName, newmode) == 0) {
diff --git a/coreutils/chown.c b/coreutils/chown.c
index 1a91276..50b06d7 100644
--- a/coreutils/chown.c
+++ b/coreutils/chown.c
@@ -6,20 +6,37 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHOWN
+//config: bool "chown"
+//config: default y
+//config: help
+//config: chown is used to change the user and/or group ownership
+//config: of files.
+//config:
+//config:config FEATURE_CHOWN_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on CHOWN && LONG_OPTS
+//config: help
+//config: Enable use of long options
+
+//applet:IF_CHOWN(APPLET_NOEXEC(chown, chown, BB_DIR_BIN, BB_SUID_DROP, chown))
+
+//kbuild:lib-$(CONFIG_CHOWN) += chown.o
/* BB_AUDIT SUSv3 defects - none? */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
//usage:#define chown_trivial_usage
-//usage: "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..."
+//usage: "[-Rh"IF_DESKTOP("LHPcvf")"]... USER[:[GRP]] FILE..."
//usage:#define chown_full_usage "\n\n"
-//usage: "Change the owner and/or group of each FILE to OWNER and/or GROUP\n"
+//usage: "Change the owner and/or group of each FILE to USER and/or GRP\n"
//usage: "\n -R Recurse"
//usage: "\n -h Affect symlinks instead of symlink targets"
+//usage: IF_DESKTOP(
//usage: "\n -L Traverse all symlinks to directories"
//usage: "\n -H Traverse symlinks on command line only"
//usage: "\n -P Don't traverse symlinks (default)"
-//usage: IF_DESKTOP(
//usage: "\n -c List changed files"
//usage: "\n -v List all files"
//usage: "\n -f Hide errors"
@@ -112,10 +129,6 @@ int chown_main(int argc UNUSED_PARAM, char **argv)
int opt, flags;
struct param_t param;
- /* Just -1 might not work: uid_t may be unsigned long */
- param.ugid.uid = -1L;
- param.ugid.gid = -1L;
-
#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
applet_long_options = chown_longopts;
#endif
diff --git a/coreutils/chroot.c b/coreutils/chroot.c
index 633e66b..5c067c1 100644
--- a/coreutils/chroot.c
+++ b/coreutils/chroot.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHROOT
+//config: bool "chroot"
+//config: default y
+//config: help
+//config: chroot is used to change the root directory and run a command.
+//config: The default command is `/bin/sh'.
+
+//applet:IF_CHROOT(APPLET(chroot, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHROOT) += chroot.o
/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
diff --git a/coreutils/cksum.c b/coreutils/cksum.c
index ac0b0c3..aeec018 100644
--- a/coreutils/cksum.c
+++ b/coreutils/cksum.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CKSUM
+//config: bool "cksum"
+//config: default y
+//config: help
+//config: cksum is used to calculate the CRC32 checksum of a file.
+
+//applet:IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
+
+//kbuild:lib-$(CONFIG_CKSUM) += cksum.o
//usage:#define cksum_trivial_usage
//usage: "FILES..."
@@ -13,6 +22,7 @@
//usage: "Calculate the CRC32 checksums of FILES"
#include "libbb.h"
+#include "common_bufsiz.h"
/* This is a NOEXEC applet. Be very careful! */
@@ -32,6 +42,7 @@ int cksum_main(int argc UNUSED_PARAM, char **argv)
argv++;
#endif
+ setup_common_bufsiz();
do {
int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input);
@@ -43,7 +54,7 @@ int cksum_main(int argc UNUSED_PARAM, char **argv)
length = 0;
#define read_buf bb_common_bufsiz1
- while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) {
+ while ((bytes_read = safe_read(fd, read_buf, COMMON_BUFSIZE)) > 0) {
length += bytes_read;
crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table);
}
diff --git a/coreutils/comm.c b/coreutils/comm.c
index cd45095..b6a1278 100644
--- a/coreutils/comm.c
+++ b/coreutils/comm.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config COMM
+//config: bool "comm"
+//config: default y
+//config: help
+//config: comm is used to compare two files line by line and return
+//config: a three-column output.
+
+//applet:IF_COMM(APPLET(comm, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_COMM) += comm.o
//usage:#define comm_trivial_usage
//usage: "[-123] FILE1 FILE2"
diff --git a/coreutils/cp.c b/coreutils/cp.c
index de2e512..4ecdaaf 100644
--- a/coreutils/cp.c
+++ b/coreutils/cp.c
@@ -7,13 +7,29 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction.
*/
+//config:config CP
+//config: bool "cp"
+//config: default y
+//config: help
+//config: cp is used to copy files and directories.
+//config:
+//config:config FEATURE_CP_LONG_OPTIONS
+//config: bool "Enable long options for cp"
+//config: default y
+//config: depends on CP && LONG_OPTS
+//config: help
+//config: Enable long options for cp.
+//config: Also add support for --parents option.
+
+//applet:IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp))
+
+//kbuild:lib-$(CONFIG_CP) += cp.o
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
//usage:#define cp_trivial_usage
//usage: "[OPTIONS] SOURCE... DEST"
@@ -31,6 +47,7 @@
//usage: "\n -f Overwrite"
//usage: "\n -i Prompt before overwrite"
//usage: "\n -l,-s Create (sym)links"
+//usage: "\n -u Copy only newer files"
#include "libbb.h"
#include "libcoreutils/coreutils.h"
@@ -49,12 +66,10 @@ int cp_main(int argc, char **argv)
int flags;
int status;
enum {
- OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
- OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
- OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
- OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
+ FILEUTILS_CP_OPTNUM = sizeof(FILEUTILS_CP_OPTSTR)-1,
#if ENABLE_FEATURE_CP_LONG_OPTIONS
- OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
+ /*OPT_rmdest = FILEUTILS_RMDEST = 1 << FILEUTILS_CP_OPTNUM */
+ OPT_parents = 1 << (FILEUTILS_CP_OPTNUM+1),
#endif
};
@@ -76,11 +91,12 @@ int cp_main(int argc, char **argv)
"recursive\0" No_argument "R"
"symbolic-link\0" No_argument "s"
"verbose\0" No_argument "v"
- "parents\0" No_argument "\xff"
+ "update\0" No_argument "u"
+ "remove-destination\0" No_argument "\xff"
+ "parents\0" No_argument "\xfe"
;
#endif
- // -v (--verbose) is ignored
- flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv");
+ flags = getopt32(argv, FILEUTILS_CP_OPTSTR);
/* Options of cp from GNU coreutils 6.10:
* -a, --archive
* -f, --force
@@ -95,6 +111,11 @@ int cp_main(int argc, char **argv)
* -d same as --no-dereference --preserve=links
* -p same as --preserve=mode,ownership,timestamps
* -c same as --preserve=context
+ * -u, --update
+ * copy only when the SOURCE file is newer than the destination
+ * file or when the destination file is missing
+ * --remove-destination
+ * remove each existing destination file before attempting to open
* --parents
* use full source file name under DIRECTORY
* NOT SUPPORTED IN BBOX:
@@ -107,8 +128,6 @@ int cp_main(int argc, char **argv)
* preserve attributes (default: mode,ownership,timestamps),
* if possible additional attributes: security context,links,all
* --no-preserve=ATTR_LIST
- * --remove-destination
- * remove each existing destination file before attempting to open
* --sparse=WHEN
* control creation of sparse files
* --strip-trailing-slashes
@@ -119,9 +138,6 @@ int cp_main(int argc, char **argv)
* copy all SOURCE arguments into DIRECTORY
* -T, --no-target-directory
* treat DEST as a normal file
- * -u, --update
- * copy only when the SOURCE file is newer than the destination
- * file or when the destination file is missing
* -x, --one-file-system
* stay on this file system
* -Z, --context=CONTEXT
@@ -157,11 +173,16 @@ int cp_main(int argc, char **argv)
return EXIT_FAILURE;
#if ENABLE_FEATURE_CP_LONG_OPTIONS
+ //bb_error_msg("flags:%x FILEUTILS_RMDEST:%x OPT_parents:%x",
+ // flags, FILEUTILS_RMDEST, OPT_parents);
if (flags & OPT_parents) {
if (!(d_flags & 2)) {
bb_error_msg_and_die("with --parents, the destination must be a directory");
}
}
+ if (flags & FILEUTILS_RMDEST) {
+ flags |= FILEUTILS_FORCE;
+ }
#endif
/* ...if neither is a directory... */
diff --git a/coreutils/cut.c b/coreutils/cut.c
index 84449c7..a33a825 100644
--- a/coreutils/cut.c
+++ b/coreutils/cut.c
@@ -8,6 +8,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CUT
+//config: bool "cut"
+//config: default y
+//config: help
+//config: cut is used to print selected parts of lines from
+//config: each file to stdout.
+
+//applet:IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut))
+
+//kbuild:lib-$(CONFIG_CUT) += cut.o
//usage:#define cut_trivial_usage
//usage: "[OPTIONS] [FILE]..."
diff --git a/coreutils/date.c b/coreutils/date.c
index 767e0d4..31b53d2 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -8,8 +8,7 @@
* bugfixes and cleanup by Bernhard Reutner-Fischer
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
-*/
-
+ */
/* This 'date' command supports only 2 time setting formats,
all the GNU strftime stuff (its in libc, lets use it),
setting time using UTC and displaying it, as well as
@@ -19,10 +18,6 @@
/* Input parsing code is always bulky - used heavy duty libc stuff as
much as possible, missed out a lot of bounds checking */
-//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_DATE) += date.o
-
//config:config DATE
//config: bool "date"
//config: default y
@@ -63,6 +58,10 @@
//config: the same format. With it on, 'date DATE' additionally supports
//config: MMDDhhmm[[YY]YY][.ss] format.
+//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DATE) += date.o
+
/* GNU coreutils 6.9 man page:
* date [OPTION]... [+FORMAT]
* date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
@@ -138,9 +137,11 @@
//usage: "Wed Apr 12 18:52:41 MDT 2000\n"
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_DATE_NANO
# include <sys/syscall.h>
#endif
+#include <android.h>
enum {
OPT_RFC2822 = (1 << 0), /* R */
@@ -368,16 +369,17 @@ int date_main(int argc UNUSED_PARAM, char **argv)
#endif
#define date_buf bb_common_bufsiz1
+ setup_common_bufsiz();
if (*fmt_dt2str == '\0') {
/* With no format string, just print a blank line */
date_buf[0] = '\0';
} else {
/* Handle special conversions */
- if (strncmp(fmt_dt2str, "%f", 2) == 0) {
+ if (is_prefixed_with(fmt_dt2str, "%f")) {
fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S";
}
/* Generate output string */
- strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time);
+ strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time);
}
puts(date_buf);
diff --git a/coreutils/dd.c b/coreutils/dd.c
index 2838f63..3d1ba2e 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -2,15 +2,62 @@
/*
* Mini dd implementation for busybox
*
- *
* Copyright (C) 2000,2001 Matt Kraai
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config DD
+//config: bool "dd"
+//config: default y
+//config: help
+//config: dd copies a file (from standard input to standard output,
+//config: by default) using specific input and output blocksizes,
+//config: while optionally performing conversions on it.
+//config:
+//config:config FEATURE_DD_SIGNAL_HANDLING
+//config: bool "Enable signal handling for status reporting"
+//config: default y
+//config: depends on DD
+//config: help
+//config: Sending a SIGUSR1 signal to a running `dd' process makes it
+//config: print to standard error the number of records read and written
+//config: so far, then to resume copying.
+//config:
+//config: $ dd if=/dev/zero of=/dev/null &
+//config: $ pid=$!; kill -USR1 $pid; sleep 1; kill $pid
+//config: 10899206+0 records in
+//config: 10899206+0 records out
+//config:
+//config:config FEATURE_DD_THIRD_STATUS_LINE
+//config: bool "Enable the third status line upon signal"
+//config: default y
+//config: depends on DD && FEATURE_DD_SIGNAL_HANDLING
+//config: help
+//config: Displays a coreutils-like third status line with transferred bytes,
+//config: elapsed time and speed.
+//config:
+//config:config FEATURE_DD_IBS_OBS
+//config: bool "Enable ibs, obs and conv options"
+//config: default y
+//config: depends on DD
+//config: help
+//config: Enables support for writing a certain number of bytes in and out,
+//config: at a time, and performing conversions on the data stream.
+//config:
+//config:config FEATURE_DD_STATUS
+//config: bool "Enable status display options"
+//config: default y
+//config: depends on DD
+//config: help
+//config: Enables support for status=noxfer/none option.
+
+//applet:IF_DD(APPLET_NOEXEC(dd, dd, BB_DIR_BIN, BB_SUID_DROP, dd))
+
+//kbuild:lib-$(CONFIG_DD) += dd.o
//usage:#define dd_trivial_usage
//usage: "[if=FILE] [of=FILE] " IF_FEATURE_DD_IBS_OBS("[ibs=N] [obs=N] ") "[bs=N] [count=N] [skip=N]\n"
-//usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync]")
+//usage: " [seek=N]" IF_FEATURE_DD_IBS_OBS(" [conv=notrunc|noerror|sync|fsync] [iflag=skip_bytes]")
//usage:#define dd_full_usage "\n\n"
//usage: "Copy a file with converting and formatting\n"
//usage: "\n if=FILE Read from FILE instead of stdin"
@@ -31,9 +78,14 @@
//usage: "\n conv=sync Pad blocks with zeros"
//usage: "\n conv=fsync Physically write data out before finishing"
//usage: "\n conv=swab Swap every pair of bytes"
+//usage: "\n iflag=skip_bytes skip=N is in bytes"
+//usage: )
+//usage: IF_FEATURE_DD_STATUS(
+//usage: "\n status=noxfer Suppress rate output"
+//usage: "\n status=none Suppress all output"
//usage: )
//usage: "\n"
-//usage: "\nN may be suffixed by c (1), w (2), b (512), kD (1000), k (1024), MD, M, GD, G"
+//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
//usage:
//usage:#define dd_example_usage
//usage: "$ dd if=/dev/zero of=/dev/ram1 bs=1M count=4\n"
@@ -41,6 +93,7 @@
//usage: "4+0 records out\n"
#include "libbb.h"
+#include "common_bufsiz.h"
/* This is a NOEXEC applet. Be very careful! */
@@ -50,33 +103,40 @@ enum {
ofd = STDOUT_FILENO,
};
-static const struct suffix_mult dd_suffixes[] = {
- { "c", 1 },
- { "w", 2 },
- { "b", 512 },
- { "kD", 1000 },
- { "k", 1024 },
- { "K", 1024 }, /* compat with coreutils dd */
- { "MD", 1000000 },
- { "M", 1048576 },
- { "GD", 1000000000 },
- { "G", 1073741824 },
- { "", 0 }
-};
-
struct globals {
off_t out_full, out_part, in_full, in_part;
#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
unsigned long long total_bytes;
unsigned long long begin_time_us;
#endif
+ int flags;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
/* we have to zero it out because of NOEXEC */ \
memset(&G, 0, sizeof(G)); \
} while (0)
+enum {
+ /* Must be in the same order as OP_conv_XXX! */
+ /* (see "flags |= (1 << what)" below) */
+ FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
+ /* end of conv flags */
+ /* start of input flags */
+ FLAG_IFLAG_SHIFT = 5,
+ FLAG_SKIP_BYTES = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
+ /* end of input flags */
+ FLAG_TWOBUFS = (1 << 6) * ENABLE_FEATURE_DD_IBS_OBS,
+ FLAG_COUNT = 1 << 7,
+ FLAG_STATUS = 1 << 8,
+ FLAG_STATUS_NONE = 1 << 9,
+ FLAG_STATUS_NOXFER = 1 << 10,
+};
static void dd_output_status(int UNUSED_PARAM cur_signal)
{
@@ -93,6 +153,13 @@ static void dd_output_status(int UNUSED_PARAM cur_signal)
G.out_full, G.out_part);
#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+# if ENABLE_FEATURE_DD_STATUS
+ if (G.flags & FLAG_STATUS_NOXFER) /* status=noxfer active? */
+ return;
+ //TODO: should status=none make dd stop reacting to USR1 entirely?
+ //So far we react to it (we print the stats),
+ //status=none only suppresses final, non-USR1 generated status message.
+# endif
fprintf(stderr, "%llu bytes (%sB) copied, ",
G.total_bytes,
/* show fractional digit, use suffixes */
@@ -145,30 +212,51 @@ static bool write_and_stats(const void *buf, size_t len, size_t obs,
# define XATOU_SFX xatoul_sfx
#endif
+#if ENABLE_FEATURE_DD_IBS_OBS
+static int parse_comma_flags(char *val, const char *words, const char *error_in)
+{
+ int flags = 0;
+ while (1) {
+ int n;
+ char *arg;
+ /* find ',', replace them with NUL so we can use val for
+ * index_in_strings() without copying.
+ * We rely on val being non-null, else strchr would fault.
+ */
+ arg = strchr(val, ',');
+ if (arg)
+ *arg = '\0';
+ n = index_in_strings(words, val);
+ if (n < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, val, error_in);
+ flags |= (1 << n);
+ if (!arg) /* no ',' left, so this was the last specifier */
+ break;
+ *arg = ','; /* to preserve ps listing */
+ val = arg + 1; /* skip this keyword and ',' */
+ }
+ return flags;
+}
+#endif
+
int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int dd_main(int argc UNUSED_PARAM, char **argv)
{
- enum {
- /* Must be in the same order as OP_conv_XXX! */
- /* (see "flags |= (1 << what)" below) */
- FLAG_NOTRUNC = (1 << 0) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_SYNC = (1 << 1) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_NOERROR = (1 << 2) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_FSYNC = (1 << 3) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_SWAB = (1 << 4) * ENABLE_FEATURE_DD_IBS_OBS,
- /* end of conv flags */
- FLAG_TWOBUFS = (1 << 5) * ENABLE_FEATURE_DD_IBS_OBS,
- FLAG_COUNT = 1 << 6,
- };
static const char keywords[] ALIGN1 =
- "bs\0""count\0""seek\0""skip\0""if\0""of\0"
+ "bs\0""count\0""seek\0""skip\0""if\0""of\0"IF_FEATURE_DD_STATUS("status\0")
#if ENABLE_FEATURE_DD_IBS_OBS
- "ibs\0""obs\0""conv\0"
+ "ibs\0""obs\0""conv\0""iflag\0"
#endif
;
#if ENABLE_FEATURE_DD_IBS_OBS
static const char conv_words[] ALIGN1 =
"notrunc\0""sync\0""noerror\0""fsync\0""swab\0";
+ static const char iflag_words[] ALIGN1 =
+ "skip_bytes\0";
+#endif
+#if ENABLE_FEATURE_DD_STATUS
+ static const char status_words[] ALIGN1 =
+ "none\0""noxfer\0";
#endif
enum {
OP_bs = 0,
@@ -177,10 +265,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
OP_skip,
OP_if,
OP_of,
+ IF_FEATURE_DD_STATUS(OP_status,)
#if ENABLE_FEATURE_DD_IBS_OBS
OP_ibs,
OP_obs,
OP_conv,
+ OP_iflag,
/* Must be in the same order as FLAG_XXX! */
OP_conv_notrunc = 0,
OP_conv_sync,
@@ -200,6 +290,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
//ibm from ASCII to alternate EBCDIC
/* Partially implemented: */
//swab swap every pair of input bytes: will abort on non-even reads
+ OP_iflag_skip_bytes,
#endif
};
smallint exitcode = EXIT_FAILURE;
@@ -215,14 +306,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
#endif
/* These are all zeroed at once! */
struct {
- int flags;
size_t oc;
ssize_t prev_read_size; /* for detecting swab failure */
off_t count;
off_t seek, skip;
const char *infile, *outfile;
} Z;
-#define flags (Z.flags )
#define oc (Z.oc )
#define prev_read_size (Z.prev_read_size)
#define count (Z.count )
@@ -258,52 +347,39 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_DD_IBS_OBS
if (what == OP_ibs) {
/* Must fit into positive ssize_t */
- ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+ ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
/*continue;*/
}
if (what == OP_obs) {
- obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+ obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
/*continue;*/
}
if (what == OP_conv) {
- while (1) {
- int n;
- /* find ',', replace them with NUL so we can use val for
- * index_in_strings() without copying.
- * We rely on val being non-null, else strchr would fault.
- */
- arg = strchr(val, ',');
- if (arg)
- *arg = '\0';
- n = index_in_strings(conv_words, val);
- if (n < 0)
- bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
- flags |= (1 << n);
- if (!arg) /* no ',' left, so this was the last specifier */
- break;
- /* *arg = ','; - to preserve ps listing? */
- val = arg + 1; /* skip this keyword and ',' */
- }
+ G.flags |= parse_comma_flags(val, conv_words, "conv");
+ /*continue;*/
+ }
+ if (what == OP_iflag) {
+ G.flags |= parse_comma_flags(val, iflag_words, "iflag") << FLAG_IFLAG_SHIFT;
/*continue;*/
}
#endif
if (what == OP_bs) {
- ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+ ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes);
obs = ibs;
/*continue;*/
}
/* These can be large: */
if (what == OP_count) {
- flags |= FLAG_COUNT;
- count = XATOU_SFX(val, dd_suffixes);
+ G.flags |= FLAG_COUNT;
+ count = XATOU_SFX(val, cwbkMG_suffixes);
/*continue;*/
}
if (what == OP_seek) {
- seek = XATOU_SFX(val, dd_suffixes);
+ seek = XATOU_SFX(val, cwbkMG_suffixes);
/*continue;*/
}
if (what == OP_skip) {
- skip = XATOU_SFX(val, dd_suffixes);
+ skip = XATOU_SFX(val, cwbkMG_suffixes);
/*continue;*/
}
if (what == OP_if) {
@@ -314,6 +390,16 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
outfile = val;
/*continue;*/
}
+#if ENABLE_FEATURE_DD_STATUS
+ if (what == OP_status) {
+ int n;
+ n = index_in_strings(status_words, val);
+ if (n < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, val, "status");
+ G.flags |= FLAG_STATUS << n;
+ /*continue;*/
+ }
+#endif
} /* end of "for (argv[i])" */
//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
@@ -321,7 +407,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
obuf = ibuf;
#if ENABLE_FEATURE_DD_IBS_OBS
if (ibs != obs) {
- flags |= FLAG_TWOBUFS;
+ G.flags |= FLAG_TWOBUFS;
obuf = xmalloc(obs);
}
#endif
@@ -341,12 +427,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
if (outfile) {
int oflag = O_WRONLY | O_CREAT;
- if (!seek && !(flags & FLAG_NOTRUNC))
+ if (!seek && !(G.flags & FLAG_NOTRUNC))
oflag |= O_TRUNC;
xmove_fd(xopen(outfile, oflag), ofd);
- if (seek && !(flags & FLAG_NOTRUNC)) {
+ if (seek && !(G.flags & FLAG_NOTRUNC)) {
if (ftruncate(ofd, seek * obs) < 0) {
struct stat st;
@@ -362,9 +448,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
outfile = bb_msg_standard_output;
}
if (skip) {
- if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
+ size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs;
+ if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) {
do {
- ssize_t n = safe_read(ifd, ibuf, ibs);
+ ssize_t n = safe_read(ifd, ibuf, blocksz);
if (n < 0)
goto die_infile;
if (n == 0)
@@ -377,7 +464,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
goto die_outfile;
}
- while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
+ while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
ssize_t n;
n = safe_read(ifd, ibuf, ibs);
@@ -385,7 +472,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
break;
if (n < 0) {
/* "Bad block" */
- if (!(flags & FLAG_NOERROR))
+ if (!(G.flags & FLAG_NOERROR))
goto die_infile;
bb_simple_perror_msg(infile);
/* GNU dd with conv=noerror skips over bad blocks */
@@ -394,7 +481,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
* conv=noerror just ignores input bad blocks */
n = 0;
}
- if (flags & FLAG_SWAB) {
+ if (G.flags & FLAG_SWAB) {
uint16_t *p16;
ssize_t n2;
@@ -419,12 +506,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
G.in_full++;
else {
G.in_part++;
- if (flags & FLAG_SYNC) {
+ if (G.flags & FLAG_SYNC) {
memset(ibuf + n, 0, ibs - n);
n = ibs;
}
}
- if (flags & FLAG_TWOBUFS) {
+ if (G.flags & FLAG_TWOBUFS) {
char *tmp = ibuf;
while (n) {
size_t d = obs - oc;
@@ -446,7 +533,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
goto out_status;
}
- if (flags & FLAG_FSYNC) {
+ if (G.flags & FLAG_FSYNC) {
if (fsync(ofd) < 0)
goto die_outfile;
}
@@ -468,11 +555,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
exitcode = EXIT_SUCCESS;
out_status:
- dd_output_status(0);
+ if (!ENABLE_FEATURE_DD_STATUS || !(G.flags & FLAG_STATUS_NONE))
+ dd_output_status(0);
if (ENABLE_FEATURE_CLEAN_UP) {
free(obuf);
- if (flags & FLAG_TWOBUFS)
+ if (G.flags & FLAG_TWOBUFS)
free(ibuf);
}
diff --git a/coreutils/df.c b/coreutils/df.c
index 5e9a867..79e4c46 100644
--- a/coreutils/df.c
+++ b/coreutils/df.c
@@ -7,10 +7,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction. Removed floating point dependency. Added error checking
@@ -21,10 +17,35 @@
*
* Implement -P and -B; better coreutils compat; cleanup
*/
+//config:config DF
+//config: bool "df"
+//config: default y
+//config: help
+//config: df reports the amount of disk space used and available
+//config: on filesystems.
+//config:
+//config:config FEATURE_DF_FANCY
+//config: bool "Enable -a, -i, -B"
+//config: default y
+//config: depends on DF
+//config: help
+//config: This option enables -a, -i and -B.
+//config:
+//config: -a Show all filesystems
+//config: -i Inodes
+//config: -B <SIZE> Blocksize
+
+//applet:IF_DF(APPLET(df, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DF) += df.o
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
//usage:#define df_trivial_usage
//usage: "[-Pk"
//usage: IF_FEATURE_HUMAN_READABLE("mh")
+//usage: "T"
//usage: IF_FEATURE_DF_FANCY("ai] [-B SIZE")
//usage: "] [FILESYSTEM]..."
//usage:#define df_full_usage "\n\n"
@@ -35,6 +56,7 @@
//usage: "\n -m 1M-byte blocks"
//usage: "\n -h Human readable (e.g. 1K 243M 2G)"
//usage: )
+//usage: "\n -T Print filesystem type"
//usage: IF_FEATURE_DF_FANCY(
//usage: "\n -a Show all filesystems"
//usage: "\n -i Inodes"
@@ -83,11 +105,12 @@ int df_main(int argc UNUSED_PARAM, char **argv)
enum {
OPT_KILO = (1 << 0),
OPT_POSIX = (1 << 1),
- OPT_ALL = (1 << 2) * ENABLE_FEATURE_DF_FANCY,
- OPT_INODE = (1 << 3) * ENABLE_FEATURE_DF_FANCY,
- OPT_BSIZE = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
- OPT_HUMAN = (1 << (2 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
- OPT_MEGA = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
+ OPT_FSTYPE = (1 << 2),
+ OPT_ALL = (1 << 3) * ENABLE_FEATURE_DF_FANCY,
+ OPT_INODE = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
+ OPT_BSIZE = (1 << 5) * ENABLE_FEATURE_DF_FANCY,
+ OPT_HUMAN = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
+ OPT_MEGA = (1 << (4 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
};
const char *disp_units_hdr = NULL;
char *chp;
@@ -99,15 +122,26 @@ int df_main(int argc UNUSED_PARAM, char **argv)
#elif ENABLE_FEATURE_HUMAN_READABLE
opt_complementary = "k-m:m-k";
#endif
- opt = getopt32(argv, "kP"
+ opt = getopt32(argv, "kPT"
IF_FEATURE_DF_FANCY("aiB:")
IF_FEATURE_HUMAN_READABLE("hm")
IF_FEATURE_DF_FANCY(, &chp));
if (opt & OPT_MEGA)
df_disp_hr = 1024*1024;
- if (opt & OPT_BSIZE)
- df_disp_hr = xatoul_range(chp, 1, ULONG_MAX); /* disallow 0 */
+ if (opt & OPT_BSIZE) {
+ /* GNU coreutils 8.25 accepts "-BMiB" form too */
+ int i;
+ for (i = 0; kmg_i_suffixes[i].suffix[0]; i++) {
+ if (strcmp(kmg_i_suffixes[i].suffix, chp) == 0) {
+ df_disp_hr = kmg_i_suffixes[i].mult;
+ goto got_it;
+ }
+ }
+ /* Range used to disallow 0 */
+ df_disp_hr = xatoul_range_sfx(chp, 1, ULONG_MAX, kmg_i_suffixes);
+ got_it: ;
+ }
/* From the manpage of df from coreutils-6.10:
* Disk space is shown in 1K blocks by default, unless the environment
@@ -134,8 +168,11 @@ int df_main(int argc UNUSED_PARAM, char **argv)
disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr);
#endif
}
- printf("Filesystem %-15sUsed Available %s Mounted on\n",
- disp_units_hdr, (opt & OPT_POSIX) ? "Capacity" : "Use%");
+
+ printf("Filesystem %s%-15sUsed Available %s Mounted on\n",
+ (opt & OPT_FSTYPE) ? "Type " : "",
+ disp_units_hdr,
+ (opt & OPT_POSIX) ? "Capacity" : "Use%");
mount_table = NULL;
argv += optind;
@@ -148,6 +185,7 @@ int df_main(int argc UNUSED_PARAM, char **argv)
while (1) {
const char *device;
const char *mount_point;
+ const char *fs_type;
if (mount_table) {
mount_entry = getmntent(mount_table);
@@ -170,17 +208,23 @@ int df_main(int argc UNUSED_PARAM, char **argv)
device = mount_entry->mnt_fsname;
mount_point = mount_entry->mnt_dir;
+ fs_type = mount_entry->mnt_type;
if (statfs(mount_point, &s) != 0) {
bb_simple_perror_msg(mount_point);
goto set_error;
}
+ /* Some uclibc versions were seen to lose f_frsize
+ * (kernel does return it, but then uclibc does not copy it)
+ */
+ if (s.f_frsize == 0)
+ s.f_frsize = s.f_bsize;
if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) {
if (opt & OPT_INODE) {
s.f_blocks = s.f_files;
s.f_bavail = s.f_bfree = s.f_ffree;
- s.f_bsize = 1;
+ s.f_frsize = 1;
if (df_disp_hr)
df_disp_hr = 1;
@@ -218,34 +262,46 @@ int df_main(int argc UNUSED_PARAM, char **argv)
printf("%s%*s", uni_dev, 20 - (int)uni_stat.unicode_width, "");
}
free(uni_dev);
+ if (opt & OPT_FSTYPE) {
+ char *uni_type = unicode_conv_to_printable(&uni_stat, fs_type);
+ if (uni_stat.unicode_width > 10 && !(opt & OPT_POSIX))
+ printf(" %s\n%31s", uni_type, "");
+ else
+ printf(" %s%*s", uni_type, 10 - (int)uni_stat.unicode_width, "");
+ free(uni_type);
+ }
}
#else
if (printf("\n%-20s" + 1, device) > 20 && !(opt & OPT_POSIX))
printf("\n%-20s", "");
+ if (opt & OPT_FSTYPE) {
+ if (printf(" %-10s", fs_type) > 11 && !(opt & OPT_POSIX))
+ printf("\n%-30s", "");
+ }
#endif
#if ENABLE_FEATURE_HUMAN_READABLE
printf(" %9s ",
- /* f_blocks x f_bsize / df_disp_hr, show one fractional,
+ /* f_blocks x f_frsize / df_disp_hr, show one fractional,
* use suffixes if df_disp_hr == 0 */
- make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
+ make_human_readable_str(s.f_blocks, s.f_frsize, df_disp_hr));
printf(" %9s " + 1,
- /* EXPR x f_bsize / df_disp_hr, show one fractional,
+ /* EXPR x f_frsize / df_disp_hr, show one fractional,
* use suffixes if df_disp_hr == 0 */
make_human_readable_str((s.f_blocks - s.f_bfree),
- s.f_bsize, df_disp_hr));
+ s.f_frsize, df_disp_hr));
printf("%9s %3u%% %s\n",
- /* f_bavail x f_bsize / df_disp_hr, show one fractional,
+ /* f_bavail x f_frsize / df_disp_hr, show one fractional,
* use suffixes if df_disp_hr == 0 */
- make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
+ make_human_readable_str(s.f_bavail, s.f_frsize, df_disp_hr),
blocks_percent_used, mount_point);
#else
printf(" %9lu %9lu %9lu %3u%% %s\n",
- kscale(s.f_blocks, s.f_bsize),
- kscale(s.f_blocks - s.f_bfree, s.f_bsize),
- kscale(s.f_bavail, s.f_bsize),
+ kscale(s.f_blocks, s.f_frsize),
+ kscale(s.f_blocks - s.f_bfree, s.f_frsize),
+ kscale(s.f_bavail, s.f_frsize),
blocks_percent_used, mount_point);
#endif
}
diff --git a/coreutils/dirname.c b/coreutils/dirname.c
index 101067c..6593818 100644
--- a/coreutils/dirname.c
+++ b/coreutils/dirname.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config DIRNAME
+//config: bool "dirname"
+//config: default y
+//config: help
+//config: dirname is used to strip a non-directory suffix from
+//config: a file name.
+
+//applet:IF_DIRNAME(APPLET_NOFORK(dirname, dirname, BB_DIR_USR_BIN, BB_SUID_DROP, dirname))
+
+//kbuild:lib-$(CONFIG_DIRNAME) += dirname.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/dirname.html */
diff --git a/coreutils/dos2unix.c b/coreutils/dos2unix.c
index c9ae76e..6d23471 100644
--- a/coreutils/dos2unix.c
+++ b/coreutils/dos2unix.c
@@ -10,7 +10,26 @@
* dos2unix filters reading input from stdin and writing output to stdout.
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
-*/
+ */
+//config:config DOS2UNIX
+//config: bool "dos2unix"
+//config: default y
+//config: help
+//config: dos2unix is used to convert a text file from DOS format to
+//config: UNIX format, and vice versa.
+//config:
+//config:config UNIX2DOS
+//config: bool "unix2dos"
+//config: default y
+//config: help
+//config: unix2dos is used to convert a text file from UNIX format to
+//config: DOS format, and vice versa.
+
+//applet:IF_DOS2UNIX(APPLET_NOEXEC(dos2unix, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, dos2unix))
+//applet:IF_UNIX2DOS(APPLET_NOEXEC(unix2dos, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, unix2dos))
+
+//kbuild:lib-$(CONFIG_DOS2UNIX) += dos2unix.o
+//kbuild:lib-$(CONFIG_UNIX2DOS) += dos2unix.o
//usage:#define dos2unix_trivial_usage
//usage: "[-ud] [FILE]"
@@ -41,36 +60,38 @@ enum {
static void convert(char *fn, int conv_type)
{
FILE *in, *out;
- int i;
- char *temp_fn = NULL;
- char *resolved_fn = NULL;
+ int ch;
+ char *temp_fn = temp_fn; /* for compiler */
+ char *resolved_fn = resolved_fn;
in = stdin;
out = stdout;
if (fn != NULL) {
struct stat st;
+ int fd;
resolved_fn = xmalloc_follow_symlinks(fn);
if (resolved_fn == NULL)
bb_simple_perror_msg_and_die(fn);
in = xfopen_for_read(resolved_fn);
- fstat(fileno(in), &st);
+ xfstat(fileno(in), &st, resolved_fn);
temp_fn = xasprintf("%sXXXXXX", resolved_fn);
- i = xmkstemp(temp_fn);
- if (fchmod(i, st.st_mode) == -1)
+ fd = xmkstemp(temp_fn);
+ if (fchmod(fd, st.st_mode) == -1)
bb_simple_perror_msg_and_die(temp_fn);
+ fchown(fd, st.st_uid, st.st_gid);
- out = xfdopen_for_write(i);
+ out = xfdopen_for_write(fd);
}
- while ((i = fgetc(in)) != EOF) {
- if (i == '\r')
+ while ((ch = fgetc(in)) != EOF) {
+ if (ch == '\r')
continue;
- if (i == '\n')
+ if (ch == '\n')
if (conv_type == CT_UNIX2DOS)
fputc('\r', out);
- fputc(i, out);
+ fputc(ch, out);
}
if (fn != NULL) {
@@ -90,9 +111,12 @@ int dos2unix_main(int argc UNUSED_PARAM, char **argv)
int o, conv_type;
/* See if we are supposed to be doing dos2unix or unix2dos */
- conv_type = CT_UNIX2DOS;
- if (applet_name[0] == 'd') {
+ if (ENABLE_DOS2UNIX
+ && (!ENABLE_UNIX2DOS || applet_name[0] == 'd')
+ ) {
conv_type = CT_DOS2UNIX;
+ } else {
+ conv_type = CT_UNIX2DOS;
}
/* -u convert to unix, -d convert to dos */
diff --git a/coreutils/du.c b/coreutils/du.c
index 9c6ff88..03e31a0 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -8,10 +8,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
@@ -22,6 +18,26 @@
* 3) Added error checking of output.
* 4) Fixed busybox bug #1284 involving long overflow with human_readable.
*/
+//config:config DU
+//config: bool "du (default blocksize of 512 bytes)"
+//config: default y
+//config: help
+//config: du is used to report the amount of disk space used
+//config: for specified files.
+//config:
+//config:config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+//config: bool "Use a default blocksize of 1024 bytes (1K)"
+//config: default y
+//config: depends on DU
+//config: help
+//config: Use a blocksize of (1K) instead of the default 512b.
+
+//applet:IF_DU(APPLET(du, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DU) += du.o
+
+/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
//usage:#define du_trivial_usage
//usage: "[-aHLdclsx" IF_FEATURE_HUMAN_READABLE("hm") "k] [FILE]..."
@@ -58,6 +74,7 @@
//usage: "2417 .\n"
#include "libbb.h"
+#include "common_bufsiz.h"
enum {
OPT_a_files_too = (1 << 0),
@@ -75,7 +92,7 @@ enum {
struct globals {
#if ENABLE_FEATURE_HUMAN_READABLE
- unsigned long disp_hr;
+ unsigned long disp_unit;
#else
unsigned disp_k;
#endif
@@ -85,22 +102,31 @@ struct globals {
int du_depth;
dev_t dir_dev;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
-/* FIXME? coreutils' du rounds sizes up:
- * for example, 1025k file is shown as "2" by du -m.
- * We round to nearest.
- */
static void print(unsigned long long size, const char *filename)
{
/* TODO - May not want to defer error checking here. */
#if ENABLE_FEATURE_HUMAN_READABLE
+# if ENABLE_DESKTOP
+ /* ~30 bytes of code for extra comtat:
+ * coreutils' du rounds sizes up:
+ * for example, 1025k file is shown as "2" by du -m.
+ * We round to nearest if human-readable [too hard to fix],
+ * else (fixed scale such as -m), we round up. To that end,
+ * add yet another half of the unit before displaying:
+ */
+ if (G.disp_unit)
+ size += (G.disp_unit-1) / (unsigned)(512 * 2);
+# endif
printf("%s\t%s\n",
- /* size x 512 / G.disp_hr, show one fractional,
- * use suffixes if G.disp_hr == 0 */
- make_human_readable_str(size, 512, G.disp_hr),
+ /* size x 512 / G.disp_unit.
+ * If G.disp_unit == 0, show one fractional
+ * and use suffixes
+ */
+ make_human_readable_str(size, 512, G.disp_unit),
filename);
#else
if (G.disp_k) {
@@ -199,10 +225,10 @@ int du_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
#if ENABLE_FEATURE_HUMAN_READABLE
- IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 1024;)
- IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_hr = 512;)
+ IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_unit = 1024;)
+ IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_unit = 512;)
if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
- G.disp_hr = 512;
+ G.disp_unit = 512;
#else
IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;)
/* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
@@ -216,21 +242,21 @@ int du_main(int argc UNUSED_PARAM, char **argv)
* ignore -a. This is consistent with -s being equivalent to -d 0.
*/
#if ENABLE_FEATURE_HUMAN_READABLE
- opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
- opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth);
+ opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s";
+ opt = getopt32(argv, "aHkLsx" "d:+" "lc" "hm", &G.max_print_depth);
argv += optind;
if (opt & OPT_h_for_humans) {
- G.disp_hr = 0;
+ G.disp_unit = 0;
}
if (opt & OPT_m_mbytes) {
- G.disp_hr = 1024*1024;
+ G.disp_unit = 1024*1024;
}
if (opt & OPT_k_kbytes) {
- G.disp_hr = 1024;
+ G.disp_unit = 1024;
}
#else
- opt_complementary = "H-L:L-H:s-d:d-s:d+";
- opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth);
+ opt_complementary = "H-L:L-H:s-d:d-s";
+ opt = getopt32(argv, "aHkLsx" "d:+" "lc", &G.max_print_depth);
argv += optind;
#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
if (opt & OPT_k_kbytes) {
diff --git a/coreutils/echo.c b/coreutils/echo.c
index 9663894..fd0d9b7 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -9,10 +9,6 @@
*
* Original copyright notice is retained at the end of this file.
*/
-
-/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Because of behavioral differences, implemented configurable SUSv3
@@ -22,6 +18,26 @@
* 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
* The previous version did not allow 4-digit octals.
*/
+//config:config ECHO
+//config: bool "echo (basic SuSv3 version taking no options)"
+//config: default y
+//config: help
+//config: echo is used to print a specified string to stdout.
+//config:
+//config:# this entry also appears in shell/Config.in, next to the echo builtin
+//config:config FEATURE_FANCY_ECHO
+//config: bool "Enable echo options (-n and -e)"
+//config: default y
+//config: depends on ECHO || ASH_BUILTIN_ECHO || HUSH
+//config: help
+//config: This adds options (-n and -e) to echo.
+
+//applet:IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo))
+
+//kbuild:lib-$(CONFIG_ECHO) += echo.o
+
+/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
//usage:#define echo_trivial_usage
//usage: IF_FEATURE_FANCY_ECHO("[-neE] ") "[ARG]..."
@@ -72,7 +88,7 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
unsigned buflen;
#if !ENABLE_FEATURE_FANCY_ECHO
enum {
- eflag = '\\',
+ eflag = 0, /* 0 -- disable escape sequences */
nflag = 1, /* 1 -- print '\n' */
};
diff --git a/coreutils/env.c b/coreutils/env.c
index 807ef13..e91eddb 100644
--- a/coreutils/env.c
+++ b/coreutils/env.c
@@ -11,16 +11,11 @@
*
* Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Fixed bug involving exit return codes if execvp fails. Also added
* output error checking.
*/
-
/*
* Modified by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
* - correct "-" option usage
@@ -28,8 +23,27 @@
* - GNU long option support
* - use xfunc_error_retval
*/
+//config:config ENV
+//config: bool "env"
+//config: default y
+//config: help
+//config: env is used to set an environment variable and run
+//config: a command; without options it displays the current
+//config: environment.
+//config:
+//config:config FEATURE_ENV_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on ENV && LONG_OPTS
+//config: help
+//config: Support long options for the env applet.
-/* This is a NOEXEC applet. Be very careful! */
+//applet:IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env))
+
+//kbuild:lib-$(CONFIG_ENV) += env.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
//usage:#define env_trivial_usage
//usage: "[-iu] [-] [name=value]... [PROG ARGS]"
@@ -54,11 +68,10 @@ int env_main(int argc UNUSED_PARAM, char **argv)
unsigned opts;
llist_t *unset_env = NULL;
- opt_complementary = "u::";
#if ENABLE_FEATURE_ENV_LONG_OPTIONS
applet_long_options = env_longopts;
#endif
- opts = getopt32(argv, "+iu:", &unset_env);
+ opts = getopt32(argv, "+iu:+", &unset_env);
argv += optind;
if (argv[0] && LONE_DASH(argv[0])) {
opts |= 1;
diff --git a/coreutils/expand.c b/coreutils/expand.c
index 9a83fad..466c11a 100644
--- a/coreutils/expand.c
+++ b/coreutils/expand.c
@@ -20,6 +20,37 @@
*
* Caveat: this versions of expand and unexpand don't accept tab lists.
*/
+//config:config EXPAND
+//config: bool "expand"
+//config: default y
+//config: help
+//config: By default, convert all tabs to spaces.
+//config:
+//config:config FEATURE_EXPAND_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on EXPAND && LONG_OPTS
+//config: help
+//config: Support long options for the expand applet.
+//config:
+//config:config UNEXPAND
+//config: bool "unexpand"
+//config: default y
+//config: help
+//config: By default, convert only leading sequences of blanks to tabs.
+//config:
+//config:config FEATURE_UNEXPAND_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on UNEXPAND && LONG_OPTS
+//config: help
+//config: Support long options for the unexpand applet.
+
+//applet:IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand))
+
+//kbuild:lib-$(CONFIG_EXPAND) += expand.o
+//kbuild:lib-$(CONFIG_UNEXPAND) += expand.o
//usage:#define expand_trivial_usage
//usage: "[-i] [-t N] [FILE]..."
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 128775b..55a19aa 100644
--- a/coreutils/expr.c
+++ b/coreutils/expr.c
@@ -13,7 +13,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
/* This program evaluates expressions. Each token (operator, operand,
* parenthesis) of the expression must be a separate argument. The
* parser used is a reasonably general one, though any incarnation of
@@ -21,9 +20,27 @@
*
* No parse tree is needed; a new node is evaluated immediately.
* One function can handle multiple operators all of equal precedence,
- * provided they all associate ((x op x) op x). */
-
-/* no getopt needed */
+ * provided they all associate ((x op x) op x).
+ */
+//config:config EXPR
+//config: bool "expr"
+//config: default y
+//config: help
+//config: expr is used to calculate numbers and print the result
+//config: to standard output.
+//config:
+//config:config EXPR_MATH_SUPPORT_64
+//config: bool "Extend Posix numbers support to 64 bit"
+//config: default y
+//config: depends on EXPR
+//config: help
+//config: Enable 64-bit math support in the expr applet. This will make
+//config: the applet slightly larger, but will allow computation with very
+//config: large numbers.
+
+//applet:IF_EXPR(APPLET(expr, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_EXPR) += expr.o
//usage:#define expr_trivial_usage
//usage: "EXPRESSION"
@@ -61,6 +78,7 @@
//usage: "of characters matched or 0."
#include "libbb.h"
+#include "common_bufsiz.h"
#include "xregex.h"
#if ENABLE_EXPR_MATH_SUPPORT_64
@@ -99,8 +117,8 @@ typedef struct valinfo VALUE;
struct globals {
char **args;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
/* forward declarations */
static VALUE *eval(void);
@@ -113,7 +131,7 @@ static VALUE *int_value(arith_t i)
VALUE *v;
v = xzalloc(sizeof(VALUE));
- if (INTEGER) /* otherwise xzaaloc did it already */
+ if (INTEGER) /* otherwise xzalloc did it already */
v->type = INTEGER;
v->u.i = i;
return v;
@@ -126,7 +144,7 @@ static VALUE *str_value(const char *s)
VALUE *v;
v = xzalloc(sizeof(VALUE));
- if (STRING) /* otherwise xzaaloc did it already */
+ if (STRING) /* otherwise xzalloc did it already */
v->type = STRING;
v->u.s = xstrdup(s);
return v;
diff --git a/coreutils/false.c b/coreutils/false.c
index 59c2f32..b8f17c6 100644
--- a/coreutils/false.c
+++ b/coreutils/false.c
@@ -6,15 +6,22 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FALSE
+//config: bool "false"
+//config: default y
+//config: help
+//config: false returns an exit code of FALSE (1).
+
+//applet:IF_FALSE(APPLET_NOFORK(false, false, BB_DIR_BIN, BB_SUID_DROP, false))
+
+//kbuild:lib-$(CONFIG_FALSE) += false.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/000095399/utilities/false.html */
-//usage:#define false_trivial_usage
-//usage: ""
-//usage:#define false_full_usage "\n\n"
-//usage: "Return an exit code of FALSE (1)"
-//usage:
+/* "false --help" is special-cased to ignore --help */
+//usage:#define false_trivial_usage NOUSAGE_STR
+//usage:#define false_full_usage ""
//usage:#define false_example_usage
//usage: "$ false\n"
//usage: "$ echo $?\n"
diff --git a/coreutils/fold.c b/coreutils/fold.c
index 0e73063..01f9bce 100644
--- a/coreutils/fold.c
+++ b/coreutils/fold.c
@@ -9,6 +9,15 @@
Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FOLD
+//config: bool "fold"
+//config: default y
+//config: help
+//config: Wrap text to fit a specific width.
+
+//applet:IF_FOLD(APPLET_NOEXEC(fold, fold, BB_DIR_USR_BIN, BB_SUID_DROP, fold))
+
+//kbuild:lib-$(CONFIG_FOLD) += fold.o
//usage:#define fold_trivial_usage
//usage: "[-bs] [-w WIDTH] [FILE]..."
diff --git a/coreutils/fsync.c b/coreutils/fsync.c
index 652a41c..596a2bc 100644
--- a/coreutils/fsync.c
+++ b/coreutils/fsync.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FSYNC
+//config: bool "fsync"
+//config: default y
+//config: help
+//config: fsync is used to flush file-related cached blocks to disk.
+
+//applet:IF_FSYNC(APPLET_NOFORK(fsync, fsync, BB_DIR_BIN, BB_SUID_DROP, fsync))
+
+//kbuild:lib-$(CONFIG_FSYNC) += fsync.o
//usage:#define fsync_trivial_usage
//usage: "[-d] FILE..."
diff --git a/coreutils/head.c b/coreutils/head.c
index 9388b02..176e91e 100644
--- a/coreutils/head.c
+++ b/coreutils/head.c
@@ -6,13 +6,28 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config HEAD
+//config: bool "head"
+//config: default y
+//config: help
+//config: head is used to print the first specified number of lines
+//config: from files.
+//config:
+//config:config FEATURE_FANCY_HEAD
+//config: bool "Enable head options (-c, -q, and -v)"
+//config: default y
+//config: depends on HEAD
+//config: help
+//config: This enables the head options (-c, -q, and -v).
+
+//applet:IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head))
+
+//kbuild:lib-$(CONFIG_HEAD) += head.o
/* BB_AUDIT SUSv3 compliant */
/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/head.html */
-//kbuild:lib-$(CONFIG_HEAD) += head.o
-
//usage:#define head_trivial_usage
//usage: "[OPTIONS] [FILE]..."
//usage:#define head_full_usage "\n\n"
diff --git a/coreutils/hostid.c b/coreutils/hostid.c
index e5b1f51..5b47de1 100644
--- a/coreutils/hostid.c
+++ b/coreutils/hostid.c
@@ -6,9 +6,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
-
//config:config HOSTID
//config: bool "hostid"
//config: default y
@@ -20,6 +17,8 @@
//kbuild:lib-$(CONFIG_HOSTID) += hostid.o
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
//usage:#define hostid_trivial_usage
//usage: ""
//usage:#define hostid_full_usage "\n\n"
diff --git a/coreutils/id.c b/coreutils/id.c
index 1f3e1c4..ab7ac1e 100644
--- a/coreutils/id.c
+++ b/coreutils/id.c
@@ -7,31 +7,30 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant. */
/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
* length and to be more similar to GNU id.
* -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
* Added -G option Tito Ragusa (C) 2008 for SUSv3.
*/
-
//config:config ID
//config: bool "id"
//config: default y
//config: help
//config: id displays the current user and group ID names.
-
+//config:
//config:config GROUPS
//config: bool "groups"
//config: default y
//config: help
//config: Print the group names associated with current user id.
+//applet:IF_GROUPS(APPLET_NOEXEC(groups, id, BB_DIR_USR_BIN, BB_SUID_DROP, groups))
+//applet:IF_ID( APPLET_NOEXEC(id, id, BB_DIR_USR_BIN, BB_SUID_DROP, id ))
+
//kbuild:lib-$(CONFIG_GROUPS) += id.o
//kbuild:lib-$(CONFIG_ID) += id.o
-//applet:IF_GROUPS(APPLET_NOEXEC(groups, id, BB_DIR_USR_BIN, BB_SUID_DROP, groups))
-//applet:IF_ID( APPLET_NOEXEC(id, id, BB_DIR_USR_BIN, BB_SUID_DROP, id ))
+/* BB_AUDIT SUSv3 compliant. */
//usage:#define id_trivial_usage
//usage: "[OPTIONS] [USER]"
diff --git a/coreutils/install.c b/coreutils/install.c
index 445497f..831f9b8 100644
--- a/coreutils/install.c
+++ b/coreutils/install.c
@@ -5,10 +5,26 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config INSTALL
+//config: bool "install"
+//config: default y
+//config: help
+//config: Copy files and set attributes.
+//config:
+//config:config FEATURE_INSTALL_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on INSTALL && LONG_OPTS
+//config: help
+//config: Support long options for the install applet.
+
+//applet:IF_INSTALL(APPLET(install, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_INSTALL) += install.o
/* -v, -b, -c are ignored */
//usage:#define install_trivial_usage
-//usage: "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [SOURCE]... DEST"
+//usage: "[-cdDsp] [-o USER] [-g GRP] [-m MODE] [-t DIR] [SOURCE]... DEST"
//usage:#define install_full_usage "\n\n"
//usage: "Copy files and set attributes\n"
//usage: "\n -c Just copy (default)"
@@ -19,6 +35,7 @@
//usage: "\n -o USER Set ownership"
//usage: "\n -g GRP Set group ownership"
//usage: "\n -m MODE Set permissions"
+//usage: "\n -t DIR Install to DIR"
//usage: IF_SELINUX(
//usage: "\n -Z Set security context"
//usage: )
@@ -28,12 +45,16 @@
#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
static const char install_longopts[] ALIGN1 =
+ IF_FEATURE_VERBOSE(
+ "verbose\0" No_argument "v"
+ )
"directory\0" No_argument "d"
"preserve-timestamps\0" No_argument "p"
"strip\0" No_argument "s"
"group\0" Required_argument "g"
"mode\0" Required_argument "m"
"owner\0" Required_argument "o"
+ "target-directory\0" Required_argument "t"
/* autofs build insists of using -b --suffix=.orig */
/* TODO? (short option for --suffix is -S) */
#if ENABLE_SELINUX
@@ -89,11 +110,11 @@ int install_main(int argc, char **argv)
const char *gid_str;
const char *uid_str;
const char *mode_str;
+ int mkdir_flags = FILEUTILS_RECUR;
int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
int opts;
- int min_args = 1;
int ret = EXIT_SUCCESS;
- int isdir = 0;
+ int isdir;
#if ENABLE_SELINUX
security_context_t scontext;
bool use_default_selinux_context = 1;
@@ -109,21 +130,22 @@ int install_main(int argc, char **argv)
OPT_GROUP = 1 << 7,
OPT_MODE = 1 << 8,
OPT_OWNER = 1 << 9,
+ OPT_TARGET = 1 << 10,
#if ENABLE_SELINUX
- OPT_SET_SECURITY_CONTEXT = 1 << 10,
- OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11,
+ OPT_SET_SECURITY_CONTEXT = 1 << 11,
+ OPT_PRESERVE_SECURITY_CONTEXT = 1 << 12,
#endif
};
#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
applet_long_options = install_longopts;
#endif
- opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
+ opt_complementary = "t--d:d--t:s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
/* -c exists for backwards compatibility, it's needed */
- /* -v is ignored ("print name of each created directory") */
/* -b is ignored ("make a backup of each existing destination file") */
- opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"),
- &gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext));
+ opts = getopt32(argv, "cvb" "Ddpsg:m:o:t:" IF_SELINUX("Z:"),
+ &gid_str, &mode_str, &uid_str, &last
+ IF_SELINUX(, &scontext));
argc -= optind;
argv += optind;
@@ -141,6 +163,11 @@ int install_main(int argc, char **argv)
}
#endif
+ if ((opts & OPT_v) && FILEUTILS_VERBOSE) {
+ mkdir_flags |= FILEUTILS_VERBOSE;
+ copy_flags |= FILEUTILS_VERBOSE;
+ }
+
/* preserve access and modification time, this is GNU behaviour,
* BSD only preserves modification time */
if (opts & OPT_PRESERVE_TIME) {
@@ -148,39 +175,44 @@ int install_main(int argc, char **argv)
}
mode = 0755; /* GNU coreutils 6.10 compat */
if (opts & OPT_MODE)
- bb_parse_mode(mode_str, &mode);
+ mode = bb_parse_mode(mode_str, mode);
uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
- last = argv[argc - 1];
- if (!(opts & OPT_DIRECTORY)) {
- argv[argc - 1] = NULL;
- min_args++;
-
+ /* If -t DIR is in use, then isdir=true, last="DIR" */
+ isdir = (opts & OPT_TARGET);
+ if (!(opts & (OPT_TARGET|OPT_DIRECTORY))) {
+ /* Neither -t DIR nor -d is in use */
+ argc--;
+ last = argv[argc];
+ argv[argc] = NULL;
/* coreutils install resolves link in this case, don't use lstat */
isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
}
- if (argc < min_args)
+ if (argc < 1)
bb_show_usage();
while ((arg = *argv++) != NULL) {
- char *dest = last;
+ char *dest;
+
if (opts & OPT_DIRECTORY) {
dest = arg;
/* GNU coreutils 6.9 does not set uid:gid
* on intermediate created directories
* (only on last one) */
- if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) {
+ if (bb_make_directory(dest, 0755, mkdir_flags)) {
ret = EXIT_FAILURE;
goto next;
}
} else {
+ dest = last;
if (opts & OPT_MKDIR_LEADING) {
char *ddir = xstrdup(dest);
- bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR);
+ bb_make_directory(dirname(ddir), 0755, mkdir_flags);
/* errors are not checked. copy_file
- * will fail if dir is not created. */
+ * will fail if dir is not created.
+ */
free(ddir);
}
if (isdir)
diff --git a/coreutils/length.c.disabled b/coreutils/length.c.disabled
deleted file mode 100644
index aee898d..0000000
--- a/coreutils/length.c.disabled
+++ b/dev/null
@@ -1,31 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-
-/* BB_AUDIT SUSv3 N/A -- Apparently a busybox (obsolete?) extension. */
-
-//usage:#define length_trivial_usage
-//usage: "STRING"
-//usage:#define length_full_usage "\n\n"
-//usage: "Print STRING's length"
-//usage:
-//usage:#define length_example_usage
-//usage: "$ length Hello\n"
-//usage: "5\n"
-
-#include "libbb.h"
-
-/* This is a NOFORK applet. Be very careful! */
-
-int length_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int length_main(int argc, char **argv)
-{
- if ((argc != 2) || (**(++argv) == '-')) {
- bb_show_usage();
- }
-
- printf("%u\n", (unsigned)strlen(*argv));
-
- return fflush_all();
-}
diff --git a/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/coreutils/libcoreutils/getopt_mk_fifo_nod.c
index 2227171..47375ff 100644
--- a/coreutils/libcoreutils/getopt_mk_fifo_nod.c
+++ b/coreutils/libcoreutils/getopt_mk_fifo_nod.c
@@ -33,7 +33,9 @@ mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv)
int opt;
opt = getopt32(argv, "m:" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext));
if (opt & 1) {
- if (bb_parse_mode(smode, &mode))
+ mode = bb_parse_mode(smode, mode);
+ if (mode != (mode_t)-1) /* if mode is valid */
+ /* make future mknod/mkfifo set mode bits exactly */
umask(0);
}
diff --git a/coreutils/ln.c b/coreutils/ln.c
index 3b822e8..0e2abac 100644
--- a/coreutils/ln.c
+++ b/coreutils/ln.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LN
+//config: bool "ln"
+//config: default y
+//config: help
+//config: ln is used to create hard or soft links between files.
+
+//applet:IF_LN(APPLET_NOEXEC(ln, ln, BB_DIR_BIN, BB_SUID_DROP, ln))
+
+//kbuild:lib-$(CONFIG_LN) += ln.o
/* BB_AUDIT SUSv3 compliant */
/* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
@@ -134,7 +143,6 @@ int ln_main(int argc, char **argv)
}
free(src_name);
-
} while ((++argv)[1]);
return status;
diff --git a/coreutils/logname.c b/coreutils/logname.c
index 10b9615..a9b1c95 100644
--- a/coreutils/logname.c
+++ b/coreutils/logname.c
@@ -6,10 +6,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* SUSv3 specifies the string used is that returned from getlogin().
@@ -19,6 +15,18 @@
* correct course of action wrt SUSv3 for a failing getlogin() is
* a diagnostic message and an error return.
*/
+//config:config LOGNAME
+//config: bool "logname"
+//config: default y
+//config: help
+//config: logname is used to print the current user's login name.
+
+//applet:IF_LOGNAME(APPLET_NOFORK(logname, logname, BB_DIR_USR_BIN, BB_SUID_DROP, logname))
+
+//kbuild:lib-$(CONFIG_LOGNAME) += logname.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
//usage:#define logname_trivial_usage
//usage: ""
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 91552d7..e7490ac 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -4,7 +4,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
/* [date unknown. Perhaps before year 2000]
* To achieve a small memory footprint, this version of 'ls' doesn't do any
* file sorting, and only has the most essential command line switches
@@ -28,6 +27,75 @@
* [2009-03]
* ls sorts listing now, and supports almost all options.
*/
+//config:config LS
+//config: bool "ls"
+//config: default y
+//config: help
+//config: ls is used to list the contents of directories.
+//config:
+//config:config FEATURE_LS_FILETYPES
+//config: bool "Enable filetyping options (-p and -F)"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Enable the ls options (-p and -F).
+//config:
+//config:config FEATURE_LS_FOLLOWLINKS
+//config: bool "Enable symlinks dereferencing (-L)"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Enable the ls option (-L).
+//config:
+//config:config FEATURE_LS_RECURSIVE
+//config: bool "Enable recursion (-R)"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Enable the ls option (-R).
+//config:
+//config:config FEATURE_LS_SORTFILES
+//config: bool "Sort the file names"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Allow ls to sort file names alphabetically.
+//config:
+//config:config FEATURE_LS_TIMESTAMPS
+//config: bool "Show file timestamps"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Allow ls to display timestamps for files.
+//config:
+//config:config FEATURE_LS_USERNAME
+//config: bool "Show username/groupnames"
+//config: default y
+//config: depends on LS
+//config: help
+//config: Allow ls to display username/groupname for files.
+//config:
+//config:config FEATURE_LS_COLOR
+//config: bool "Allow use of color to identify file types"
+//config: default y
+//config: depends on LS && LONG_OPTS
+//config: help
+//config: This enables the --color option to ls.
+//config:
+//config:config FEATURE_LS_COLOR_IS_DEFAULT
+//config: bool "Produce colored ls output by default"
+//config: default y
+//config: depends on FEATURE_LS_COLOR
+//config: help
+//config: Saying yes here will turn coloring on by default,
+//config: even if no "--color" option is given to the ls command.
+//config: This is not recommended, since the colors are not
+//config: configurable, and the output may not be legible on
+//config: many output screens.
+
+//applet:IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
+
+//kbuild:lib-$(CONFIG_LS) += ls.o
//usage:#define ls_trivial_usage
//usage: "[-1AaCxd"
@@ -93,6 +161,7 @@
//usage: )
#include "libbb.h"
+#include "common_bufsiz.h"
#include "unicode.h"
@@ -365,8 +434,9 @@ struct globals {
time_t current_time_t;
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
/* we have to zero it out because of NOEXEC */ \
memset(&G, 0, sizeof(G)); \
IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
@@ -581,12 +651,12 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
#if ENABLE_FEATURE_LS_TIMESTAMPS
if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
char *filetime;
- time_t ttime = dn->dn_mtime;
+ const time_t *ttime = &dn->dn_mtime;
if (G.all_fmt & TIME_ACCESS)
- ttime = dn->dn_atime;
+ ttime = &dn->dn_atime;
if (G.all_fmt & TIME_CHANGE)
- ttime = dn->dn_ctime;
- filetime = ctime(&ttime);
+ ttime = &dn->dn_ctime;
+ filetime = ctime(ttime);
/* filetime's format: "Wed Jun 30 21:49:08 1993\n" */
if (G.all_fmt & LIST_FULLTIME) { /* -e */
/* Note: coreutils 8.4 ls --full-time prints:
@@ -595,13 +665,16 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
column += printf("%.24s ", filetime);
} else { /* LIST_DATE_TIME */
/* G.current_time_t ~== time(NULL) */
- time_t age = G.current_time_t - ttime;
- printf("%.6s ", filetime + 4); /* "Jun 30" */
+ time_t age = G.current_time_t - *ttime;
if (age < 3600L * 24 * 365 / 2 && age > -15 * 60) {
- /* hh:mm if less than 6 months old */
- printf("%.5s ", filetime + 11);
- } else { /* year. buggy if year > 9999 ;) */
- printf(" %.4s ", filetime + 20);
+ /* less than 6 months old */
+ /* "mmm dd hh:mm " */
+ printf("%.12s ", filetime + 4);
+ } else {
+ /* "mmm dd yyyy " */
+ /* "mmm dd yyyyy " after year 9999 :) */
+ strchr(filetime + 20, '\n')[0] = ' ';
+ printf("%.7s%6s", filetime + 4, filetime + 20);
}
column += 13;
}
@@ -680,7 +753,7 @@ static void display_files(struct dnode **dn, unsigned nfiles)
if ((int)column_width < len)
column_width = len;
}
- column_width += 1 +
+ column_width += 2 +
IF_SELINUX( ((G.all_fmt & LIST_CONTEXT) ? 33 : 0) + )
((G.all_fmt & LIST_INO) ? 8 : 0) +
((G.all_fmt & LIST_BLOCKS) ? 5 : 0);
@@ -708,8 +781,8 @@ static void display_files(struct dnode **dn, unsigned nfiles)
if (i < nfiles) {
if (column > 0) {
nexttab -= column;
- printf("%*s ", nexttab, "");
- column += nexttab + 1;
+ printf("%*s", nexttab, "");
+ column += nexttab;
}
nexttab = column + column_width;
column += display_single(dn[i]);
@@ -1047,7 +1120,7 @@ static void scan_and_display_dirs_recur(struct dnode **dn, int first)
}
subdnp = scan_one_dir((*dn)->fullname, &nfiles);
#if ENABLE_DESKTOP
- if ((G.all_fmt & STYLE_MASK) == STYLE_LONG)
+ if ((G.all_fmt & STYLE_MASK) == STYLE_LONG || (G.all_fmt & LIST_BLOCKS))
printf("total %"OFF_FMT"u\n", calculate_blocks(subdnp));
#endif
if (nfiles > 0) {
@@ -1117,7 +1190,7 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_AUTOWIDTH
/* obtain the terminal width */
- get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
+ G_terminal_width = get_terminal_width(STDIN_FILENO);
/* go one less... */
G_terminal_width--;
#endif
diff --git a/coreutils/md5_sha1_sum.c b/coreutils/md5_sha1_sum.c
index 1a5342e..783f440 100644
--- a/coreutils/md5_sha1_sum.c
+++ b/coreutils/md5_sha1_sum.c
@@ -5,6 +5,60 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config MD5SUM
+//config: bool "md5sum"
+//config: default y
+//config: help
+//config: md5sum is used to print or check MD5 checksums.
+//config:
+//config:config SHA1SUM
+//config: bool "sha1sum"
+//config: default y
+//config: help
+//config: Compute and check SHA1 message digest
+//config:
+//config:config SHA256SUM
+//config: bool "sha256sum"
+//config: default y
+//config: help
+//config: Compute and check SHA256 message digest
+//config:
+//config:config SHA512SUM
+//config: bool "sha512sum"
+//config: default y
+//config: help
+//config: Compute and check SHA512 message digest
+//config:
+//config:config SHA3SUM
+//config: bool "sha3sum"
+//config: default y
+//config: help
+//config: Compute and check SHA3 message digest
+//config:
+//config:comment "Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum"
+//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
+//config:
+//config:config FEATURE_MD5_SHA1_SUM_CHECK
+//config: bool "Enable -c, -s and -w options"
+//config: default y
+//config: depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM || SHA3SUM
+//config: help
+//config: Enabling the -c options allows files to be checked
+//config: against pre-calculated hash values.
+//config:
+//config: -s and -w are useful options when verifying checksums.
+
+//applet:IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
+//applet:IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
+//applet:IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
+//applet:IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
+//applet:IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
+
+//kbuild:lib-$(CONFIG_MD5SUM) += md5_sha1_sum.o
+//kbuild:lib-$(CONFIG_SHA1SUM) += md5_sha1_sum.o
+//kbuild:lib-$(CONFIG_SHA256SUM) += md5_sha1_sum.o
+//kbuild:lib-$(CONFIG_SHA512SUM) += md5_sha1_sum.o
+//kbuild:lib-$(CONFIG_SHA3SUM) += md5_sha1_sum.o
//usage:#define md5sum_trivial_usage
//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
@@ -57,15 +111,20 @@
//usage: )
//usage:
//usage:#define sha3sum_trivial_usage
-//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[FILE]..."
+//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK("[-c[sw]] ")"[-a BITS] [FILE]..."
//usage:#define sha3sum_full_usage "\n\n"
-//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3-512 checksums"
+//usage: "Print" IF_FEATURE_MD5_SHA1_SUM_CHECK(" or check") " SHA3 checksums"
//usage: IF_FEATURE_MD5_SHA1_SUM_CHECK( "\n"
//usage: "\n -c Check sums against list in FILEs"
//usage: "\n -s Don't output anything, status code shows success"
//usage: "\n -w Warn about improperly formatted checksum lines"
+//usage: "\n -a BITS 224 (default), 256, 384, 512"
//usage: )
+//FIXME: GNU coreutils 8.25 has no -s option, it has only these two long opts:
+// --quiet don't print OK for each successfully verified file
+// --status don't output anything, status code shows success
+
#include "libbb.h"
/* This is a NOEXEC applet. Be very careful! */
@@ -93,7 +152,10 @@ static unsigned char *hash_bin_to_hex(unsigned char *hash_value,
return (unsigned char *)hex_value;
}
-static uint8_t *hash_file(const char *filename)
+#if !ENABLE_SHA3SUM
+# define hash_file(f,w) hash_file(f)
+#endif
+static uint8_t *hash_file(const char *filename, unsigned sha3_width)
{
int src_fd, hash_len, count;
union _ctx_ {
@@ -121,27 +183,47 @@ static uint8_t *hash_file(const char *filename)
update = (void*)md5_hash;
final = (void*)md5_end;
hash_len = 16;
- } else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
+ }
+ else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
sha1_begin(&context.sha1);
update = (void*)sha1_hash;
final = (void*)sha1_end;
hash_len = 20;
- } else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
+ }
+ else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
sha256_begin(&context.sha256);
update = (void*)sha256_hash;
final = (void*)sha256_end;
hash_len = 32;
- } else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
+ }
+ else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
sha512_begin(&context.sha512);
update = (void*)sha512_hash;
final = (void*)sha512_end;
hash_len = 64;
- } else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
+ }
+#if ENABLE_SHA3SUM
+ else if (ENABLE_SHA3SUM && hash_algo == HASH_SHA3) {
sha3_begin(&context.sha3);
update = (void*)sha3_hash;
final = (void*)sha3_end;
- hash_len = 64;
- } else {
+ /*
+ * Should support 224, 256, 384, 512.
+ * We allow any value which does not blow the algorithm up.
+ */
+ if (sha3_width >= 1600/2 /* input block can't be <= 0 */
+ || sha3_width == 0 /* hash len can't be 0 */
+ || (sha3_width & 0x1f) /* should be multiple of 32 */
+ /* (because input uses up to 8 byte wide word XORs. 32/4=8) */
+ ) {
+ bb_error_msg_and_die("bad -a%u", sha3_width);
+ }
+ sha3_width /= 4;
+ context.sha3.input_block_bytes = 1600/8 - sha3_width;
+ hash_len = sha3_width/2;
+ }
+#endif
+ else {
xfunc_die(); /* can't reach this */
}
@@ -172,28 +254,33 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
{
int return_value = EXIT_SUCCESS;
unsigned flags;
+#if ENABLE_SHA3SUM
+ unsigned sha3_width = 224;
+#endif
if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK) {
+ /* -s and -w require -c */
+ opt_complementary = "s?c:w?c";
/* -b "binary", -t "text" are ignored (shaNNNsum compat) */
- flags = getopt32(argv, "scwbt");
- argv += optind;
- //argc -= optind;
+#if ENABLE_SHA3SUM
+ if (applet_name[3] == HASH_SHA3)
+ flags = getopt32(argv, "scwbta:+", &sha3_width);
+ else
+#endif
+ flags = getopt32(argv, "scwbt");
} else {
- argv += 1;
- //argc -= 1;
+#if ENABLE_SHA3SUM
+ if (applet_name[3] == HASH_SHA3)
+ getopt32(argv, "a:+", &sha3_width);
+ else
+#endif
+ getopt32(argv, "");
}
+ argv += optind;
+ //argc -= optind;
if (!*argv)
*--argv = (char*)"-";
- if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && !(flags & FLAG_CHECK)) {
- if (flags & FLAG_SILENT) {
- bb_error_msg_and_die("-%c is meaningful only with -c", 's');
- }
- if (flags & FLAG_WARN) {
- bb_error_msg_and_die("-%c is meaningful only with -c", 'w');
- }
- }
-
do {
if (ENABLE_FEATURE_MD5_SHA1_SUM_CHECK && (flags & FLAG_CHECK)) {
FILE *pre_computed_stream;
@@ -225,7 +312,7 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
*filename_ptr = '\0';
filename_ptr += 2;
- hash_value = hash_file(filename_ptr);
+ hash_value = hash_file(filename_ptr, sha3_width);
if (hash_value && (strcmp((char*)hash_value, line) == 0)) {
if (!(flags & FLAG_SILENT))
@@ -244,9 +331,17 @@ int md5_sha1_sum_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg("WARNING: %d of %d computed checksums did NOT match",
count_failed, count_total);
}
+ if (count_total == 0) {
+ return_value = EXIT_FAILURE;
+ /*
+ * md5sum from GNU coreutils 8.25 says:
+ * md5sum: <FILE>: no properly formatted MD5 checksum lines found
+ */
+ bb_error_msg("%s: no checksum lines found", *argv);
+ }
fclose_if_not_stdin(pre_computed_stream);
} else {
- uint8_t *hash_value = hash_file(*argv);
+ uint8_t *hash_value = hash_file(*argv, sha3_width);
if (hash_value == NULL) {
return_value = EXIT_FAILURE;
} else {
diff --git a/coreutils/mkdir.c b/coreutils/mkdir.c
index 4a8e43e..3afe76c 100644
--- a/coreutils/mkdir.c
+++ b/coreutils/mkdir.c
@@ -6,18 +6,32 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Fixed broken permission setting when -p was used; especially in
* conjunction with -m.
*/
-
/* Nov 28, 2006 Yoshinori Sato <ysato@users.sourceforge.jp>: Add SELinux Support.
*/
+//config:config MKDIR
+//config: bool "mkdir"
+//config: default y
+//config: help
+//config: mkdir is used to create directories with the specified names.
+//config:
+//config:config FEATURE_MKDIR_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on MKDIR && LONG_OPTS
+//config: help
+//config: Support long options for the mkdir applet.
+
+//applet:IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir))
+
+//kbuild:lib-$(CONFIG_MKDIR) += mkdir.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
//usage:#define mkdir_trivial_usage
//usage: "[OPTIONS] DIRECTORY..."
@@ -48,7 +62,9 @@ static const char mkdir_longopts[] ALIGN1 =
#if ENABLE_SELINUX
"context\0" Required_argument "Z"
#endif
+#if ENABLE_FEATURE_VERBOSE
"verbose\0" No_argument "v"
+#endif
;
#endif
@@ -67,18 +83,20 @@ int mkdir_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
applet_long_options = mkdir_longopts;
#endif
- opt = getopt32(argv, "m:p" IF_SELINUX("Z:") "v", &smode IF_SELINUX(,&scontext));
+ opt = getopt32(argv, "m:pv" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext));
if (opt & 1) {
- mode_t mmode = 0777;
- if (!bb_parse_mode(smode, &mmode)) {
+ mode_t mmode = bb_parse_mode(smode, 0777);
+ if (mmode == (mode_t)-1) {
bb_error_msg_and_die("invalid mode '%s'", smode);
}
mode = mmode;
}
if (opt & 2)
flags |= FILEUTILS_RECUR;
+ if ((opt & 4) && FILEUTILS_VERBOSE)
+ flags |= FILEUTILS_VERBOSE;
#if ENABLE_SELINUX
- if (opt & 4) {
+ if (opt & 8) {
selinux_or_die();
setfscreatecon_or_die(scontext);
}
diff --git a/coreutils/mkfifo.c b/coreutils/mkfifo.c
index ef58325..66509a9 100644
--- a/coreutils/mkfifo.c
+++ b/coreutils/mkfifo.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config MKFIFO
+//config: bool "mkfifo"
+//config: default y
+//config: help
+//config: mkfifo is used to create FIFOs (named pipes).
+//config: The 'mknod' program can also create FIFOs.
+
+//applet:IF_MKFIFO(APPLET_NOEXEC(mkfifo, mkfifo, BB_DIR_USR_BIN, BB_SUID_DROP, mkfifo))
+
+//kbuild:lib-$(CONFIG_MKFIFO) += mkfifo.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkfifo.html */
diff --git a/coreutils/mknod.c b/coreutils/mknod.c
index aa04504..466ef5c 100644
--- a/coreutils/mknod.c
+++ b/coreutils/mknod.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config MKNOD
+//config: bool "mknod"
+//config: default y
+//config: help
+//config: mknod is used to create FIFOs or block/character special
+//config: files with the specified names.
+
+//applet:IF_MKNOD(APPLET_NOEXEC(mknod, mknod, BB_DIR_BIN, BB_SUID_DROP, mknod))
+
+//kbuild:lib-$(CONFIG_MKNOD) += mknod.o
/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
diff --git a/coreutils/mv.c b/coreutils/mv.c
index f127dfa..1cc318f 100644
--- a/coreutils/mv.c
+++ b/coreutils/mv.c
@@ -7,14 +7,26 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction and improved error checking.
*/
+//config:config MV
+//config: bool "mv"
+//config: default y
+//config: help
+//config: mv is used to move or rename files or directories.
+//config:
+//config:config FEATURE_MV_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on MV && LONG_OPTS
+//config: help
+//config: Support long options for the mv applet.
-#include "libbb.h"
-#include "libcoreutils/coreutils.h"
+//applet:IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MV) += mv.o
//usage:#define mv_trivial_usage
//usage: "[-fin] SOURCE DEST\n"
@@ -28,18 +40,25 @@
//usage:#define mv_example_usage
//usage: "$ mv /tmp/foo /bin/bar\n"
+#include "libbb.h"
+#include "libcoreutils/coreutils.h"
+
#if ENABLE_FEATURE_MV_LONG_OPTIONS
static const char mv_longopts[] ALIGN1 =
"interactive\0" No_argument "i"
"force\0" No_argument "f"
"no-clobber\0" No_argument "n"
+ IF_FEATURE_VERBOSE(
"verbose\0" No_argument "v"
+ )
;
#endif
#define OPT_FORCE (1 << 0)
#define OPT_INTERACTIVE (1 << 1)
#define OPT_NOCLOBBER (1 << 2)
+#define OPT_VERBOSE ((1 << 3) * ENABLE_FEATURE_VERBOSE)
+
int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int mv_main(int argc, char **argv)
@@ -58,7 +77,6 @@ int mv_main(int argc, char **argv)
/* Need at least two arguments.
* If more than one of -f, -i, -n is specified , only the final one
* takes effect (it unsets previous options).
- * -v is accepted but ignored.
*/
opt_complementary = "-2:f-in:i-fn:n-fi";
flags = getopt32(argv, "finv");
@@ -148,6 +166,9 @@ int mv_main(int argc, char **argv)
status = 1;
}
RET_0:
+ if (flags & OPT_VERBOSE) {
+ printf("'%s' -> '%s'\n", *argv, dest);
+ }
if (dest != last) {
free((void *) dest);
}
diff --git a/coreutils/nice.c b/coreutils/nice.c
index ce75991..3676ee6 100644
--- a/coreutils/nice.c
+++ b/coreutils/nice.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config NICE
+//config: bool "nice"
+//config: default y
+//config: help
+//config: nice runs a program with modified scheduling priority.
+
+//applet:IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NICE) += nice.o
//usage:#define nice_trivial_usage
//usage: "[-n ADJUST] [PROG ARGS]"
diff --git a/coreutils/nohup.c b/coreutils/nohup.c
index 63853fd..d848968 100644
--- a/coreutils/nohup.c
+++ b/coreutils/nohup.c
@@ -9,6 +9,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config NOHUP
+//config: bool "nohup"
+//config: default y
+//config: help
+//config: run a command immune to hangups, with output to a non-tty.
+
+//applet:IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NOHUP) += nohup.o
//usage:#define nohup_trivial_usage
//usage: "PROG ARGS"
diff --git a/coreutils/od.c b/coreutils/od.c
index fb11fcf..4b05ee7 100644
--- a/coreutils/od.c
+++ b/coreutils/od.c
@@ -10,6 +10,15 @@
*
* Original copyright notice is retained at the end of this file.
*/
+//config:config OD
+//config: bool "od"
+//config: default y
+//config: help
+//config: od is used to dump binary files in octal and other formats.
+
+//applet:IF_OD(APPLET(od, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_OD) += od.o
//usage:#if !ENABLE_DESKTOP
//usage:#define od_trivial_usage
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 6b00a43..475fb3f 100644
--- a/coreutils/od_bloaty.c
+++ b/coreutils/od_bloaty.c
@@ -20,6 +20,7 @@
/* #include "libbb.h" - done in od.c */
+#include "common_bufsiz.h"
#define assert(a) ((void)0)
@@ -61,12 +62,12 @@ enum {
};
#define OD_GETOPT32() getopt32(argv, \
- "A:N:abcdfhij:lot:vxsS:w::", \
+ "A:N:abcdfhij:lot:*vxsS:w:+:", \
/* -w with optional param */ \
/* -S was -s and also had optional parameter */ \
/* but in coreutils 6.3 it was renamed and now has */ \
/* _mandatory_ parameter */ \
- &str_A, &str_N, &str_j, &lst_t, &str_S, &bytes_per_block)
+ &str_A, &str_N, &str_j, &lst_t, &str_S, &G.bytes_per_block)
/* Check for 0x7f is a coreutils 6.3 addition */
@@ -174,38 +175,53 @@ struct ERR_width_bytes_has_bad_size {
char ERR_width_bytes_has_bad_size[ARRAY_SIZE(width_bytes) == N_SIZE_SPECS ? 1 : -1];
};
-static smallint exit_code;
+struct globals {
+ smallint exit_code;
-static unsigned string_min;
+ unsigned string_min;
-/* An array of specs describing how to format each input block. */
-static size_t n_specs;
-static struct tspec *spec;
+ /* An array of specs describing how to format each input block. */
+ unsigned n_specs;
+ struct tspec *spec;
-/* Function that accepts an address and an optional following char,
- and prints the address and char to stdout. */
-static void (*format_address)(off_t, char);
-/* The difference between the old-style pseudo starting address and
- the number of bytes to skip. */
+ /* Function that accepts an address and an optional following char,
+ and prints the address and char to stdout. */
+ void (*format_address)(off_t, char);
+
+ /* The difference between the old-style pseudo starting address and
+ the number of bytes to skip. */
#if ENABLE_LONG_OPTS
-static off_t pseudo_offset;
-#else
-enum { pseudo_offset = 0 };
+ off_t pseudo_offset;
+# define G_pseudo_offset G.pseudo_offset
#endif
-/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
- input is formatted. */
-
-/* The number of input bytes formatted per output line. It must be
- a multiple of the least common multiple of the sizes associated with
- the specified output types. It should be as large as possible, but
- no larger than 16 -- unless specified with the -w option. */
-static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
-
-/* A NULL-terminated list of the file-arguments from the command line. */
-static const char *const *file_list;
+ /* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
+ input is formatted. */
+
+ /* The number of input bytes formatted per output line. It must be
+ a multiple of the least common multiple of the sizes associated with
+ the specified output types. It should be as large as possible, but
+ no larger than 16 -- unless specified with the -w option. */
+ unsigned bytes_per_block; /* have to use unsigned, not size_t */
+
+ /* A NULL-terminated list of the file-arguments from the command line. */
+ const char *const *file_list;
+
+ /* The input stream associated with the current file. */
+ FILE *in_stream;
+
+ bool not_first;
+ bool prev_pair_equal;
+} FIX_ALIASING;
+#if !ENABLE_LONG_OPTS
+enum { G_pseudo_offset = 0 };
+#endif
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+ G.bytes_per_block = 32; \
+} while (0)
-/* The input stream associated with the current file. */
-static FILE *in_stream;
#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
@@ -387,11 +403,11 @@ print_named_ascii(size_t n_bytes, const char *block,
" sp"
};
// buf[N] pos: 01234 56789
- char buf[12] = " x\0 0xx\0";
- // actually " x\0 xxx\0", but want to share string with print_ascii.
+ char buf[12] = " x\0 xxx\0";
// [12] because we take three 32bit stack slots anyway, and
// gcc is too dumb to initialize with constant stores,
// it copies initializer from rodata. Oh well.
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65410
while (n_bytes--) {
unsigned masked_c = *(unsigned char *) block++;
@@ -419,7 +435,7 @@ print_ascii(size_t n_bytes, const char *block,
const char *unused_fmt_string UNUSED_PARAM)
{
// buf[N] pos: 01234 56789
- char buf[12] = " x\0 0xx\0";
+ char buf[12] = " x\0 xxx\0";
while (n_bytes--) {
const char *s;
@@ -455,11 +471,9 @@ print_ascii(size_t n_bytes, const char *block,
case '\v':
s = " \\v";
break;
- case '\x7f':
- s = " 177";
- break;
- default: /* c is never larger than 040 */
- buf[7] = (c >> 3) + '0';
+ default:
+ buf[6] = (c >> 6 & 3) + '0';
+ buf[7] = (c >> 3 & 7) + '0';
buf[8] = (c & 7) + '0';
s = buf + 5;
}
@@ -478,17 +492,17 @@ static void
open_next_file(void)
{
while (1) {
- if (!*file_list)
+ if (!*G.file_list)
return;
- in_stream = fopen_or_warn_stdin(*file_list++);
- if (in_stream) {
+ G.in_stream = fopen_or_warn_stdin(*G.file_list++);
+ if (G.in_stream) {
break;
}
- exit_code = 1;
+ G.exit_code = 1;
}
if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
- setbuf(in_stream, NULL);
+ setbuf(G.in_stream, NULL);
}
/* Test whether there have been errors on in_stream, and close it if
@@ -501,16 +515,16 @@ open_next_file(void)
static void
check_and_close(void)
{
- if (in_stream) {
- if (ferror(in_stream)) {
- bb_error_msg("%s: read error", (in_stream == stdin)
+ if (G.in_stream) {
+ if (ferror(G.in_stream)) {
+ bb_error_msg("%s: read error", (G.in_stream == stdin)
? bb_msg_standard_input
- : file_list[-1]
+ : G.file_list[-1]
);
- exit_code = 1;
+ G.exit_code = 1;
}
- fclose_if_not_stdin(in_stream);
- in_stream = NULL;
+ fclose_if_not_stdin(G.in_stream);
+ G.in_stream = NULL;
}
if (ferror(stdout)) {
@@ -746,9 +760,9 @@ decode_format_string(const char *s)
assert(s != next);
s = next;
- spec = xrealloc_vector(spec, 4, n_specs);
- memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
- n_specs++;
+ G.spec = xrealloc_vector(G.spec, 4, G.n_specs);
+ memcpy(&G.spec[G.n_specs], &tspec, sizeof(G.spec[0]));
+ G.n_specs++;
}
}
@@ -765,7 +779,7 @@ skip(off_t n_skip)
if (n_skip == 0)
return;
- while (in_stream) { /* !EOF */
+ while (G.in_stream) { /* !EOF */
struct stat file_stats;
/* First try seeking. For large offsets, this extra work is
@@ -783,15 +797,15 @@ skip(off_t n_skip)
If the number of bytes left to skip is at least
as large as the size of the current file, we can
decrement n_skip and go on to the next file. */
- if (fstat(fileno(in_stream), &file_stats) == 0
+ if (fstat(fileno(G.in_stream), &file_stats) == 0
&& S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
) {
if (file_stats.st_size < n_skip) {
n_skip -= file_stats.st_size;
/* take "check & close / open_next" route */
} else {
- if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
- exit_code = 1;
+ if (fseeko(G.in_stream, n_skip, SEEK_CUR) != 0)
+ G.exit_code = 1;
return;
}
} else {
@@ -804,7 +818,7 @@ skip(off_t n_skip)
while (n_skip > 0) {
if (n_skip < n_bytes_to_read)
n_bytes_to_read = n_skip;
- n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
+ n_bytes_read = fread(buf, 1, n_bytes_to_read, G.in_stream);
n_skip -= n_bytes_read;
if (n_bytes_read != n_bytes_to_read)
break; /* EOF on this file or error */
@@ -857,7 +871,7 @@ static void
format_address_label(off_t address, char c)
{
format_address_std(address, ' ');
- format_address_paren(address + pseudo_offset, c);
+ format_address_paren(address + G_pseudo_offset, c);
}
#endif
@@ -888,36 +902,34 @@ static void
write_block(off_t current_offset, size_t n_bytes,
const char *prev_block, const char *curr_block)
{
- static char first = 1;
- static char prev_pair_equal = 0;
- size_t i;
+ unsigned i;
if (!(option_mask32 & OPT_v)
- && !first
- && n_bytes == bytes_per_block
- && memcmp(prev_block, curr_block, bytes_per_block) == 0
+ && G.not_first
+ && n_bytes == G.bytes_per_block
+ && memcmp(prev_block, curr_block, G.bytes_per_block) == 0
) {
- if (prev_pair_equal) {
+ if (G.prev_pair_equal) {
/* The two preceding blocks were equal, and the current
block is the same as the last one, so print nothing. */
} else {
puts("*");
- prev_pair_equal = 1;
+ G.prev_pair_equal = 1;
}
} else {
- first = 0;
- prev_pair_equal = 0;
- for (i = 0; i < n_specs; i++) {
+ G.not_first = 1;
+ G.prev_pair_equal = 0;
+ for (i = 0; i < G.n_specs; i++) {
if (i == 0)
- format_address(current_offset, '\0');
+ G.format_address(current_offset, '\0');
else
printf("%*s", address_pad_len_char - '0', "");
- (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
- if (spec[i].hexl_mode_trailer) {
+ (*G.spec[i].print_function) (n_bytes, curr_block, G.spec[i].fmt_string);
+ if (G.spec[i].hexl_mode_trailer) {
/* space-pad out to full line width, then dump the trailer */
- unsigned datum_width = width_bytes[spec[i].size];
- unsigned blank_fields = (bytes_per_block - n_bytes) / datum_width;
- unsigned field_width = spec[i].field_width + 1;
+ unsigned datum_width = width_bytes[G.spec[i].size];
+ unsigned blank_fields = (G.bytes_per_block - n_bytes) / datum_width;
+ unsigned field_width = G.spec[i].field_width + 1;
printf("%*s", blank_fields * field_width, "");
dump_hexl_mode_trailer(n_bytes, curr_block);
}
@@ -929,19 +941,19 @@ write_block(off_t current_offset, size_t n_bytes,
static void
read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
{
- assert(0 < n && n <= bytes_per_block);
+ assert(0 < n && n <= G.bytes_per_block);
*n_bytes_in_buffer = 0;
if (n == 0)
return;
- while (in_stream != NULL) { /* EOF. */
+ while (G.in_stream != NULL) { /* EOF. */
size_t n_needed;
size_t n_read;
n_needed = n - *n_bytes_in_buffer;
- n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
+ n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, G.in_stream);
*n_bytes_in_buffer += n_read;
if (n_read == n_needed)
break;
@@ -960,8 +972,8 @@ get_lcm(void)
size_t i;
int l_c_m = 1;
- for (i = 0; i < n_specs; i++)
- l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
+ for (i = 0; i < G.n_specs; i++)
+ l_c_m = lcm(l_c_m, width_bytes[(int) G.spec[i].size]);
return l_c_m;
}
@@ -982,8 +994,8 @@ dump(off_t current_offset, off_t end_offset)
int idx;
size_t n_bytes_read;
- block[0] = xmalloc(2 * bytes_per_block);
- block[1] = block[0] + bytes_per_block;
+ block[0] = xmalloc(2 * G.bytes_per_block);
+ block[1] = block[0] + G.bytes_per_block;
idx = 0;
if (option_mask32 & OPT_N) {
@@ -993,21 +1005,21 @@ dump(off_t current_offset, off_t end_offset)
n_bytes_read = 0;
break;
}
- n_needed = MIN(end_offset - current_offset, (off_t) bytes_per_block);
+ n_needed = MIN(end_offset - current_offset, (off_t) G.bytes_per_block);
read_block(n_needed, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
+ if (n_bytes_read < G.bytes_per_block)
break;
- assert(n_bytes_read == bytes_per_block);
+ assert(n_bytes_read == G.bytes_per_block);
write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
current_offset += n_bytes_read;
idx ^= 1;
}
} else {
while (1) {
- read_block(bytes_per_block, block[idx], &n_bytes_read);
- if (n_bytes_read < bytes_per_block)
+ read_block(G.bytes_per_block, block[idx], &n_bytes_read);
+ if (n_bytes_read < G.bytes_per_block)
break;
- assert(n_bytes_read == bytes_per_block);
+ assert(n_bytes_read == G.bytes_per_block);
write_block(current_offset, n_bytes_read, block[idx ^ 1], block[idx]);
current_offset += n_bytes_read;
idx ^= 1;
@@ -1030,7 +1042,7 @@ dump(off_t current_offset, off_t end_offset)
current_offset += n_bytes_read;
}
- format_address(current_offset, '\n');
+ G.format_address(current_offset, '\n');
if ((option_mask32 & OPT_N) && current_offset >= end_offset)
check_and_close();
@@ -1061,16 +1073,16 @@ dump(off_t current_offset, off_t end_offset)
static void
dump_strings(off_t address, off_t end_offset)
{
- unsigned bufsize = MAX(100, string_min);
+ unsigned bufsize = MAX(100, G.string_min);
unsigned char *buf = xmalloc(bufsize);
while (1) {
size_t i;
int c;
- /* See if the next 'string_min' chars are all printing chars. */
+ /* See if the next 'G.string_min' chars are all printing chars. */
tryline:
- if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
+ if ((option_mask32 & OPT_N) && (end_offset - G.string_min <= address))
break;
i = 0;
while (!(option_mask32 & OPT_N) || address < end_offset) {
@@ -1079,8 +1091,8 @@ dump_strings(off_t address, off_t end_offset)
buf = xrealloc(buf, bufsize);
}
- while (in_stream) { /* !EOF */
- c = fgetc(in_stream);
+ while (G.in_stream) { /* !EOF */
+ c = fgetc(G.in_stream);
if (c != EOF)
goto got_char;
check_and_close();
@@ -1097,12 +1109,12 @@ dump_strings(off_t address, off_t end_offset)
buf[i++] = c; /* String continues; store it all. */
}
- if (i < string_min) /* Too short! */
+ if (i < G.string_min) /* Too short! */
goto tryline;
/* If we get here, the string is all printable and NUL-terminated */
buf[i] = 0;
- format_address(address - i - 1, ' ');
+ G.format_address(address - i - 1, ' ');
for (i = 0; (c = buf[i]); i++) {
switch (c) {
@@ -1120,7 +1132,7 @@ dump_strings(off_t address, off_t end_offset)
}
/* We reach this point only if we search through
- (max_bytes_to_format - string_min) bytes before reaching EOF. */
+ (max_bytes_to_format - G.string_min) bytes before reaching EOF. */
check_and_close();
ret:
free(buf);
@@ -1192,13 +1204,14 @@ int od_main(int argc UNUSED_PARAM, char **argv)
/* The maximum number of bytes that will be formatted. */
off_t max_bytes_to_format = 0;
- spec = NULL;
- format_address = format_address_std;
+ INIT_G();
+
+ /*G.spec = NULL; - already is */
+ G.format_address = format_address_std;
address_base_char = 'o';
address_pad_len_char = '7';
/* Parse command line */
- opt_complementary = "w+:t::"; /* -w N, -t is a list */
#if ENABLE_LONG_OPTS
applet_long_options = od_longopts;
#endif
@@ -1219,7 +1232,7 @@ int od_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("bad output address radix "
"'%c' (must be [doxn])", str_A[0]);
pos = p - doxn;
- if (pos == 3) format_address = format_address_none;
+ if (pos == 3) G.format_address = format_address_none;
address_base_char = doxn_address_base_char[pos];
address_pad_len_char = doxn_address_pad_len_char[pos];
}
@@ -1242,11 +1255,11 @@ int od_main(int argc UNUSED_PARAM, char **argv)
if (opt & OPT_x) decode_format_string("x2");
if (opt & OPT_s) decode_format_string("d2");
if (opt & OPT_S) {
- string_min = xstrtou_sfx(str_S, 0, bkm_suffixes);
+ G.string_min = xstrtou_sfx(str_S, 0, bkm_suffixes);
}
// Bloat:
- //if ((option_mask32 & OPT_S) && n_specs > 0)
+ //if ((option_mask32 & OPT_S) && G.n_specs > 0)
// bb_error_msg_and_die("no type may be specified when dumping strings");
/* If the --traditional option is used, there may be from
@@ -1302,14 +1315,14 @@ int od_main(int argc UNUSED_PARAM, char **argv)
}
if (pseudo_start >= 0) {
- if (format_address == format_address_none) {
+ if (G.format_address == format_address_none) {
address_base_char = 'o';
address_pad_len_char = '7';
- format_address = format_address_paren;
+ G.format_address = format_address_paren;
} else {
- format_address = format_address_label;
+ G.format_address = format_address_label;
}
- pseudo_offset = pseudo_start - n_bytes_to_skip;
+ G_pseudo_offset = pseudo_start - n_bytes_to_skip;
}
}
/* else: od --traditional (without args) */
@@ -1322,45 +1335,45 @@ int od_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("SKIP + SIZE is too large");
}
- if (n_specs == 0) {
+ if (G.n_specs == 0) {
decode_format_string("o2");
- /*n_specs = 1; - done by decode_format_string */
+ /*G.n_specs = 1; - done by decode_format_string */
}
/* If no files were listed on the command line,
set the global pointer FILE_LIST so that it
references the null-terminated list of one name: "-". */
- file_list = bb_argv_dash;
+ G.file_list = bb_argv_dash;
if (argv[0]) {
/* Set the global pointer FILE_LIST so that it
references the first file-argument on the command-line. */
- file_list = (char const *const *) argv;
+ G.file_list = (char const *const *) argv;
}
/* Open the first input file */
open_next_file();
/* Skip over any unwanted header bytes */
skip(n_bytes_to_skip);
- if (!in_stream)
+ if (!G.in_stream)
return EXIT_FAILURE;
/* Compute output block length */
l_c_m = get_lcm();
if (opt & OPT_w) { /* -w: width */
- if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
+ if (!G.bytes_per_block || G.bytes_per_block % l_c_m != 0) {
bb_error_msg("warning: invalid width %u; using %d instead",
- (unsigned)bytes_per_block, l_c_m);
- bytes_per_block = l_c_m;
+ (unsigned)G.bytes_per_block, l_c_m);
+ G.bytes_per_block = l_c_m;
}
} else {
- bytes_per_block = l_c_m;
+ G.bytes_per_block = l_c_m;
if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
- bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
+ G.bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
}
#ifdef DEBUG
- for (i = 0; i < n_specs; i++) {
+ for (i = 0; i < G.n_specs; i++) {
printf("%d: fmt=\"%s\" width=%d\n",
i, spec[i].fmt_string, width_bytes[spec[i].size]);
}
@@ -1374,5 +1387,5 @@ int od_main(int argc UNUSED_PARAM, char **argv)
if (fclose(stdin))
bb_perror_msg_and_die("%s", bb_msg_standard_input);
- return exit_code;
+ return G.exit_code;
}
diff --git a/coreutils/printenv.c b/coreutils/printenv.c
index bd5db70..fbd6494 100644
--- a/coreutils/printenv.c
+++ b/coreutils/printenv.c
@@ -7,6 +7,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PRINTENV
+//config: bool "printenv"
+//config: default y
+//config: help
+//config: printenv is used to print all or part of environment.
+
+//applet:IF_PRINTENV(APPLET_NOFORK(printenv, printenv, BB_DIR_BIN, BB_SUID_DROP, printenv))
+
+//kbuild:lib-$(CONFIG_PRINTENV) += printenv.o
//usage:#define printenv_trivial_usage
//usage: "[VARIABLE]..."
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 3dd43a9..6c8e115 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -38,6 +38,17 @@
/* 19990508 Busy Boxed! Dave Cinege */
+//config:config PRINTF
+//config: bool "printf"
+//config: default y
+//config: help
+//config: printf is used to format and print specified strings.
+//config: It's similar to `echo' except it has more options.
+
+//applet:IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
+
+//kbuild:lib-$(CONFIG_PRINTF) += printf.o
+
//usage:#define printf_trivial_usage
//usage: "FORMAT [ARG]..."
//usage:#define printf_full_usage "\n\n"
@@ -131,8 +142,8 @@ static double my_xstrtod(const char *arg)
return result;
}
-/* Handles %b */
-static void print_esc_string(const char *str)
+/* Handles %b; return 1 if output is to be short-circuited by \c */
+static int print_esc_string(const char *str)
{
char c;
while ((c = *str) != '\0') {
@@ -145,6 +156,9 @@ static void print_esc_string(const char *str)
str++;
}
}
+ else if (*str == 'c') {
+ return 1;
+ }
{
/* optimization: don't force arg to be on-stack,
* use another variable for that. */
@@ -155,6 +169,8 @@ static void print_esc_string(const char *str)
}
putchar(c);
}
+
+ return 0;
}
static void print_direc(char *format, unsigned fmt_length,
@@ -280,7 +296,8 @@ static char **print_formatted(char *f, char **argv, int *conv_err)
}
if (*f == 'b') {
if (*argv) {
- print_esc_string(*argv);
+ if (print_esc_string(*argv))
+ return saved_argv; /* causes main() to exit */
++argv;
}
break;
diff --git a/coreutils/pwd.c b/coreutils/pwd.c
index bb3ad04..05dee49 100644
--- a/coreutils/pwd.c
+++ b/coreutils/pwd.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PWD
+//config: bool "pwd"
+//config: default y
+//config: help
+//config: pwd is used to print the current directory.
+
+//applet:IF_PWD(APPLET_NOFORK(pwd, pwd, BB_DIR_BIN, BB_SUID_DROP, pwd))
+
+//kbuild:lib-$(CONFIG_PWD) += pwd.o
//usage:#define pwd_trivial_usage
//usage: ""
@@ -18,8 +27,6 @@
#include "libbb.h"
-/* This is a NOFORK applet. Be very careful! */
-
static int logical_getcwd(void)
{
struct stat st1;
diff --git a/coreutils/readlink.c b/coreutils/readlink.c
index d73ef4d..1b223b3 100644
--- a/coreutils/readlink.c
+++ b/coreutils/readlink.c
@@ -6,6 +6,23 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config READLINK
+//config: bool "readlink"
+//config: default y
+//config: help
+//config: This program reads a symbolic link and returns the name
+//config: of the file it points to
+//config:
+//config:config FEATURE_READLINK_FOLLOW
+//config: bool "Enable canonicalization by following all symlinks (-f)"
+//config: default y
+//config: depends on READLINK
+//config: help
+//config: Enable the readlink option (-f).
+
+//applet:IF_READLINK(APPLET(readlink, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_READLINK) += readlink.o
//usage:#define readlink_trivial_usage
//usage: IF_FEATURE_READLINK_FOLLOW("[-fnv] ") "FILE"
diff --git a/coreutils/realpath.c b/coreutils/realpath.c
index c513b55..0c2d544 100644
--- a/coreutils/realpath.c
+++ b/coreutils/realpath.c
@@ -1,7 +1,4 @@
/* vi: set sw=4 ts=4: */
-
-/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Now does proper error checking on output and returns a failure exit code
@@ -9,6 +6,18 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config REALPATH
+//config: bool "realpath"
+//config: default y
+//config: help
+//config: Return the canonicalized absolute pathname.
+//config: This isn't provided by GNU shellutils, but where else does it belong.
+
+//applet:IF_REALPATH(APPLET(realpath, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_REALPATH) += realpath.o
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
//usage:#define realpath_trivial_usage
//usage: "FILE..."
diff --git a/coreutils/rm.c b/coreutils/rm.c
index 042fba1..cec34cb 100644
--- a/coreutils/rm.c
+++ b/coreutils/rm.c
@@ -6,14 +6,22 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reduction.
*/
+//config:config RM
+//config: bool "rm"
+//config: default y
+//config: help
+//config: rm is used to remove files or directories.
+
+//applet:IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
+
+//kbuild:lib-$(CONFIG_RM) += rm.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
//usage:#define rm_trivial_usage
//usage: "[-irf] FILE..."
@@ -38,7 +46,6 @@ int rm_main(int argc UNUSED_PARAM, char **argv)
unsigned opt;
opt_complementary = "f-i:i-f";
- /* -v (verbose) is ignored */
opt = getopt32(argv, "fiRrv");
argv += optind;
if (opt & 1)
@@ -47,6 +54,8 @@ int rm_main(int argc UNUSED_PARAM, char **argv)
flags |= FILEUTILS_INTERACTIVE;
if (opt & (8|4))
flags |= FILEUTILS_RECUR;
+ if ((opt & 16) && FILEUTILS_VERBOSE)
+ flags |= FILEUTILS_VERBOSE;
if (*argv != NULL) {
do {
diff --git a/coreutils/rmdir.c b/coreutils/rmdir.c
index cc2dea0..8979941 100644
--- a/coreutils/rmdir.c
+++ b/coreutils/rmdir.c
@@ -6,6 +6,23 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config RMDIR
+//config: bool "rmdir"
+//config: default y
+//config: help
+//config: rmdir is used to remove empty directories.
+//config:
+//config:config FEATURE_RMDIR_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on RMDIR && LONG_OPTS
+//config: help
+//config: Support long options for the rmdir applet, including
+//config: --ignore-fail-on-non-empty for compatibility with GNU rmdir.
+
+//applet:IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir))
+
+//kbuild:lib-$(CONFIG_RMDIR) += rmdir.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/rmdir.html */
@@ -31,7 +48,7 @@
#define PARENTS (1 << 0)
-//efine VERBOSE (1 << 1) //accepted but ignored
+#define VERBOSE ((1 << 1) * ENABLE_FEATURE_VERBOSE)
#define IGNORE_NON_EMPTY (1 << 2)
int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -44,10 +61,12 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS
static const char rmdir_longopts[] ALIGN1 =
"parents\0" No_argument "p"
- "verbose\0" No_argument "v"
/* Debian etch: many packages fail to be purged or installed
* because they desperately want this option: */
"ignore-fail-on-non-empty\0" No_argument "\xff"
+ IF_FEATURE_VERBOSE(
+ "verbose\0" No_argument "v"
+ )
;
applet_long_options = rmdir_longopts;
#endif
@@ -62,6 +81,10 @@ int rmdir_main(int argc UNUSED_PARAM, char **argv)
path = *argv;
while (1) {
+ if (flags & VERBOSE) {
+ printf("rmdir: removing directory, '%s'\n", path);
+ }
+
if (rmdir(path) < 0) {
#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS
if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY)
diff --git a/coreutils/seq.c b/coreutils/seq.c
index 10dabe9..7388f79 100644
--- a/coreutils/seq.c
+++ b/coreutils/seq.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SEQ
+//config: bool "seq"
+//config: default y
+//config: help
+//config: print a sequence of numbers
+
+//applet:IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
+
+//kbuild:lib-$(CONFIG_SEQ) += seq.o
//usage:#define seq_trivial_usage
//usage: "[-w] [-s SEP] [FIRST [INC]] LAST"
diff --git a/coreutils/shuf.c b/coreutils/shuf.c
new file mode 100644
index 0000000..9f61f2f
--- a/dev/null
+++ b/coreutils/shuf.c
@@ -0,0 +1,153 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * shuf: Write a random permutation of the input lines to standard output.
+ *
+ * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config SHUF
+//config: bool "shuf"
+//config: default y
+//config: help
+//config: Generate random permutations
+
+//kbuild:lib-$(CONFIG_SHUF) += shuf.o
+//applet:IF_SHUF(APPLET_NOEXEC(shuf, shuf, BB_DIR_USR_BIN, BB_SUID_DROP, shuf))
+
+//usage:#define shuf_trivial_usage
+//usage: "[-e|-i L-H] [-n NUM] [-o FILE] [-z] [FILE|ARG...]"
+//usage:#define shuf_full_usage "\n\n"
+//usage: "Randomly permute lines\n"
+//usage: "\n -e Treat ARGs as lines"
+//usage: "\n -i L-H Treat numbers L-H as lines"
+//usage: "\n -n NUM Output at most NUM lines"
+//usage: "\n -o FILE Write to FILE, not standard output"
+//usage: "\n -z End lines with zero byte, not newline"
+
+#include "libbb.h"
+
+/* This is a NOEXEC applet. Be very careful! */
+
+#define OPT_e (1 << 0)
+#define OPT_i (1 << 1)
+#define OPT_n (1 << 2)
+#define OPT_o (1 << 3)
+#define OPT_z (1 << 4)
+#define OPT_STR "ei:n:o:z"
+
+/*
+ * Use the Fisher-Yates shuffle algorithm on an array of lines.
+ */
+static void shuffle_lines(char **lines, unsigned numlines)
+{
+ unsigned i;
+ unsigned r;
+ char *tmp;
+
+ srand(monotonic_us());
+
+ for (i = numlines-1; i > 0; i--) {
+ r = rand();
+ /* RAND_MAX can be as small as 32767 */
+ if (i > RAND_MAX)
+ r ^= rand() << 15;
+ r %= i;
+ tmp = lines[i];
+ lines[i] = lines[r];
+ lines[r] = tmp;
+ }
+}
+
+int shuf_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int shuf_main(int argc, char **argv)
+{
+ unsigned opts;
+ char *opt_i_str, *opt_n_str, *opt_o_str;
+ unsigned i;
+ char **lines;
+ unsigned numlines;
+ char eol;
+
+ opt_complementary = "e--i:i--e"; /* mutually exclusive */
+ opts = getopt32(argv, OPT_STR, &opt_i_str, &opt_n_str, &opt_o_str);
+
+ argc -= optind;
+ argv += optind;
+
+ /* Prepare lines for shuffling - either: */
+ if (opts & OPT_e) {
+ /* make lines from command-line arguments */
+ numlines = argc;
+ lines = argv;
+ } else
+ if (opts & OPT_i) {
+ /* create a range of numbers */
+ char *dash;
+ unsigned lo, hi;
+
+ dash = strchr(opt_i_str, '-');
+ if (!dash) {
+ bb_error_msg_and_die("bad range '%s'", opt_i_str);
+ }
+ *dash = '\0';
+ lo = xatou(opt_i_str);
+ hi = xatou(dash + 1);
+ *dash = '-';
+ if (hi < lo) {
+ bb_error_msg_and_die("bad range '%s'", opt_i_str);
+ }
+
+ numlines = (hi+1) - lo;
+ lines = xmalloc(numlines * sizeof(lines[0]));
+ for (i = 0; i < numlines; i++) {
+ lines[i] = (char*)(uintptr_t)lo;
+ lo++;
+ }
+ } else {
+ /* default - read lines from stdin or the input file */
+ FILE *fp;
+
+ if (argc > 1)
+ bb_show_usage();
+
+ fp = xfopen_stdin(argv[0] ? argv[0] : "-");
+ lines = NULL;
+ numlines = 0;
+ for (;;) {
+ char *line = xmalloc_fgetline(fp);
+ if (!line)
+ break;
+ lines = xrealloc_vector(lines, 6, numlines);
+ lines[numlines++] = line;
+ }
+ fclose_if_not_stdin(fp);
+ }
+
+ if (numlines != 0)
+ shuffle_lines(lines, numlines);
+
+ if (opts & OPT_o)
+ xmove_fd(xopen(opt_o_str, O_WRONLY|O_CREAT|O_TRUNC), STDOUT_FILENO);
+
+ if (opts & OPT_n) {
+ unsigned maxlines;
+ maxlines = xatou(opt_n_str);
+ if (numlines > maxlines)
+ numlines = maxlines;
+ }
+
+ eol = '\n';
+ if (opts & OPT_z)
+ eol = '\0';
+
+ for (i = 0; i < numlines; i++) {
+ if (opts & OPT_i)
+ printf("%u%c", (unsigned)(uintptr_t)lines[i], eol);
+ else
+ printf("%s%c", lines[i], eol);
+ }
+
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/coreutils/sleep.c b/coreutils/sleep.c
index 0ffbd16..ad2d6b5 100644
--- a/coreutils/sleep.c
+++ b/coreutils/sleep.c
@@ -6,17 +6,48 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Rewritten to do proper arg and error checking.
* Also, added a 'fancy' configuration to accept multiple args with
* time suffixes for seconds, minutes, hours, and days.
*/
+//config:config SLEEP
+//config: bool "sleep"
+//config: default y
+//config: help
+//config: sleep is used to pause for a specified number of seconds.
+//config: It comes in 3 versions:
+//config: - small: takes one integer parameter
+//config: - fancy: takes multiple integer arguments with suffixes:
+//config: sleep 1d 2h 3m 15s
+//config: - fancy with fractional numbers:
+//config: sleep 2.3s 4.5h sleeps for 16202.3 seconds
+//config: Last one is "the most compatible" with coreutils sleep,
+//config: but it adds around 1k of code.
+//config:
+//config:config FEATURE_FANCY_SLEEP
+//config: bool "Enable multiple arguments and s/m/h/d suffixes"
+//config: default y
+//config: depends on SLEEP
+//config: help
+//config: Allow sleep to pause for specified minutes, hours, and days.
+//config:
+//config:config FEATURE_FLOAT_SLEEP
+//config: bool "Enable fractional arguments"
+//config: default y
+//config: depends on FEATURE_FANCY_SLEEP
+//config: help
+//config: Allow for fractional numeric parameters.
+
+/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */
+//applet:IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SLEEP) += sleep.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
//usage:#define sleep_trivial_usage
//usage: IF_FEATURE_FANCY_SLEEP("[") "N" IF_FEATURE_FANCY_SLEEP("]...")
@@ -35,9 +66,6 @@
#include "libbb.h"
-/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */
-
-
#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP
static const struct suffix_mult sfx[] = {
{ "s", 1 },
diff --git a/coreutils/sort.c b/coreutils/sort.c
index d78dba2..64b85e1 100644
--- a/coreutils/sort.c
+++ b/coreutils/sort.c
@@ -11,28 +11,49 @@
* See SuS3 sort standard at:
* http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
*/
+//config:config SORT
+//config: bool "sort"
+//config: default y
+//config: help
+//config: sort is used to sort lines of text in specified files.
+//config:
+//config:config FEATURE_SORT_BIG
+//config: bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)"
+//config: default y
+//config: depends on SORT
+//config: help
+//config: Without this, sort only supports -r, -u, and an integer version
+//config: of -n. Selecting this adds sort keys, floating point support, and
+//config: more. This adds a little over 3k to a nonstatic build on x86.
+//config:
+//config: The SuSv3 sort standard is available at:
+//config: http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+
+//applet:IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort))
+
+//kbuild:lib-$(CONFIG_SORT) += sort.o
//usage:#define sort_trivial_usage
//usage: "[-nru"
-//usage: IF_FEATURE_SORT_BIG("gMcszbdfimSTokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR")
+//usage: IF_FEATURE_SORT_BIG("gMcszbdfiokt] [-o FILE] [-k start[.offset][opts][,end[.offset][opts]] [-t CHAR")
//usage: "] [FILE]..."
//usage:#define sort_full_usage "\n\n"
//usage: "Sort lines of text\n"
//usage: IF_FEATURE_SORT_BIG(
-//usage: "\n -b Ignore leading blanks"
+//usage: "\n -o FILE Output to FILE"
//usage: "\n -c Check whether input is sorted"
-//usage: "\n -d Dictionary order (blank or alphanumeric only)"
+//usage: "\n -b Ignore leading blanks"
//usage: "\n -f Ignore case"
-//usage: "\n -g General numerical sort"
//usage: "\n -i Ignore unprintable characters"
-//usage: "\n -k Sort key"
+//usage: "\n -d Dictionary order (blank or alphanumeric only)"
+//usage: "\n -g General numerical sort"
//usage: "\n -M Sort month"
//usage: )
+//-h, --human-numeric-sort: compare human readable numbers (e.g., 2K 1G)
//usage: "\n -n Sort numbers"
//usage: IF_FEATURE_SORT_BIG(
-//usage: "\n -o Output to file"
-//usage: "\n -k Sort by key"
-//usage: "\n -t CHAR Key separator"
+//usage: "\n -t CHAR Field separator"
+//usage: "\n -k N[,M] Sort by Nth field"
//usage: )
//usage: "\n -r Reverse sort order"
//usage: IF_FEATURE_SORT_BIG(
@@ -41,7 +62,10 @@
//usage: "\n -u Suppress duplicate lines"
//usage: IF_FEATURE_SORT_BIG(
//usage: "\n -z Lines are terminated by NUL, not newline"
-//usage: "\n -mST Ignored for GNU compatibility")
+////usage: "\n -m Ignored for GNU compatibility"
+////usage: "\n -S BUFSZ Ignored for GNU compatibility"
+////usage: "\n -T TMPDIR Ignored for GNU compatibility"
+//usage: )
//usage:
//usage:#define sort_example_usage
//usage: "$ echo -e \"e\\nf\\nb\\nd\\nc\\na\" | sort\n"
@@ -70,7 +94,7 @@
*/
/* These are sort types */
-static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:";
+static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:*t:";
enum {
FLAG_n = 1, /* Numeric sort */
FLAG_g = 2, /* Sort using strtod() */
@@ -106,7 +130,9 @@ static struct sort_key {
static char *get_key(char *str, struct sort_key *key, int flags)
{
- int start = 0, end = 0, len, j;
+ int start = start; /* for compiler */
+ int end;
+ int len, j;
unsigned i;
/* Special case whole string, so we don't have to make a copy */
@@ -123,12 +149,15 @@ static char *get_key(char *str, struct sort_key *key, int flags)
end = len;
/* Loop through fields */
else {
+ unsigned char ch = 0;
+
end = 0;
for (i = 1; i < key->range[2*j] + j; i++) {
if (key_separator) {
/* Skip body of key and separator */
- while (str[end]) {
- if (str[end++] == key_separator)
+ while ((ch = str[end]) != '\0') {
+ end++;
+ if (ch == key_separator)
break;
}
} else {
@@ -136,34 +165,44 @@ static char *get_key(char *str, struct sort_key *key, int flags)
while (isspace(str[end]))
end++;
/* Skip body of key */
- while (str[end]) {
+ while (str[end] != '\0') {
if (isspace(str[end]))
break;
end++;
}
}
}
+ /* Remove last delim: "abc:def:" => "abc:def" */
+ if (j && ch) {
+ //if (str[end-1] != key_separator)
+ // bb_error_msg(_and_die("BUG! "
+ // "str[start:%d,end:%d]:'%.*s'",
+ // start, end, (int)(end-start), &str[start]);
+ end--;
+ }
}
if (!j) start = end;
}
/* Strip leading whitespace if necessary */
-//XXX: skip_whitespace()
if (flags & FLAG_b)
+ /* not using skip_whitespace() for speed */
while (isspace(str[start])) start++;
/* Strip trailing whitespace if necessary */
if (flags & FLAG_bb)
while (end > start && isspace(str[end-1])) end--;
- /* Handle offsets on start and end */
+ /* -kSTART,N.ENDCHAR: honor ENDCHAR (1-based) */
if (key->range[3]) {
- end += key->range[3] - 1;
+ end = key->range[3];
if (end > len) end = len;
}
+ /* -kN.STARTCHAR[,...]: honor STARTCHAR (1-based) */
if (key->range[1]) {
start += key->range[1] - 1;
if (start > len) start = len;
}
/* Make the copy */
- if (end < start) end = start;
+ if (end < start)
+ end = start;
str = xstrndup(str+start, end-start);
/* Handle -d */
if (flags & FLAG_d) {
@@ -225,7 +264,7 @@ static int compare_keys(const void *xarg, const void *yarg)
y = *(char **)yarg;
#endif
/* Perform actual comparison */
- switch (flags & 7) {
+ switch (flags & (FLAG_n | FLAG_M | FLAG_g)) {
default:
bb_error_msg_and_die("unknown sort type");
break;
@@ -277,7 +316,7 @@ static int compare_keys(const void *xarg, const void *yarg)
else if (!yy)
retval = 1;
else
- retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon;
+ retval = dx - thyme.tm_mon;
break;
}
/* Full floating point version of -n */
@@ -302,10 +341,14 @@ static int compare_keys(const void *xarg, const void *yarg)
} /* for */
/* Perform fallback sort if necessary */
- if (!retval && !(option_mask32 & FLAG_s))
+ if (!retval && !(option_mask32 & FLAG_s)) {
+ flags = option_mask32;
retval = strcmp(*(char **)xarg, *(char **)yarg);
+ }
+
+ if (flags & FLAG_r)
+ return -retval;
- if (flags & FLAG_r) return -retval;
return retval;
}
@@ -328,7 +371,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
char *line, **lines;
char *str_ignored, *str_o, *str_t;
llist_t *lst_k = NULL;
- int i, flag;
+ int i;
int linecount;
unsigned opts;
@@ -336,8 +379,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
/* Parse command line options */
/* -o and -t can be given at most once */
- opt_complementary = "o--o:t--t:" /* -t, -o: at most one of each */
- "k::"; /* -k takes list */
+ opt_complementary = "o--o:t--t"; /* -t, -o: at most one of each */
opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t);
/* global b strips leading and trailing spaces */
if (opts & FLAG_b)
@@ -351,7 +393,7 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
/* note: below this point we use option_mask32, not opts,
* since that reduces register pressure and makes code smaller */
- /* parse sort key */
+ /* Parse sort key */
while (lst_k) {
enum {
FLAG_allowed_for_k =
@@ -378,17 +420,18 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
key->range[2*i+1] = str2u(&str_k);
}
while (*str_k) {
- const char *temp2;
+ int flag;
+ const char *idx;
if (*str_k == ',' && !i++) {
str_k++;
break;
} /* no else needed: fall through to syntax error
because comma isn't in OPT_STR */
- temp2 = strchr(OPT_STR, *str_k);
- if (!temp2)
+ idx = strchr(OPT_STR, *str_k);
+ if (!idx)
bb_error_msg_and_die("unknown key option");
- flag = 1 << (temp2 - OPT_STR);
+ flag = 1 << (idx - OPT_STR);
if (flag & ~FLAG_allowed_for_k)
bb_error_msg_and_die("unknown sort type");
/* b after ',' means strip _trailing_ space */
@@ -422,10 +465,10 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
} while (*++argv);
#if ENABLE_FEATURE_SORT_BIG
- /* if no key, perform alphabetic sort */
+ /* If no key, perform alphabetic sort */
if (!key_list)
add_key()->range[0] = 1;
- /* handle -c */
+ /* Handle -c */
if (option_mask32 & FLAG_c) {
int j = (option_mask32 & FLAG_u) ? -1 : 0;
for (i = 1; i < linecount; i++) {
@@ -439,20 +482,21 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
#endif
/* Perform the actual sort */
qsort(lines, linecount, sizeof(lines[0]), compare_keys);
- /* handle -u */
+
+ /* Handle -u */
if (option_mask32 & FLAG_u) {
- flag = 0;
+ int j = 0;
/* coreutils 6.3 drop lines for which only key is the same */
/* -- disabling last-resort compare... */
option_mask32 |= FLAG_s;
for (i = 1; i < linecount; i++) {
- if (compare_keys(&lines[flag], &lines[i]) == 0)
+ if (compare_keys(&lines[j], &lines[i]) == 0)
free(lines[i]);
else
- lines[++flag] = lines[i];
+ lines[++j] = lines[i];
}
if (linecount)
- linecount = flag+1;
+ linecount = j+1;
}
/* Print it */
@@ -461,9 +505,11 @@ int sort_main(int argc UNUSED_PARAM, char **argv)
if (option_mask32 & FLAG_o)
xmove_fd(xopen3(str_o, O_WRONLY|O_CREAT|O_TRUNC, 0666), STDOUT_FILENO);
#endif
- flag = (option_mask32 & FLAG_z) ? '\0' : '\n';
- for (i = 0; i < linecount; i++)
- printf("%s%c", lines[i], flag);
+ {
+ int ch = (option_mask32 & FLAG_z) ? '\0' : '\n';
+ for (i = 0; i < linecount; i++)
+ printf("%s%c", lines[i], ch);
+ }
fflush_stdout_and_exit(EXIT_SUCCESS);
}
diff --git a/coreutils/split.c b/coreutils/split.c
index 1e1673e..50918a1 100644
--- a/coreutils/split.c
+++ b/coreutils/split.c
@@ -5,6 +5,25 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SPLIT
+//config: bool "split"
+//config: default y
+//config: help
+//config: split a file into pieces.
+//config:
+//config:config FEATURE_SPLIT_FANCY
+//config: bool "Fancy extensions"
+//config: default y
+//config: depends on SPLIT
+//config: help
+//config: Add support for features not required by SUSv3.
+//config: Supports additional suffixes 'b' for 512 bytes,
+//config: 'g' for 1GiB for the -b option.
+
+//applet:IF_SPLIT(APPLET(split, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SPLIT) += split.o
+
/* BB_AUDIT: SUSv3 compliant
* SUSv3 requirements:
* http://www.opengroup.org/onlinepubs/009695399/utilities/split.html
@@ -22,6 +41,7 @@
//usage: "$ cat TODO | split -a 2 -l 2 TODO_\n"
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_SPLIT_FANCY
static const struct suffix_mult split_suffixes[] = {
@@ -78,8 +98,10 @@ int split_main(int argc UNUSED_PARAM, char **argv)
ssize_t bytes_read, to_write;
char *src;
- opt_complementary = "?2:a+"; /* max 2 args; -a N */
- opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len);
+ setup_common_bufsiz();
+
+ opt_complementary = "?2"; /* max 2 args; -a N */
+ opt = getopt32(argv, "l:b:a:+", &count_p, &count_p, &suffix_len);
if (opt & SPLIT_OPT_l)
cnt = XATOOFF(count_p);
diff --git a/coreutils/stat.c b/coreutils/stat.c
index 769fac0..b918ec6 100644
--- a/coreutils/stat.c
+++ b/coreutils/stat.c
@@ -12,54 +12,86 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config STAT
+//config: bool "stat"
+//config: default y
+//config: help
+//config: display file or filesystem status.
+//config:
+//config:config FEATURE_STAT_FORMAT
+//config: bool "Enable custom formats (-c)"
+//config: default y
+//config: depends on STAT
+//config: help
+//config: Without this, stat will not support the '-c format' option where
+//config: users can pass a custom format string for output. This adds about
+//config: 7k to a nonstatic build on amd64.
+//config:
+//config:config FEATURE_STAT_FILESYSTEM
+//config: bool "Enable display of filesystem status (-f)"
+//config: default y
+//config: depends on STAT
+//config: select PLATFORM_LINUX # statfs()
+//config: help
+//config: Without this, stat will not support the '-f' option to display
+//config: information about filesystem status.
+
+//applet:IF_STAT(APPLET(stat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_STAT) += stat.o
//usage:#define stat_trivial_usage
//usage: "[OPTIONS] FILE..."
//usage:#define stat_full_usage "\n\n"
-//usage: "Display file (default) or filesystem status\n"
+//usage: "Display file"
+//usage: IF_FEATURE_STAT_FILESYSTEM(" (default) or filesystem")
+//usage: " status\n"
//usage: IF_FEATURE_STAT_FORMAT(
-//usage: "\n -c fmt Use the specified format"
+//usage: "\n -c FMT Use the specified format"
//usage: )
+//usage: IF_FEATURE_STAT_FILESYSTEM(
//usage: "\n -f Display filesystem status"
+//usage: )
//usage: "\n -L Follow links"
-//usage: "\n -t Display info in terse form"
+//usage: "\n -t Terse display"
//usage: IF_SELINUX(
//usage: "\n -Z Print security context"
//usage: )
//usage: IF_FEATURE_STAT_FORMAT(
-//usage: "\n\nValid format sequences for files:\n"
+//usage: "\n\nFMT sequences"IF_FEATURE_STAT_FILESYSTEM(" for files")":\n"
//usage: " %a Access rights in octal\n"
//usage: " %A Access rights in human readable form\n"
//usage: " %b Number of blocks allocated (see %B)\n"
-//usage: " %B The size in bytes of each block reported by %b\n"
+//usage: " %B Size in bytes of each block reported by %b\n"
//usage: " %d Device number in decimal\n"
//usage: " %D Device number in hex\n"
//usage: " %f Raw mode in hex\n"
//usage: " %F File type\n"
-//usage: " %g Group ID of owner\n"
-//usage: " %G Group name of owner\n"
+//usage: " %g Group ID\n"
+//usage: " %G Group name\n"
//usage: " %h Number of hard links\n"
//usage: " %i Inode number\n"
//usage: " %n File name\n"
//usage: " %N File name, with -> TARGET if symlink\n"
//usage: " %o I/O block size\n"
-//usage: " %s Total size, in bytes\n"
+//usage: " %s Total size in bytes\n"
//usage: " %t Major device type in hex\n"
//usage: " %T Minor device type in hex\n"
-//usage: " %u User ID of owner\n"
-//usage: " %U User name of owner\n"
+//usage: " %u User ID\n"
+//usage: " %U User name\n"
//usage: " %x Time of last access\n"
//usage: " %X Time of last access as seconds since Epoch\n"
//usage: " %y Time of last modification\n"
//usage: " %Y Time of last modification as seconds since Epoch\n"
//usage: " %z Time of last change\n"
//usage: " %Z Time of last change as seconds since Epoch\n"
-//usage: "\nValid format sequences for file systems:\n"
+//usage: IF_FEATURE_STAT_FILESYSTEM(
+//usage: "\nFMT sequences for file systems:\n"
//usage: " %a Free blocks available to non-superuser\n"
-//usage: " %b Total data blocks in file system\n"
-//usage: " %c Total file nodes in file system\n"
-//usage: " %d Free file nodes in file system\n"
-//usage: " %f Free blocks in file system\n"
+//usage: " %b Total data blocks\n"
+//usage: " %c Total file nodes\n"
+//usage: " %d Free file nodes\n"
+//usage: " %f Free blocks\n"
//usage: IF_SELINUX(
//usage: " %C Security context in selinux\n"
//usage: )
@@ -71,13 +103,17 @@
//usage: " %t Type in hex\n"
//usage: " %T Type in human readable form"
//usage: )
+//usage: )
#include "libbb.h"
+#include "common_bufsiz.h"
-#define OPT_FILESYS (1 << 0)
-#define OPT_TERSE (1 << 1)
-#define OPT_DEREFERENCE (1 << 2)
-#define OPT_SELINUX (1 << 3)
+enum {
+ OPT_TERSE = (1 << 0),
+ OPT_DEREFERENCE = (1 << 1),
+ OPT_FILESYS = (1 << 2) * ENABLE_FEATURE_STAT_FILESYSTEM,
+ OPT_SELINUX = (1 << (2+ENABLE_FEATURE_STAT_FILESYSTEM)) * ENABLE_SELINUX,
+};
#if ENABLE_FEATURE_STAT_FORMAT
typedef bool (*statfunc_ptr)(const char *, const char *);
@@ -126,12 +162,13 @@ static const char *human_time(time_t t)
/*static char buf[sizeof("YYYY-MM-DD HH:MM:SS.000000000")] ALIGN1;*/
#define buf bb_common_bufsiz1
-
- strcpy(strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &t), ".000000000");
+ setup_common_bufsiz();
+ strcpy(strftime_YYYYMMDDHHMMSS(buf, COMMON_BUFSIZE, &t), ".000000000");
return buf;
#undef buf
}
+#if ENABLE_FEATURE_STAT_FILESYSTEM
/* Return the type of the specified file system.
* Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
* Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
@@ -202,6 +239,7 @@ static unsigned long long get_f_fsid(const struct statfs *statfsbuf)
while (--sz > 0);
return r;
}
+#endif /* FEATURE_STAT_FILESYSTEM */
#if ENABLE_FEATURE_STAT_FORMAT
static void strcatc(char *str, char c)
@@ -217,6 +255,7 @@ static void printfs(char *pformat, const char *msg)
printf(pformat, msg);
}
+#if ENABLE_FEATURE_STAT_FILESYSTEM
/* print statfs info */
static void FAST_FUNC print_statfs(char *pformat, const char m,
const char *const filename, const void *data
@@ -263,6 +302,7 @@ static void FAST_FUNC print_statfs(char *pformat, const char m,
printf(pformat, m);
}
}
+#endif
/* print stat info */
static void FAST_FUNC print_stat(char *pformat, const char m,
@@ -374,7 +414,7 @@ static void print_it(const char *masterformat,
{
/* Create a working copy of the format string */
char *format = xstrdup(masterformat);
- /* Add 2 to accomodate our conversion of the stat '%s' format string
+ /* Add 2 to accommodate our conversion of the stat '%s' format string
* to the printf '%llu' one. */
char *dest = xmalloc(strlen(format) + 2 + 1);
char *b;
@@ -423,6 +463,7 @@ static void print_it(const char *masterformat,
}
#endif /* FEATURE_STAT_FORMAT */
+#if ENABLE_FEATURE_STAT_FILESYSTEM
/* Stat the file system and print what we find. */
#if !ENABLE_FEATURE_STAT_FORMAT
#define do_statfs(filename, format) do_statfs(filename)
@@ -538,6 +579,7 @@ static bool do_statfs(const char *filename, const char *format)
#endif /* FEATURE_STAT_FORMAT */
return 1;
}
+#endif /* FEATURE_STAT_FILESYSTEM */
/* stat the file and print what we find */
#if !ENABLE_FEATURE_STAT_FORMAT
@@ -721,12 +763,15 @@ int stat_main(int argc UNUSED_PARAM, char **argv)
statfunc_ptr statfunc = do_stat;
opt_complementary = "-1"; /* min one arg */
- opts = getopt32(argv, "ftL"
+ opts = getopt32(argv, "tL"
+ IF_FEATURE_STAT_FILESYSTEM("f")
IF_SELINUX("Z")
IF_FEATURE_STAT_FORMAT("c:", &format)
);
+#if ENABLE_FEATURE_STAT_FILESYSTEM
if (opts & OPT_FILESYS) /* -f */
statfunc = do_statfs;
+#endif
#if ENABLE_SELINUX
if (opts & OPT_SELINUX) {
selinux_or_die();
diff --git a/coreutils/stty.c b/coreutils/stty.c
index 378a848..e818d57 100644
--- a/coreutils/stty.c
+++ b/coreutils/stty.c
@@ -18,8 +18,16 @@
David MacKenzie <djm@gnu.ai.mit.edu>
Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+*/
+//config:config STTY
+//config: bool "stty"
+//config: default y
+//config: help
+//config: stty is used to change and print terminal line settings.
+
+//applet:IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP))
- */
+//kbuild:lib-$(CONFIG_STTY) += stty.o
//usage:#define stty_trivial_usage
//usage: "[-a|g] [-F DEVICE] [SETTING]..."
@@ -32,6 +40,7 @@
//usage: "\n [SETTING] See manpage"
#include "libbb.h"
+#include "common_bufsiz.h"
#ifndef _POSIX_VDISABLE
# define _POSIX_VDISABLE ((unsigned char) 0)
@@ -317,7 +326,7 @@ enum {
#define MI_ENTRY(N,T,F,B,M) N "\0"
/* Mode names given on command line */
-static const char mode_name[] =
+static const char mode_name[] ALIGN1 =
MI_ENTRY("evenp", combination, REV | OMIT, 0, 0 )
MI_ENTRY("parity", combination, REV | OMIT, 0, 0 )
MI_ENTRY("oddp", combination, REV | OMIT, 0, 0 )
@@ -680,7 +689,7 @@ enum {
#define CI_ENTRY(n,s,o) n "\0"
/* Name given on command line */
-static const char control_name[] =
+static const char control_name[] ALIGN1 =
CI_ENTRY("intr", CINTR, VINTR )
CI_ENTRY("quit", CQUIT, VQUIT )
CI_ENTRY("erase", CERASE, VERASE )
@@ -722,7 +731,7 @@ static const char control_name[] =
#undef CI_ENTRY
#define CI_ENTRY(n,s,o) { s, o },
-static const struct control_info control_info[] = {
+static const struct control_info control_info[] ALIGN2 = {
/* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
CI_ENTRY("intr", CINTR, VINTR )
CI_ENTRY("quit", CQUIT, VQUIT )
@@ -775,7 +784,7 @@ struct globals {
unsigned current_col;
char buf[10];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
G.device_name = bb_msg_standard_input; \
G.max_col = 80; \
@@ -1403,7 +1412,7 @@ int stty_main(int argc UNUSED_PARAM, char **argv)
perror_on_device_and_die("%s");
if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
- get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
+ G.max_col = get_terminal_width(STDOUT_FILENO);
output_func(&mode, display_all);
return EXIT_SUCCESS;
}
diff --git a/coreutils/sum.c b/coreutils/sum.c
index deb068e..c55293d 100644
--- a/coreutils/sum.c
+++ b/coreutils/sum.c
@@ -12,6 +12,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SUM
+//config: bool "sum"
+//config: default y
+//config: help
+//config: checksum and count the blocks in a file
+
+//applet:IF_SUM(APPLET(sum, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SUM) += sum.o
//usage:#define sum_trivial_usage
//usage: "[-rs] [FILE]..."
@@ -21,6 +30,7 @@
//usage: "\n -s Use System V sum algorithm (512byte blocks)"
#include "libbb.h"
+#include "common_bufsiz.h"
enum { SUM_BSD, PRINT_NAME, SUM_SYSV };
@@ -30,18 +40,20 @@ enum { SUM_BSD, PRINT_NAME, SUM_SYSV };
/* Return 1 if successful. */
static unsigned sum_file(const char *file, unsigned type)
{
-#define buf bb_common_bufsiz1
unsigned long long total_bytes = 0;
int fd, r;
/* The sum of all the input bytes, modulo (UINT_MAX + 1). */
unsigned s = 0;
+#define buf bb_common_bufsiz1
+ setup_common_bufsiz();
+
fd = open_or_warn_stdin(file);
if (fd == -1)
return 0;
while (1) {
- size_t bytes_read = safe_read(fd, buf, BUFSIZ);
+ size_t bytes_read = safe_read(fd, buf, COMMON_BUFSIZE);
if ((ssize_t)bytes_read <= 0) {
r = (fd && close(fd) != 0);
diff --git a/coreutils/sync.c b/coreutils/sync.c
index 7d98a1e..1a5aae5 100644
--- a/coreutils/sync.c
+++ b/coreutils/sync.c
@@ -3,16 +3,40 @@
* Mini sync implementation for busybox
*
* Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 2015 by Ari Sundholm <ari@tuxera.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SYNC
+//config: bool "sync"
+//config: default y
+//config: help
+//config: sync is used to flush filesystem buffers.
+//config:config FEATURE_SYNC_FANCY
+//config: bool "Enable -d and -f flags (requires syncfs(2) in libc)"
+//config: default y
+//config: depends on SYNC
+//config: help
+//config: sync -d FILE... executes fdatasync() on each FILE.
+//config: sync -f FILE... executes syncfs() on each FILE.
+
+//applet:IF_SYNC(APPLET_NOFORK(sync, sync, BB_DIR_BIN, BB_SUID_DROP, sync))
+
+//kbuild:lib-$(CONFIG_SYNC) += sync.o
/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
//usage:#define sync_trivial_usage
-//usage: ""
+//usage: ""IF_FEATURE_SYNC_FANCY("[-df] [FILE]...")
//usage:#define sync_full_usage "\n\n"
+//usage: IF_NOT_FEATURE_SYNC_FANCY(
//usage: "Write all buffered blocks to disk"
+//usage: )
+//usage: IF_FEATURE_SYNC_FANCY(
+//usage: "Write all buffered blocks (in FILEs) to disk"
+//usage: "\n -d Avoid syncing metadata"
+//usage: "\n -f Sync filesystems underlying FILEs"
+//usage: )
#include "libbb.h"
diff --git a/coreutils/tac.c b/coreutils/tac.c
index 94d669d..ca5617c 100644
--- a/coreutils/tac.c
+++ b/coreutils/tac.c
@@ -1,20 +1,26 @@
/* vi: set sw=4 ts=4: */
/*
* tac implementation for busybox
+ * tac - concatenate and print files in reverse
*
* Copyright (C) 2003 Yang Xiaopeng <yxp at hanwang.com.cn>
* Copyright (C) 2007 Natanael Copa <natanael.copa@gmail.com>
* Copyright (C) 2007 Tito Ragusa <farmatito@tiscali.it>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
- *
*/
-
-/* tac - concatenate and print files in reverse */
-
/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch
* http://www.uclibc.org/lists/busybox/2003-July/008813.html
*/
+//config:config TAC
+//config: bool "tac"
+//config: default y
+//config: help
+//config: tac is used to concatenate and print files in reverse.
+
+//applet:IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
+
+//kbuild:lib-$(CONFIG_TAC) += tac.o
//usage:#define tac_trivial_usage
//usage: "[FILE]..."
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 0e12443..406edf8 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -6,11 +6,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
-/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
@@ -23,24 +18,48 @@
* 6) no check for lseek error
* 7) lseek attempted when count==0 even if arg was +0 (from top)
*/
+//config:config TAIL
+//config: bool "tail"
+//config: default y
+//config: help
+//config: tail is used to print the last specified number of lines
+//config: from files.
+//config:
+//config:config FEATURE_FANCY_TAIL
+//config: bool "Enable extra tail options (-q, -s, -v, and -F)"
+//config: default y
+//config: depends on TAIL
+//config: help
+//config: The options (-q, -s, -v and -F) are provided by GNU tail, but
+//config: are not specific in the SUSv3 standard.
+//config:
+//config: -q Never output headers giving file names
+//config: -s SEC Wait SEC seconds between reads with -f
+//config: -v Always output headers giving file names
+//config: -F Same as -f, but keep retrying
+
+//applet:IF_TAIL(APPLET(tail, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_TAIL) += tail.o
+/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
+
//usage:#define tail_trivial_usage
//usage: "[OPTIONS] [FILE]..."
//usage:#define tail_full_usage "\n\n"
//usage: "Print last 10 lines of each FILE (or stdin) to stdout.\n"
//usage: "With more than one FILE, precede each with a filename header.\n"
//usage: "\n -f Print data as file grows"
-//usage: IF_FEATURE_FANCY_TAIL(
-//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
-//usage: )
+//usage: "\n -c [+]N[kbm] Print last N bytes"
//usage: "\n -n N[kbm] Print last N lines"
//usage: "\n -n +N[kbm] Start on Nth line and print the rest"
//usage: IF_FEATURE_FANCY_TAIL(
-//usage: "\n -c [+]N[kbm] Print last N bytes"
//usage: "\n -q Never print headers"
+//usage: "\n -s SECONDS Wait SECONDS between reads with -f"
//usage: "\n -v Always print headers"
+//usage: "\n -F Same as -f, but keep retrying"
//usage: "\n"
//usage: "\nN may be suffixed by k (x1024), b (x512), or m (x1024^2)."
//usage: )
@@ -50,13 +69,14 @@
//usage: "nameserver 10.0.0.1\n"
#include "libbb.h"
+#include "common_bufsiz.h"
struct globals {
bool from_top;
bool exitcode;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void tail_xprint_header(const char *fmt, const char *filename)
{
@@ -121,8 +141,8 @@ int tail_main(int argc, char **argv)
#endif
/* -s NUM, -F imlies -f */
- IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";)
- opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"),
+ IF_FEATURE_FANCY_TAIL(opt_complementary = "Ff";)
+ opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:+vF"),
&str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period));
#define FOLLOW (opt & 0x1)
#define COUNT_BYTES (opt & 0x2)
diff --git a/coreutils/tee.c b/coreutils/tee.c
index 48cc050..602d067 100644
--- a/coreutils/tee.c
+++ b/coreutils/tee.c
@@ -6,6 +6,23 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config TEE
+//config: bool "tee"
+//config: default y
+//config: help
+//config: tee is used to read from standard input and write
+//config: to standard output and files.
+//config:
+//config:config FEATURE_TEE_USE_BLOCK_IO
+//config: bool "Enable block I/O (larger/faster) instead of byte I/O"
+//config: default y
+//config: depends on TEE
+//config: help
+//config: Enable this option for a faster tee, at expense of size.
+
+//applet:IF_TEE(APPLET(tee, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TEE) += tee.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/tee.html */
@@ -23,6 +40,7 @@
//usage: "Hello\n"
#include "libbb.h"
+#include "common_bufsiz.h"
int tee_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int tee_main(int argc, char **argv)
@@ -37,6 +55,7 @@ int tee_main(int argc, char **argv)
#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
ssize_t c;
# define buf bb_common_bufsiz1
+ setup_common_bufsiz();
#else
int c;
#endif
@@ -79,7 +98,7 @@ int tee_main(int argc, char **argv)
/* names[0] will be filled later */
#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
- while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
+ while ((c = safe_read(STDIN_FILENO, buf, COMMON_BUFSIZE)) > 0) {
fp = files;
do
fwrite(buf, 1, c, *fp);
diff --git a/coreutils/test.c b/coreutils/test.c
index 92a210f..2c1e624 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -19,11 +19,6 @@
* Original copyright notice states:
* "This program is in the Public Domain."
*/
-
-//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
-//kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o
-//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
-
//config:config TEST
//config: bool "test"
//config: default y
@@ -32,21 +27,40 @@
//config: returning an appropriate exit code. The bash shell
//config: has test built in, ash can build it in optionally.
//config:
+//config:config TEST1
+//config: bool "test as ["
+//config: default y
+//config: help
+//config: Provide test command in the "[ EXPR ]" form
+//config:
+//config:config TEST2
+//config: bool "test as [["
+//config: default y
+//config: help
+//config: Provide test command in the "[[ EXPR ]]" form
+//config:
//config:config FEATURE_TEST_64
//config: bool "Extend test to 64 bit"
//config: default y
-//config: depends on TEST || ASH_BUILTIN_TEST || HUSH
+//config: depends on TEST || TEST1 || TEST2 || ASH_BUILTIN_TEST || HUSH
//config: help
//config: Enable 64-bit support in test.
-/* "test --help" does not print help (POSIX compat), only "[ --help" does.
- * We display "<applet> EXPRESSION ]" here (not "<applet> EXPRESSION")
- * Unfortunately, it screws up generated BusyBox.html. TODO. */
-//usage:#define test_trivial_usage
-//usage: "EXPRESSION ]"
-//usage:#define test_full_usage "\n\n"
-//usage: "Check file types, compare values etc. Return a 0/1 exit code\n"
-//usage: "depending on logical value of EXPRESSION"
+//applet:IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
+//applet:IF_TEST1(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
+//applet:IF_TEST2(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
+
+//kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_TEST1) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_TEST2) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_ASH_BUILTIN_TEST) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_SH_IS_HUSH) += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_BASH_IS_HUSH) += test.o test_ptr_hack.o
+
+/* "test --help" is special-cased to ignore --help */
+//usage:#define test_trivial_usage NOUSAGE_STR
+//usage:#define test_full_usage ""
//usage:
//usage:#define test_example_usage
//usage: "$ test 1 -eq 2\n"
@@ -399,6 +413,7 @@ extern struct test_statics *const test_ptr_to_statics;
barrier(); \
} while (0)
#define DEINIT_S() do { \
+ free(group_array); \
free(test_ptr_to_statics); \
} while (0)
@@ -826,10 +841,11 @@ int test_main(int argc, char **argv)
{
int res;
const char *arg0;
-// bool negate = 0;
arg0 = bb_basename(argv[0]);
- if (arg0[0] == '[') {
+ if ((ENABLE_TEST1 || ENABLE_TEST2 || ENABLE_ASH_BUILTIN_TEST || ENABLE_HUSH)
+ && (arg0[0] == '[')
+ ) {
--argc;
if (!arg0[1]) { /* "[" ? */
if (NOT_LONE_CHAR(argv[argc], ']')) {
@@ -844,6 +860,7 @@ int test_main(int argc, char **argv)
}
argv[argc] = NULL;
}
+ /* argc is unused after this point */
/* We must do DEINIT_S() prior to returning */
INIT_S();
@@ -862,43 +879,45 @@ int test_main(int argc, char **argv)
*/
/*ngroups = 0; - done by INIT_S() */
- //argc--;
argv++;
+ args = argv;
- /* Implement special cases from POSIX.2, section 4.62.4 */
- if (!argv[0]) { /* "test" */
- res = 1;
- goto ret;
- }
-#if 0
-// Now it's fixed in the parser and should not be needed
- if (LONE_CHAR(argv[0], '!') && argv[1]) {
- negate = 1;
- //argc--;
- argv++;
- }
- if (!argv[1]) { /* "test [!] arg" */
- res = (*argv[0] == '\0');
- goto ret;
- }
- if (argv[2] && !argv[3]) {
- check_operator(argv[1]);
- if (last_operator->op_type == BINOP) {
- /* "test [!] arg1 <binary_op> arg2" */
- args = argv;
- res = (binop() == 0);
- goto ret;
+ /* Implement special cases from POSIX.2, section 4.62.4.
+ * Testcase: "test '(' = '('"
+ * The general parser would misinterpret '(' as group start.
+ */
+ if (1) {
+ int negate = 0;
+ again:
+ if (!argv[0]) {
+ /* "test" */
+ res = 1;
+ goto ret_special;
+ }
+ if (!argv[1]) {
+ /* "test [!] arg" */
+ res = (argv[0][0] == '\0');
+ goto ret_special;
+ }
+ if (argv[2] && !argv[3]) {
+ check_operator(argv[1]);
+ if (last_operator->op_type == BINOP) {
+ /* "test [!] arg1 <binary_op> arg2" */
+ args = argv;
+ res = (binop() == 0);
+ ret_special:
+ /* If there was leading "!" op... */
+ res ^= negate;
+ goto ret;
+ }
+ }
+ if (LONE_CHAR(argv[0], '!')) {
+ argv++;
+ negate ^= 1;
+ goto again;
}
}
- /* Some complex expression. Undo '!' removal */
- if (negate) {
- negate = 0;
- //argc++;
- argv--;
- }
-#endif
- args = argv;
res = !oexpr(check_operator(*args));
if (*args != NULL && *++args != NULL) {
@@ -911,6 +930,5 @@ int test_main(int argc, char **argv)
}
ret:
DEINIT_S();
-// return negate ? !res : res;
return res;
}
diff --git a/coreutils/touch.c b/coreutils/touch.c
index 293a968..7d58470 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -6,19 +6,12 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m not supported. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Previous version called open() and then utime(). While this will be
* be necessary to implement -r and -t, it currently only makes things bigger.
* Also, exiting on a failure was a bug. All args should be processed.
*/
-
-#include "libbb.h"
-
//config:config TOUCH
//config: bool "touch"
//config: default y
@@ -45,6 +38,9 @@
//kbuild:lib-$(CONFIG_TOUCH) += touch.o
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m not supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
+
//usage:#define touch_trivial_usage
//usage: "[-c]" IF_FEATURE_TOUCH_SUSV3(" [-d DATE] [-t DATE] [-r FILE]") " FILE..."
//usage:#define touch_full_usage "\n\n"
@@ -66,8 +62,6 @@
//usage: "$ ls -l /tmp/foo\n"
//usage: "-rw-rw-r-- 1 andersen andersen 0 Apr 15 01:11 /tmp/foo\n"
-/* This is a NOFORK applet. Be very careful! */
-
/* coreutils implements:
* -a change only the access time
* -c, --no-create
@@ -85,6 +79,8 @@
* change the specified time: WORD is access, atime, or use
*/
+#include "libbb.h"
+
int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int touch_main(int argc UNUSED_PARAM, char **argv)
{
@@ -168,7 +164,7 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
int result;
result = (
#if ENABLE_FEATURE_TOUCH_NODEREF
- (opts & OPT_h) ? lutimes :
+ (opts & OPT_h) ? utimes :
#endif
utimes)(*argv, (reference_file || date_str) ? timebuf : NULL);
if (result != 0) {
diff --git a/coreutils/tr.c b/coreutils/tr.c
index e67948a..f552096 100644
--- a/coreutils/tr.c
+++ b/coreutils/tr.c
@@ -18,9 +18,6 @@
/* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html
* TODO: graph, print
*/
-
-//kbuild:lib-$(CONFIG_TR) += tr.o
-
//config:config TR
//config: bool "tr"
//config: default y
@@ -47,6 +44,10 @@
//config: useful for cases when no other way of expressing a character
//config: is possible.
+//applet:IF_TR(APPLET(tr, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TR) += tr.o
+
//usage:#define tr_trivial_usage
//usage: "[-cds] STRING1 [STRING2]"
//usage:#define tr_full_usage "\n\n"
@@ -91,7 +92,6 @@ static void map(char *pvector,
* Character classes, e.g. [:upper:] ==> A...Z
* Equiv classess, e.g. [=A=] ==> A (hmmmmmmm?)
* not supported:
- * \ooo-\ooo - octal ranges
* [x*N] - repeat char x N times
* [x*] - repeat char x until it fills STRING2:
* # echo qwe123 | /usr/bin/tr 123456789 '[d]'
@@ -99,7 +99,7 @@ static void map(char *pvector,
* # echo qwe123 | /usr/bin/tr 123456789 '[d*]'
* qweddd
*/
-static unsigned expand(const char *arg, char **buffer_p)
+static unsigned expand(char *arg, char **buffer_p)
{
char *buffer = *buffer_p;
unsigned pos = 0;
@@ -113,9 +113,17 @@ static unsigned expand(const char *arg, char **buffer_p)
*buffer_p = buffer = xrealloc(buffer, size);
}
if (*arg == '\\') {
+ const char *z;
arg++;
- buffer[pos++] = bb_process_escape_sequence(&arg);
- continue;
+ z = arg;
+ ac = bb_process_escape_sequence(&z);
+ arg = (char *)z;
+ arg--;
+ *arg = ac;
+ /*
+ * fall through, there may be a range.
+ * If not, current char will be treated anyway.
+ */
}
if (arg[1] == '-') { /* "0-9..." */
ac = arg[2];
@@ -124,9 +132,15 @@ static unsigned expand(const char *arg, char **buffer_p)
continue; /* next iter will copy '-' and stop */
}
i = (unsigned char) *arg;
+ arg += 3; /* skip 0-9 or 0-\ */
+ if (ac == '\\') {
+ const char *z;
+ z = arg;
+ ac = bb_process_escape_sequence(&z);
+ arg = (char *)z;
+ }
while (i <= ac) /* ok: i is unsigned _int_ */
buffer[pos++] = i++;
- arg += 3; /* skip 0-9 */
continue;
}
if ((ENABLE_FEATURE_TR_CLASSES || ENABLE_FEATURE_TR_EQUIV)
diff --git a/coreutils/true.c b/coreutils/true.c
index 382e476..6a9493f 100644
--- a/coreutils/true.c
+++ b/coreutils/true.c
@@ -6,15 +6,22 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config TRUE
+//config: bool "true"
+//config: default y
+//config: help
+//config: true returns an exit code of TRUE (0).
+
+//applet:IF_TRUE(APPLET_NOFORK(true, true, BB_DIR_BIN, BB_SUID_DROP, true))
+
+//kbuild:lib-$(CONFIG_TRUE) += true.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/true.html */
-//usage:#define true_trivial_usage
-//usage: ""
-//usage:#define true_full_usage "\n\n"
-//usage: "Return an exit code of TRUE (0)"
-//usage:
+/* "true --help" is special-cased to ignore --help */
+//usage:#define true_trivial_usage NOUSAGE_STR
+//usage:#define true_full_usage ""
//usage:#define true_example_usage
//usage: "$ true\n"
//usage: "$ echo $?\n"
diff --git a/coreutils/truncate.c b/coreutils/truncate.c
new file mode 100644
index 0000000..253fe00
--- a/dev/null
+++ b/coreutils/truncate.c
@@ -0,0 +1,87 @@
+/*
+ * Mini truncate implementation for busybox
+ *
+ * Copyright (C) 2015 by Ari Sundholm <ari@tuxera.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+//config:config TRUNCATE
+//config: bool "truncate"
+//config: default y
+//config: help
+//config: truncate truncates files to a given size. If a file does
+//config: not exist, it is created unless told otherwise.
+
+//applet:IF_TRUNCATE(APPLET_NOFORK(truncate, truncate, BB_DIR_USR_BIN, BB_SUID_DROP, truncate))
+
+//kbuild:lib-$(CONFIG_TRUNCATE) += truncate.o
+
+//usage:#define truncate_trivial_usage
+//usage: "[-c] -s SIZE FILE..."
+//usage:#define truncate_full_usage "\n\n"
+//usage: "Truncate FILEs to the given size\n"
+//usage: "\n -c Do not create files"
+//usage: "\n -s SIZE Truncate to SIZE"
+//usage:
+//usage:#define truncate_example_usage
+//usage: "$ truncate -s 1G foo"
+
+#include "libbb.h"
+
+#if ENABLE_LFS
+# define XATOU_SFX xatoull_sfx
+#else
+# define XATOU_SFX xatoul_sfx
+#endif
+
+/* This is a NOFORK applet. Be very careful! */
+
+int truncate_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int truncate_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opts;
+ int flags = O_WRONLY | O_NONBLOCK;
+ int ret = EXIT_SUCCESS;
+ char *size_str;
+ off_t size;
+
+ enum {
+ OPT_NOCREATE = (1 << 0),
+ OPT_SIZE = (1 << 1),
+ };
+
+ opt_complementary = "s:-1";
+ opts = getopt32(argv, "cs:", &size_str);
+
+ if (!(opts & OPT_NOCREATE))
+ flags |= O_CREAT;
+
+ // TODO: coreutils 8.17 also support "m" (lowercase) suffix
+ // with truncate, but not with dd!
+ // We share kMG_suffixes[], so we can't make both tools
+ // compatible at once...
+ size = XATOU_SFX(size_str, kMG_suffixes);
+
+ argv += optind;
+ while (*argv) {
+ int fd = open(*argv, flags, 0666);
+ if (fd < 0) {
+ if (errno != ENOENT || !(opts & OPT_NOCREATE)) {
+ bb_perror_msg("%s: open", *argv);
+ ret = EXIT_FAILURE;
+ }
+ /* else: ENOENT && OPT_NOCREATE:
+ * do not report error, exitcode is also 0.
+ */
+ } else {
+ if (ftruncate(fd, size) == -1) {
+ bb_perror_msg("%s: truncate", *argv);
+ ret = EXIT_FAILURE;
+ }
+ xclose(fd);
+ }
+ ++argv;
+ }
+
+ return ret;
+}
diff --git a/coreutils/tty.c b/coreutils/tty.c
index 4517505..359e5bc 100644
--- a/coreutils/tty.c
+++ b/coreutils/tty.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config TTY
+//config: bool "tty"
+//config: default y
+//config: help
+//config: tty is used to print the name of the current terminal to
+//config: standard output.
+
+//applet:IF_TTY(APPLET(tty, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TTY) += tty.o
/* BB_AUDIT SUSv4 compliant */
/* http://www.opengroup.org/onlinepubs/9699919799/utilities/tty.html */
diff --git a/coreutils/uname.c b/coreutils/uname.c
index b96d76b..4d98fde 100644
--- a/coreutils/uname.c
+++ b/coreutils/uname.c
@@ -4,10 +4,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
-
/* Option Example
* -s, --sysname SunOS
* -n, --nodename rocky8
@@ -37,7 +33,6 @@
* -a, --all: all of the above, in the order shown.
* If -p or -i is not known, don't show them
*/
-
/* Busyboxed by Erik Andersen
*
* Before 2003: Glenn McGrath and Manuel Novoa III
@@ -47,18 +42,40 @@
* Jan 2009:
* Fix handling of -a to not print "unknown", add -o and -i support.
*/
+//config:config UNAME
+//config: bool "uname"
+//config: default y
+//config: help
+//config: uname is used to print system information.
+//config:
+//config:config UNAME_OSNAME
+//config: string "Operating system name"
+//config: default "GNU/Linux"
+//config: depends on UNAME
+//config: help
+//config: Sets the operating system name reported by uname -o. The
+//config: default is "GNU/Linux".
+
+//applet:IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UNAME) += uname.o
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
//usage:#define uname_trivial_usage
-//usage: "[-amnrspv]"
+//usage: "[-amnrspvio]"
//usage:#define uname_full_usage "\n\n"
//usage: "Print system information\n"
//usage: "\n -a Print all"
//usage: "\n -m The machine (hardware) type"
//usage: "\n -n Hostname"
-//usage: "\n -r OS release"
-//usage: "\n -s OS name (default)"
+//usage: "\n -r Kernel release"
+//usage: "\n -s Kernel name (default)"
//usage: "\n -p Processor type"
-//usage: "\n -v OS version"
+//usage: "\n -v Kernel version"
+//usage: "\n -i The hardware platform"
+//usage: "\n -o OS name"
//usage:
//usage:#define uname_example_usage
//usage: "$ uname -a\n"
@@ -72,7 +89,7 @@ typedef struct {
struct utsname name;
char processor[sizeof(((struct utsname*)NULL)->machine)];
char platform[sizeof(((struct utsname*)NULL)->machine)];
- char os[sizeof("GNU/Linux")];
+ char os[sizeof(CONFIG_UNAME_OSNAME)];
} uname_info_t;
static const char options[] ALIGN1 = "snrvmpioa";
@@ -139,7 +156,7 @@ int uname_main(int argc UNUSED_PARAM, char **argv)
#endif
strcpy(uname_info.processor, unknown_str);
strcpy(uname_info.platform, unknown_str);
- strcpy(uname_info.os, "GNU/Linux");
+ strcpy(uname_info.os, CONFIG_UNAME_OSNAME);
#if 0
/* Fedora does something like this */
strcpy(uname_info.processor, uname_info.name.machine);
diff --git a/coreutils/uniq.c b/coreutils/uniq.c
index 9208d34..be550b5 100644
--- a/coreutils/uniq.c
+++ b/coreutils/uniq.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config UNIQ
+//config: bool "uniq"
+//config: default y
+//config: help
+//config: uniq is used to remove duplicate lines from a sorted file.
+
+//applet:IF_UNIQ(APPLET(uniq, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UNIQ) += uniq.o
/* BB_AUDIT SUSv3 compliant */
/* http://www.opengroup.org/onlinepubs/007904975/utilities/uniq.html */
@@ -50,8 +59,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
skip_fields = skip_chars = 0;
max_chars = INT_MAX;
- opt_complementary = "f+:s+:w+";
- opt = getopt32(argv, "cduf:s:w:", &skip_fields, &skip_chars, &max_chars);
+ opt = getopt32(argv, "cduf:+s:+w:+", &skip_fields, &skip_chars, &max_chars);
argv += optind;
input_filename = argv[0];
@@ -112,7 +120,7 @@ int uniq_main(int argc UNUSED_PARAM, char **argv)
/* %7lu matches GNU coreutils 6.9 */
printf("%7lu ", dups + 1);
}
- printf("%s\n", old_line);
+ puts(old_line);
}
free(old_line);
}
diff --git a/coreutils/unlink.c b/coreutils/unlink.c
new file mode 100644
index 0000000..2879638
--- a/dev/null
+++ b/coreutils/unlink.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/* unlink for busybox
+ *
+ * Copyright (C) 2014 Isaac Dunham <ibid.ag@gmail.com>
+ *
+ * Licensed under GPLv2, see LICENSE in this source tree
+ */
+//config:config UNLINK
+//config: bool "unlink"
+//config: default y
+//config: help
+//config: unlink deletes a file by calling unlink()
+
+//applet:IF_UNLINK(APPLET(unlink, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UNLINK) += unlink.o
+
+//usage:#define unlink_trivial_usage
+//usage: "FILE"
+//usage:#define unlink_full_usage "\n\n"
+//usage: "Delete FILE by calling unlink()"
+
+#include "libbb.h"
+
+int unlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int unlink_main(int argc UNUSED_PARAM, char **argv)
+{
+ opt_complementary = "=1"; /* must have exactly 1 param */
+ getopt32(argv, "");
+ argv += optind;
+ xunlink(argv[0]);
+ return 0;
+}
diff --git a/coreutils/usleep.c b/coreutils/usleep.c
index 2e4eb57..7bc30c2 100644
--- a/coreutils/usleep.c
+++ b/coreutils/usleep.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config USLEEP
+//config: bool "usleep"
+//config: default y
+//config: help
+//config: usleep is used to pause for a specified number of microseconds.
+
+//applet:IF_USLEEP(APPLET_NOFORK(usleep, usleep, BB_DIR_BIN, BB_SUID_DROP, usleep))
+
+//kbuild:lib-$(CONFIG_USLEEP) += usleep.o
/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
diff --git a/coreutils/uudecode.c b/coreutils/uudecode.c
index b298fcb..ddce254 100644
--- a/coreutils/uudecode.c
+++ b/coreutils/uudecode.c
@@ -10,6 +10,15 @@
* Bugs: the spec doesn't mention anything about "`\n`\n" prior to the
* "end" line
*/
+//config:config UUDECODE
+//config: bool "uudecode"
+//config: default y
+//config: help
+//config: uudecode is used to decode a uuencoded file.
+
+//applet:IF_UUDECODE(APPLET(uudecode, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UUDECODE) += uudecode.o
//usage:#define uudecode_trivial_usage
//usage: "[-o OUTFILE] [INFILE]"
@@ -29,9 +38,19 @@ static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags U
{
char *line;
- while ((line = xmalloc_fgetline(src_stream)) != NULL) {
+ for (;;) {
int encoded_len, str_len;
char *line_ptr, *dst;
+ size_t line_len;
+
+ line_len = 64 * 1024;
+ line = xmalloc_fgets_str_len(src_stream, "\n", &line_len);
+ if (!line)
+ break;
+ /* Handle both Unix and MSDOS text, and stray trailing spaces */
+ str_len = line_len;
+ while (--str_len >= 0 && isspace(line[str_len]))
+ line[str_len] = '\0';
if (strcmp(line, "end") == 0) {
return; /* the only non-error exit */
@@ -46,7 +65,7 @@ static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags U
encoded_len = line[0] * 4 / 3;
/* Check that line is not too short. (we tolerate
- * overly _long_ line to accomodate possible extra '`').
+ * overly _long_ line to accommodate possible extra '`').
* Empty line case is also caught here. */
if (str_len <= encoded_len) {
break; /* go to bb_error_msg_and_die("short file"); */
@@ -110,10 +129,10 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
FILE *dst_stream;
int mode;
- if (strncmp(line, "begin-base64 ", 13) == 0) {
+ if (is_prefixed_with(line, "begin-base64 ")) {
line_ptr = line + 13;
decode_fn_ptr = read_base64;
- } else if (strncmp(line, "begin ", 6) == 0) {
+ } else if (is_prefixed_with(line, "begin ")) {
line_ptr = line + 6;
decode_fn_ptr = read_stduu;
} else {
@@ -128,6 +147,7 @@ int uudecode_main(int argc UNUSED_PARAM, char **argv)
if (!outname)
break;
outname++;
+ trim(outname); /* remove trailing space (and '\r' for DOS text) */
if (!outname[0])
break;
}
diff --git a/coreutils/uuencode.c b/coreutils/uuencode.c
index 673ef36..917cdae 100644
--- a/coreutils/uuencode.c
+++ b/coreutils/uuencode.c
@@ -7,6 +7,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config UUENCODE
+//config: bool "uuencode"
+//config: default y
+//config: help
+//config: uuencode is used to uuencode a file.
+
+//applet:IF_UUENCODE(APPLET(uuencode, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UUENCODE) += uuencode.o
//usage:#define uuencode_trivial_usage
//usage: "[-m] [FILE] STORED_FILENAME"
diff --git a/coreutils/wc.c b/coreutils/wc.c
index a410e40..7383714 100644
--- a/coreutils/wc.c
+++ b/coreutils/wc.c
@@ -6,10 +6,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 compliant. */
-/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Rewritten to fix a number of problems and do some size optimizations.
@@ -36,6 +32,27 @@
*
* for which 'wc -c' should output '0'.
*/
+//config:config WC
+//config: bool "wc"
+//config: default y
+//config: help
+//config: wc is used to print the number of bytes, words, and lines,
+//config: in specified files.
+//config:
+//config:config FEATURE_WC_LARGE
+//config: bool "Support very large files in wc"
+//config: default y
+//config: depends on WC
+//config: help
+//config: Use "unsigned long long" in wc for counter variables.
+
+//applet:IF_WC(APPLET(wc, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WC) += wc.o
+
+/* BB_AUDIT SUSv3 compliant. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
+
#include "libbb.h"
#include "unicode.h"
diff --git a/coreutils/who.c b/coreutils/who.c
index f955ce6..ac19dc7 100644
--- a/coreutils/who.c
+++ b/coreutils/who.c
@@ -16,15 +16,13 @@
*
*----------------------------------------------------------------------
*/
-/* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'. */
-
//config:config WHO
//config: bool "who"
//config: default y
//config: depends on FEATURE_UTMP
//config: help
//config: who is used to show who is logged on.
-
+//config:
//config:config USERS
//config: bool "users"
//config: default y
@@ -33,11 +31,13 @@
//config: Print users currently logged on.
//applet:IF_USERS(APPLET_ODDNAME(users, who, BB_DIR_USR_BIN, BB_SUID_DROP, users))
-//applet:IF_WHO( APPLET( who, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_WHO(APPLET(who, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_USERS) += who.o
//kbuild:lib-$(CONFIG_WHO) += who.o
+/* BB_AUDIT SUSv3 _NOT_ compliant -- missing options -b, -d, -l, -m, -p, -q, -r, -s, -t, -T, -u; Missing argument 'file'. */
+
//usage:#define users_trivial_usage
//usage: ""
//usage:#define users_full_usage "\n\n"
@@ -73,7 +73,7 @@ static void idle_string(char *str6, time_t t)
int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int who_main(int argc UNUSED_PARAM, char **argv)
{
- struct utmp *ut;
+ struct utmpx *ut;
unsigned opt;
int do_users = (ENABLE_USERS && (!ENABLE_WHO || applet_name[0] == 'u'));
const char *fmt = "%s";
@@ -81,10 +81,10 @@ int who_main(int argc UNUSED_PARAM, char **argv)
opt_complementary = "=0";
opt = getopt32(argv, do_users ? "" : "aH");
if (opt & 2) // -H
- printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n");
+ puts("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST");
- setutent();
- while ((ut = getutent()) != NULL) {
+ setutxent();
+ while ((ut = getutxent()) != NULL) {
if (ut->ut_user[0]
&& ((opt & 1) || ut->ut_type == USER_PROCESS)
) {
@@ -126,6 +126,6 @@ int who_main(int argc UNUSED_PARAM, char **argv)
if (do_users)
bb_putchar('\n');
if (ENABLE_FEATURE_CLEAN_UP)
- endutent();
+ endutxent();
return EXIT_SUCCESS;
}
diff --git a/coreutils/whoami.c b/coreutils/whoami.c
index 30b17ca..6357128 100644
--- a/coreutils/whoami.c
+++ b/coreutils/whoami.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config WHOAMI
+//config: bool "whoami"
+//config: default y
+//config: help
+//config: whoami is used to print the username of the current
+//config: user id (same as id -un).
+
+//applet:IF_WHOAMI(APPLET_NOFORK(whoami, whoami, BB_DIR_USR_BIN, BB_SUID_DROP, whoami))
+
+//kbuild:lib-$(CONFIG_WHOAMI) += whoami.o
/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
@@ -16,8 +26,6 @@
#include "libbb.h"
-/* This is a NOFORK applet. Be very careful! */
-
int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int whoami_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
diff --git a/coreutils/yes.c b/coreutils/yes.c
index 5d799f0..81d8755 100644
--- a/coreutils/yes.c
+++ b/coreutils/yes.c
@@ -6,23 +6,30 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
-
/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
*
* Size reductions and removed redundant applet name prefix from error messages.
*/
+//config:config YES
+//config: bool "yes"
+//config: default y
+//config: help
+//config: yes is used to repeatedly output a specific string, or
+//config: the default string `y'.
-#include "libbb.h"
+//applet:IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
-/* This is a NOFORK applet. Be very careful! */
+//kbuild:lib-$(CONFIG_YES) += yes.o
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
//usage:#define yes_trivial_usage
//usage: "[STRING]"
//usage:#define yes_full_usage "\n\n"
//usage: "Repeatedly output a line with STRING, or 'y'"
+#include "libbb.h"
+
int yes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int yes_main(int argc UNUSED_PARAM, char **argv)
{
diff --git a/debianutils/Config.src b/debianutils/Config.src
index cbc09b5..61daeb0 100644
--- a/debianutils/Config.src
+++ b/debianutils/Config.src
@@ -7,79 +7,4 @@ menu "Debian Utilities"
INSERT
-config MKTEMP
- bool "mktemp"
- default y
- help
- mktemp is used to create unique temporary files
-
-config PIPE_PROGRESS
- bool "pipe_progress"
- default y
- help
- Display a dot to indicate pipe activity.
-
-config RUN_PARTS
- bool "run-parts"
- default y
- help
- run-parts is a utility designed to run all the scripts in a directory.
-
- It is useful to set up a directory like cron.daily, where you need to
- execute all the scripts in that directory.
-
- In this implementation of run-parts some features (such as report
- mode) are not implemented.
-
- Unless you know that run-parts is used in some of your scripts
- you can safely say N here.
-
-config FEATURE_RUN_PARTS_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on RUN_PARTS && LONG_OPTS
- help
- Support long options for the run-parts applet.
-
-config FEATURE_RUN_PARTS_FANCY
- bool "Support additional arguments"
- default y
- depends on RUN_PARTS
- help
- Support additional options:
- -l --list print the names of the all matching files (not
- limited to executables), but don't actually run them.
-
-config START_STOP_DAEMON
- bool "start-stop-daemon"
- default y
- help
- start-stop-daemon is used to control the creation and
- termination of system-level processes, usually the ones
- started during the startup of the system.
-
-config FEATURE_START_STOP_DAEMON_FANCY
- bool "Support additional arguments"
- default y
- depends on START_STOP_DAEMON
- help
- Support additional arguments.
- -o|--oknodo ignored since we exit with 0 anyway
- -v|--verbose
- -N|--nicelevel N
-
-config FEATURE_START_STOP_DAEMON_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on START_STOP_DAEMON && LONG_OPTS
- help
- Support long options for the start-stop-daemon applet.
-
-config WHICH
- bool "which"
- default y
- help
- which is used to find programs in your PATH and
- print out their pathnames.
-
endmenu
diff --git a/debianutils/Kbuild.src b/debianutils/Kbuild.src
index d41b5c8..6b4fb74 100644
--- a/debianutils/Kbuild.src
+++ b/debianutils/Kbuild.src
@@ -7,8 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_MKTEMP) += mktemp.o
-lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o
-lib-$(CONFIG_RUN_PARTS) += run_parts.o
-lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
-lib-$(CONFIG_WHICH) += which.o
diff --git a/debianutils/mktemp.c b/debianutils/mktemp.c
index 10dd0de..8f9479f 100755..100644
--- a/debianutils/mktemp.c
+++ b/debianutils/mktemp.c
@@ -30,6 +30,15 @@
* a directory: $TMPDIR, if set; else the directory specified via
* -p; else /tmp [deprecated]
*/
+//config:config MKTEMP
+//config: bool "mktemp"
+//config: default y
+//config: help
+//config: mktemp is used to create unique temporary files
+
+//applet:IF_MKTEMP(APPLET(mktemp, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MKTEMP) += mktemp.o
//usage:#define mktemp_trivial_usage
//usage: "[-dt] [-p DIR] [TEMPLATE]"
@@ -54,6 +63,7 @@
#include "libbb.h"
#ifdef __BIONIC__
+#include <android.h>
#define mktemp(s) bb_mktemp(s)
#endif
diff --git a/debianutils/pipe_progress.c b/debianutils/pipe_progress.c
index 2c7444f..21d330b 100644
--- a/debianutils/pipe_progress.c
+++ b/debianutils/pipe_progress.c
@@ -6,6 +6,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PIPE_PROGRESS
+//config: bool "pipe_progress"
+//config: default y
+//config: help
+//config: Display a dot to indicate pipe activity.
+
+//applet:IF_PIPE_PROGRESS(APPLET(pipe_progress, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o
//usage:#define pipe_progress_trivial_usage NOUSAGE_STR
//usage:#define pipe_progress_full_usage ""
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index 527fae2..0bb666a 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -22,6 +22,40 @@
* report mode. As the original run-parts support only long options, I've
* broken compatibility because the BusyBox policy doesn't allow them.
*/
+//config:config RUN_PARTS
+//config: bool "run-parts"
+//config: default y
+//config: help
+//config: run-parts is a utility designed to run all the scripts in a directory.
+//config:
+//config: It is useful to set up a directory like cron.daily, where you need to
+//config: execute all the scripts in that directory.
+//config:
+//config: In this implementation of run-parts some features (such as report
+//config: mode) are not implemented.
+//config:
+//config: Unless you know that run-parts is used in some of your scripts
+//config: you can safely say N here.
+//config:
+//config:config FEATURE_RUN_PARTS_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on RUN_PARTS && LONG_OPTS
+//config: help
+//config: Support long options for the run-parts applet.
+//config:
+//config:config FEATURE_RUN_PARTS_FANCY
+//config: bool "Support additional arguments"
+//config: default y
+//config: depends on RUN_PARTS
+//config: help
+//config: Support additional options:
+//config: -l --list print the names of the all matching files (not
+//config: limited to executables), but don't actually run them.
+
+//applet:IF_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, BB_DIR_BIN, BB_SUID_DROP, run_parts))
+
+//kbuild:lib-$(CONFIG_RUN_PARTS) += run_parts.o
//usage:#define run_parts_trivial_usage
//usage: "[-a ARG]... [-u UMASK] "
@@ -55,17 +89,18 @@
//usage: "+ shutdown -h +4m"
#include "libbb.h"
+#include "common_bufsiz.h"
struct globals {
char **names;
int cur;
char *cmd[2 /* using 1 provokes compiler warning */];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define names (G.names)
#define cur (G.cur )
#define cmd (G.cmd )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
enum { NUM_CMD = (COMMON_BUFSIZE - sizeof(G)) / sizeof(cmd[0]) - 1 };
@@ -146,8 +181,8 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
applet_long_options = runparts_longopts;
#endif
/* We require exactly one argument: the directory name */
- opt_complementary = "=1:a::";
- getopt32(argv, "a:u:", &arg_list, &umask_p);
+ opt_complementary = "=1";
+ getopt32(argv, "a:*u:", &arg_list, &umask_p);
umask(xstrtou_range(umask_p, 8, 0, 07777));
@@ -189,7 +224,7 @@ int run_parts_main(int argc UNUSED_PARAM, char **argv)
if (ret < 0)
bb_perror_msg("can't execute '%s'", name);
else /* ret > 0 */
- bb_error_msg("%s exited with code %d", name, ret & 0xff);
+ bb_error_msg("%s: exit status %u", name, ret & 0xff);
if (option_mask32 & OPT_e)
xfunc_die();
diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c
index 7dadc3c..3625ffe 100644
--- a/debianutils/start_stop_daemon.c
+++ b/debianutils/start_stop_daemon.c
@@ -56,6 +56,34 @@ Misc options:
-q,--quiet Quiet
-v,--verbose Verbose
*/
+//config:config START_STOP_DAEMON
+//config: bool "start-stop-daemon"
+//config: default y
+//config: help
+//config: start-stop-daemon is used to control the creation and
+//config: termination of system-level processes, usually the ones
+//config: started during the startup of the system.
+//config:
+//config:config FEATURE_START_STOP_DAEMON_FANCY
+//config: bool "Support additional arguments"
+//config: default y
+//config: depends on START_STOP_DAEMON
+//config: help
+//config: Support additional arguments.
+//config: -o|--oknodo ignored since we exit with 0 anyway
+//config: -v|--verbose
+//config: -N|--nicelevel N
+//config:
+//config:config FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on START_STOP_DAEMON && LONG_OPTS
+//config: help
+//config: Support long options for the start-stop-daemon applet.
+
+//applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
+
+//kbuild:lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
//usage:#define start_stop_daemon_trivial_usage
//usage: "[OPTIONS] [-S|-K] ... [-- ARGS...]"
@@ -125,6 +153,7 @@ Misc options:
/* Override ENABLE_FEATURE_PIDFILE */
#define WANT_PIDFILE 1
#include "libbb.h"
+#include "common_bufsiz.h"
struct pid_list {
struct pid_list *next;
@@ -163,7 +192,7 @@ struct globals {
int user_id;
smallint signal_nr;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define userspec (G.userspec )
#define cmdname (G.cmdname )
#define execname (G.execname )
@@ -171,6 +200,7 @@ struct globals {
#define user_id (G.user_id )
#define signal_nr (G.signal_nr )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
user_id = -1; \
signal_nr = 15; \
} while (0)
@@ -511,15 +541,15 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
write_pidfile(pidfile);
}
if (opt & OPT_c) {
- struct bb_uidgid_t ugid = { -1, -1 };
+ struct bb_uidgid_t ugid;
parse_chown_usergroup_or_die(&ugid, chuid);
- if (ugid.uid != (uid_t) -1) {
+ if (ugid.uid != (uid_t) -1L) {
struct passwd *pw = xgetpwuid(ugid.uid);
- if (ugid.gid != (gid_t) -1)
+ if (ugid.gid != (gid_t) -1L)
pw->pw_gid = ugid.gid;
/* initgroups, setgid, setuid: */
change_identity(pw);
- } else if (ugid.gid != (gid_t) -1) {
+ } else if (ugid.gid != (gid_t) -1L) {
xsetgid(ugid.gid);
setgroups(1, &ugid.gid);
}
diff --git a/debianutils/which.c b/debianutils/which.c
index 15fd598..c0f8978 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -1,14 +1,20 @@
/* vi: set sw=4 ts=4: */
/*
- * Which implementation for busybox
- *
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
* Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
- *
- * Based on which from debianutils
*/
+//config:config WHICH
+//config: bool "which"
+//config: default y
+//config: help
+//config: which is used to find programs in your PATH and
+//config: print out their pathnames.
+
+//applet:IF_WHICH(APPLET(which, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WHICH) += which.o
//usage:#define which_trivial_usage
//usage: "[COMMAND]..."
@@ -24,76 +30,43 @@
int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int which_main(int argc UNUSED_PARAM, char **argv)
{
- IF_DESKTOP(int opt;)
- int status = EXIT_SUCCESS;
- char *path;
- char *p;
+ const char *env_path;
+ int status = 0;
+
+ env_path = getenv("PATH");
+ if (!env_path)
+ env_path = bb_default_root_path;
opt_complementary = "-1"; /* at least one argument */
- IF_DESKTOP(opt =) getopt32(argv, "a");
+ getopt32(argv, "a");
argv += optind;
- /* This matches what is seen on e.g. ubuntu.
- * "which" there is a shell script. */
- path = getenv("PATH");
- if (!path) {
- path = (char*)bb_PATH_root_path;
- putenv(path);
- path += 5; /* skip "PATH=" */
- }
-
do {
-#if ENABLE_DESKTOP
-/* Much bloat just to support -a */
- if (strchr(*argv, '/')) {
- if (execable_file(*argv)) {
- puts(*argv);
- continue;
- }
- status = EXIT_FAILURE;
- } else {
- char *path2 = xstrdup(path);
- char *tmp = path2;
+ int missing = 1;
- p = find_execable(*argv, &tmp);
- if (!p)
- status = EXIT_FAILURE;
- else {
- print:
- puts(p);
- free(p);
- if (opt) {
- /* -a: show matches in all PATH components */
- if (tmp) {
- p = find_execable(*argv, &tmp);
- if (p)
- goto print;
- }
- }
- }
- free(path2);
- }
-#else
-/* Just ignoring -a */
+ /* If file contains a slash don't use PATH */
if (strchr(*argv, '/')) {
- if (execable_file(*argv)) {
+ if (file_is_executable(*argv)) {
+ missing = 0;
puts(*argv);
- continue;
}
} else {
- char *path2 = xstrdup(path);
- char *tmp = path2;
- p = find_execable(*argv, &tmp);
- free(path2);
- if (p) {
+ char *path;
+ char *tmp;
+ char *p;
+
+ path = tmp = xstrdup(env_path);
+ while ((p = find_executable(*argv, &tmp)) != NULL) {
+ missing = 0;
puts(p);
free(p);
- continue;
+ if (!option_mask32) /* -a not set */
+ break;
}
+ free(path);
}
- status = EXIT_FAILURE;
-#endif
- } while (*(++argv) != NULL);
+ status |= missing;
+ } while (*++argv);
- fflush_stdout_and_exit(status);
+ return status;
}
diff --git a/docs/busybox_footer.pod b/docs/busybox_footer.pod
index c346c73..92748eb 100644
--- a/docs/busybox_footer.pod
+++ b/docs/busybox_footer.pod
@@ -1,5 +1,3 @@
-=back
-
=head1 LIBC NSS
GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
diff --git a/docs/cgi/env.html b/docs/cgi/env.html
index b83c750..66a548b 100644
--- a/docs/cgi/env.html
+++ b/docs/cgi/env.html
@@ -38,7 +38,7 @@ fulfilled by the gateway program: <p>
</p><ul>
<li> <a name="protocol"><code>SERVER_PROTOCOL</code></a> <p>
- The name and revision of the information protcol this request came
+ The name and revision of the information protocol this request came
in with. Format: protocol/revision </p><p>
</p></li><li> <code>SERVER_PORT</code> <p>
diff --git a/docs/ifupdown_design.txt b/docs/ifupdown_design.txt
index 8ab4e51..39e28a9 100644
--- a/docs/ifupdown_design.txt
+++ b/docs/ifupdown_design.txt
@@ -21,7 +21,7 @@ static int dhcp_down(struct interface_defn_t *ifd, execfn *exec)
#if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
int i ;
for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
- if (exists_execable(ext_dhcp_clients[i].name))
+ if (executable_exists(ext_dhcp_clients[i].name))
return execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
}
bb_error_msg("no dhcp clients found, using static interface shutdown");
diff --git a/docs/keep_data_small.txt b/docs/keep_data_small.txt
index 9fc7996..218d4f2 100644
--- a/docs/keep_data_small.txt
+++ b/docs/keep_data_small.txt
@@ -59,7 +59,7 @@ wait
Example 1
One example how to reduce global data usage is in
-archival/libarchive/decompress_unzip.c:
+archival/libarchive/decompress_gunzip.c:
/* This is somewhat complex-looking arrangement, but it allows
* to place decompressor state either in bss or in
@@ -87,7 +87,7 @@ take a look at archival/gzip.c. Here all global data is replaced by
single global pointer (ptr_to_globals) to allocated storage.
In order to not duplicate ptr_to_globals in every applet, you can
-reuse single common one. It is defined in libbb/messages.c
+reuse single common one. It is defined in libbb/ptr_to_globals.c
as struct globals *const ptr_to_globals, but the struct globals is
NOT defined in libbb.h. You first define your own struct:
@@ -103,7 +103,15 @@ smaller code. In order to assign it, use SET_PTR_TO_GLOBALS macro:
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
-Typically it is done in <applet>_main().
+Typically it is done in <applet>_main(). Another variation is
+to use stack:
+
+int <applet>_main(...)
+{
+#undef G
+ struct globals G;
+ memset(&G, 0, sizeof(G));
+ SET_PTR_TO_GLOBALS(&G);
Now you can reference "globals" by G.a, G.buf and so on, in any function.
diff --git a/docs/logging_and_backgrounding.txt b/docs/logging_and_backgrounding.txt
index 7e68855..c76cd36 100644
--- a/docs/logging_and_backgrounding.txt
+++ b/docs/logging_and_backgrounding.txt
@@ -45,6 +45,8 @@ udhcpc - auto-backgrounds unless -f after lease is obtained,
udhcpd - auto-backgrounds and do not log to stderr unless -f,
otherwise logs to stderr, but option -S makes it log *also* to syslog
zcip - auto-backgrounds and logs *also* to syslog unless -f
+ behaviour can be overridden with experimental LOGGING env.var
+ (can be set to either "none" or "syslog")
Total: 13 applets (+1 obsolete),
4 log to syslog by default (crond fakeidentd inetd zcip),
diff --git a/docs/new-applet-HOWTO.txt b/docs/new-applet-HOWTO.txt
index 6a8054d..078e77b 100644
--- a/docs/new-applet-HOWTO.txt
+++ b/docs/new-applet-HOWTO.txt
@@ -6,7 +6,7 @@ This document details the steps you must take to add a new applet to BusyBox.
Credits:
Matt Kraai - initial writeup
Mark Whitley - the remix
-Thomas Lundquist - Trying to keep it updated.
+Thomas Lundquist - trying to keep it updated
When doing this you should consider using the latest git HEAD.
This is a good thing if you plan to getting it committed into mainline.
@@ -16,14 +16,14 @@ Initial Write
First, write your applet. Be sure to include copyright information at the top,
such as who you stole the code from and so forth. Also include the mini-GPL
-boilerplate. Be sure to name the main function <applet>_main instead of main.
-And be sure to put it in <applet>.c. Usage does not have to be taken care of by
-your applet.
-Make sure to #include "libbb.h" as the first include file in your applet.
+boilerplate and Config.in/Kbuild/usage/applet.h snippets (more on that below
+in this document). Be sure to name the main function <applet>_main instead
+of main. And be sure to put it in <applet>.c. Make sure to #include "libbb.h"
+as the first include file in your applet.
For a new applet mu, here is the code that would go in mu.c:
-(busybox.h already includes most usual header files. You do not need
+(libbb.h already includes most usual header files. You do not need
#include <stdio.h> etc...)
@@ -41,6 +41,22 @@ For a new applet mu, here is the code that would go in mu.c:
#include "libbb.h"
#include "other.h"
+//config:config MU
+//config: bool "MU"
+//config: default y
+//config: help
+//config: Returns an indeterminate value.
+
+//kbuild:lib-$(CONFIG_MU) += mu.o
+//applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//usage:#define mu_trivial_usage
+//usage: "[-abcde] FILE..."
+//usage:#define mu_full_usage
+//usage: "Returns an indeterminate value\n"
+//usage: "\n -a First function"
+//usage: "\n -b Second function"
+
int mu_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int mu_main(int argc, char **argv)
{
@@ -90,6 +106,8 @@ Make a new file named <function_name>.c
#include "libbb.h"
#include "other.h"
+//kbuild:lib-y += function.o
+
int function(char *a)
{
return *a;
@@ -97,9 +115,7 @@ int function(char *a)
----end example code------
-Add <function_name>.o in the right alphabetically sorted place
-in libbb/Kbuild.src. You should look at the conditional part of
-libbb/Kbuild.src as well.
+Remember about the kbuild snippet.
You should also try to find a suitable place in include/libbb.h for
the function declaration. If not, add it somewhere anyway, with or without
@@ -109,60 +125,68 @@ You can look at libbb/Config.src and try to find out if the function is
tunable and add it there if it is.
+Kbuild/Config.in/usage/applets.h snippets in .c files
+-----------------------------------------------------
+
+The old way of adding new applets was to put all the information needed by the
+configuration and build system into appropriate files (namely: Kbuild.src and
+Config.src in new applet's directory) and to add the applet declaration and
+usage info text to include/applets.src.h and include/usage.src.h respectively.
+
+Since the scripts/gen_build_files.sh script had been introduced, the preferred
+way is to have all these declarations contained within the applet .c files.
+
+Every line intended to be processed by gen_build_files.sh should start as a
+comment without any preceding whitespaces and be followed by an appropriate
+keyword - kbuild, config, usage or applet - and a colon, just like shown in the
+first example above.
+
+
Placement / Directory
---------------------
Find the appropriate directory for your new applet.
-Make sure you find the appropriate places in the files, the applets are
-sorted alphabetically.
+Add the kbuild snippet to the .c file:
-Add the applet to Kbuild.src in the chosen directory:
+//kbuild:lib-$(CONFIG_MU) += mu.o
-lib-$(CONFIG_MU) += mu.o
+Add the config snippet to the .c file:
-Add the applet to Config.src in the chosen directory:
-
-config MU
- bool "MU"
- default n
- help
- Returns an indeterminate value.
+//config:config MU
+//config: bool "MU"
+//config: default y
+//config: help
+//config: Returns an indeterminate value.
Usage String(s)
---------------
-Next, add usage information for you applet to include/usage.src.h.
+Next, add usage information for your applet to the .c file.
This should look like the following:
- #define mu_trivial_usage \
- "-[abcde] FILES"
- #define mu_full_usage \
- "Returns an indeterminate value.\n\n" \
- "Options:\n" \
- "\t-a\t\tfirst function\n" \
- "\t-b\t\tsecond function\n" \
- ...
+//usage:#define mu_trivial_usage
+//usage: "[-abcde] FILE..."
+//usage:#define mu_full_usage
+//usage: "Returns an indeterminate value\n"
+//usage: "\n -a First function"
+//usage: "\n -b Second function"
+//usage: ...
If your program supports flags, the flags should be mentioned on the first
-line (-[abcde]) and a detailed description of each flag should go in the
-mu_full_usage section, one flag per line. (Numerous examples of this
-currently exist in usage.src.h.)
+line ([-abcde]) and a detailed description of each flag should go in the
+mu_full_usage section, one flag per line.
Header Files
------------
-Next, add an entry to include/applets.src.h. Be *sure* to keep the list
-in alphabetical order, or else it will break the binary-search lookup
-algorithm in busybox.c and the Gods of BusyBox smite you. Yea, verily:
-
-Be sure to read the top of applets.src.h before adding your applet.
+Finally add the applet declaration snippet. Be sure to read the top of
+applets.src.h before adding your applet - it contains important info
+on applet macros and conventions.
- /* all programs above here are alphabetically "less than" 'mu' */
- IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP))
- /* all programs below here are alphabetically "greater than" 'mu' */
+//applet:IF_MU(APPLET(mu, BB_DIR_USR_BIN, BB_SUID_DROP))
The Grand Announcement
diff --git a/docs/posix_conformance.txt b/docs/posix_conformance.txt
index 5b616d7..c0582dc 100644
--- a/docs/posix_conformance.txt
+++ b/docs/posix_conformance.txt
@@ -178,6 +178,7 @@ dd POSIX options:
conv=noerror | yes | |
conv=notrunc | yes | |
conv=sync | yes | |
+ iflag=skip_bytes| yes | |
dd Busybox specific options:
conv=fsync
diff --git a/docs/unit-tests.txt b/docs/unit-tests.txt
new file mode 100644
index 0000000..0fb5220
--- a/dev/null
+++ b/docs/unit-tests.txt
@@ -0,0 +1,50 @@
+Busybox unit test framework
+===========================
+
+This document describes what you need to do to write test cases using the
+Busybox unit test framework.
+
+
+Building unit tests
+-------------------
+
+The framework and all tests are built as a regular Busybox applet if option
+CONFIG_UNIT_TEST (found in General Configuration -> Debugging Options) is set.
+
+
+Writing test cases
+------------------
+
+Unit testing interface can be found in include/bbunit.h.
+
+Tests can be placed in any .c file in Busybox tree - preferably right next to
+the functions they test. Test cases should be enclosed within an #if, and
+should start with BBUNIT_DEFINE_TEST macro and end with BBUNIT_ENDTEST within
+the test curly brackets. If an assertion fails the test ends immediately, ie.
+the following assertions will not be reached. Any code placed after
+BBUNIT_ENDTEST is executed regardless of the test result. Here's an example:
+
+#if ENABLE_UNIT_TEST
+
+BBUNIT_DEFINE_TEST(test_name)
+{
+ int *i;
+
+ i = malloc(sizeof(int));
+ BBUNIT_ASSERT_NOTNULL(i);
+ *i = 2;
+ BBUNIT_ASSERT_EQ((*i)*(*i), 4);
+
+ BBUNIT_ENDTEST;
+
+ free(i);
+}
+
+#endif /* ENABLE_UNIT_TEST */
+
+
+Running the unit test suite
+---------------------------
+
+To run the tests you can either directly run 'busybox unit' or use 'make test'
+to run both the unit tests (if compiled) and regular test suite.
diff --git a/e2fsprogs/Config.src b/e2fsprogs/Config.src
index 743e1e1..a20d849 100644
--- a/e2fsprogs/Config.src
+++ b/e2fsprogs/Config.src
@@ -7,12 +7,6 @@ menu "Linux Ext2 FS Progs"
INSERT
-config CHATTR
- bool "chattr"
- default y
- help
- chattr changes the file attributes on a second extended file system.
-
### config E2FSCK
### bool "e2fsck"
### default y
@@ -22,21 +16,6 @@ config CHATTR
### The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
### provided.
-config FSCK
- bool "fsck"
- default y
- help
- fsck is used to check and optionally repair one or more filesystems.
- In actuality, fsck is simply a front-end for the various file system
- checkers (fsck.fstype) available under Linux.
-
-config LSATTR
- bool "lsattr"
- default y
- select PLATFORM_LINUX
- help
- lsattr lists the file attributes on a second extended file system.
-
### config MKE2FS
### bool "mke2fs"
### default y
@@ -44,13 +23,6 @@ config LSATTR
### mke2fs is used to create an ext2/ext3 filesystem. The normal compat
### symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
-config TUNE2FS
- bool "tune2fs"
- default n # off: it is too limited compared to upstream version
- help
- tune2fs allows the system administrator to adjust various tunable
- filesystem parameters on Linux ext2/ext3 filesystems.
-
### config E2LABEL
### bool "e2label"
### default y
diff --git a/e2fsprogs/Kbuild.src b/e2fsprogs/Kbuild.src
index b7a14c3..6b4fb74 100644
--- a/e2fsprogs/Kbuild.src
+++ b/e2fsprogs/Kbuild.src
@@ -7,9 +7,3 @@
lib-y:=
INSERT
-
-lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
-lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
-
-lib-$(CONFIG_FSCK) += fsck.o
-lib-$(CONFIG_TUNE2FS) += tune2fs.o
diff --git a/e2fsprogs/chattr.c b/e2fsprogs/chattr.c
index f1cc838..043f395 100644
--- a/e2fsprogs/chattr.c
+++ b/e2fsprogs/chattr.c
@@ -9,24 +9,22 @@
* This file can be redistributed under the terms of the GNU General
* Public License
*/
+//config:config CHATTR
+//config: bool "chattr"
+//config: default y
+//config: help
+//config: chattr changes the file attributes on a second extended file system.
-/*
- * History:
- * 93/10/30 - Creation
- * 93/11/13 - Replace stat() calls by lstat() to avoid loops
- * 94/02/27 - Integrated in Ted's distribution
- * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
- * 98/12/29 - Display version info only when -V specified (G M Sipe)
- */
+//applet:IF_CHATTR(APPLET(chattr, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
//usage:#define chattr_trivial_usage
//usage: "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..."
//usage:#define chattr_full_usage "\n\n"
-//usage: "Change file attributes on an ext2 fs\n"
+//usage: "Change ext2 file attributes\n"
//usage: "\nModifiers:"
-//usage: "\n - Remove attributes"
-//usage: "\n + Add attributes"
-//usage: "\n = Set attributes"
+//usage: "\n -,+,= Remove/add/set attributes"
//usage: "\nAttributes:"
//usage: "\n A Don't track atime"
//usage: "\n a Append mode only"
@@ -36,11 +34,11 @@
//usage: "\n i Cannot be modified (immutable)"
//usage: "\n j Write all data to journal first"
//usage: "\n s Zero disk storage when deleted"
-//usage: "\n S Write file contents synchronously"
+//usage: "\n S Write synchronously"
//usage: "\n t Disable tail-merging of partial blocks with other files"
//usage: "\n u Allow file to be undeleted"
//usage: "\n -R Recurse"
-//usage: "\n -v Set the file's version/generation number"
+//usage: "\n -v VER Set version/generation number"
#include "libbb.h"
#include "e2fs_lib.h"
diff --git a/e2fsprogs/e2fs_lib.c b/e2fsprogs/e2fs_lib.c
index a6aec94..6ce655b 100644
--- a/e2fsprogs/e2fs_lib.c
+++ b/e2fsprogs/e2fs_lib.c
@@ -149,14 +149,14 @@ const uint32_t e2attr_flags_value[] = {
EXT2_TOPDIR_FL
};
-const char e2attr_flags_sname[] =
+const char e2attr_flags_sname[] ALIGN1 =
#ifdef ENABLE_COMPRESSION
"BZXE"
#endif
"I"
"suSDiadAcjtT";
-static const char e2attr_flags_lname[] =
+static const char e2attr_flags_lname[] ALIGN1 =
#ifdef ENABLE_COMPRESSION
"Compressed_File" "\0"
"Compressed_Dirty_File" "\0"
diff --git a/e2fsprogs/fsck.c b/e2fsprogs/fsck.c
index d32f396..6414988 100644
--- a/e2fsprogs/fsck.c
+++ b/e2fsprogs/fsck.c
@@ -33,9 +33,20 @@
* spawns actual fsck.something for each filesystem to check.
* It doesn't guess filesystem types from on-disk format.
*/
+//config:config FSCK
+//config: bool "fsck"
+//config: default y
+//config: help
+//config: fsck is used to check and optionally repair one or more filesystems.
+//config: In actuality, fsck is simply a front-end for the various file system
+//config: checkers (fsck.fstype) available under Linux.
+
+//applet:IF_FSCK(APPLET(fsck, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FSCK) += fsck.o
//usage:#define fsck_trivial_usage
-//usage: "[-ANPRTV] [-C FD] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..."
+//usage: "[-ANPRTV] [-t FSTYPE] [FS_OPTS] [BLOCKDEV]..."
//usage:#define fsck_full_usage "\n\n"
//usage: "Check and repair filesystems\n"
//usage: "\n -A Walk /etc/fstab and check all filesystems"
@@ -44,10 +55,12 @@
//usage: "\n -R With -A, skip the root filesystem"
//usage: "\n -T Don't show title on startup"
//usage: "\n -V Verbose"
-//usage: "\n -C n Write status information to specified filedescriptor"
+//DO_PROGRESS_INDICATOR is off:
+////usage: "\n -C FD Write status information to specified file descriptor"
//usage: "\n -t TYPE List of filesystem types to check"
#include "libbb.h"
+#include "common_bufsiz.h"
/* "progress indicator" code is somewhat buggy and ext[23] specific.
* We should be filesystem agnostic. IOW: there should be a well-defined
@@ -125,35 +138,43 @@ static const char really_wanted[] ALIGN1 =
#define BASE_MD "/dev/md"
-static char **args;
-static int num_args;
-static int verbose;
+struct globals {
+ char **args;
+ int num_args;
+ int verbose;
#define FS_TYPE_FLAG_NORMAL 0
#define FS_TYPE_FLAG_OPT 1
#define FS_TYPE_FLAG_NEGOPT 2
-static char **fs_type_list;
-static uint8_t *fs_type_flag;
-static smallint fs_type_negated;
-
-static smallint noexecute;
-static smallint serialize;
-static smallint skip_root;
-/* static smallint like_mount; */
-static smallint parallel_root;
-static smallint force_all_parallel;
+ char **fs_type_list;
+ uint8_t *fs_type_flag;
+ smallint fs_type_negated;
+
+ smallint noexecute;
+ smallint serialize;
+ smallint skip_root;
+ /* smallint like_mount; */
+ smallint parallel_root;
+ smallint force_all_parallel;
+ smallint kill_sent;
#if DO_PROGRESS_INDICATOR
-static smallint progress;
-static int progress_fd;
+ smallint progress;
+ int progress_fd;
#endif
-static int num_running;
-static int max_running;
-static char *fstype;
-static struct fs_info *filesys_info;
-static struct fs_info *filesys_last;
-static struct fsck_instance *instance_list;
+ int num_running;
+ int max_running;
+ char *fstype;
+ struct fs_info *filesys_info;
+ struct fs_info *filesys_last;
+ struct fsck_instance *instance_list;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+} while (0)
/*
* Return the "base device" given a particular device; this is used to
@@ -199,7 +220,7 @@ static char *base_device(const char *device)
}
/* Handle DAC 960 devices */
- if (strncmp(cp, "rd/", 3) == 0) {
+ if (is_prefixed_with(cp, "rd/")) {
cp += 3;
if (cp[0] != 'c' || !isdigit(cp[1])
|| cp[2] != 'd' || !isdigit(cp[3]))
@@ -224,9 +245,9 @@ static char *base_device(const char *device)
#if ENABLE_FEATURE_DEVFS
/* Now let's handle devfs (ugh) names */
len = 0;
- if (strncmp(cp, "ide/", 4) == 0)
+ if (is_prefixed_with(cp, "ide/"))
len = 4;
- if (strncmp(cp, "scsi/", 5) == 0)
+ if (is_prefixed_with(cp, "scsi/"))
len = 5;
if (len) {
cp += len;
@@ -237,38 +258,38 @@ static char *base_device(const char *device)
* some number of digits at each level, abort.
*/
for (hier = devfs_hier; *hier; hier++) {
- len = strlen(*hier);
- if (strncmp(cp, *hier, len) != 0)
+ cp = is_prefixed_with(cp, *hier);
+ if (!cp)
goto errout;
- cp += len;
- while (*cp != '/' && *cp != 0) {
+ while (*cp != '/' && *cp != '\0') {
if (!isdigit(*cp))
goto errout;
cp++;
}
+//FIXME: what if *cp = '\0' now? cp++ moves past it!!!
cp++;
}
- cp[-1] = 0;
+ cp[-1] = '\0';
return str;
}
/* Now handle devfs /dev/disc or /dev/disk names */
- disk = 0;
- if (strncmp(cp, "discs/", 6) == 0)
+ disk = NULL;
+ if (is_prefixed_with(cp, "discs/"))
disk = "disc";
- else if (strncmp(cp, "disks/", 6) == 0)
+ else if (is_prefixed_with(cp, "disks/"))
disk = "disk";
if (disk) {
cp += 6;
- if (strncmp(cp, disk, 4) != 0)
+ cp = is_prefixed_with(cp, disk);
+ if (!cp)
goto errout;
- cp += 4;
- while (*cp != '/' && *cp != 0) {
+ while (*cp != '/' && *cp != '\0') {
if (!isdigit(*cp))
goto errout;
cp++;
}
- *cp = 0;
+ *cp = '\0';
return str;
}
#endif
@@ -302,11 +323,11 @@ static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
/*fs->flags = 0; */
/*fs->next = NULL; */
- if (!filesys_info)
- filesys_info = fs;
+ if (!G.filesys_info)
+ G.filesys_info = fs;
else
- filesys_last->next = fs;
- filesys_last = fs;
+ G.filesys_last->next = fs;
+ G.filesys_last = fs;
return fs;
}
@@ -316,6 +337,7 @@ static void load_fs_info(const char *filename)
{
FILE *fstab;
struct mntent mte;
+ char buf[1024];
fstab = setmntent(filename, "r");
if (!fstab) {
@@ -324,8 +346,8 @@ static void load_fs_info(const char *filename)
}
// Loop through entries
- while (getmntent_r(fstab, &mte, bb_common_bufsiz1, COMMON_BUFSIZE)) {
- //bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
+ while (getmntent_r(fstab, &mte, buf, sizeof(buf))) {
+ //bb_error_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
// mte.mnt_type, mte.mnt_opts,
// mte.mnt_passno);
create_fs_device(mte.mnt_fsname, mte.mnt_dir,
@@ -340,7 +362,7 @@ static struct fs_info *lookup(char *filesys)
{
struct fs_info *fs;
- for (fs = filesys_info; fs; fs = fs->next) {
+ for (fs = G.filesys_info; fs; fs = fs->next) {
if (strcmp(filesys, fs->device) == 0
|| (fs->mountpt && strcmp(filesys, fs->mountpt) == 0)
)
@@ -355,7 +377,7 @@ static int progress_active(void)
{
struct fsck_instance *inst;
- for (inst = instance_list; inst; inst = inst->next) {
+ for (inst = G.instance_list; inst; inst = inst->next) {
if (inst->flags & FLAG_DONE)
continue;
if (inst->flags & FLAG_PROGRESS)
@@ -371,19 +393,17 @@ static int progress_active(void)
*/
static void kill_all_if_got_signal(void)
{
- static smallint kill_sent;
-
struct fsck_instance *inst;
- if (!bb_got_signal || kill_sent)
+ if (!bb_got_signal || G.kill_sent)
return;
- for (inst = instance_list; inst; inst = inst->next) {
+ for (inst = G.instance_list; inst; inst = inst->next) {
if (inst->flags & FLAG_DONE)
continue;
kill(inst->pid, SIGTERM);
}
- kill_sent = 1;
+ G.kill_sent = 1;
}
/*
@@ -398,9 +418,9 @@ static int wait_one(int flags)
struct fsck_instance *inst, *prev;
pid_t pid;
- if (!instance_list)
+ if (!G.instance_list)
return -1;
- /* if (noexecute) { already returned -1; } */
+ /* if (G.noexecute) { already returned -1; } */
while (1) {
pid = waitpid(-1, &status, flags);
@@ -418,7 +438,7 @@ static int wait_one(int flags)
continue;
}
prev = NULL;
- inst = instance_list;
+ inst = G.instance_list;
do {
if (inst->pid == pid)
goto child_died;
@@ -428,9 +448,8 @@ static int wait_one(int flags)
}
child_died:
- if (WIFEXITED(status))
- status = WEXITSTATUS(status);
- else if (WIFSIGNALED(status)) {
+ status = WEXITSTATUS(status);
+ if (WIFSIGNALED(status)) {
sig = WTERMSIG(status);
status = EXIT_UNCORRECTED;
if (sig != SIGINT) {
@@ -439,16 +458,12 @@ static int wait_one(int flags)
inst->prog, inst->device, sig);
status = EXIT_ERROR;
}
- } else {
- printf("%s %s: status is %x, should never happen\n",
- inst->prog, inst->device, status);
- status = EXIT_ERROR;
}
#if DO_PROGRESS_INDICATOR
if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) {
struct fsck_instance *inst2;
- for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+ for (inst2 = G.instance_list; inst2; inst2 = inst2->next) {
if (inst2->flags & FLAG_DONE)
continue;
if (strcmp(inst2->type, "ext2") != 0
@@ -475,11 +490,11 @@ static int wait_one(int flags)
if (prev)
prev->next = inst->next;
else
- instance_list = inst->next;
- if (verbose > 1)
+ G.instance_list = inst->next;
+ if (G.verbose > 1)
printf("Finished with %s (exit status %d)\n",
inst->device, status);
- num_running--;
+ G.num_running--;
free_instance(inst);
return status;
@@ -515,51 +530,51 @@ static void execute(const char *type, const char *device,
struct fsck_instance *inst;
pid_t pid;
- args[0] = xasprintf("fsck.%s", type);
+ G.args[0] = xasprintf("fsck.%s", type);
#if DO_PROGRESS_INDICATOR
if (progress && !progress_active()) {
if (strcmp(type, "ext2") == 0
|| strcmp(type, "ext3") == 0
) {
- args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */
+ G.args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */
inst->flags |= FLAG_PROGRESS;
}
}
#endif
- args[num_args - 2] = (char*)device;
- /* args[num_args - 1] = NULL; - already is */
+ G.args[G.num_args - 2] = (char*)device;
+ /* G.args[G.num_args - 1] = NULL; - already is */
- if (verbose || noexecute) {
- printf("[%s (%d) -- %s]", args[0], num_running,
+ if (G.verbose || G.noexecute) {
+ printf("[%s (%d) -- %s]", G.args[0], G.num_running,
mntpt ? mntpt : device);
- for (i = 0; args[i]; i++)
- printf(" %s", args[i]);
+ for (i = 0; G.args[i]; i++)
+ printf(" %s", G.args[i]);
bb_putchar('\n');
}
/* Fork and execute the correct program. */
pid = -1;
- if (!noexecute) {
- pid = spawn(args);
+ if (!G.noexecute) {
+ pid = spawn(G.args);
if (pid < 0)
- bb_simple_perror_msg(args[0]);
+ bb_simple_perror_msg(G.args[0]);
}
#if DO_PROGRESS_INDICATOR
- free(args[XXX]);
+ free(G.args[XXX]);
#endif
/* No child, so don't record an instance */
if (pid <= 0) {
- free(args[0]);
+ free(G.args[0]);
return;
}
inst = xzalloc(sizeof(*inst));
inst->pid = pid;
- inst->prog = args[0];
+ inst->prog = G.args[0];
inst->device = xstrdup(device);
inst->base_device = base_device(device);
#if DO_PROGRESS_INDICATOR
@@ -568,8 +583,8 @@ static void execute(const char *type, const char *device,
/* Add to the list of running fsck's.
* (was adding to the end, but adding to the front is simpler...) */
- inst->next = instance_list;
- instance_list = inst;
+ inst->next = G.instance_list;
+ G.instance_list = inst;
}
/*
@@ -588,27 +603,27 @@ static void fsck_device(struct fs_info *fs /*, int interactive */)
if (strcmp(fs->type, "auto") != 0) {
type = fs->type;
- if (verbose > 2)
- bb_info_msg("using filesystem type '%s' %s",
+ if (G.verbose > 2)
+ printf("using filesystem type '%s' %s\n",
type, "from fstab");
- } else if (fstype
- && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */
- && strncmp(fstype, "opts=", 5) != 0
- && strncmp(fstype, "loop", 4) != 0
- && !strchr(fstype, ',')
+ } else if (G.fstype
+ && (G.fstype[0] != 'n' || G.fstype[1] != 'o') /* != "no" */
+ && !is_prefixed_with(G.fstype, "opts=")
+ && !is_prefixed_with(G.fstype, "loop")
+ && !strchr(G.fstype, ',')
) {
- type = fstype;
- if (verbose > 2)
- bb_info_msg("using filesystem type '%s' %s",
+ type = G.fstype;
+ if (G.verbose > 2)
+ printf("using filesystem type '%s' %s\n",
type, "from -t");
} else {
type = "auto";
- if (verbose > 2)
- bb_info_msg("using filesystem type '%s' %s",
+ if (G.verbose > 2)
+ printf("using filesystem type '%s' %s\n",
type, "(default)");
}
- num_running++;
+ G.num_running++;
execute(type, fs->device, fs->mountpt /*, interactive */);
}
@@ -621,14 +636,14 @@ static int device_already_active(char *device)
struct fsck_instance *inst;
char *base;
- if (force_all_parallel)
+ if (G.force_all_parallel)
return 0;
#ifdef BASE_MD
/* Don't check a soft raid disk with any other disk */
- if (instance_list
- && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1)
- || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))
+ if (G.instance_list
+ && (is_prefixed_with(G.instance_list->device, BASE_MD)
+ || is_prefixed_with(device, BASE_MD))
) {
return 1;
}
@@ -640,10 +655,10 @@ static int device_already_active(char *device)
* already active if there are any fsck instances running.
*/
if (!base)
- return (instance_list != NULL);
+ return (G.instance_list != NULL);
- for (inst = instance_list; inst; inst = inst->next) {
- if (!inst->base_device || !strcmp(base, inst->base_device)) {
+ for (inst = G.instance_list; inst; inst = inst->next) {
+ if (!inst->base_device || strcmp(base, inst->base_device) == 0) {
free(base);
return 1;
}
@@ -687,17 +702,17 @@ static int fs_match(struct fs_info *fs)
int n, ret, checked_type;
char *cp;
- if (!fs_type_list)
+ if (!G.fs_type_list)
return 1;
ret = 0;
checked_type = 0;
n = 0;
while (1) {
- cp = fs_type_list[n];
+ cp = G.fs_type_list[n];
if (!cp)
break;
- switch (fs_type_flag[n]) {
+ switch (G.fs_type_flag[n]) {
case FS_TYPE_FLAG_NORMAL:
checked_type++;
if (strcmp(cp, fs->type) == 0)
@@ -717,7 +732,7 @@ static int fs_match(struct fs_info *fs)
if (checked_type == 0)
return 1;
- return (fs_type_negated ? !ret : ret);
+ return (G.fs_type_negated ? !ret : ret);
}
/* Check if we should ignore this filesystem. */
@@ -753,7 +768,7 @@ static int check_all(void)
smallint pass_done;
int passno;
- if (verbose)
+ if (G.verbose)
puts("Checking all filesystems");
/*
@@ -761,17 +776,17 @@ static int check_all(void)
* which should be ignored as done, and resolve any "auto"
* filesystem types (done as a side-effect of calling ignore()).
*/
- for (fs = filesys_info; fs; fs = fs->next)
+ for (fs = G.filesys_info; fs; fs = fs->next)
if (ignore(fs))
fs->flags |= FLAG_DONE;
/*
* Find and check the root filesystem.
*/
- if (!parallel_root) {
- for (fs = filesys_info; fs; fs = fs->next) {
+ if (!G.parallel_root) {
+ for (fs = G.filesys_info; fs; fs = fs->next) {
if (LONE_CHAR(fs->mountpt, '/')) {
- if (!skip_root && !ignore(fs)) {
+ if (!G.skip_root && !ignore(fs)) {
fsck_device(fs /*, 1*/);
status |= wait_many(FLAG_WAIT_ALL);
if (status > EXIT_NONDESTRUCT)
@@ -787,8 +802,8 @@ static int check_all(void)
* filesystem listed twice.
* "Skip root" will skip _all_ root entries.
*/
- if (skip_root)
- for (fs = filesys_info; fs; fs = fs->next)
+ if (G.skip_root)
+ for (fs = G.filesys_info; fs; fs = fs->next)
if (LONE_CHAR(fs->mountpt, '/'))
fs->flags |= FLAG_DONE;
@@ -798,7 +813,7 @@ static int check_all(void)
not_done_yet = 0;
pass_done = 1;
- for (fs = filesys_info; fs; fs = fs->next) {
+ for (fs = G.filesys_info; fs; fs = fs->next) {
if (bb_got_signal)
break;
if (fs->flags & FLAG_DONE)
@@ -824,7 +839,7 @@ static int check_all(void)
/*
* Spawn off the fsck process
*/
- fsck_device(fs /*, serialize*/);
+ fsck_device(fs /*, G.serialize*/);
fs->flags |= FLAG_DONE;
/*
@@ -832,8 +847,8 @@ static int check_all(void)
* have a limit on the number of fsck's extant
* at one time, apply that limit.
*/
- if (serialize
- || (max_running && (num_running >= max_running))
+ if (G.serialize
+ || (G.num_running >= G.max_running)
) {
pass_done = 0;
break;
@@ -841,12 +856,12 @@ static int check_all(void)
}
if (bb_got_signal)
break;
- if (verbose > 1)
+ if (G.verbose > 1)
printf("--waiting-- (pass %d)\n", passno);
status |= wait_many(pass_done ? FLAG_WAIT_ALL :
FLAG_WAIT_ATLEAST_ONE);
if (pass_done) {
- if (verbose > 1)
+ if (G.verbose > 1)
puts("----------------------------------");
passno++;
} else
@@ -874,9 +889,9 @@ static void compile_fs_type(char *fs_type)
s++;
}
- fs_type_list = xzalloc(num * sizeof(fs_type_list[0]));
- fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0]));
- fs_type_negated = -1; /* not yet known is it negated or not */
+ G.fs_type_list = xzalloc(num * sizeof(G.fs_type_list[0]));
+ G.fs_type_flag = xzalloc(num * sizeof(G.fs_type_flag[0]));
+ G.fs_type_negated = -1; /* not yet known is it negated or not */
num = 0;
s = fs_type;
@@ -895,21 +910,21 @@ static void compile_fs_type(char *fs_type)
if (strcmp(s, "loop") == 0)
/* loop is really short-hand for opts=loop */
goto loop_special_case;
- if (strncmp(s, "opts=", 5) == 0) {
+ if (is_prefixed_with(s, "opts=")) {
s += 5;
loop_special_case:
- fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
+ G.fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
} else {
- if (fs_type_negated == -1)
- fs_type_negated = negate;
- if (fs_type_negated != negate)
+ if (G.fs_type_negated == -1)
+ G.fs_type_negated = negate;
+ if (G.fs_type_negated != negate)
bb_error_msg_and_die(
"either all or none of the filesystem types passed to -t must be prefixed "
"with 'no' or '!'");
}
- comma = strchr(s, ',');
- fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s);
- if (!comma)
+ comma = strchrnul(s, ',');
+ G.fs_type_list[num++] = xstrndup(s, comma-s);
+ if (*comma == '\0')
break;
s = comma + 1;
}
@@ -917,8 +932,8 @@ static void compile_fs_type(char *fs_type)
static char **new_args(void)
{
- args = xrealloc_vector(args, 2, num_args);
- return &args[num_args++];
+ G.args = xrealloc_vector(G.args, 2, G.num_args);
+ return &G.args[G.num_args++];
}
int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -935,6 +950,8 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
smallint doall;
smallint notitle;
+ INIT_G();
+
/* we want wait() to be interruptible */
signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
@@ -944,8 +961,8 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
opts_for_fsck = doall = notitle = 0;
devices = NULL;
num_devices = 0;
- new_args(); /* args[0] = NULL, will be replaced by fsck.<type> */
- /* instance_list = NULL; - in bss, so already zeroed */
+ new_args(); /* G.args[0] = NULL, will be replaced by fsck.<type> */
+ /* G.instance_list = NULL; - in bss, so already zeroed */
while (*++argv) {
int j;
@@ -994,13 +1011,13 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
goto next_arg;
#endif
case 'V':
- verbose++;
+ G.verbose++;
break;
case 'N':
- noexecute = 1;
+ G.noexecute = 1;
break;
case 'R':
- skip_root = 1;
+ G.skip_root = 1;
break;
case 'T':
notitle = 1;
@@ -1009,13 +1026,13 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
like_mount = 1;
break; */
case 'P':
- parallel_root = 1;
+ G.parallel_root = 1;
break;
case 's':
- serialize = 1;
+ G.serialize = 1;
break;
case 't':
- if (fstype)
+ if (G.fstype)
bb_show_usage();
if (arg[++j])
tmp = &arg[j];
@@ -1023,8 +1040,8 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
tmp = *argv;
else
bb_show_usage();
- fstype = xstrdup(tmp);
- compile_fs_type(fstype);
+ G.fstype = xstrdup(tmp);
+ compile_fs_type(G.fstype);
goto next_arg;
case '?':
bb_show_usage();
@@ -1045,12 +1062,13 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
}
}
if (getenv("FSCK_FORCE_ALL_PARALLEL"))
- force_all_parallel = 1;
+ G.force_all_parallel = 1;
tmp = getenv("FSCK_MAX_INST");
+ G.max_running = INT_MAX;
if (tmp)
- max_running = xatoi(tmp);
- new_args(); /* args[num_args - 2] will be replaced by <device> */
- new_args(); /* args[num_args - 1] is the last, NULL element */
+ G.max_running = xatoi(tmp);
+ new_args(); /* G.args[G.num_args - 2] will be replaced by <device> */
+ new_args(); /* G.args[G.num_args - 1] is the last, NULL element */
if (!notitle)
puts("fsck (busybox "BB_VER", "BB_BT")");
@@ -1062,10 +1080,10 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
fstab = "/etc/fstab";
load_fs_info(fstab);
- /*interactive = (num_devices == 1) | serialize;*/
+ /*interactive = (num_devices == 1) | G.serialize;*/
if (num_devices == 0)
- /*interactive =*/ serialize = doall = 1;
+ /*interactive =*/ G.serialize = doall = 1;
if (doall)
return check_all();
@@ -1081,13 +1099,13 @@ int fsck_main(int argc UNUSED_PARAM, char **argv)
fs = create_fs_device(devices[i], "", "auto", NULL, -1);
fsck_device(fs /*, interactive */);
- if (serialize
- || (max_running && (num_running >= max_running))
+ if (G.serialize
+ || (G.num_running >= G.max_running)
) {
int exit_status = wait_one(0);
if (exit_status >= 0)
status |= exit_status;
- if (verbose > 1)
+ if (G.verbose > 1)
puts("----------------------------------");
}
}
diff --git a/e2fsprogs/lsattr.c b/e2fsprogs/lsattr.c
index 1312fe7..d2348b5 100644
--- a/e2fsprogs/lsattr.c
+++ b/e2fsprogs/lsattr.c
@@ -9,24 +9,26 @@
* This file can be redistributed under the terms of the GNU General
* Public License
*/
+//config:config LSATTR
+//config: bool "lsattr"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: lsattr lists the file attributes on a second extended file system.
-/*
- * History:
- * 93/10/30 - Creation
- * 93/11/13 - Replace stat() calls by lstat() to avoid loops
- * 94/02/27 - Integrated in Ted's distribution
- * 98/12/29 - Display version info only when -V specified (G M Sipe)
- */
+//applet:IF_LSATTR(APPLET(lsattr, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
//usage:#define lsattr_trivial_usage
//usage: "[-Radlv] [FILE]..."
//usage:#define lsattr_full_usage "\n\n"
-//usage: "List file attributes on an ext2 fs\n"
+//usage: "List ext2 file attributes\n"
//usage: "\n -R Recurse"
//usage: "\n -a Don't hide entries starting with ."
//usage: "\n -d List directory entries instead of contents"
//usage: "\n -l List long flag names"
-//usage: "\n -v List the file's version/generation number"
+//usage: "\n -v List version/generation number"
#include "libbb.h"
#include "e2fs_lib.h"
diff --git a/e2fsprogs/old_e2fsprogs/Config.src b/e2fsprogs/old_e2fsprogs/Config.src
deleted file mode 100644
index bbec08e..0000000
--- a/e2fsprogs/old_e2fsprogs/Config.src
+++ b/dev/null
@@ -1,69 +0,0 @@
-#
-# For a description of the syntax of this configuration file,
-# see scripts/kbuild/config-language.txt.
-#
-
-menu "Linux Ext2 FS Progs"
-
-INSERT
-
-config CHATTR
- bool "chattr"
- default n
- help
- chattr changes the file attributes on a second extended file system.
-
-config E2FSCK
- bool "e2fsck"
- default n
- help
- e2fsck is used to check Linux second extended file systems (ext2fs).
- e2fsck also supports ext2 filesystems countaining a journal (ext3).
- The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
- provided.
-
-config FSCK
- bool "fsck"
- default n
- help
- fsck is used to check and optionally repair one or more filesystems.
- In actuality, fsck is simply a front-end for the various file system
- checkers (fsck.fstype) available under Linux.
-
-config LSATTR
- bool "lsattr"
- default n
- help
- lsattr lists the file attributes on a second extended file system.
-
-config MKE2FS
- bool "mke2fs"
- default n
- help
- mke2fs is used to create an ext2/ext3 filesystem. The normal compat
- symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
-
-config TUNE2FS
- bool "tune2fs"
- default n
- help
- tune2fs allows the system administrator to adjust various tunable
- filesystem parameters on Linux ext2/ext3 filesystems.
-
-config E2LABEL
- bool "e2label"
- default n
- depends on TUNE2FS
- help
- e2label will display or change the filesystem label on the ext2
- filesystem located on device.
-
-config FINDFS
- bool "findfs"
- default n
- depends on TUNE2FS
- help
- findfs will search the disks in the system looking for a filesystem
- which has a label matching label or a UUID equal to uuid.
-
-endmenu
diff --git a/e2fsprogs/old_e2fsprogs/Kbuild.src b/e2fsprogs/old_e2fsprogs/Kbuild.src
deleted file mode 100644
index fff1a0d..0000000
--- a/e2fsprogs/old_e2fsprogs/Kbuild.src
+++ b/dev/null
@@ -1,18 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under GPLv2, see file LICENSE in this source tree.
-
-lib-y:=
-
-INSERT
-
-lib-$(CONFIG_CHATTR) += chattr.o
-lib-$(CONFIG_E2FSCK) += e2fsck.o util.o
-lib-$(CONFIG_FSCK) += fsck.o util.o
-lib-$(CONFIG_LSATTR) += lsattr.o
-lib-$(CONFIG_MKE2FS) += mke2fs.o util.o
-lib-$(CONFIG_TUNE2FS) += tune2fs.o util.o
-
-CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/e2fsprogs/old_e2fsprogs/README b/e2fsprogs/old_e2fsprogs/README
deleted file mode 100644
index fac0901..0000000
--- a/e2fsprogs/old_e2fsprogs/README
+++ b/dev/null
@@ -1,3 +0,0 @@
-This is a pretty straight rip from the e2fsprogs pkg.
-
-See README's in subdirs for specific info.
diff --git a/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src b/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src
deleted file mode 100644
index 02b4d24..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src
+++ b/dev/null
@@ -1,26 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under GPLv2, see file LICENSE in this source tree.
-
-NEEDED-$(CONFIG_E2FSCK) = y
-NEEDED-$(CONFIG_FSCK) = y
-NEEDED-$(CONFIG_MKE2FS) = y
-NEEDED-$(CONFIG_TUNE2FS) = y
-
-lib-y:=
-
-INSERT
-
-lib-$(NEEDED-y) += cache.o dev.o devname.o devno.o blkid_getsize.o \
- probe.o read.o resolve.o save.o tag.o list.o
-
-CFLAGS_dev.o := -include $(srctree)/include/busybox.h
-CFLAGS_devname.o := -include $(srctree)/include/busybox.h
-CFLAGS_devno.o := -include $(srctree)/include/busybox.h
-CFLAGS_blkid_getsize.o := -include $(srctree)/include/busybox.h
-CFLAGS_probe.o := -include $(srctree)/include/busybox.h
-CFLAGS_save.o := -include $(srctree)/include/busybox.h
-CFLAGS_tag.o := -include $(srctree)/include/busybox.h
-CFLAGS_list.o := -include $(srctree)/include/busybox.h
diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid.h b/e2fsprogs/old_e2fsprogs/blkid/blkid.h
deleted file mode 100644
index 9a3c2af..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/blkid.h
+++ b/dev/null
@@ -1,104 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * blkid.h - Interface for libblkid, a library to identify block devices
- *
- * Copyright (C) 2001 Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-#ifndef BLKID_BLKID_H
-#define BLKID_BLKID_H 1
-
-#include <sys/types.h>
-#include <linux/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define BLKID_VERSION "1.0.0"
-#define BLKID_DATE "12-Feb-2003"
-
-typedef struct blkid_struct_dev *blkid_dev;
-typedef struct blkid_struct_cache *blkid_cache;
-typedef __s64 blkid_loff_t;
-
-typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
-typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
-
-/*
- * Flags for blkid_get_dev
- *
- * BLKID_DEV_CREATE Create an empty device structure if not found
- * in the cache.
- * BLKID_DEV_VERIFY Make sure the device structure corresponds
- * with reality.
- * BLKID_DEV_FIND Just look up a device entry, and return NULL
- * if it is not found.
- * BLKID_DEV_NORMAL Get a valid device structure, either from the
- * cache or by probing the device.
- */
-#define BLKID_DEV_FIND 0x0000
-#define BLKID_DEV_CREATE 0x0001
-#define BLKID_DEV_VERIFY 0x0002
-#define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
-
-/* cache.c */
-extern void blkid_put_cache(blkid_cache cache);
-extern int blkid_get_cache(blkid_cache *cache, const char *filename);
-
-/* dev.c */
-extern const char *blkid_dev_devname(blkid_dev dev);
-
-extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
-extern int blkid_dev_set_search(blkid_dev_iterate iter,
- char *search_type, char *search_value);
-extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
-extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
-
-/* devno.c */
-extern char *blkid_devno_to_devname(dev_t devno);
-
-/* devname.c */
-extern int blkid_probe_all(blkid_cache cache);
-extern int blkid_probe_all_new(blkid_cache cache);
-extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname,
- int flags);
-
-/* getsize.c */
-extern blkid_loff_t blkid_get_dev_size(int fd);
-
-/* probe.c */
-int blkid_known_fstype(const char *fstype);
-extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
-
-/* read.c */
-
-/* resolve.c */
-extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
- const char *devname);
-extern char *blkid_get_devname(blkid_cache cache, const char *token,
- const char *value);
-
-/* tag.c */
-extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev);
-extern int blkid_tag_next(blkid_tag_iterate iterate,
- const char **type, const char **value);
-extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
-extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
- const char *value);
-extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
- const char *type,
- const char *value);
-extern int blkid_parse_tag_string(const char *token, char **ret_type,
- char **ret_val);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkidP.h b/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
deleted file mode 100644
index bbadc8e..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
+++ b/dev/null
@@ -1,182 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * blkidP.h - Internal interfaces for libblkid
- *
- * Copyright (C) 2001 Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-#ifndef BLKID_BLKIDP_H
-#define BLKID_BLKIDP_H 1
-
-#include <sys/types.h>
-#include <stdio.h>
-
-#include "blkid.h"
-#include "list.h"
-
-#ifdef __GNUC__
-#define __BLKID_ATTR(x) __attribute__(x)
-#else
-#define __BLKID_ATTR(x)
-#endif
-
-
-/*
- * This describes the attributes of a specific device.
- * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
- * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
- * values, if they exist.
- */
-struct blkid_struct_dev
-{
- struct list_head bid_devs; /* All devices in the cache */
- struct list_head bid_tags; /* All tags for this device */
- blkid_cache bid_cache; /* Dev belongs to this cache */
- char *bid_name; /* Device inode pathname */
- char *bid_type; /* Preferred device TYPE */
- int bid_pri; /* Device priority */
- dev_t bid_devno; /* Device major/minor number */
- time_t bid_time; /* Last update time of device */
- unsigned int bid_flags; /* Device status bitflags */
- char *bid_label; /* Shortcut to device LABEL */
- char *bid_uuid; /* Shortcut to binary UUID */
-};
-
-#define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */
-#define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */
-
-/*
- * Each tag defines a NAME=value pair for a particular device. The tags
- * are linked via bit_names for a single device, so that traversing the
- * names list will get you a list of all tags associated with a device.
- * They are also linked via bit_values for all devices, so one can easily
- * search all tags with a given NAME for a specific value.
- */
-struct blkid_struct_tag
-{
- struct list_head bit_tags; /* All tags for this device */
- struct list_head bit_names; /* All tags with given NAME */
- char *bit_name; /* NAME of tag (shared) */
- char *bit_val; /* value of tag */
- blkid_dev bit_dev; /* pointer to device */
-};
-typedef struct blkid_struct_tag *blkid_tag;
-
-/*
- * Minimum number of seconds between device probes, even when reading
- * from the cache. This is to avoid re-probing all devices which were
- * just probed by another program that does not share the cache.
- */
-#define BLKID_PROBE_MIN 2
-
-/*
- * Time in seconds an entry remains verified in the in-memory cache
- * before being reverified (in case of long-running processes that
- * keep a cache in memory and continue to use it for a long time).
- */
-#define BLKID_PROBE_INTERVAL 200
-
-/* This describes an entire blkid cache file and probed devices.
- * We can traverse all of the found devices via bic_list.
- * We can traverse all of the tag types by bic_tags, which hold empty tags
- * for each tag type. Those tags can be used as list_heads for iterating
- * through all devices with a specific tag type (e.g. LABEL).
- */
-struct blkid_struct_cache
-{
- struct list_head bic_devs; /* List head of all devices */
- struct list_head bic_tags; /* List head of all tag types */
- time_t bic_time; /* Last probe time */
- time_t bic_ftime; /* Mod time of the cachefile */
- unsigned int bic_flags; /* Status flags of the cache */
- char *bic_filename; /* filename of cache */
-};
-
-#define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */
-#define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */
-
-extern char *blkid_strdup(const char *s);
-extern char *blkid_strndup(const char *s, const int length);
-
-#define BLKID_CACHE_FILE "/etc/blkid.tab"
-extern const char *blkid_devdirs[];
-
-#define BLKID_ERR_IO 5
-#define BLKID_ERR_PROC 9
-#define BLKID_ERR_MEM 12
-#define BLKID_ERR_CACHE 14
-#define BLKID_ERR_DEV 19
-#define BLKID_ERR_PARAM 22
-#define BLKID_ERR_BIG 27
-
-/*
- * Priority settings for different types of devices
- */
-#define BLKID_PRI_EVMS 30
-#define BLKID_PRI_LVM 20
-#define BLKID_PRI_MD 10
-
-#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
-#define CONFIG_BLKID_DEBUG
-#endif
-
-#define DEBUG_CACHE 0x0001
-#define DEBUG_DUMP 0x0002
-#define DEBUG_DEV 0x0004
-#define DEBUG_DEVNAME 0x0008
-#define DEBUG_DEVNO 0x0010
-#define DEBUG_PROBE 0x0020
-#define DEBUG_READ 0x0040
-#define DEBUG_RESOLVE 0x0080
-#define DEBUG_SAVE 0x0100
-#define DEBUG_TAG 0x0200
-#define DEBUG_INIT 0x8000
-#define DEBUG_ALL 0xFFFF
-
-#ifdef CONFIG_BLKID_DEBUG
-#include <stdio.h>
-extern int blkid_debug_mask;
-#define DBG(m,x) if ((m) & blkid_debug_mask) x;
-#else
-#define DBG(m,x)
-#endif
-
-#ifdef CONFIG_BLKID_DEBUG
-extern void blkid_debug_dump_dev(blkid_dev dev);
-extern void blkid_debug_dump_tag(blkid_tag tag);
-#endif
-
-/* lseek.c */
-/* extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); */
-#ifdef CONFIG_LFS
-# define blkid_llseek lseek64
-#else
-# define blkid_llseek lseek
-#endif
-
-/* read.c */
-extern void blkid_read_cache(blkid_cache cache);
-
-/* save.c */
-extern int blkid_flush_cache(blkid_cache cache);
-
-/*
- * Functions to create and find a specific tag type: tag.c
- */
-extern void blkid_free_tag(blkid_tag tag);
-extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type);
-extern int blkid_set_tag(blkid_dev dev, const char *name,
- const char *value, const int vlength);
-
-/*
- * Functions to create and find a specific tag type: dev.c
- */
-extern blkid_dev blkid_new_dev(void);
-extern void blkid_free_dev(blkid_dev dev);
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
deleted file mode 100644
index e1f6ba6..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
+++ b/dev/null
@@ -1,179 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * getsize.c --- get the size of a partition.
- *
- * Copyright (C) 1995, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-/* include this before sys/queues.h! */
-#include "blkidP.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_LINUX_FD_H
-#include <linux/fd.h>
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
-#include <sys/disklabel.h>
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_DISK_H
-#ifdef HAVE_SYS_QUEUE_H
-#include <sys/queue.h> /* for LIST_HEAD */
-#endif
-#include <sys/disk.h>
-#endif
-#ifdef __linux__
-#include <sys/utsname.h>
-#endif
-
-#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
-#define BLKGETSIZE _IO(0x12,96) /* return device size */
-#endif
-
-#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
-#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
-#endif
-
-#ifdef APPLE_DARWIN
-#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
-#endif /* APPLE_DARWIN */
-
-static int valid_offset(int fd, blkid_loff_t offset)
-{
- char ch;
-
- if (blkid_llseek(fd, offset, 0) < 0)
- return 0;
- if (read(fd, &ch, 1) < 1)
- return 0;
- return 1;
-}
-
-/*
- * Returns the number of blocks in a partition
- */
-blkid_loff_t blkid_get_dev_size(int fd)
-{
- int valid_blkgetsize64 = 1;
-#ifdef __linux__
- struct utsname ut;
-#endif
- unsigned long long size64;
- unsigned long size;
- blkid_loff_t high, low;
-#ifdef FDGETPRM
- struct floppy_struct this_floppy;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
- int part = -1;
- struct disklabel lab;
- struct partition *pp;
- char ch;
- struct stat st;
-#endif /* HAVE_SYS_DISKLABEL_H */
-
-#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
- if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
- if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
- && (size64 << 9 > 0xFFFFFFFF))
- return 0; /* EFBIG */
- return (blkid_loff_t) size64 << 9;
- }
-#endif
-
-#ifdef BLKGETSIZE64
-#ifdef __linux__
- uname(&ut);
- if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
- (ut.release[2] < '6') && (ut.release[3] == '.'))
- valid_blkgetsize64 = 0;
-#endif
- if (valid_blkgetsize64 &&
- ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
- if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
- && ((size64) > 0xFFFFFFFF))
- return 0; /* EFBIG */
- return size64;
- }
-#endif
-
-#ifdef BLKGETSIZE
- if (ioctl(fd, BLKGETSIZE, &size) >= 0)
- return (blkid_loff_t)size << 9;
-#endif
-
-#ifdef FDGETPRM
- if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
- return (blkid_loff_t)this_floppy.size << 9;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
-#if 0
- /*
- * This should work in theory but I haven't tested it. Anyone
- * on a BSD system want to test this for me? In the meantime,
- * binary search mechanism should work just fine.
- */
- if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode))
- part = st.st_rdev & 7;
- if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
- pp = &lab.d_partitions[part];
- if (pp->p_size)
- return pp->p_size << 9;
- }
-#endif
-#endif /* HAVE_SYS_DISKLABEL_H */
-
- /*
- * OK, we couldn't figure it out by using a specialized ioctl,
- * which is generally the best way. So do binary search to
- * find the size of the partition.
- */
- low = 0;
- for (high = 1024; valid_offset(fd, high); high *= 2)
- low = high;
- while (low < high - 1)
- {
- const blkid_loff_t mid = (low + high) / 2;
-
- if (valid_offset(fd, mid))
- low = mid;
- else
- high = mid;
- }
- return low + 1;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- blkid_loff_t bytes;
- int fd;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s device\n"
- "Determine the size of a device\n", argv[0]);
- return 1;
- }
-
- if ((fd = open(argv[1], O_RDONLY)) < 0)
- perror(argv[0]);
-
- bytes = blkid_get_dev_size(fd);
- printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10);
-
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/cache.c b/e2fsprogs/old_e2fsprogs/blkid/cache.c
deleted file mode 100644
index 251e499..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/cache.c
+++ b/dev/null
@@ -1,125 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * cache.c - allocation/initialization/free routines for cache
- *
- * Copyright (C) 2001 Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "blkidP.h"
-
-int blkid_debug_mask = 0;
-
-int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
-{
- blkid_cache cache;
-
-#ifdef CONFIG_BLKID_DEBUG
- if (!(blkid_debug_mask & DEBUG_INIT)) {
- char *dstr = getenv("BLKID_DEBUG");
-
- if (dstr)
- blkid_debug_mask = strtoul(dstr, 0, 0);
- blkid_debug_mask |= DEBUG_INIT;
- }
-#endif
-
- DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
- filename ? filename : "default cache"));
-
- cache = xzalloc(sizeof(struct blkid_struct_cache));
-
- INIT_LIST_HEAD(&cache->bic_devs);
- INIT_LIST_HEAD(&cache->bic_tags);
-
- if (filename && !strlen(filename))
- filename = 0;
- if (!filename && (getuid() == geteuid()))
- filename = getenv("BLKID_FILE");
- if (!filename)
- filename = BLKID_CACHE_FILE;
- cache->bic_filename = blkid_strdup(filename);
-
- blkid_read_cache(cache);
-
- *ret_cache = cache;
- return 0;
-}
-
-void blkid_put_cache(blkid_cache cache)
-{
- if (!cache)
- return;
-
- (void) blkid_flush_cache(cache);
-
- DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
-
- /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
-
- while (!list_empty(&cache->bic_devs)) {
- blkid_dev dev = list_entry(cache->bic_devs.next,
- struct blkid_struct_dev,
- bid_devs);
- blkid_free_dev(dev);
- }
-
- while (!list_empty(&cache->bic_tags)) {
- blkid_tag tag = list_entry(cache->bic_tags.next,
- struct blkid_struct_tag,
- bit_tags);
-
- while (!list_empty(&tag->bit_names)) {
- blkid_tag bad = list_entry(tag->bit_names.next,
- struct blkid_struct_tag,
- bit_names);
-
- DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
- bad->bit_name, bad->bit_val));
- blkid_free_tag(bad);
- }
- blkid_free_tag(tag);
- }
- free(cache->bic_filename);
-
- free(cache);
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char** argv)
-{
- blkid_cache cache = NULL;
- int ret;
-
- blkid_debug_mask = DEBUG_ALL;
- if ((argc > 2)) {
- fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
- exit(1);
- }
-
- if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
- fprintf(stderr, "error %d parsing cache file %s\n", ret,
- argv[1] ? argv[1] : BLKID_CACHE_FILE);
- exit(1);
- }
- if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
- if ((ret = blkid_probe_all(cache)) < 0)
- fprintf(stderr, "error probing devices\n");
-
- blkid_put_cache(cache);
-
- return ret;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/dev.c b/e2fsprogs/old_e2fsprogs/blkid/dev.c
deleted file mode 100644
index 260e49c..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/dev.c
+++ b/dev/null
@@ -1,213 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dev.c - allocation/initialization/free routines for dev
- *
- * Copyright (C) 2001 Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "blkidP.h"
-
-blkid_dev blkid_new_dev(void)
-{
- blkid_dev dev;
-
- dev = xzalloc(sizeof(struct blkid_struct_dev));
-
- INIT_LIST_HEAD(&dev->bid_devs);
- INIT_LIST_HEAD(&dev->bid_tags);
-
- return dev;
-}
-
-void blkid_free_dev(blkid_dev dev)
-{
- if (!dev)
- return;
-
- DBG(DEBUG_DEV,
- printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type));
- DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
-
- list_del(&dev->bid_devs);
- while (!list_empty(&dev->bid_tags)) {
- blkid_tag tag = list_entry(dev->bid_tags.next,
- struct blkid_struct_tag,
- bit_tags);
- blkid_free_tag(tag);
- }
- if (dev->bid_name)
- free(dev->bid_name);
- free(dev);
-}
-
-/*
- * Given a blkid device, return its name
- */
-const char *blkid_dev_devname(blkid_dev dev)
-{
- return dev->bid_name;
-}
-
-#ifdef CONFIG_BLKID_DEBUG
-void blkid_debug_dump_dev(blkid_dev dev)
-{
- struct list_head *p;
-
- if (!dev) {
- printf(" dev: NULL\n");
- return;
- }
-
- printf(" dev: name = %s\n", dev->bid_name);
- printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
- printf(" dev: TIME=\"%lu\"\n", dev->bid_time);
- printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
- printf(" dev: flags = 0x%08X\n", dev->bid_flags);
-
- list_for_each(p, &dev->bid_tags) {
- blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
- if (tag)
- printf(" tag: %s=\"%s\"\n", tag->bit_name,
- tag->bit_val);
- else
- printf(" tag: NULL\n");
- }
- bb_putchar('\n');
-}
-#endif
-
-/*
- * dev iteration routines for the public libblkid interface.
- *
- * These routines do not expose the list.h implementation, which are a
- * contamination of the namespace, and which force us to reveal far, far
- * too much of our internal implemenation. I'm not convinced I want
- * to keep list.h in the long term, anyway. It's fine for kernel
- * programming, but performance is not the #1 priority for this
- * library, and I really don't like the tradeoff of type-safety for
- * performance for this application. [tytso:20030125.2007EST]
- */
-
-/*
- * This series of functions iterate over all devices in a blkid cache
- */
-#define DEV_ITERATE_MAGIC 0x01a5284c
-
-struct blkid_struct_dev_iterate {
- int magic;
- blkid_cache cache;
- struct list_head *p;
-};
-
-blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
-{
- blkid_dev_iterate iter;
-
- iter = xmalloc(sizeof(struct blkid_struct_dev_iterate));
- iter->magic = DEV_ITERATE_MAGIC;
- iter->cache = cache;
- iter->p = cache->bic_devs.next;
- return iter;
-}
-
-/*
- * Return 0 on success, -1 on error
- */
-extern int blkid_dev_next(blkid_dev_iterate iter,
- blkid_dev *dev)
-{
- *dev = 0;
- if (!iter || iter->magic != DEV_ITERATE_MAGIC ||
- iter->p == &iter->cache->bic_devs)
- return -1;
- *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
- iter->p = iter->p->next;
- return 0;
-}
-
-void blkid_dev_iterate_end(blkid_dev_iterate iter)
-{
- if (!iter || iter->magic != DEV_ITERATE_MAGIC)
- return;
- iter->magic = 0;
- free(iter);
-}
-
-#ifdef TEST_PROGRAM
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#else
-extern char *optarg;
-extern int optind;
-#endif
-
-void usage(char *prog)
-{
- fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
- fprintf(stderr, "\tList all devices and exit\n");
- exit(1);
-}
-
-int main(int argc, char **argv)
-{
- blkid_dev_iterate iter;
- blkid_cache cache = NULL;
- blkid_dev dev;
- int c, ret;
- char *tmp;
- char *file = NULL;
- char *search_type = NULL;
- char *search_value = NULL;
-
- while ((c = getopt (argc, argv, "m:f:")) != EOF)
- switch (c) {
- case 'f':
- file = optarg;
- break;
- case 'm':
- blkid_debug_mask = strtoul (optarg, &tmp, 0);
- if (*tmp) {
- fprintf(stderr, "Invalid debug mask: %s\n",
- optarg);
- exit(1);
- }
- break;
- case '?':
- usage(argv[0]);
- }
- if (argc >= optind+2) {
- search_type = argv[optind];
- search_value = argv[optind+1];
- optind += 2;
- }
- if (argc != optind)
- usage(argv[0]);
-
- if ((ret = blkid_get_cache(&cache, file)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
-
- iter = blkid_dev_iterate_begin(cache);
- if (search_type)
- blkid_dev_set_search(iter, search_type, search_value);
- while (blkid_dev_next(iter, &dev) == 0) {
- printf("Device: %s\n", blkid_dev_devname(dev));
- }
- blkid_dev_iterate_end(iter);
-
-
- blkid_put_cache(cache);
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/devname.c b/e2fsprogs/old_e2fsprogs/blkid/devname.c
deleted file mode 100644
index fad92cb..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/devname.c
+++ b/dev/null
@@ -1,367 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * devname.c - get a dev by its device inode name
- *
- * Copyright (C) Andries Brouwer
- * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
- * Copyright (C) 2001 Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#include <sys/stat.h>
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-#include <time.h>
-
-#include "blkidP.h"
-
-/*
- * Find a dev struct in the cache by device name, if available.
- *
- * If there is no entry with the specified device name, and the create
- * flag is set, then create an empty device entry.
- */
-blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
-{
- blkid_dev dev = NULL, tmp;
- struct list_head *p;
-
- if (!cache || !devname)
- return NULL;
-
- list_for_each(p, &cache->bic_devs) {
- tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
- if (strcmp(tmp->bid_name, devname))
- continue;
-
- DBG(DEBUG_DEVNAME,
- printf("found devname %s in cache\n", tmp->bid_name));
- dev = tmp;
- break;
- }
-
- if (!dev && (flags & BLKID_DEV_CREATE)) {
- dev = blkid_new_dev();
- if (!dev)
- return NULL;
- dev->bid_name = blkid_strdup(devname);
- dev->bid_cache = cache;
- list_add_tail(&dev->bid_devs, &cache->bic_devs);
- cache->bic_flags |= BLKID_BIC_FL_CHANGED;
- }
-
- if (flags & BLKID_DEV_VERIFY)
- dev = blkid_verify(cache, dev);
- return dev;
-}
-
-/*
- * Probe a single block device to add to the device cache.
- */
-static void probe_one(blkid_cache cache, const char *ptname,
- dev_t devno, int pri)
-{
- blkid_dev dev = NULL;
- struct list_head *p;
- const char **dir;
- char *devname = NULL;
-
- /* See if we already have this device number in the cache. */
- list_for_each(p, &cache->bic_devs) {
- blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
- bid_devs);
- if (tmp->bid_devno == devno) {
- dev = blkid_verify(cache, tmp);
- break;
- }
- }
- if (dev && dev->bid_devno == devno)
- goto set_pri;
-
- /*
- * Take a quick look at /dev/ptname for the device number. We check
- * all of the likely device directories. If we don't find it, or if
- * the stat information doesn't check out, use blkid_devno_to_devname()
- * to find it via an exhaustive search for the device major/minor.
- */
- for (dir = blkid_devdirs; *dir; dir++) {
- struct stat st;
- char device[256];
-
- sprintf(device, "%s/%s", *dir, ptname);
- if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
- dev->bid_devno == devno)
- goto set_pri;
-
- if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
- st.st_rdev == devno) {
- devname = blkid_strdup(device);
- break;
- }
- }
- if (!devname) {
- devname = blkid_devno_to_devname(devno);
- if (!devname)
- return;
- }
- dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
- free(devname);
-
-set_pri:
- if (!pri && !strncmp(ptname, "md", 2))
- pri = BLKID_PRI_MD;
- if (dev)
- dev->bid_pri = pri;
-}
-
-#define PROC_PARTITIONS "/proc/partitions"
-#define VG_DIR "/proc/lvm/VGs"
-
-/*
- * This function initializes the UUID cache with devices from the LVM
- * proc hierarchy. We currently depend on the names of the LVM
- * hierarchy giving us the device structure in /dev. (XXX is this a
- * safe thing to do?)
- */
-#ifdef VG_DIR
-#include <dirent.h>
-static dev_t lvm_get_devno(const char *lvm_device)
-{
- FILE *lvf;
- char buf[1024];
- int ma, mi;
- dev_t ret = 0;
-
- DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
- if ((lvf = fopen_for_read(lvm_device)) == NULL) {
- DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
- strerror(errno)));
- return 0;
- }
-
- while (fgets(buf, sizeof(buf), lvf)) {
- if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
- ret = makedev(ma, mi);
- break;
- }
- }
- fclose(lvf);
-
- return ret;
-}
-
-static void lvm_probe_all(blkid_cache cache)
-{
- DIR *vg_list;
- struct dirent *vg_iter;
- int vg_len = strlen(VG_DIR);
- dev_t dev;
-
- if ((vg_list = opendir(VG_DIR)) == NULL)
- return;
-
- DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
-
- while ((vg_iter = readdir(vg_list)) != NULL) {
- DIR *lv_list;
- char *vdirname;
- char *vg_name;
- struct dirent *lv_iter;
-
- vg_name = vg_iter->d_name;
- if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, ".."))
- continue;
- vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
- sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
-
- lv_list = opendir(vdirname);
- free(vdirname);
- if (lv_list == NULL)
- continue;
-
- while ((lv_iter = readdir(lv_list)) != NULL) {
- char *lv_name, *lvm_device;
-
- lv_name = lv_iter->d_name;
- if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, ".."))
- continue;
-
- lvm_device = xmalloc(vg_len + strlen(vg_name) +
- strlen(lv_name) + 8);
- sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
- lv_name);
- dev = lvm_get_devno(lvm_device);
- sprintf(lvm_device, "%s/%s", vg_name, lv_name);
- DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
- lvm_device,
- (unsigned int) dev));
- probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
- free(lvm_device);
- }
- closedir(lv_list);
- }
- closedir(vg_list);
-}
-#endif
-
-#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
-
-static int
-evms_probe_all(blkid_cache cache)
-{
- char line[100];
- int ma, mi, sz, num = 0;
- FILE *procpt;
- char device[110];
-
- procpt = fopen_for_read(PROC_EVMS_VOLUMES);
- if (!procpt)
- return 0;
- while (fgets(line, sizeof(line), procpt)) {
- if (sscanf(line, " %d %d %d %*s %*s %[^\n ]",
- &ma, &mi, &sz, device) != 4)
- continue;
-
- DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
- device, ma, mi));
-
- probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
- num++;
- }
- fclose(procpt);
- return num;
-}
-
-/*
- * Read the device data for all available block devices in the system.
- */
-int blkid_probe_all(blkid_cache cache)
-{
- FILE *proc;
- char line[1024];
- char ptname0[128], ptname1[128], *ptname = NULL;
- char *ptnames[2];
- dev_t devs[2];
- int ma, mi;
- unsigned long long sz;
- int lens[2] = { 0, 0 };
- int which = 0, last = 0;
-
- ptnames[0] = ptname0;
- ptnames[1] = ptname1;
-
- if (!cache)
- return -BLKID_ERR_PARAM;
-
- if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
- time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL)
- return 0;
-
- blkid_read_cache(cache);
- evms_probe_all(cache);
-#ifdef VG_DIR
- lvm_probe_all(cache);
-#endif
-
- proc = fopen_for_read(PROC_PARTITIONS);
- if (!proc)
- return -BLKID_ERR_PROC;
-
- while (fgets(line, sizeof(line), proc)) {
- last = which;
- which ^= 1;
- ptname = ptnames[which];
-
- if (sscanf(line, " %d %d %llu %128[^\n ]",
- &ma, &mi, &sz, ptname) != 4)
- continue;
- devs[which] = makedev(ma, mi);
-
- DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
-
- /* Skip whole disk devs unless they have no partitions
- * If we don't have a partition on this dev, also
- * check previous dev to see if it didn't have a partn.
- * heuristic: partition name ends in a digit.
- *
- * Skip extended partitions.
- * heuristic: size is 1
- *
- * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
- */
-
- lens[which] = strlen(ptname);
- if (isdigit(ptname[lens[which] - 1])) {
- DBG(DEBUG_DEVNAME,
- printf("partition dev %s, devno 0x%04X\n",
- ptname, (unsigned int) devs[which]));
-
- if (sz > 1)
- probe_one(cache, ptname, devs[which], 0);
- lens[which] = 0;
- lens[last] = 0;
- } else if (lens[last] && strncmp(ptnames[last], ptname,
- lens[last])) {
- DBG(DEBUG_DEVNAME,
- printf("whole dev %s, devno 0x%04X\n",
- ptnames[last], (unsigned int) devs[last]));
- probe_one(cache, ptnames[last], devs[last], 0);
- lens[last] = 0;
- }
- }
-
- /* Handle the last device if it wasn't partitioned */
- if (lens[which])
- probe_one(cache, ptname, devs[which], 0);
-
- fclose(proc);
-
- cache->bic_time = time(NULL);
- cache->bic_flags |= BLKID_BIC_FL_PROBED;
- blkid_flush_cache(cache);
- return 0;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- blkid_cache cache = NULL;
- int ret;
-
- blkid_debug_mask = DEBUG_ALL;
- if (argc != 1) {
- fprintf(stderr, "Usage: %s\n"
- "Probe all devices and exit\n", argv[0]);
- exit(1);
- }
- if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
- if (blkid_probe_all(cache) < 0)
- printf("%s: error probing devices\n", argv[0]);
-
- blkid_put_cache(cache);
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/devno.c b/e2fsprogs/old_e2fsprogs/blkid/devno.c
deleted file mode 100644
index ae326f8..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/devno.c
+++ b/dev/null
@@ -1,222 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * devno.c - find a particular device by its device number (major/minor)
- *
- * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
- * Copyright (C) 2001 Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#include <sys/stat.h>
-#include <dirent.h>
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-
-#include "blkidP.h"
-
-struct dir_list {
- char *name;
- struct dir_list *next;
-};
-
-char *blkid_strndup(const char *s, int length)
-{
- char *ret;
-
- if (!s)
- return NULL;
-
- if (!length)
- length = strlen(s);
-
- ret = xmalloc(length + 1);
- strncpy(ret, s, length);
- ret[length] = '\0';
- return ret;
-}
-
-char *blkid_strdup(const char *s)
-{
- return blkid_strndup(s, 0);
-}
-
-/*
- * This function adds an entry to the directory list
- */
-static void add_to_dirlist(const char *name, struct dir_list **list)
-{
- struct dir_list *dp;
-
- dp = xmalloc(sizeof(struct dir_list));
- dp->name = blkid_strdup(name);
- dp->next = *list;
- *list = dp;
-}
-
-/*
- * This function frees a directory list
- */
-static void free_dirlist(struct dir_list **list)
-{
- struct dir_list *dp, *next;
-
- for (dp = *list; dp; dp = next) {
- next = dp->next;
- free(dp->name);
- free(dp);
- }
- *list = NULL;
-}
-
-static void scan_dir(char *dir_name, dev_t devno, struct dir_list **list,
- char **devname)
-{
- DIR *dir;
- struct dirent *dp;
- char path[1024];
- int dirlen;
- struct stat st;
-
- if ((dir = opendir(dir_name)) == NULL)
- return;
- dirlen = strlen(dir_name) + 2;
- while ((dp = readdir(dir)) != 0) {
- if (dirlen + strlen(dp->d_name) >= sizeof(path))
- continue;
-
- if (dp->d_name[0] == '.' &&
- ((dp->d_name[1] == 0) ||
- ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
- continue;
-
- sprintf(path, "%s/%s", dir_name, dp->d_name);
- if (stat(path, &st) < 0)
- continue;
-
- if (S_ISDIR(st.st_mode))
- add_to_dirlist(path, list);
- else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
- *devname = blkid_strdup(path);
- DBG(DEBUG_DEVNO,
- printf("found 0x%llx at %s (%p)\n", devno,
- path, *devname));
- break;
- }
- }
- closedir(dir);
-}
-
-/* Directories where we will try to search for device numbers */
-const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL };
-
-/*
- * This function finds the pathname to a block device with a given
- * device number. It returns a pointer to allocated memory to the
- * pathname on success, and NULL on failure.
- */
-char *blkid_devno_to_devname(dev_t devno)
-{
- struct dir_list *list = NULL, *new_list = NULL;
- char *devname = NULL;
- const char **dir;
-
- /*
- * Add the starting directories to search in reverse order of
- * importance, since we are using a stack...
- */
- for (dir = blkid_devdirs; *dir; dir++)
- add_to_dirlist(*dir, &list);
-
- while (list) {
- struct dir_list *current = list;
-
- list = list->next;
- DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
- scan_dir(current->name, devno, &new_list, &devname);
- free(current->name);
- free(current);
- if (devname)
- break;
- /*
- * If we're done checking at this level, descend to
- * the next level of subdirectories. (breadth-first)
- */
- if (list == NULL) {
- list = new_list;
- new_list = NULL;
- }
- }
- free_dirlist(&list);
- free_dirlist(&new_list);
-
- if (!devname) {
- DBG(DEBUG_DEVNO,
- printf("blkid: cannot find devno 0x%04lx\n",
- (unsigned long) devno));
- } else {
- DBG(DEBUG_DEVNO,
- printf("found devno 0x%04llx as %s\n", devno, devname));
- }
-
-
- return devname;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char** argv)
-{
- char *devname, *tmp;
- int major, minor;
- dev_t devno;
- const char *errmsg = "Cannot parse %s: %s\n";
-
- blkid_debug_mask = DEBUG_ALL;
- if ((argc != 2) && (argc != 3)) {
- fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
- "Resolve a device number to a device name\n",
- argv[0], argv[0]);
- exit(1);
- }
- if (argc == 2) {
- devno = strtoul(argv[1], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "device number", argv[1]);
- exit(1);
- }
- } else {
- major = strtoul(argv[1], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "major number", argv[1]);
- exit(1);
- }
- minor = strtoul(argv[2], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "minor number", argv[2]);
- exit(1);
- }
- devno = makedev(major, minor);
- }
- printf("Looking for device 0x%04Lx\n", devno);
- devname = blkid_devno_to_devname(devno);
- free(devname);
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/list.c b/e2fsprogs/old_e2fsprogs/blkid/list.c
deleted file mode 100644
index 04d61a1..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/list.c
+++ b/dev/null
@@ -1,110 +0,0 @@
-/* vi: set sw=4 ts=4: */
-
-#include "list.h"
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-void __list_add(struct list_head * add,
- struct list_head * prev,
- struct list_head * next)
-{
- next->prev = add;
- add->next = next;
- add->prev = prev;
- prev->next = add;
-}
-
-/*
- * list_add - add a new entry
- * @add: 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.
- */
-void list_add(struct list_head *add, struct list_head *head)
-{
- __list_add(add, head, head->next);
-}
-
-/*
- * list_add_tail - add a new entry
- * @add: 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.
- */
-void list_add_tail(struct list_head *add, struct list_head *head)
-{
- __list_add(add, head->prev, head);
-}
-
-/*
- * 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!
- */
-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.
- *
- * list_empty() on @entry does not return true after this, @entry is
- * in an undefined state.
- */
-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.
- */
-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.
- */
-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.
- */
-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;
- }
-}
diff --git a/e2fsprogs/old_e2fsprogs/blkid/list.h b/e2fsprogs/old_e2fsprogs/blkid/list.h
deleted file mode 100644
index a24baaa..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/list.h
+++ b/dev/null
@@ -1,73 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
-#define BLKID_LIST_H 1
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Simple doubly linked list implementation.
- *
- * 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)
-
-void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next);
-void list_add(struct list_head *add, struct list_head *head);
-void list_add_tail(struct list_head *add, struct list_head *head);
-void __list_del(struct list_head * prev, struct list_head * next);
-void list_del(struct list_head *entry);
-void list_del_init(struct list_head *entry);
-int list_empty(struct list_head *head);
-void list_splice(struct list_head *list, struct list_head *head);
-
-/**
- * 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 elements in 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 elements in a list, but don't dereference
- * pos after the body is done (in case it is freed)
- * @pos: the &struct list_head to use as a loop counter.
- * @pnext: the &struct list_head to use as a pointer to the next item.
- * @head: the head for your list (not included in iteration).
- */
-#define list_for_each_safe(pos, pnext, head) \
- for (pos = (head)->next, pnext = pos->next; pos != (head); \
- pos = pnext, pnext = pos->next)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/probe.c b/e2fsprogs/old_e2fsprogs/blkid/probe.c
deleted file mode 100644
index 651193b..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/probe.c
+++ b/dev/null
@@ -1,726 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * probe.c - identify a block device by its contents, and return a dev
- * struct with the details
- *
- * Copyright (C) 1999 by Andries Brouwer
- * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
- * Copyright (C) 2001 by Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include "blkidP.h"
-#include "../uuid/uuid.h"
-#include "probe.h"
-
-/*
- * This is a special case code to check for an MDRAID device. We do
- * this special since it requires checking for a superblock at the end
- * of the device.
- */
-static int check_mdraid(int fd, unsigned char *ret_uuid)
-{
- struct mdp_superblock_s *md;
- blkid_loff_t offset;
- char buf[4096];
-
- if (fd < 0)
- return -BLKID_ERR_PARAM;
-
- offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536;
-
- if (blkid_llseek(fd, offset, 0) < 0 ||
- read(fd, buf, 4096) != 4096)
- return -BLKID_ERR_IO;
-
- /* Check for magic number */
- if (memcmp("\251+N\374", buf, 4))
- return -BLKID_ERR_PARAM;
-
- if (!ret_uuid)
- return 0;
- *ret_uuid = 0;
-
- /* The MD UUID is not contiguous in the superblock, make it so */
- md = (struct mdp_superblock_s *)buf;
- if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) {
- memcpy(ret_uuid, &md->set_uuid0, 4);
- memcpy(ret_uuid, &md->set_uuid1, 12);
- }
- return 0;
-}
-
-static void set_uuid(blkid_dev dev, uuid_t uuid)
-{
- char str[37];
-
- if (!uuid_is_null(uuid)) {
- uuid_unparse(uuid, str);
- blkid_set_tag(dev, "UUID", str, sizeof(str));
- }
-}
-
-static void get_ext2_info(blkid_dev dev, unsigned char *buf)
-{
- struct ext2_super_block *es = (struct ext2_super_block *) buf;
- const char *label = NULL;
-
- DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
- blkid_le32(es->s_feature_compat),
- blkid_le32(es->s_feature_incompat),
- blkid_le32(es->s_feature_ro_compat)));
-
- if (strlen(es->s_volume_name))
- label = es->s_volume_name;
- blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name));
-
- set_uuid(dev, es->s_uuid);
-}
-
-static int probe_ext3(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct ext2_super_block *es;
-
- es = (struct ext2_super_block *)buf;
-
- /* Distinguish between jbd and ext2/3 fs */
- if (blkid_le32(es->s_feature_incompat) &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
- return -BLKID_ERR_PARAM;
-
- /* Distinguish between ext3 and ext2 */
- if (!(blkid_le32(es->s_feature_compat) &
- EXT3_FEATURE_COMPAT_HAS_JOURNAL))
- return -BLKID_ERR_PARAM;
-
- get_ext2_info(dev, buf);
-
- blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2"));
-
- return 0;
-}
-
-static int probe_ext2(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct ext2_super_block *es;
-
- es = (struct ext2_super_block *)buf;
-
- /* Distinguish between jbd and ext2/3 fs */
- if (blkid_le32(es->s_feature_incompat) &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
- return -BLKID_ERR_PARAM;
-
- get_ext2_info(dev, buf);
-
- return 0;
-}
-
-static int probe_jbd(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct ext2_super_block *es = (struct ext2_super_block *) buf;
-
- if (!(blkid_le32(es->s_feature_incompat) &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
- return -BLKID_ERR_PARAM;
-
- get_ext2_info(dev, buf);
-
- return 0;
-}
-
-static int probe_vfat(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct vfat_super_block *vs;
- char serno[10];
- const char *label = NULL;
- int label_len = 0;
-
- vs = (struct vfat_super_block *)buf;
-
- if (strncmp(vs->vs_label, "NO NAME", 7)) {
- char *end = vs->vs_label + sizeof(vs->vs_label) - 1;
-
- while (*end == ' ' && end >= vs->vs_label)
- --end;
- if (end >= vs->vs_label) {
- label = vs->vs_label;
- label_len = end - vs->vs_label + 1;
- }
- }
-
- /* We can't just print them as %04X, because they are unaligned */
- sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2],
- vs->vs_serno[1], vs->vs_serno[0]);
- blkid_set_tag(dev, "LABEL", label, label_len);
- blkid_set_tag(dev, "UUID", serno, sizeof(serno));
-
- return 0;
-}
-
-static int probe_msdos(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct msdos_super_block *ms = (struct msdos_super_block *) buf;
- char serno[10];
- const char *label = NULL;
- int label_len = 0;
-
- if (strncmp(ms->ms_label, "NO NAME", 7)) {
- char *end = ms->ms_label + sizeof(ms->ms_label) - 1;
-
- while (*end == ' ' && end >= ms->ms_label)
- --end;
- if (end >= ms->ms_label) {
- label = ms->ms_label;
- label_len = end - ms->ms_label + 1;
- }
- }
-
- /* We can't just print them as %04X, because they are unaligned */
- sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2],
- ms->ms_serno[1], ms->ms_serno[0]);
- blkid_set_tag(dev, "UUID", serno, 0);
- blkid_set_tag(dev, "LABEL", label, label_len);
- blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos"));
-
- return 0;
-}
-
-static int probe_xfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct xfs_super_block *xs;
- const char *label = NULL;
-
- xs = (struct xfs_super_block *)buf;
-
- if (strlen(xs->xs_fname))
- label = xs->xs_fname;
- blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname));
- set_uuid(dev, xs->xs_uuid);
- return 0;
-}
-
-static int probe_reiserfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id, unsigned char *buf)
-{
- struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf;
- unsigned int blocksize;
- const char *label = NULL;
-
- blocksize = blkid_le16(rs->rs_blocksize);
-
- /* If the superblock is inside the journal, we have the wrong one */
- if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block))
- return -BLKID_ERR_BIG;
-
- /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
- if (!strcmp(id->bim_magic, "ReIsEr2Fs") ||
- !strcmp(id->bim_magic, "ReIsEr3Fs")) {
- if (strlen(rs->rs_label))
- label = rs->rs_label;
- set_uuid(dev, rs->rs_uuid);
- }
- blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label));
-
- return 0;
-}
-
-static int probe_jfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct jfs_super_block *js;
- const char *label = NULL;
-
- js = (struct jfs_super_block *)buf;
-
- if (strlen((char *) js->js_label))
- label = (char *) js->js_label;
- blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label));
- set_uuid(dev, js->js_uuid);
- return 0;
-}
-
-static int probe_romfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct romfs_super_block *ros;
- const char *label = NULL;
-
- ros = (struct romfs_super_block *)buf;
-
- if (strlen((char *) ros->ros_volume))
- label = (char *) ros->ros_volume;
- blkid_set_tag(dev, "LABEL", label, 0);
- return 0;
-}
-
-static int probe_cramfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct cramfs_super_block *csb;
- const char *label = NULL;
-
- csb = (struct cramfs_super_block *)buf;
-
- if (strlen((char *) csb->name))
- label = (char *) csb->name;
- blkid_set_tag(dev, "LABEL", label, 0);
- return 0;
-}
-
-static int probe_swap0(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf __BLKID_ATTR((unused)))
-{
- blkid_set_tag(dev, "UUID", 0, 0);
- blkid_set_tag(dev, "LABEL", 0, 0);
- return 0;
-}
-
-static int probe_swap1(int fd,
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf __BLKID_ATTR((unused)))
-{
- struct swap_id_block *sws;
-
- probe_swap0(fd, cache, dev, id, buf);
- /*
- * Version 1 swap headers are always located at offset of 1024
- * bytes, although the swap signature itself is located at the
- * end of the page (which may vary depending on hardware
- * pagesize).
- */
- if (lseek(fd, 1024, SEEK_SET) < 0) return 1;
- sws = xmalloc(1024);
- if (read(fd, sws, 1024) != 1024) {
- free(sws);
- return 1;
- }
-
- /* arbitrary sanity check.. is there any garbage down there? */
- if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) {
- if (sws->sws_volume[0])
- blkid_set_tag(dev, "LABEL", (const char*)sws->sws_volume,
- sizeof(sws->sws_volume));
- if (sws->sws_uuid[0])
- set_uuid(dev, sws->sws_uuid);
- }
- free(sws);
-
- return 0;
-}
-
-static const char
-* const udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02",
- "NSR03", "TEA01", 0 };
-
-static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev __BLKID_ATTR((unused)),
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf __BLKID_ATTR((unused)))
-{
- int j, bs;
- struct iso_volume_descriptor isosb;
- const char *const *m;
-
- /* determine the block size by scanning in 2K increments
- (block sizes larger than 2K will be null padded) */
- for (bs = 1; bs < 16; bs++) {
- lseek(fd, bs*2048+32768, SEEK_SET);
- if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb))
- return 1;
- if (isosb.id[0])
- break;
- }
-
- /* Scan up to another 64 blocks looking for additional VSD's */
- for (j = 1; j < 64; j++) {
- if (j > 1) {
- lseek(fd, j*bs*2048+32768, SEEK_SET);
- if (read(fd, (char *)&isosb, sizeof(isosb))
- != sizeof(isosb))
- return 1;
- }
- /* If we find NSR0x then call it udf:
- NSR01 for UDF 1.00
- NSR02 for UDF 1.50
- NSR03 for UDF 2.00 */
- if (!strncmp(isosb.id, "NSR0", 4))
- return 0;
- for (m = udf_magic; *m; m++)
- if (!strncmp(*m, isosb.id, 5))
- break;
- if (*m == 0)
- return 1;
- }
- return 1;
-}
-
-static int probe_ocfs(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct ocfs_volume_header ovh;
- struct ocfs_volume_label ovl;
- __u32 major;
-
- memcpy(&ovh, buf, sizeof(ovh));
- memcpy(&ovl, buf+512, sizeof(ovl));
-
- major = ocfsmajor(ovh);
- if (major == 1)
- blkid_set_tag(dev, "SEC_TYPE", "ocfs1", sizeof("ocfs1"));
- else if (major >= 9)
- blkid_set_tag(dev, "SEC_TYPE", "ntocfs", sizeof("ntocfs"));
-
- blkid_set_tag(dev, "LABEL", (const char*)ovl.label, ocfslabellen(ovl));
- blkid_set_tag(dev, "MOUNT", (const char*)ovh.mount, ocfsmountlen(ovh));
- set_uuid(dev, ovl.vol_id);
- return 0;
-}
-
-static int probe_ocfs2(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct ocfs2_super_block *osb;
-
- osb = (struct ocfs2_super_block *)buf;
-
- blkid_set_tag(dev, "LABEL", (const char*)osb->s_label, sizeof(osb->s_label));
- set_uuid(dev, osb->s_uuid);
- return 0;
-}
-
-static int probe_oracleasm(int fd __BLKID_ATTR((unused)),
- blkid_cache cache __BLKID_ATTR((unused)),
- blkid_dev dev,
- const struct blkid_magic *id __BLKID_ATTR((unused)),
- unsigned char *buf)
-{
- struct oracle_asm_disk_label *dl;
-
- dl = (struct oracle_asm_disk_label *)buf;
-
- blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id));
- return 0;
-}
-
-/*
- * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined
- * in the type_array table below + bim_kbalign.
- *
- * When probing for a lot of magics, we handle everything in 1kB buffers so
- * that we don't have to worry about reading each combination of block sizes.
- */
-#define BLKID_BLK_OFFS 64 /* currently reiserfs */
-
-/*
- * Various filesystem magics that we can check for. Note that kboff and
- * sboff are in kilobytes and bytes respectively. All magics are in
- * byte strings so we don't worry about endian issues.
- */
-static const struct blkid_magic type_array[] = {
-/* type kboff sboff len magic probe */
- { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm },
- { "ntfs", 0, 3, 8, "NTFS ", 0 },
- { "jbd", 1, 0x38, 2, "\123\357", probe_jbd },
- { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 },
- { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 },
- { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs },
- { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs },
- { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs },
- { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs },
- { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs },
- { "vfat", 0, 0x52, 5, "MSWIN", probe_vfat },
- { "vfat", 0, 0x52, 8, "FAT32 ", probe_vfat },
- { "vfat", 0, 0x36, 5, "MSDOS", probe_msdos },
- { "vfat", 0, 0x36, 8, "FAT16 ", probe_msdos },
- { "vfat", 0, 0x36, 8, "FAT12 ", probe_msdos },
- { "minix", 1, 0x10, 2, "\177\023", 0 },
- { "minix", 1, 0x10, 2, "\217\023", 0 },
- { "minix", 1, 0x10, 2, "\150\044", 0 },
- { "minix", 1, 0x10, 2, "\170\044", 0 },
- { "vxfs", 1, 0, 4, "\365\374\001\245", 0 },
- { "xfs", 0, 0, 4, "XFSB", probe_xfs },
- { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs },
- { "bfs", 0, 0, 4, "\316\372\173\033", 0 },
- { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs },
- { "qnx4", 0, 4, 6, "QNX4FS", 0 },
- { "udf", 32, 1, 5, "BEA01", probe_udf },
- { "udf", 32, 1, 5, "BOOT2", probe_udf },
- { "udf", 32, 1, 5, "CD001", probe_udf },
- { "udf", 32, 1, 5, "CDW02", probe_udf },
- { "udf", 32, 1, 5, "NSR02", probe_udf },
- { "udf", 32, 1, 5, "NSR03", probe_udf },
- { "udf", 32, 1, 5, "TEA01", probe_udf },
- { "iso9660", 32, 1, 5, "CD001", 0 },
- { "iso9660", 32, 9, 5, "CDROM", 0 },
- { "jfs", 32, 0, 4, "JFS1", probe_jfs },
- { "hfs", 1, 0, 2, "BD", 0 },
- { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 },
- { "hpfs", 8, 0, 4, "I\350\225\371", 0 },
- { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 },
- { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 },
- { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 },
- { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 },
- { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 },
- { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 },
- { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 },
- { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 },
- { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 },
- { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 },
- { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 },
- { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs },
- { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 },
- { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 },
- { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 },
- { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 },
- { NULL, 0, 0, 0, NULL, NULL }
-};
-
-/*
- * Verify that the data in dev is consistent with what is on the actual
- * block device (using the devname field only). Normally this will be
- * called when finding items in the cache, but for long running processes
- * is also desirable to revalidate an item before use.
- *
- * If we are unable to revalidate the data, we return the old data and
- * do not set the BLKID_BID_FL_VERIFIED flag on it.
- */
-blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
-{
- const struct blkid_magic *id;
- unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf;
- const char *type;
- struct stat st;
- time_t diff, now;
- int fd, idx;
-
- if (!dev)
- return NULL;
-
- now = time(NULL);
- diff = now - dev->bid_time;
-
- if ((now < dev->bid_time) ||
- (diff < BLKID_PROBE_MIN) ||
- (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
- diff < BLKID_PROBE_INTERVAL))
- return dev;
-
- DBG(DEBUG_PROBE,
- printf("need to revalidate %s (time since last check %lu)\n",
- dev->bid_name, diff));
-
- fd = open(dev->bid_name, O_RDONLY);
- if (fd < 0
- || fstat(fd, &st) < 0
- ) {
- if (fd >= 0)
- close(fd);
- if (errno == ENXIO || errno == ENODEV || errno == ENOENT) {
- blkid_free_dev(dev);
- return NULL;
- }
- /* We don't have read permission, just return cache data. */
- DBG(DEBUG_PROBE,
- printf("returning unverified data for %s\n",
- dev->bid_name));
- return dev;
- }
-
- memset(bufs, 0, sizeof(bufs));
-
- /*
- * Iterate over the type array. If we already know the type,
- * then try that first. If it doesn't work, then blow away
- * the type information, and try again.
- *
- */
-try_again:
- type = 0;
- if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) {
- uuid_t uuid;
-
- if (check_mdraid(fd, uuid) == 0) {
- set_uuid(dev, uuid);
- type = "mdraid";
- goto found_type;
- }
- }
- for (id = type_array; id->bim_type; id++) {
- if (dev->bid_type &&
- strcmp(id->bim_type, dev->bid_type))
- continue;
-
- idx = id->bim_kboff + (id->bim_sboff >> 10);
- if (idx > BLKID_BLK_OFFS || idx < 0)
- continue;
- buf = bufs[idx];
- if (!buf) {
- if (lseek(fd, idx << 10, SEEK_SET) < 0)
- continue;
-
- buf = xmalloc(1024);
-
- if (read(fd, buf, 1024) != 1024) {
- free(buf);
- continue;
- }
- bufs[idx] = buf;
- }
-
- if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff),
- id->bim_len))
- continue;
-
- if ((id->bim_probe == NULL) ||
- (id->bim_probe(fd, cache, dev, id, buf) == 0)) {
- type = id->bim_type;
- goto found_type;
- }
- }
-
- if (!id->bim_type && dev->bid_type) {
- /*
- * Zap the device filesystem type and try again
- */
- blkid_set_tag(dev, "TYPE", 0, 0);
- blkid_set_tag(dev, "SEC_TYPE", 0, 0);
- blkid_set_tag(dev, "LABEL", 0, 0);
- blkid_set_tag(dev, "UUID", 0, 0);
- goto try_again;
- }
-
- if (!dev->bid_type) {
- blkid_free_dev(dev);
- close(fd);
- return NULL;
- }
-
-found_type:
- if (dev && type) {
- dev->bid_devno = st.st_rdev;
- dev->bid_time = time(NULL);
- dev->bid_flags |= BLKID_BID_FL_VERIFIED;
- cache->bic_flags |= BLKID_BIC_FL_CHANGED;
-
- blkid_set_tag(dev, "TYPE", type, 0);
-
- DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
- dev->bid_name, st.st_rdev, type));
- }
-
- close(fd);
-
- return dev;
-}
-
-int blkid_known_fstype(const char *fstype)
-{
- const struct blkid_magic *id;
-
- for (id = type_array; id->bim_type; id++) {
- if (strcmp(fstype, id->bim_type) == 0)
- return 1;
- }
- return 0;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- blkid_dev dev;
- blkid_cache cache;
- int ret;
-
- blkid_debug_mask = DEBUG_ALL;
- if (argc != 2) {
- fprintf(stderr, "Usage: %s device\n"
- "Probe a single device to determine type\n", argv[0]);
- exit(1);
- }
- if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
- dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
- if (!dev) {
- printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
- return 1;
- }
- printf("%s is type %s\n", argv[1], dev->bid_type ?
- dev->bid_type : "(null)");
- if (dev->bid_label)
- printf("\tlabel is '%s'\n", dev->bid_label);
- if (dev->bid_uuid)
- printf("\tuuid is %s\n", dev->bid_uuid);
-
- blkid_free_dev(dev);
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/probe.h b/e2fsprogs/old_e2fsprogs/blkid/probe.h
deleted file mode 100644
index b6d8f8e..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/probe.h
+++ b/dev/null
@@ -1,374 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * probe.h - constants and on-disk structures for extracting device data
- *
- * Copyright (C) 1999 by Andries Brouwer
- * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
- * Copyright (C) 2001 by Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-#ifndef BLKID_PROBE_H
-#define BLKID_PROBE_H 1
-
-#include <linux/types.h>
-
-struct blkid_magic;
-
-typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev,
- const struct blkid_magic *id, unsigned char *buf);
-
-struct blkid_magic {
- const char *bim_type; /* type name for this magic */
- long bim_kboff; /* kilobyte offset of superblock */
- unsigned bim_sboff; /* byte offset within superblock */
- unsigned bim_len; /* length of magic */
- const char *bim_magic; /* magic string */
- blkid_probe_t bim_probe; /* probe function */
-};
-
-/*
- * Structures for each of the content types we want to extract information
- * from. We do not necessarily need the magic field here, because we have
- * already identified the content type before we get this far. It may still
- * be useful if there are probe functions which handle multiple content types.
- */
-struct ext2_super_block {
- __u32 s_inodes_count;
- __u32 s_blocks_count;
- __u32 s_r_blocks_count;
- __u32 s_free_blocks_count;
- __u32 s_free_inodes_count;
- __u32 s_first_data_block;
- __u32 s_log_block_size;
- __u32 s_dummy3[7];
- unsigned char s_magic[2];
- __u16 s_state;
- __u32 s_dummy5[8];
- __u32 s_feature_compat;
- __u32 s_feature_incompat;
- __u32 s_feature_ro_compat;
- unsigned char s_uuid[16];
- char s_volume_name[16];
-};
-#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
-#define EXT3_FEATURE_INCOMPAT_RECOVER 0x00000004
-#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
-
-struct xfs_super_block {
- unsigned char xs_magic[4];
- __u32 xs_blocksize;
- __u64 xs_dblocks;
- __u64 xs_rblocks;
- __u32 xs_dummy1[2];
- unsigned char xs_uuid[16];
- __u32 xs_dummy2[15];
- char xs_fname[12];
- __u32 xs_dummy3[2];
- __u64 xs_icount;
- __u64 xs_ifree;
- __u64 xs_fdblocks;
-};
-
-struct reiserfs_super_block {
- __u32 rs_blocks_count;
- __u32 rs_free_blocks;
- __u32 rs_root_block;
- __u32 rs_journal_block;
- __u32 rs_journal_dev;
- __u32 rs_orig_journal_size;
- __u32 rs_dummy2[5];
- __u16 rs_blocksize;
- __u16 rs_dummy3[3];
- unsigned char rs_magic[12];
- __u32 rs_dummy4[5];
- unsigned char rs_uuid[16];
- char rs_label[16];
-};
-
-struct jfs_super_block {
- unsigned char js_magic[4];
- __u32 js_version;
- __u64 js_size;
- __u32 js_bsize;
- __u32 js_dummy1;
- __u32 js_pbsize;
- __u32 js_dummy2[27];
- unsigned char js_uuid[16];
- unsigned char js_label[16];
- unsigned char js_loguuid[16];
-};
-
-struct romfs_super_block {
- unsigned char ros_magic[8];
- __u32 ros_dummy1[2];
- unsigned char ros_volume[16];
-};
-
-struct cramfs_super_block {
- __u8 magic[4];
- __u32 size;
- __u32 flags;
- __u32 future;
- __u8 signature[16];
- struct cramfs_info {
- __u32 crc;
- __u32 edition;
- __u32 blocks;
- __u32 files;
- } info;
- __u8 name[16];
-};
-
-struct swap_id_block {
-/* unsigned char sws_boot[1024]; */
- __u32 sws_version;
- __u32 sws_lastpage;
- __u32 sws_nrbad;
- unsigned char sws_uuid[16];
- char sws_volume[16];
- unsigned char sws_pad[117];
- __u32 sws_badpg;
-};
-
-/* Yucky misaligned values */
-struct vfat_super_block {
-/* 00*/ unsigned char vs_ignored[3];
-/* 03*/ unsigned char vs_sysid[8];
-/* 0b*/ unsigned char vs_sector_size[2];
-/* 0d*/ __u8 vs_cluster_size;
-/* 0e*/ __u16 vs_reserved;
-/* 10*/ __u8 vs_fats;
-/* 11*/ unsigned char vs_dir_entries[2];
-/* 13*/ unsigned char vs_sectors[2];
-/* 15*/ unsigned char vs_media;
-/* 16*/ __u16 vs_fat_length;
-/* 18*/ __u16 vs_secs_track;
-/* 1a*/ __u16 vs_heads;
-/* 1c*/ __u32 vs_hidden;
-/* 20*/ __u32 vs_total_sect;
-/* 24*/ __u32 vs_fat32_length;
-/* 28*/ __u16 vs_flags;
-/* 2a*/ __u8 vs_version[2];
-/* 2c*/ __u32 vs_root_cluster;
-/* 30*/ __u16 vs_insfo_sector;
-/* 32*/ __u16 vs_backup_boot;
-/* 34*/ __u16 vs_reserved2[6];
-/* 40*/ unsigned char vs_unknown[3];
-/* 43*/ unsigned char vs_serno[4];
-/* 47*/ char vs_label[11];
-/* 52*/ unsigned char vs_magic[8];
-/* 5a*/ unsigned char vs_dummy2[164];
-/*1fe*/ unsigned char vs_pmagic[2];
-};
-
-/* Yucky misaligned values */
-struct msdos_super_block {
-/* 00*/ unsigned char ms_ignored[3];
-/* 03*/ unsigned char ms_sysid[8];
-/* 0b*/ unsigned char ms_sector_size[2];
-/* 0d*/ __u8 ms_cluster_size;
-/* 0e*/ __u16 ms_reserved;
-/* 10*/ __u8 ms_fats;
-/* 11*/ unsigned char ms_dir_entries[2];
-/* 13*/ unsigned char ms_sectors[2];
-/* 15*/ unsigned char ms_media;
-/* 16*/ __u16 ms_fat_length;
-/* 18*/ __u16 ms_secs_track;
-/* 1a*/ __u16 ms_heads;
-/* 1c*/ __u32 ms_hidden;
-/* 20*/ __u32 ms_total_sect;
-/* 24*/ unsigned char ms_unknown[3];
-/* 27*/ unsigned char ms_serno[4];
-/* 2b*/ char ms_label[11];
-/* 36*/ unsigned char ms_magic[8];
-/* 3d*/ unsigned char ms_dummy2[192];
-/*1fe*/ unsigned char ms_pmagic[2];
-};
-
-struct minix_super_block {
- __u16 ms_ninodes;
- __u16 ms_nzones;
- __u16 ms_imap_blocks;
- __u16 ms_zmap_blocks;
- __u16 ms_firstdatazone;
- __u16 ms_log_zone_size;
- __u32 ms_max_size;
- unsigned char ms_magic[2];
- __u16 ms_state;
- __u32 ms_zones;
-};
-
-struct mdp_superblock_s {
- __u32 md_magic;
- __u32 major_version;
- __u32 minor_version;
- __u32 patch_version;
- __u32 gvalid_words;
- __u32 set_uuid0;
- __u32 ctime;
- __u32 level;
- __u32 size;
- __u32 nr_disks;
- __u32 raid_disks;
- __u32 md_minor;
- __u32 not_persistent;
- __u32 set_uuid1;
- __u32 set_uuid2;
- __u32 set_uuid3;
-};
-
-struct hfs_super_block {
- char h_magic[2];
- char h_dummy[18];
- __u32 h_blksize;
-};
-
-struct ocfs_volume_header {
- unsigned char minor_version[4];
- unsigned char major_version[4];
- unsigned char signature[128];
- char mount[128];
- unsigned char mount_len[2];
-};
-
-struct ocfs_volume_label {
- unsigned char disk_lock[48];
- char label[64];
- unsigned char label_len[2];
- unsigned char vol_id[16];
- unsigned char vol_id_len[2];
-};
-
-#define ocfsmajor(o) ((__u32)o.major_version[0] \
- + (((__u32) o.major_version[1]) << 8) \
- + (((__u32) o.major_version[2]) << 16) \
- + (((__u32) o.major_version[3]) << 24))
-#define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8))
-#define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8))
-
-#define OCFS_MAGIC "OracleCFS"
-
-struct ocfs2_super_block {
- unsigned char signature[8];
- unsigned char s_dummy1[184];
- unsigned char s_dummy2[80];
- char s_label[64];
- unsigned char s_uuid[16];
-};
-
-#define OCFS2_MIN_BLOCKSIZE 512
-#define OCFS2_MAX_BLOCKSIZE 4096
-
-#define OCFS2_SUPER_BLOCK_BLKNO 2
-
-#define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2"
-
-struct oracle_asm_disk_label {
- char dummy[32];
- char dl_tag[8];
- char dl_id[24];
-};
-
-#define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK"
-#define ORACLE_ASM_DISK_LABEL_OFFSET 32
-
-#define ISODCL(from, to) (to - from + 1)
-struct iso_volume_descriptor {
- char type[ISODCL(1,1)]; /* 711 */
- char id[ISODCL(2,6)];
- char version[ISODCL(7,7)];
- char data[ISODCL(8,2048)];
-};
-
-/*
- * Byte swap functions
- */
-#ifdef __GNUC__
-#define _INLINE_ static __inline__
-#else /* For Watcom C */
-#define _INLINE_ static inline
-#endif
-
-static __u16 blkid_swab16(__u16 val);
-static __u32 blkid_swab32(__u32 val);
-static __u64 blkid_swab64(__u64 val);
-
-#if ((defined __GNUC__) && \
- (defined(__i386__) || defined(__i486__) || defined(__i586__)))
-
-#define _BLKID_HAVE_ASM_BITOPS_
-
-_INLINE_ __u32 blkid_swab32(__u32 val)
-{
-#ifdef EXT2FS_REQUIRE_486
- __asm__("bswap %0" : "=r" (val) : "0" (val));
-#else
- __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */
- "rorl $16,%0\n\t" /* swap words */
- "xchgb %b0,%h0" /* swap higher bytes */
- :"=q" (val)
- : "0" (val));
-#endif
- return val;
-}
-
-_INLINE_ __u16 blkid_swab16(__u16 val)
-{
- __asm__("xchgb %b0,%h0" /* swap bytes */
- : "=q" (val)
- : "0" (val));
- return val;
-}
-
-_INLINE_ __u64 blkid_swab64(__u64 val)
-{
- return blkid_swab32(val >> 32) |
- ( ((__u64)blkid_swab32((__u32)val)) << 32 );
-}
-#endif
-
-#if !defined(_BLKID_HAVE_ASM_BITOPS_)
-
-_INLINE_ __u16 blkid_swab16(__u16 val)
-{
- return (val >> 8) | (val << 8);
-}
-
-_INLINE_ __u32 blkid_swab32(__u32 val)
-{
- return (val>>24) | ((val>>8) & 0xFF00) |
- ((val<<8) & 0xFF0000) | (val<<24);
-}
-
-_INLINE_ __u64 blkid_swab64(__u64 val)
-{
- return blkid_swab32(val >> 32) |
- ( ((__u64)blkid_swab32((__u32)val)) << 32 );
-}
-#endif
-
-
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define blkid_le16(x) blkid_swab16(x)
-#define blkid_le32(x) blkid_swab32(x)
-#define blkid_le64(x) blkid_swab64(x)
-#define blkid_be16(x) (x)
-#define blkid_be32(x) (x)
-#define blkid_be64(x) (x)
-#else
-#define blkid_le16(x) (x)
-#define blkid_le32(x) (x)
-#define blkid_le64(x) (x)
-#define blkid_be16(x) blkid_swab16(x)
-#define blkid_be32(x) blkid_swab32(x)
-#define blkid_be64(x) blkid_swab64(x)
-#endif
-
-#undef _INLINE_
-
-#endif /* _BLKID_PROBE_H */
diff --git a/e2fsprogs/old_e2fsprogs/blkid/read.c b/e2fsprogs/old_e2fsprogs/blkid/read.c
deleted file mode 100644
index feeda51..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/read.c
+++ b/dev/null
@@ -1,459 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * read.c - read the blkid cache from disk, to avoid scanning all devices
- *
- * Copyright (C) 2001, 2003 Theodore Y. Ts'o
- * Copyright (C) 2001 Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include "blkidP.h"
-#include "../uuid/uuid.h"
-
-#ifdef HAVE_STRTOULL
-#define __USE_ISOC9X
-#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
-#else
-/* FIXME: need to support real strtoull here */
-#define STRTOULL strtoul
-#endif
-
-#include <stdlib.h>
-
-#ifdef TEST_PROGRAM
-#define blkid_debug_dump_dev(dev) (debug_dump_dev(dev))
-static void debug_dump_dev(blkid_dev dev);
-#endif
-
-/*
- * File format:
- *
- * <device [<NAME="value"> ...]>device_name</device>
- *
- * The following tags are required for each entry:
- * <ID="id"> unique (within this file) ID number of this device
- * <TIME="time"> (ascii time_t) time this entry was last read from disk
- * <TYPE="type"> (detected) type of filesystem/data for this partition
- *
- * The following tags may be present, depending on the device contents
- * <LABEL="label"> (user supplied) label (volume name, etc)
- * <UUID="uuid"> (generated) universally unique identifier (serial no)
- */
-
-static char *skip_over_blank(char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
- return cp;
-}
-
-static char *skip_over_word(char *cp)
-{
- char ch;
-
- while ((ch = *cp)) {
- /* If we see a backslash, skip the next character */
- if (ch == '\\') {
- cp++;
- if (*cp == '\0')
- break;
- cp++;
- continue;
- }
- if (isspace(ch) || ch == '<' || ch == '>')
- break;
- cp++;
- }
- return cp;
-}
-
-static char *strip_line(char *line)
-{
- char *p;
-
- line = skip_over_blank(line);
-
- p = line + strlen(line) - 1;
-
- while (*line) {
- if (isspace(*p))
- *p-- = '\0';
- else
- break;
- }
-
- return line;
-}
-
-/*
- * Start parsing a new line from the cache.
- *
- * line starts with "<device" return 1 -> continue parsing line
- * line starts with "<foo", empty, or # return 0 -> skip line
- * line starts with other, return -BLKID_ERR_CACHE -> error
- */
-static int parse_start(char **cp)
-{
- char *p;
-
- p = strip_line(*cp);
-
- /* Skip comment or blank lines. We can't just NUL the first '#' char,
- * in case it is inside quotes, or escaped.
- */
- if (*p == '\0' || *p == '#')
- return 0;
-
- if (!strncmp(p, "<device", 7)) {
- DBG(DEBUG_READ, printf("found device header: %8s\n", p));
- p += 7;
-
- *cp = p;
- return 1;
- }
-
- if (*p == '<')
- return 0;
-
- return -BLKID_ERR_CACHE;
-}
-
-/* Consume the remaining XML on the line (cosmetic only) */
-static int parse_end(char **cp)
-{
- *cp = skip_over_blank(*cp);
-
- if (!strncmp(*cp, "</device>", 9)) {
- DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
- *cp += 9;
- return 0;
- }
-
- return -BLKID_ERR_CACHE;
-}
-
-/*
- * Allocate a new device struct with device name filled in. Will handle
- * finding the device on lines of the form:
- * <device foo=bar>devname</device>
- * <device>devname<foo>bar</foo></device>
- */
-static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
-{
- char *start, *tmp, *end, *name;
- int ret;
-
- if ((ret = parse_start(cp)) <= 0)
- return ret;
-
- start = tmp = strchr(*cp, '>');
- if (!start) {
- DBG(DEBUG_READ,
- printf("blkid: short line parsing dev: %s\n", *cp));
- return -BLKID_ERR_CACHE;
- }
- start = skip_over_blank(start + 1);
- end = skip_over_word(start);
-
- DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start));
-
- if (**cp == '>')
- *cp = end;
- else
- (*cp)++;
-
- *tmp = '\0';
-
- if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
- DBG(DEBUG_READ,
- printf("blkid: missing </device> ending: %s\n", end));
- } else if (tmp)
- *tmp = '\0';
-
- if (end - start <= 1) {
- DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
- return -BLKID_ERR_CACHE;
- }
-
- name = blkid_strndup(start, end-start);
- if (name == NULL)
- return -BLKID_ERR_MEM;
-
- DBG(DEBUG_READ, printf("found dev %s\n", name));
-
- if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE)))
- return -BLKID_ERR_MEM;
-
- free(name);
- return 1;
-}
-
-/*
- * Extract a tag of the form NAME="value" from the line.
- */
-static int parse_token(char **name, char **value, char **cp)
-{
- char *end;
-
- if (!name || !value || !cp)
- return -BLKID_ERR_PARAM;
-
- if (!(*value = strchr(*cp, '=')))
- return 0;
-
- **value = '\0';
- *name = strip_line(*cp);
- *value = skip_over_blank(*value + 1);
-
- if (**value == '"') {
- end = strchr(*value + 1, '"');
- if (!end) {
- DBG(DEBUG_READ,
- printf("unbalanced quotes at: %s\n", *value));
- *cp = *value;
- return -BLKID_ERR_CACHE;
- }
- (*value)++;
- *end = '\0';
- end++;
- } else {
- end = skip_over_word(*value);
- if (*end) {
- *end = '\0';
- end++;
- }
- }
- *cp = end;
-
- return 1;
-}
-
-/*
- * Extract a tag of the form <NAME>value</NAME> from the line.
- */
-/*
-static int parse_xml(char **name, char **value, char **cp)
-{
- char *end;
-
- if (!name || !value || !cp)
- return -BLKID_ERR_PARAM;
-
- *name = strip_line(*cp);
-
- if ((*name)[0] != '<' || (*name)[1] == '/')
- return 0;
-
- FIXME: finish this.
-}
-*/
-
-/*
- * Extract a tag from the line.
- *
- * Return 1 if a valid tag was found.
- * Return 0 if no tag found.
- * Return -ve error code.
- */
-static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
-{
- char *name;
- char *value;
- int ret;
-
- if (!cache || !dev)
- return -BLKID_ERR_PARAM;
-
- if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
- (ret = parse_xml(&name, &value, cp)) <= 0 */)
- return ret;
-
- /* Some tags are stored directly in the device struct */
- if (!strcmp(name, "DEVNO"))
- dev->bid_devno = STRTOULL(value, 0, 0);
- else if (!strcmp(name, "PRI"))
- dev->bid_pri = strtol(value, 0, 0);
- else if (!strcmp(name, "TIME"))
- /* FIXME: need to parse a long long eventually */
- dev->bid_time = strtol(value, 0, 0);
- else
- ret = blkid_set_tag(dev, name, value, strlen(value));
-
- DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value));
-
- return ret < 0 ? ret : 1;
-}
-
-/*
- * Parse a single line of data, and return a newly allocated dev struct.
- * Add the new device to the cache struct, if one was read.
- *
- * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
- *
- * Returns -ve value on error.
- * Returns 0 otherwise.
- * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
- * (e.g. comment lines, unknown XML content, etc).
- */
-static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
-{
- blkid_dev dev;
- int ret;
-
- if (!cache || !dev_p)
- return -BLKID_ERR_PARAM;
-
- *dev_p = NULL;
-
- DBG(DEBUG_READ, printf("line: %s\n", cp));
-
- if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
- return ret;
-
- dev = *dev_p;
-
- while ((ret = parse_tag(cache, dev, &cp)) > 0) {
- ;
- }
-
- if (dev->bid_type == NULL) {
- DBG(DEBUG_READ,
- printf("blkid: device %s has no TYPE\n",dev->bid_name));
- blkid_free_dev(dev);
- }
-
- DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
-
- return ret;
-}
-
-/*
- * Parse the specified filename, and return the data in the supplied or
- * a newly allocated cache struct. If the file doesn't exist, return a
- * new empty cache struct.
- */
-void blkid_read_cache(blkid_cache cache)
-{
- FILE *file;
- char buf[4096];
- int fd, lineno = 0;
- struct stat st;
-
- if (!cache)
- return;
-
- /*
- * If the file doesn't exist, then we just return an empty
- * struct so that the cache can be populated.
- */
- if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
- return;
- if (fstat(fd, &st) < 0)
- goto errout;
- if ((st.st_mtime == cache->bic_ftime) ||
- (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
- DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
- cache->bic_filename));
- goto errout;
- }
-
- DBG(DEBUG_CACHE, printf("reading cache file %s\n",
- cache->bic_filename));
-
- file = xfdopen_for_read(fd);
-
- while (fgets(buf, sizeof(buf), file)) {
- blkid_dev dev;
- unsigned int end;
-
- lineno++;
- if (buf[0] == 0)
- continue;
- end = strlen(buf) - 1;
- /* Continue reading next line if it ends with a backslash */
- while (end < sizeof(buf) - 2 && buf[end] == '\\' &&
- fgets(buf + end, sizeof(buf) - end, file)) {
- end = strlen(buf) - 1;
- lineno++;
- }
-
- if (blkid_parse_line(cache, &dev, buf) < 0) {
- DBG(DEBUG_READ,
- printf("blkid: bad format on line %d\n", lineno));
- continue;
- }
- }
- fclose(file);
-
- /*
- * Initially we do not need to write out the cache file.
- */
- cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
- cache->bic_ftime = st.st_mtime;
-
- return;
-errout:
- close(fd);
-}
-
-#ifdef TEST_PROGRAM
-static void debug_dump_dev(blkid_dev dev)
-{
- struct list_head *p;
-
- if (!dev) {
- printf(" dev: NULL\n");
- return;
- }
-
- printf(" dev: name = %s\n", dev->bid_name);
- printf(" dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
- printf(" dev: TIME=\"%lu\"\n", dev->bid_time);
- printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
- printf(" dev: flags = 0x%08X\n", dev->bid_flags);
-
- list_for_each(p, &dev->bid_tags) {
- blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
- if (tag)
- printf(" tag: %s=\"%s\"\n", tag->bit_name,
- tag->bit_val);
- else
- printf(" tag: NULL\n");
- }
- bb_putchar('\n');
-}
-
-int main(int argc, char**argv)
-{
- blkid_cache cache = NULL;
- int ret;
-
- blkid_debug_mask = DEBUG_ALL;
- if (argc > 2) {
- fprintf(stderr, "Usage: %s [filename]\n"
- "Test parsing of the cache (filename)\n", argv[0]);
- exit(1);
- }
- if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
- fprintf(stderr, "error %d reading cache file %s\n", ret,
- argv[1] ? argv[1] : BLKID_CACHE_FILE);
-
- blkid_put_cache(cache);
-
- return ret;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/resolve.c b/e2fsprogs/old_e2fsprogs/blkid/resolve.c
deleted file mode 100644
index 295ca61..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/resolve.c
+++ b/dev/null
@@ -1,139 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * resolve.c - resolve names and tags into specific devices
- *
- * Copyright (C) 2001, 2003 Theodore Ts'o.
- * Copyright (C) 2001 Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "blkidP.h"
-#include "probe.h"
-
-/*
- * Find a tagname (e.g. LABEL or UUID) on a specific device.
- */
-char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
- const char *devname)
-{
- blkid_tag found;
- blkid_dev dev;
- blkid_cache c = cache;
- char *ret = NULL;
-
- DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
-
- if (!devname)
- return NULL;
-
- if (!cache) {
- if (blkid_get_cache(&c, NULL) < 0)
- return NULL;
- }
-
- if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
- (found = blkid_find_tag_dev(dev, tagname)))
- ret = blkid_strdup(found->bit_val);
-
- if (!cache)
- blkid_put_cache(c);
-
- return ret;
-}
-
-/*
- * Locate a device name from a token (NAME=value string), or (name, value)
- * pair. In the case of a token, value is ignored. If the "token" is not
- * of the form "NAME=value" and there is no value given, then it is assumed
- * to be the actual devname and a copy is returned.
- */
-char *blkid_get_devname(blkid_cache cache, const char *token,
- const char *value)
-{
- blkid_dev dev;
- blkid_cache c = cache;
- char *t = NULL, *v = NULL;
- char *ret = NULL;
-
- if (!token)
- return NULL;
-
- if (!cache) {
- if (blkid_get_cache(&c, NULL) < 0)
- return NULL;
- }
-
- DBG(DEBUG_RESOLVE,
- printf("looking for %s%s%s %s\n", token, value ? "=" : "",
- value ? value : "", cache ? "in cache" : "from disk"));
-
- if (!value) {
- if (!strchr(token, '='))
- return blkid_strdup(token);
- blkid_parse_tag_string(token, &t, &v);
- if (!t || !v)
- goto errout;
- token = t;
- value = v;
- }
-
- dev = blkid_find_dev_with_tag(c, token, value);
- if (!dev)
- goto errout;
-
- ret = blkid_strdup(blkid_dev_devname(dev));
-
-errout:
- free(t);
- free(v);
- if (!cache) {
- blkid_put_cache(c);
- }
- return ret;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- char *value;
- blkid_cache cache;
-
- blkid_debug_mask = DEBUG_ALL;
- if (argc != 2 && argc != 3) {
- fprintf(stderr, "Usage:\t%s tagname=value\n"
- "\t%s tagname devname\n"
- "Find which device holds a given token or\n"
- "Find what the value of a tag is in a device\n",
- argv[0], argv[0]);
- exit(1);
- }
- if (blkid_get_cache(&cache, bb_dev_null) < 0) {
- fprintf(stderr, "Can't get blkid cache\n");
- exit(1);
- }
-
- if (argv[2]) {
- value = blkid_get_tag_value(cache, argv[1], argv[2]);
- printf("%s has tag %s=%s\n", argv[2], argv[1],
- value ? value : "<missing>");
- } else {
- value = blkid_get_devname(cache, argv[1], NULL);
- printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
- }
- blkid_put_cache(cache);
- return value ? 0 : 1;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/save.c b/e2fsprogs/old_e2fsprogs/blkid/save.c
deleted file mode 100644
index e60cca4..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/save.c
+++ b/dev/null
@@ -1,189 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * save.c - write the cache struct to disk
- *
- * Copyright (C) 2001 by Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include "blkidP.h"
-
-static int save_dev(blkid_dev dev, FILE *file)
-{
- struct list_head *p;
-
- if (!dev || dev->bid_name[0] != '/')
- return 0;
-
- DBG(DEBUG_SAVE,
- printf("device %s, type %s\n", dev->bid_name, dev->bid_type));
-
- fprintf(file,
- "<device DEVNO=\"0x%04lx\" TIME=\"%lu\"",
- (unsigned long) dev->bid_devno, dev->bid_time);
- if (dev->bid_pri)
- fprintf(file, " PRI=\"%d\"", dev->bid_pri);
- list_for_each(p, &dev->bid_tags) {
- blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
- fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
- }
- fprintf(file, ">%s</device>\n", dev->bid_name);
-
- return 0;
-}
-
-/*
- * Write out the cache struct to the cache file on disk.
- */
-int blkid_flush_cache(blkid_cache cache)
-{
- struct list_head *p;
- char *tmp = NULL;
- const char *opened = NULL;
- const char *filename;
- FILE *file = NULL;
- int fd, ret = 0;
- struct stat st;
-
- if (!cache)
- return -BLKID_ERR_PARAM;
-
- if (list_empty(&cache->bic_devs) ||
- !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
- DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
- return 0;
- }
-
- filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE;
-
- /* If we can't write to the cache file, then don't even try */
- if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
- (ret == 0 && access(filename, W_OK) < 0)) {
- DBG(DEBUG_SAVE,
- printf("can't write to cache file %s\n", filename));
- return 0;
- }
-
- /*
- * Try and create a temporary file in the same directory so
- * that in case of error we don't overwrite the cache file.
- * If the cache file doesn't yet exist, it isn't a regular
- * file (e.g. /dev/null or a socket), or we couldn't create
- * a temporary file then we open it directly.
- */
- if (ret == 0 && S_ISREG(st.st_mode)) {
- tmp = xmalloc(strlen(filename) + 8);
- sprintf(tmp, "%s-XXXXXX", filename);
- fd = mkstemp(tmp);
- if (fd >= 0) {
- file = xfdopen_for_write(fd);
- opened = tmp;
- }
- fchmod(fd, 0644);
- }
-
- if (!file) {
- file = fopen_for_write(filename);
- opened = filename;
- }
-
- DBG(DEBUG_SAVE,
- printf("writing cache file %s (really %s)\n",
- filename, opened));
-
- if (!file) {
- ret = errno;
- goto errout;
- }
-
- list_for_each(p, &cache->bic_devs) {
- blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
- if (!dev->bid_type)
- continue;
- if ((ret = save_dev(dev, file)) < 0)
- break;
- }
-
- if (ret >= 0) {
- cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
- ret = 1;
- }
-
- fclose(file);
- if (opened != filename) {
- if (ret < 0) {
- unlink(opened);
- DBG(DEBUG_SAVE,
- printf("unlinked temp cache %s\n", opened));
- } else {
- char *backup;
-
- backup = xmalloc(strlen(filename) + 5);
- sprintf(backup, "%s.old", filename);
- unlink(backup);
- link(filename, backup);
- free(backup);
- rename(opened, filename);
- DBG(DEBUG_SAVE,
- printf("moved temp cache %s\n", opened));
- }
- }
-
-errout:
- free(tmp);
- return ret;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- blkid_cache cache = NULL;
- int ret;
-
- blkid_debug_mask = DEBUG_ALL;
- if (argc > 2) {
- fprintf(stderr, "Usage: %s [filename]\n"
- "Test loading/saving a cache (filename)\n", argv[0]);
- exit(1);
- }
-
- if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
- if ((ret = blkid_probe_all(cache)) < 0) {
- fprintf(stderr, "error (%d) probing devices\n", ret);
- exit(1);
- }
- cache->bic_filename = blkid_strdup(argv[1]);
-
- if ((ret = blkid_flush_cache(cache)) < 0) {
- fprintf(stderr, "error (%d) saving cache\n", ret);
- exit(1);
- }
-
- blkid_put_cache(cache);
-
- return ret;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/blkid/tag.c b/e2fsprogs/old_e2fsprogs/blkid/tag.c
deleted file mode 100644
index 7424ede..0000000
--- a/e2fsprogs/old_e2fsprogs/blkid/tag.c
+++ b/dev/null
@@ -1,431 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * tag.c - allocation/initialization/free routines for tag structs
- *
- * Copyright (C) 2001 Andreas Dilger
- * Copyright (C) 2003 Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the
- * GNU Lesser General Public License.
- * %End-Header%
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "blkidP.h"
-
-static blkid_tag blkid_new_tag(void)
-{
- blkid_tag tag;
-
- tag = xzalloc(sizeof(struct blkid_struct_tag));
-
- INIT_LIST_HEAD(&tag->bit_tags);
- INIT_LIST_HEAD(&tag->bit_names);
-
- return tag;
-}
-
-#ifdef CONFIG_BLKID_DEBUG
-void blkid_debug_dump_tag(blkid_tag tag)
-{
- if (!tag) {
- printf(" tag: NULL\n");
- return;
- }
-
- printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
-}
-#endif
-
-void blkid_free_tag(blkid_tag tag)
-{
- if (!tag)
- return;
-
- DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name,
- tag->bit_val ? tag->bit_val : "(NULL)"));
- DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
-
- list_del(&tag->bit_tags); /* list of tags for this device */
- list_del(&tag->bit_names); /* list of tags with this type */
-
- free(tag->bit_name);
- free(tag->bit_val);
- free(tag);
-}
-
-/*
- * Find the desired tag on a device. If value is NULL, then the
- * first such tag is returned, otherwise return only exact tag if found.
- */
-blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
-{
- struct list_head *p;
-
- if (!dev || !type)
- return NULL;
-
- list_for_each(p, &dev->bid_tags) {
- blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
- bit_tags);
-
- if (!strcmp(tmp->bit_name, type))
- return tmp;
- }
- return NULL;
-}
-
-/*
- * Find the desired tag type in the cache.
- * We return the head tag for this tag type.
- */
-static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
-{
- blkid_tag head = NULL, tmp;
- struct list_head *p;
-
- if (!cache || !type)
- return NULL;
-
- list_for_each(p, &cache->bic_tags) {
- tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
- if (!strcmp(tmp->bit_name, type)) {
- DBG(DEBUG_TAG,
- printf(" found cache tag head %s\n", type));
- head = tmp;
- break;
- }
- }
- return head;
-}
-
-/*
- * Set a tag on an existing device.
- *
- * If value is NULL, then delete the tagsfrom the device.
- */
-int blkid_set_tag(blkid_dev dev, const char *name,
- const char *value, const int vlength)
-{
- blkid_tag t = 0, head = 0;
- char *val = NULL;
-
- if (!dev || !name)
- return -BLKID_ERR_PARAM;
-
- if (!(val = blkid_strndup(value, vlength)) && value)
- return -BLKID_ERR_MEM;
- t = blkid_find_tag_dev(dev, name);
- if (!value) {
- blkid_free_tag(t);
- } else if (t) {
- if (!strcmp(t->bit_val, val)) {
- /* Same thing, exit */
- free(val);
- return 0;
- }
- free(t->bit_val);
- t->bit_val = val;
- } else {
- /* Existing tag not present, add to device */
- if (!(t = blkid_new_tag()))
- goto errout;
- t->bit_name = blkid_strdup(name);
- t->bit_val = val;
- t->bit_dev = dev;
-
- list_add_tail(&t->bit_tags, &dev->bid_tags);
-
- if (dev->bid_cache) {
- head = blkid_find_head_cache(dev->bid_cache,
- t->bit_name);
- if (!head) {
- head = blkid_new_tag();
- if (!head)
- goto errout;
-
- DBG(DEBUG_TAG,
- printf(" creating new cache tag head %s\n", name));
- head->bit_name = blkid_strdup(name);
- if (!head->bit_name)
- goto errout;
- list_add_tail(&head->bit_tags,
- &dev->bid_cache->bic_tags);
- }
- list_add_tail(&t->bit_names, &head->bit_names);
- }
- }
-
- /* Link common tags directly to the device struct */
- if (!strcmp(name, "TYPE"))
- dev->bid_type = val;
- else if (!strcmp(name, "LABEL"))
- dev->bid_label = val;
- else if (!strcmp(name, "UUID"))
- dev->bid_uuid = val;
-
- if (dev->bid_cache)
- dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
- return 0;
-
-errout:
- blkid_free_tag(t);
- if (!t)
- free(val);
- blkid_free_tag(head);
- return -BLKID_ERR_MEM;
-}
-
-
-/*
- * Parse a "NAME=value" string. This is slightly different than
- * parse_token, because that will end an unquoted value at a space, while
- * this will assume that an unquoted value is the rest of the token (e.g.
- * if we are passed an already quoted string from the command-line we don't
- * have to both quote and escape quote so that the quotes make it to
- * us).
- *
- * Returns 0 on success, and -1 on failure.
- */
-int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
-{
- char *name, *value, *cp;
-
- DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
-
- if (!token || !(cp = strchr(token, '=')))
- return -1;
-
- name = blkid_strdup(token);
- if (!name)
- return -1;
- value = name + (cp - token);
- *value++ = '\0';
- if (*value == '"' || *value == '\'') {
- char c = *value++;
- if (!(cp = strrchr(value, c)))
- goto errout; /* missing closing quote */
- *cp = '\0';
- }
- value = blkid_strdup(value);
- if (!value)
- goto errout;
-
- *ret_type = name;
- *ret_val = value;
-
- return 0;
-
-errout:
- free(name);
- return -1;
-}
-
-/*
- * Tag iteration routines for the public libblkid interface.
- *
- * These routines do not expose the list.h implementation, which are a
- * contamination of the namespace, and which force us to reveal far, far
- * too much of our internal implemenation. I'm not convinced I want
- * to keep list.h in the long term, anyway. It's fine for kernel
- * programming, but performance is not the #1 priority for this
- * library, and I really don't like the tradeoff of type-safety for
- * performance for this application. [tytso:20030125.2007EST]
- */
-
-/*
- * This series of functions iterate over all tags in a device
- */
-#define TAG_ITERATE_MAGIC 0x01a5284c
-
-struct blkid_struct_tag_iterate {
- int magic;
- blkid_dev dev;
- struct list_head *p;
-};
-
-blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
-{
- blkid_tag_iterate iter;
-
- iter = xmalloc(sizeof(struct blkid_struct_tag_iterate));
- iter->magic = TAG_ITERATE_MAGIC;
- iter->dev = dev;
- iter->p = dev->bid_tags.next;
- return iter;
-}
-
-/*
- * Return 0 on success, -1 on error
- */
-extern int blkid_tag_next(blkid_tag_iterate iter,
- const char **type, const char **value)
-{
- blkid_tag tag;
-
- *type = 0;
- *value = 0;
- if (!iter || iter->magic != TAG_ITERATE_MAGIC ||
- iter->p == &iter->dev->bid_tags)
- return -1;
- tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
- *type = tag->bit_name;
- *value = tag->bit_val;
- iter->p = iter->p->next;
- return 0;
-}
-
-void blkid_tag_iterate_end(blkid_tag_iterate iter)
-{
- if (!iter || iter->magic != TAG_ITERATE_MAGIC)
- return;
- iter->magic = 0;
- free(iter);
-}
-
-/*
- * This function returns a device which matches a particular
- * type/value pair. If there is more than one device that matches the
- * search specification, it returns the one with the highest priority
- * value. This allows us to give preference to EVMS or LVM devices.
- *
- * XXX there should also be an interface which uses an iterator so we
- * can get all of the devices which match a type/value search parameter.
- */
-extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
- const char *type,
- const char *value)
-{
- blkid_tag head;
- blkid_dev dev;
- int pri;
- struct list_head *p;
-
- if (!cache || !type || !value)
- return NULL;
-
- blkid_read_cache(cache);
-
- DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
-
-try_again:
- pri = -1;
- dev = 0;
- head = blkid_find_head_cache(cache, type);
-
- if (head) {
- list_for_each(p, &head->bit_names) {
- blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
- bit_names);
-
- if (!strcmp(tmp->bit_val, value) &&
- tmp->bit_dev->bid_pri > pri) {
- dev = tmp->bit_dev;
- pri = dev->bid_pri;
- }
- }
- }
- if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
- dev = blkid_verify(cache, dev);
- if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
- goto try_again;
- }
-
- if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
- if (blkid_probe_all(cache) < 0)
- return NULL;
- goto try_again;
- }
- return dev;
-}
-
-#ifdef TEST_PROGRAM
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#else
-extern char *optarg;
-extern int optind;
-#endif
-
-void usage(char *prog)
-{
- fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
- "[type value]\n",
- prog);
- fprintf(stderr, "\tList all tags for a device and exit\n");
- exit(1);
-}
-
-int main(int argc, char **argv)
-{
- blkid_tag_iterate iter;
- blkid_cache cache = NULL;
- blkid_dev dev;
- int c, ret, found;
- int flags = BLKID_DEV_FIND;
- char *tmp;
- char *file = NULL;
- char *devname = NULL;
- char *search_type = NULL;
- char *search_value = NULL;
- const char *type, *value;
-
- while ((c = getopt (argc, argv, "m:f:")) != EOF)
- switch (c) {
- case 'f':
- file = optarg;
- break;
- case 'm':
- blkid_debug_mask = strtoul (optarg, &tmp, 0);
- if (*tmp) {
- fprintf(stderr, "Invalid debug mask: %s\n",
- optarg);
- exit(1);
- }
- break;
- case '?':
- usage(argv[0]);
- }
- if (argc > optind)
- devname = argv[optind++];
- if (argc > optind)
- search_type = argv[optind++];
- if (argc > optind)
- search_value = argv[optind++];
- if (!devname || (argc != optind))
- usage(argv[0]);
-
- if ((ret = blkid_get_cache(&cache, file)) != 0) {
- fprintf(stderr, "%s: error creating cache (%d)\n",
- argv[0], ret);
- exit(1);
- }
-
- dev = blkid_get_dev(cache, devname, flags);
- if (!dev) {
- fprintf(stderr, "%s: cannot find device in blkid cache\n", devname);
- exit(1);
- }
- if (search_type) {
- found = blkid_dev_has_tag(dev, search_type, search_value);
- printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
- search_type, search_value ? search_value : "NULL",
- found ? "FOUND" : "NOT FOUND");
- return !found;
- }
- printf("Device %s...\n", blkid_dev_devname(dev));
-
- iter = blkid_tag_iterate_begin(dev);
- while (blkid_tag_next(iter, &type, &value) == 0) {
- printf("\tTag %s has value %s\n", type, value);
- }
- blkid_tag_iterate_end(iter);
-
- blkid_put_cache(cache);
- return 0;
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/chattr.c b/e2fsprogs/old_e2fsprogs/chattr.c
deleted file mode 100644
index ae39d92..0000000
--- a/e2fsprogs/old_e2fsprogs/chattr.c
+++ b/dev/null
@@ -1,220 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * chattr.c - Change file attributes on an ext2 file system
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- * 93/11/13 - Replace stat() calls by lstat() to avoid loops
- * 94/02/27 - Integrated in Ted's distribution
- * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
- * 98/12/29 - Display version info only when -V specified (G M Sipe)
- */
-
-#include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include "ext2fs/ext2_fs.h"
-
-#ifdef __GNUC__
-# define EXT2FS_ATTR(x) __attribute__(x)
-#else
-# define EXT2FS_ATTR(x)
-#endif
-
-#include "e2fsbb.h"
-#include "e2p/e2p.h"
-
-#define OPT_ADD 1
-#define OPT_REM 2
-#define OPT_SET 4
-#define OPT_SET_VER 8
-static int flags;
-static int recursive;
-
-static unsigned long version;
-
-static unsigned long af;
-static unsigned long rf;
-static unsigned long sf;
-
-struct flags_char {
- unsigned long flag;
- char optchar;
-};
-
-static const struct flags_char flags_array[] = {
- { EXT2_NOATIME_FL, 'A' },
- { EXT2_SYNC_FL, 'S' },
- { EXT2_DIRSYNC_FL, 'D' },
- { EXT2_APPEND_FL, 'a' },
- { EXT2_COMPR_FL, 'c' },
- { EXT2_NODUMP_FL, 'd' },
- { EXT2_IMMUTABLE_FL, 'i' },
- { EXT3_JOURNAL_DATA_FL, 'j' },
- { EXT2_SECRM_FL, 's' },
- { EXT2_UNRM_FL, 'u' },
- { EXT2_NOTAIL_FL, 't' },
- { EXT2_TOPDIR_FL, 'T' },
- { 0, 0 }
-};
-
-static unsigned long get_flag(char c)
-{
- const struct flags_char *fp;
- for (fp = flags_array; fp->flag; fp++)
- if (fp->optchar == c)
- return fp->flag;
- bb_show_usage();
- return 0;
-}
-
-static int decode_arg(char *arg)
-{
- unsigned long *fl;
- char opt = *arg++;
-
- if (opt == '-') {
- flags |= OPT_REM;
- fl = &rf;
- } else if (opt == '+') {
- flags |= OPT_ADD;
- fl = &af;
- } else if (opt == '=') {
- flags |= OPT_SET;
- fl = &sf;
- } else
- return EOF;
-
- for (; *arg; ++arg)
- (*fl) |= get_flag(*arg);
-
- return 1;
-}
-
-static int chattr_dir_proc(const char *, struct dirent *, void *);
-
-static void change_attributes(const char * name)
-{
- unsigned long fsflags;
- struct stat st;
-
- if (lstat(name, &st) == -1) {
- bb_error_msg("stat %s failed", name);
- return;
- }
- if (S_ISLNK(st.st_mode) && recursive)
- return;
-
- /* Don't try to open device files, fifos etc. We probably
- * ought to display an error if the file was explicitly given
- * on the command line (whether or not recursive was
- * requested). */
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
- return;
-
- if (flags & OPT_SET_VER)
- if (fsetversion(name, version) == -1)
- bb_error_msg("setting version on %s", name);
-
- if (flags & OPT_SET) {
- fsflags = sf;
- } else {
- if (fgetflags(name, &fsflags) == -1) {
- bb_error_msg("reading flags on %s", name);
- goto skip_setflags;
- }
- if (flags & OPT_REM)
- fsflags &= ~rf;
- if (flags & OPT_ADD)
- fsflags |= af;
- if (!S_ISDIR(st.st_mode))
- fsflags &= ~EXT2_DIRSYNC_FL;
- }
- if (fsetflags(name, fsflags) == -1)
- bb_error_msg("setting flags on %s", name);
-
-skip_setflags:
- if (S_ISDIR(st.st_mode) && recursive)
- iterate_on_dir(name, chattr_dir_proc, NULL);
-}
-
-static int chattr_dir_proc(const char *dir_name, struct dirent *de,
- void *private EXT2FS_ATTR((unused)))
-{
- /*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/
- if (de->d_name[0] == '.'
- && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))
- ) {
- char *path = concat_subpath_file(dir_name, de->d_name);
- if (path) {
- change_attributes(path);
- free(path);
- }
- }
- return 0;
-}
-
-int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int chattr_main(int argc, char **argv)
-{
- int i;
- char *arg;
-
- /* parse the args */
- for (i = 1; i < argc; ++i) {
- arg = argv[i];
-
- /* take care of -R and -v <version> */
- if (arg[0] == '-') {
- if (arg[1] == 'R' && arg[2] == '\0') {
- recursive = 1;
- continue;
- } else if (arg[1] == 'v' && arg[2] == '\0') {
- char *tmp;
- ++i;
- if (i >= argc)
- bb_show_usage();
- version = strtol(argv[i], &tmp, 0);
- if (*tmp)
- bb_error_msg_and_die("bad version '%s'", arg);
- flags |= OPT_SET_VER;
- continue;
- }
- }
-
- if (decode_arg(arg) == EOF)
- break;
- }
-
- /* run sanity checks on all the arguments given us */
- if (i >= argc)
- bb_show_usage();
- if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM)))
- bb_error_msg_and_die("= is incompatible with - and +");
- if ((rf & af) != 0)
- bb_error_msg_and_die("Can't set and unset a flag");
- if (!flags)
- bb_error_msg_and_die("Must use '-v', =, - or +");
-
- /* now run chattr on all the files passed to us */
- while (i < argc)
- change_attributes(argv[i++]);
-
- return EXIT_SUCCESS;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2fsbb.h b/e2fsprogs/old_e2fsprogs/e2fsbb.h
deleted file mode 100644
index d31c319..0000000
--- a/e2fsprogs/old_e2fsprogs/e2fsbb.h
+++ b/dev/null
@@ -1,43 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * File: e2fsbb.h
- *
- * Redefine a bunch of e2fsprogs stuff to use busybox routines
- * instead. This makes upgrade between e2fsprogs versions easy.
- */
-
-#ifndef E2FSBB_H
-#define E2FSBB_H 1
-
-#include "libbb.h"
-
-/* version we've last synced against */
-#define E2FSPROGS_VERSION "1.38"
-#define E2FSPROGS_DATE "30-Jun-2005"
-
-typedef long errcode_t;
-#define ERRCODE_RANGE 8
-#define error_message(code) strerror((int) (code & ((1<<ERRCODE_RANGE)-1)))
-
-/* header defines */
-#define ENABLE_HTREE 1
-#define HAVE_ERRNO_H 1
-#define HAVE_EXT2_IOCTLS 1
-#define HAVE_LINUX_FD_H 1
-#define HAVE_MNTENT_H 1
-#define HAVE_NETINET_IN_H 1
-#define HAVE_NET_IF_H 1
-#define HAVE_SYS_IOCTL_H 1
-#define HAVE_SYS_MOUNT_H 1
-#define HAVE_SYS_QUEUE_H 1
-#define HAVE_SYS_STAT_H 1
-#define HAVE_SYS_TYPES_H 1
-#define HAVE_UNISTD_H 1
-
-/* Endianness */
-#if BB_BIG_ENDIAN
-#define ENABLE_SWAPFS 1
-#define WORDS_BIGENDIAN 1
-#endif
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/e2fsck.c b/e2fsprogs/old_e2fsprogs/e2fsck.c
deleted file mode 100644
index 8400a92..0000000
--- a/e2fsprogs/old_e2fsprogs/e2fsck.c
+++ b/dev/null
@@ -1,13516 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * e2fsck
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
- * Copyright (C) 2006 Garrett Kajmowicz
- *
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
- * Free Software License:
- * All rights are reserved by the author, with the following exceptions:
- * Permission is granted to freely reproduce and distribute this software,
- * possibly in exchange for a fee, provided that this copyright notice appears
- * intact. Permission is also granted to adapt this software to produce
- * derivative works, as long as the modified versions carry this copyright
- * notice and additional notices stating that the work has been modified.
- * This source code may be translated into executable form and incorporated
- * into proprietary software; there is no requirement for such software to
- * contain a copyright notice related to this source.
- *
- * linux/fs/recovery and linux/fs/revoke
- * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
- *
- * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
- *
- * Journal recovery routines for the generic filesystem journaling code;
- * part of the ext2fs journaling system.
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
-
-/*
-//usage:#define e2fsck_trivial_usage
-//usage: "[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] "
-//usage: "[-I inode_buffer_blocks] [-P process_inode_size] "
-//usage: "[-l|-L bad_blocks_file] [-C fd] [-j external_journal] "
-//usage: "[-E extended-options] device"
-//usage:#define e2fsck_full_usage "\n\n"
-//usage: "Check ext2/ext3 file system\n"
-//usage: "\n -p Automatic repair (no questions)"
-//usage: "\n -n Make no changes to the filesystem"
-//usage: "\n -y Assume 'yes' to all questions"
-//usage: "\n -c Check for bad blocks and add them to the badblock list"
-//usage: "\n -f Force checking even if filesystem is marked clean"
-//usage: "\n -v Verbose"
-//usage: "\n -b superblock Use alternative superblock"
-//usage: "\n -B blocksize Force blocksize when looking for superblock"
-//usage: "\n -j journal Set location of the external journal"
-//usage: "\n -l file Add to badblocks list"
-//usage: "\n -L file Set badblocks list"
-*/
-
-#include "e2fsck.h" /*Put all of our defines here to clean things up*/
-
-#define _(x) x
-#define N_(x) x
-
-/*
- * Procedure declarations
- */
-
-static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
-
-/* pass1.c */
-static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
-
-/* pass2.c */
-static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
- ext2_ino_t ino, char *buf);
-
-/* pass3.c */
-static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
-static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
- int num, int gauranteed_size);
-static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
-static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
- int adj);
-
-/* rehash.c */
-static void e2fsck_rehash_directories(e2fsck_t ctx);
-
-/* util.c */
-static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
- const char *description);
-static int ask(e2fsck_t ctx, const char * string, int def);
-static void e2fsck_read_bitmaps(e2fsck_t ctx);
-static void preenhalt(e2fsck_t ctx);
-static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, const char * proc);
-static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, const char * proc);
-static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
- const char *name, io_manager manager);
-
-/* unix.c */
-static void e2fsck_clear_progbar(e2fsck_t ctx);
-static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
- float percent, unsigned int dpynum);
-
-
-/*
- * problem.h --- e2fsck problem error codes
- */
-
-typedef __u32 problem_t;
-
-struct problem_context {
- errcode_t errcode;
- ext2_ino_t ino, ino2, dir;
- struct ext2_inode *inode;
- struct ext2_dir_entry *dirent;
- blk_t blk, blk2;
- e2_blkcnt_t blkcount;
- int group;
- __u64 num;
- const char *str;
-};
-
-
-/*
- * Function declarations
- */
-static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
-static int end_problem_latch(e2fsck_t ctx, int mask);
-static int set_latch_flags(int mask, int setflags, int clearflags);
-static void clear_problem_context(struct problem_context *ctx);
-
-/*
- * Dictionary Abstract Data Type
- * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
- *
- * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
- * kazlib_1_20
- */
-
-#ifndef DICT_H
-#define DICT_H
-
-/*
- * Blurb for inclusion into C++ translation units
- */
-
-typedef unsigned long dictcount_t;
-#define DICTCOUNT_T_MAX ULONG_MAX
-
-/*
- * The dictionary is implemented as a red-black tree
- */
-
-typedef enum { dnode_red, dnode_black } dnode_color_t;
-
-typedef struct dnode_t {
- struct dnode_t *dict_left;
- struct dnode_t *dict_right;
- struct dnode_t *dict_parent;
- dnode_color_t dict_color;
- const void *dict_key;
- void *dict_data;
-} dnode_t;
-
-typedef int (*dict_comp_t)(const void *, const void *);
-typedef void (*dnode_free_t)(dnode_t *);
-
-typedef struct dict_t {
- dnode_t dict_nilnode;
- dictcount_t dict_nodecount;
- dictcount_t dict_maxcount;
- dict_comp_t dict_compare;
- dnode_free_t dict_freenode;
- int dict_dupes;
-} dict_t;
-
-typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
-
-typedef struct dict_load_t {
- dict_t *dict_dictptr;
- dnode_t dict_nilnode;
-} dict_load_t;
-
-#define dict_count(D) ((D)->dict_nodecount)
-#define dnode_get(N) ((N)->dict_data)
-#define dnode_getkey(N) ((N)->dict_key)
-
-#endif
-
-/*
- * Compatibility header file for e2fsck which should be included
- * instead of linux/jfs.h
- *
- * Copyright (C) 2000 Stephen C. Tweedie
- */
-
-/*
- * Pull in the definition of the e2fsck context structure
- */
-
-struct buffer_head {
- char b_data[8192];
- e2fsck_t b_ctx;
- io_channel b_io;
- int b_size;
- blk_t b_blocknr;
- int b_dirty;
- int b_uptodate;
- int b_err;
-};
-
-
-#define K_DEV_FS 1
-#define K_DEV_JOURNAL 2
-
-#define lock_buffer(bh) do {} while (0)
-#define unlock_buffer(bh) do {} while (0)
-#define buffer_req(bh) 1
-#define do_readahead(journal, start) do {} while (0)
-
-static e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
-
-typedef struct {
- int object_length;
-} kmem_cache_t;
-
-#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
-
-/*
- * We use the standard libext2fs portability tricks for inline
- * functions.
- */
-
-static kmem_cache_t * do_cache_create(int len)
-{
- kmem_cache_t *new_cache;
-
- new_cache = xmalloc(sizeof(*new_cache));
- new_cache->object_length = len;
- return new_cache;
-}
-
-static void do_cache_destroy(kmem_cache_t *cache)
-{
- free(cache);
-}
-
-
-/*
- * Dictionary Abstract Data Type
- */
-
-
-/*
- * These macros provide short convenient names for structure members,
- * which are embellished with dict_ prefixes so that they are
- * properly confined to the documented namespace. It's legal for a
- * program which uses dict to define, for instance, a macro called ``parent''.
- * Such a macro would interfere with the dnode_t struct definition.
- * In general, highly portable and reusable C modules which expose their
- * structures need to confine structure member names to well-defined spaces.
- * The resulting identifiers aren't necessarily convenient to use, nor
- * readable, in the implementation, however!
- */
-
-#define left dict_left
-#define right dict_right
-#define parent dict_parent
-#define color dict_color
-#define key dict_key
-#define data dict_data
-
-#define nilnode dict_nilnode
-#define maxcount dict_maxcount
-#define compare dict_compare
-#define dupes dict_dupes
-
-#define dict_root(D) ((D)->nilnode.left)
-#define dict_nil(D) (&(D)->nilnode)
-
-static void dnode_free(dnode_t *node);
-
-/*
- * Perform a ``left rotation'' adjustment on the tree. The given node P and
- * its right child C are rearranged so that the P instead becomes the left
- * child of C. The left subtree of C is inherited as the new right subtree
- * for P. The ordering of the keys within the tree is thus preserved.
- */
-
-static void rotate_left(dnode_t *upper)
-{
- dnode_t *lower, *lowleft, *upparent;
-
- lower = upper->right;
- upper->right = lowleft = lower->left;
- lowleft->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- /* don't need to check for root node here because root->parent is
- the sentinel nil node, and root->parent->left points back to root */
-
- if (upper == upparent->left) {
- upparent->left = lower;
- } else {
- assert (upper == upparent->right);
- upparent->right = lower;
- }
-
- lower->left = upper;
- upper->parent = lower;
-}
-
-/*
- * This operation is the ``mirror'' image of rotate_left. It is
- * the same procedure, but with left and right interchanged.
- */
-
-static void rotate_right(dnode_t *upper)
-{
- dnode_t *lower, *lowright, *upparent;
-
- lower = upper->left;
- upper->left = lowright = lower->right;
- lowright->parent = upper;
-
- lower->parent = upparent = upper->parent;
-
- if (upper == upparent->right) {
- upparent->right = lower;
- } else {
- assert (upper == upparent->left);
- upparent->left = lower;
- }
-
- lower->right = upper;
- upper->parent = lower;
-}
-
-/*
- * Do a postorder traversal of the tree rooted at the specified
- * node and free everything under it. Used by dict_free().
- */
-
-static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
-{
- if (node == nil)
- return;
- free_nodes(dict, node->left, nil);
- free_nodes(dict, node->right, nil);
- dict->dict_freenode(node);
-}
-
-/*
- * Verify that the tree contains the given node. This is done by
- * traversing all of the nodes and comparing their pointers to the
- * given pointer. Returns 1 if the node is found, otherwise
- * returns zero. It is intended for debugging purposes.
- */
-
-static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
-{
- if (root != nil) {
- return root == node
- || verify_dict_has_node(nil, root->left, node)
- || verify_dict_has_node(nil, root->right, node);
- }
- return 0;
-}
-
-
-/*
- * Select a different set of node allocator routines.
- */
-
-static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
-{
- assert(dict_count(dict) == 0);
- dict->dict_freenode = fr;
-}
-
-/*
- * Free all the nodes in the dictionary by using the dictionary's
- * installed free routine. The dictionary is emptied.
- */
-
-static void dict_free_nodes(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
- free_nodes(dict, root, nil);
- dict->dict_nodecount = 0;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
-}
-
-/*
- * Initialize a user-supplied dictionary object.
- */
-
-static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
-{
- dict->compare = comp;
- dict->dict_freenode = dnode_free;
- dict->dict_nodecount = 0;
- dict->maxcount = maxcount;
- dict->nilnode.left = &dict->nilnode;
- dict->nilnode.right = &dict->nilnode;
- dict->nilnode.parent = &dict->nilnode;
- dict->nilnode.color = dnode_black;
- dict->dupes = 0;
- return dict;
-}
-
-/*
- * Locate a node in the dictionary having the given key.
- * If the node is not found, a null a pointer is returned (rather than
- * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
- * located node is returned.
- */
-
-static dnode_t *dict_lookup(dict_t *dict, const void *key)
-{
- dnode_t *root = dict_root(dict);
- dnode_t *nil = dict_nil(dict);
- dnode_t *saved;
- int result;
-
- /* simple binary search adapted for trees that contain duplicate keys */
-
- while (root != nil) {
- result = dict->compare(key, root->key);
- if (result < 0)
- root = root->left;
- else if (result > 0)
- root = root->right;
- else {
- if (!dict->dupes) { /* no duplicates, return match */
- return root;
- } else { /* could be dupes, find leftmost one */
- do {
- saved = root;
- root = root->left;
- while (root != nil && dict->compare(key, root->key))
- root = root->right;
- } while (root != nil);
- return saved;
- }
- }
- }
-
- return NULL;
-}
-
-/*
- * Insert a node into the dictionary. The node should have been
- * initialized with a data field. All other fields are ignored.
- * The behavior is undefined if the user attempts to insert into
- * a dictionary that is already full (for which the dict_isfull()
- * function returns true).
- */
-
-static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
-{
- dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
- dnode_t *parent = nil, *uncle, *grandpa;
- int result = -1;
-
- node->key = key;
-
- /* basic binary tree insert */
-
- while (where != nil) {
- parent = where;
- result = dict->compare(key, where->key);
- /* trap attempts at duplicate key insertion unless it's explicitly allowed */
- assert(dict->dupes || result != 0);
- if (result < 0)
- where = where->left;
- else
- where = where->right;
- }
-
- assert(where == nil);
-
- if (result < 0)
- parent->left = node;
- else
- parent->right = node;
-
- node->parent = parent;
- node->left = nil;
- node->right = nil;
-
- dict->dict_nodecount++;
-
- /* red black adjustments */
-
- node->color = dnode_red;
-
- while (parent->color == dnode_red) {
- grandpa = parent->parent;
- if (parent == grandpa->left) {
- uncle = grandpa->right;
- if (uncle->color == dnode_red) { /* red parent, red uncle */
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else { /* red parent, black uncle */
- if (node == parent->right) {
- rotate_left(parent);
- parent = node;
- assert (grandpa == parent->parent);
- /* rotation between parent and child preserves grandpa */
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_right(grandpa);
- break;
- }
- } else { /* symmetric cases: parent == parent->parent->right */
- uncle = grandpa->left;
- if (uncle->color == dnode_red) {
- parent->color = dnode_black;
- uncle->color = dnode_black;
- grandpa->color = dnode_red;
- node = grandpa;
- parent = grandpa->parent;
- } else {
- if (node == parent->left) {
- rotate_right(parent);
- parent = node;
- assert (grandpa == parent->parent);
- }
- parent->color = dnode_black;
- grandpa->color = dnode_red;
- rotate_left(grandpa);
- break;
- }
- }
- }
-
- dict_root(dict)->color = dnode_black;
-}
-
-/*
- * Allocate a node using the dictionary's allocator routine, give it
- * the data item.
- */
-
-static dnode_t *dnode_init(dnode_t *dnode, void *data)
-{
- dnode->data = data;
- dnode->parent = NULL;
- dnode->left = NULL;
- dnode->right = NULL;
- return dnode;
-}
-
-static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
-{
- dnode_t *node = xmalloc(sizeof(dnode_t));
-
- dnode_init(node, data);
- dict_insert(dict, node, key);
- return 1;
-}
-
-/*
- * Return the node with the lowest (leftmost) key. If the dictionary is empty
- * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
- */
-
-static dnode_t *dict_first(dict_t *dict)
-{
- dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
-
- if (root != nil)
- while ((left = root->left) != nil)
- root = left;
-
- return (root == nil) ? NULL : root;
-}
-
-/*
- * Return the given node's successor node---the node which has the
- * next key in the left to right ordering. If the node has
- * no successor, a null pointer is returned rather than a pointer to
- * the nil node.
- */
-
-static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
-{
- dnode_t *nil = dict_nil(dict), *parent, *left;
-
- if (curr->right != nil) {
- curr = curr->right;
- while ((left = curr->left) != nil)
- curr = left;
- return curr;
- }
-
- parent = curr->parent;
-
- while (parent != nil && curr == parent->right) {
- curr = parent;
- parent = curr->parent;
- }
-
- return (parent == nil) ? NULL : parent;
-}
-
-
-static void dnode_free(dnode_t *node)
-{
- free(node);
-}
-
-
-#undef left
-#undef right
-#undef parent
-#undef color
-#undef key
-#undef data
-
-#undef nilnode
-#undef maxcount
-#undef compare
-#undef dupes
-
-
-/*
- * dirinfo.c --- maintains the directory information table for e2fsck.
- */
-
-/*
- * This subroutine is called during pass1 to create a directory info
- * entry. During pass1, the passed-in parent is 0; it will get filled
- * in during pass2.
- */
-static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
-{
- struct dir_info *dir;
- int i, j;
- ext2_ino_t num_dirs;
- errcode_t retval;
- unsigned long old_size;
-
- if (!ctx->dir_info) {
- ctx->dir_info_count = 0;
- retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
- if (retval)
- num_dirs = 1024; /* Guess */
- ctx->dir_info_size = num_dirs + 10;
- ctx->dir_info = (struct dir_info *)
- e2fsck_allocate_memory(ctx, ctx->dir_info_size
- * sizeof (struct dir_info),
- "directory map");
- }
-
- if (ctx->dir_info_count >= ctx->dir_info_size) {
- old_size = ctx->dir_info_size * sizeof(struct dir_info);
- ctx->dir_info_size += 10;
- retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
- sizeof(struct dir_info),
- &ctx->dir_info);
- if (retval) {
- ctx->dir_info_size -= 10;
- return;
- }
- }
-
- /*
- * Normally, add_dir_info is called with each inode in
- * sequential order; but once in a while (like when pass 3
- * needs to recreate the root directory or lost+found
- * directory) it is called out of order. In those cases, we
- * need to move the dir_info entries down to make room, since
- * the dir_info array needs to be sorted by inode number for
- * get_dir_info()'s sake.
- */
- if (ctx->dir_info_count &&
- ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
- for (i = ctx->dir_info_count-1; i > 0; i--)
- if (ctx->dir_info[i-1].ino < ino)
- break;
- dir = &ctx->dir_info[i];
- if (dir->ino != ino)
- for (j = ctx->dir_info_count++; j > i; j--)
- ctx->dir_info[j] = ctx->dir_info[j-1];
- } else
- dir = &ctx->dir_info[ctx->dir_info_count++];
-
- dir->ino = ino;
- dir->dotdot = parent;
- dir->parent = parent;
-}
-
-/*
- * get_dir_info() --- given an inode number, try to find the directory
- * information entry for it.
- */
-static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
-{
- int low, high, mid;
-
- low = 0;
- high = ctx->dir_info_count-1;
- if (!ctx->dir_info)
- return 0;
- if (ino == ctx->dir_info[low].ino)
- return &ctx->dir_info[low];
- if (ino == ctx->dir_info[high].ino)
- return &ctx->dir_info[high];
-
- while (low < high) {
- mid = (low+high)/2;
- if (mid == low || mid == high)
- break;
- if (ino == ctx->dir_info[mid].ino)
- return &ctx->dir_info[mid];
- if (ino < ctx->dir_info[mid].ino)
- high = mid;
- else
- low = mid;
- }
- return 0;
-}
-
-/*
- * Free the dir_info structure when it isn't needed any more.
- */
-static void e2fsck_free_dir_info(e2fsck_t ctx)
-{
- ext2fs_free_mem(&ctx->dir_info);
- ctx->dir_info_size = 0;
- ctx->dir_info_count = 0;
-}
-
-/*
- * Return the count of number of directories in the dir_info structure
- */
-static int e2fsck_get_num_dirinfo(e2fsck_t ctx)
-{
- return ctx->dir_info_count;
-}
-
-/*
- * A simple interator function
- */
-static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
-{
- if (*control >= ctx->dir_info_count)
- return 0;
-
- return ctx->dir_info + (*control)++;
-}
-
-/*
- * dirinfo.c --- maintains the directory information table for e2fsck.
- *
- */
-
-#ifdef ENABLE_HTREE
-
-/*
- * This subroutine is called during pass1 to create a directory info
- * entry. During pass1, the passed-in parent is 0; it will get filled
- * in during pass2.
- */
-static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
-{
- struct dx_dir_info *dir;
- int i, j;
- errcode_t retval;
- unsigned long old_size;
-
- if (!ctx->dx_dir_info) {
- ctx->dx_dir_info_count = 0;
- ctx->dx_dir_info_size = 100; /* Guess */
- ctx->dx_dir_info = (struct dx_dir_info *)
- e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
- * sizeof (struct dx_dir_info),
- "directory map");
- }
-
- if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
- old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
- ctx->dx_dir_info_size += 10;
- retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
- sizeof(struct dx_dir_info),
- &ctx->dx_dir_info);
- if (retval) {
- ctx->dx_dir_info_size -= 10;
- return;
- }
- }
-
- /*
- * Normally, add_dx_dir_info is called with each inode in
- * sequential order; but once in a while (like when pass 3
- * needs to recreate the root directory or lost+found
- * directory) it is called out of order. In those cases, we
- * need to move the dx_dir_info entries down to make room, since
- * the dx_dir_info array needs to be sorted by inode number for
- * get_dx_dir_info()'s sake.
- */
- if (ctx->dx_dir_info_count &&
- ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
- for (i = ctx->dx_dir_info_count-1; i > 0; i--)
- if (ctx->dx_dir_info[i-1].ino < ino)
- break;
- dir = &ctx->dx_dir_info[i];
- if (dir->ino != ino)
- for (j = ctx->dx_dir_info_count++; j > i; j--)
- ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
- } else
- dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
-
- dir->ino = ino;
- dir->numblocks = num_blocks;
- dir->hashversion = 0;
- dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
- * sizeof (struct dx_dirblock_info),
- "dx_block info array");
-}
-
-/*
- * get_dx_dir_info() --- given an inode number, try to find the directory
- * information entry for it.
- */
-static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
-{
- int low, high, mid;
-
- low = 0;
- high = ctx->dx_dir_info_count-1;
- if (!ctx->dx_dir_info)
- return 0;
- if (ino == ctx->dx_dir_info[low].ino)
- return &ctx->dx_dir_info[low];
- if (ino == ctx->dx_dir_info[high].ino)
- return &ctx->dx_dir_info[high];
-
- while (low < high) {
- mid = (low+high)/2;
- if (mid == low || mid == high)
- break;
- if (ino == ctx->dx_dir_info[mid].ino)
- return &ctx->dx_dir_info[mid];
- if (ino < ctx->dx_dir_info[mid].ino)
- high = mid;
- else
- low = mid;
- }
- return 0;
-}
-
-/*
- * Free the dx_dir_info structure when it isn't needed any more.
- */
-static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
-{
- int i;
- struct dx_dir_info *dir;
-
- if (ctx->dx_dir_info) {
- dir = ctx->dx_dir_info;
- for (i=0; i < ctx->dx_dir_info_count; i++) {
- ext2fs_free_mem(&dir->dx_block);
- }
- ext2fs_free_mem(&ctx->dx_dir_info);
- }
- ctx->dx_dir_info_size = 0;
- ctx->dx_dir_info_count = 0;
-}
-
-/*
- * A simple interator function
- */
-static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
-{
- if (*control >= ctx->dx_dir_info_count)
- return 0;
-
- return ctx->dx_dir_info + (*control)++;
-}
-
-#endif /* ENABLE_HTREE */
-/*
- * e2fsck.c - a consistency checker for the new extended file system.
- *
- */
-
-/*
- * This function allocates an e2fsck context
- */
-static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
-{
- e2fsck_t context;
- errcode_t retval;
-
- retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
- if (retval)
- return retval;
-
- memset(context, 0, sizeof(struct e2fsck_struct));
-
- context->process_inode_size = 256;
- context->ext_attr_ver = 2;
-
- *ret = context;
- return 0;
-}
-
-struct ea_refcount_el {
- blk_t ea_blk;
- int ea_count;
-};
-
-struct ea_refcount {
- blk_t count;
- blk_t size;
- blk_t cursor;
- struct ea_refcount_el *list;
-};
-
-static void ea_refcount_free(ext2_refcount_t refcount)
-{
- if (!refcount)
- return;
-
- ext2fs_free_mem(&refcount->list);
- ext2fs_free_mem(&refcount);
-}
-
-/*
- * This function resets an e2fsck context; it is called when e2fsck
- * needs to be restarted.
- */
-static errcode_t e2fsck_reset_context(e2fsck_t ctx)
-{
- ctx->flags = 0;
- ctx->lost_and_found = 0;
- ctx->bad_lost_and_found = 0;
- ext2fs_free_inode_bitmap(ctx->inode_used_map);
- ctx->inode_used_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_dir_map);
- ctx->inode_dir_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_reg_map);
- ctx->inode_reg_map = 0;
- ext2fs_free_block_bitmap(ctx->block_found_map);
- ctx->block_found_map = 0;
- ext2fs_free_icount(ctx->inode_link_info);
- ctx->inode_link_info = 0;
- if (ctx->journal_io) {
- if (ctx->fs && ctx->fs->io != ctx->journal_io)
- io_channel_close(ctx->journal_io);
- ctx->journal_io = 0;
- }
- if (ctx->fs) {
- ext2fs_free_dblist(ctx->fs->dblist);
- ctx->fs->dblist = 0;
- }
- e2fsck_free_dir_info(ctx);
-#ifdef ENABLE_HTREE
- e2fsck_free_dx_dir_info(ctx);
-#endif
- ea_refcount_free(ctx->refcount);
- ctx->refcount = 0;
- ea_refcount_free(ctx->refcount_extra);
- ctx->refcount_extra = 0;
- ext2fs_free_block_bitmap(ctx->block_dup_map);
- ctx->block_dup_map = 0;
- ext2fs_free_block_bitmap(ctx->block_ea_map);
- ctx->block_ea_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_bad_map);
- ctx->inode_bad_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
- ctx->inode_imagic_map = 0;
- ext2fs_u32_list_free(ctx->dirs_to_hash);
- ctx->dirs_to_hash = 0;
-
- /*
- * Clear the array of invalid meta-data flags
- */
- ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
- ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
- ext2fs_free_mem(&ctx->invalid_inode_table_flag);
-
- /* Clear statistic counters */
- ctx->fs_directory_count = 0;
- ctx->fs_regular_count = 0;
- ctx->fs_blockdev_count = 0;
- ctx->fs_chardev_count = 0;
- ctx->fs_links_count = 0;
- ctx->fs_symlinks_count = 0;
- ctx->fs_fast_symlinks_count = 0;
- ctx->fs_fifo_count = 0;
- ctx->fs_total_count = 0;
- ctx->fs_sockets_count = 0;
- ctx->fs_ind_count = 0;
- ctx->fs_dind_count = 0;
- ctx->fs_tind_count = 0;
- ctx->fs_fragmented = 0;
- ctx->large_files = 0;
-
- /* Reset the superblock to the user's requested value */
- ctx->superblock = ctx->use_superblock;
-
- return 0;
-}
-
-static void e2fsck_free_context(e2fsck_t ctx)
-{
- if (!ctx)
- return;
-
- e2fsck_reset_context(ctx);
- if (ctx->blkid)
- blkid_put_cache(ctx->blkid);
-
- ext2fs_free_mem(&ctx);
-}
-
-/*
- * ea_refcount.c
- */
-
-/*
- * The strategy we use for keeping track of EA refcounts is as
- * follows. We keep a sorted array of first EA blocks and its
- * reference counts. Once the refcount has dropped to zero, it is
- * removed from the array to save memory space. Once the EA block is
- * checked, its bit is set in the block_ea_map bitmap.
- */
-
-
-static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
-{
- ext2_refcount_t refcount;
- errcode_t retval;
- size_t bytes;
-
- retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
- if (retval)
- return retval;
- memset(refcount, 0, sizeof(struct ea_refcount));
-
- if (!size)
- size = 500;
- refcount->size = size;
- bytes = (size_t) (size * sizeof(struct ea_refcount_el));
-#ifdef DEBUG
- printf("Refcount allocated %d entries, %lu bytes.\n",
- refcount->size, bytes);
-#endif
- retval = ext2fs_get_mem(bytes, &refcount->list);
- if (retval)
- goto errout;
- memset(refcount->list, 0, bytes);
-
- refcount->count = 0;
- refcount->cursor = 0;
-
- *ret = refcount;
- return 0;
-
-errout:
- ea_refcount_free(refcount);
- return retval;
-}
-
-/*
- * collapse_refcount() --- go through the refcount array, and get rid
- * of any count == zero entries
- */
-static void refcount_collapse(ext2_refcount_t refcount)
-{
- unsigned int i, j;
- struct ea_refcount_el *list;
-
- list = refcount->list;
- for (i = 0, j = 0; i < refcount->count; i++) {
- if (list[i].ea_count) {
- if (i != j)
- list[j] = list[i];
- j++;
- }
- }
-#if defined(DEBUG) || defined(TEST_PROGRAM)
- printf("Refcount_collapse: size was %d, now %d\n",
- refcount->count, j);
-#endif
- refcount->count = j;
-}
-
-
-/*
- * insert_refcount_el() --- Insert a new entry into the sorted list at a
- * specified position.
- */
-static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
- blk_t blk, int pos)
-{
- struct ea_refcount_el *el;
- errcode_t retval;
- blk_t new_size = 0;
- int num;
-
- if (refcount->count >= refcount->size) {
- new_size = refcount->size + 100;
-#ifdef DEBUG
- printf("Reallocating refcount %d entries...\n", new_size);
-#endif
- retval = ext2fs_resize_mem((size_t) refcount->size *
- sizeof(struct ea_refcount_el),
- (size_t) new_size *
- sizeof(struct ea_refcount_el),
- &refcount->list);
- if (retval)
- return 0;
- refcount->size = new_size;
- }
- num = (int) refcount->count - pos;
- if (num < 0)
- return 0; /* should never happen */
- if (num) {
- memmove(&refcount->list[pos+1], &refcount->list[pos],
- sizeof(struct ea_refcount_el) * num);
- }
- refcount->count++;
- el = &refcount->list[pos];
- el->ea_count = 0;
- el->ea_blk = blk;
- return el;
-}
-
-
-/*
- * get_refcount_el() --- given an block number, try to find refcount
- * information in the sorted list. If the create flag is set,
- * and we can't find an entry, create one in the sorted list.
- */
-static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
- blk_t blk, int create)
-{
- float range;
- int low, high, mid;
- blk_t lowval, highval;
-
- if (!refcount || !refcount->list)
- return 0;
-retry:
- low = 0;
- high = (int) refcount->count-1;
- if (create && ((refcount->count == 0) ||
- (blk > refcount->list[high].ea_blk))) {
- if (refcount->count >= refcount->size)
- refcount_collapse(refcount);
-
- return insert_refcount_el(refcount, blk,
- (unsigned) refcount->count);
- }
- if (refcount->count == 0)
- return 0;
-
- if (refcount->cursor >= refcount->count)
- refcount->cursor = 0;
- if (blk == refcount->list[refcount->cursor].ea_blk)
- return &refcount->list[refcount->cursor++];
-#ifdef DEBUG
- printf("Non-cursor get_refcount_el: %u\n", blk);
-#endif
- while (low <= high) {
- if (low == high)
- mid = low;
- else {
- /* Interpolate for efficiency */
- lowval = refcount->list[low].ea_blk;
- highval = refcount->list[high].ea_blk;
-
- if (blk < lowval)
- range = 0;
- else if (blk > highval)
- range = 1;
- else
- range = ((float) (blk - lowval)) /
- (highval - lowval);
- mid = low + ((int) (range * (high-low)));
- }
-
- if (blk == refcount->list[mid].ea_blk) {
- refcount->cursor = mid+1;
- return &refcount->list[mid];
- }
- if (blk < refcount->list[mid].ea_blk)
- high = mid-1;
- else
- low = mid+1;
- }
- /*
- * If we need to create a new entry, it should be right at
- * low (where high will be left at low-1).
- */
- if (create) {
- if (refcount->count >= refcount->size) {
- refcount_collapse(refcount);
- if (refcount->count < refcount->size)
- goto retry;
- }
- return insert_refcount_el(refcount, blk, low);
- }
- return 0;
-}
-
-static errcode_t
-ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
-{
- struct ea_refcount_el *el;
-
- el = get_refcount_el(refcount, blk, 1);
- if (!el)
- return EXT2_ET_NO_MEMORY;
- el->ea_count++;
-
- if (ret)
- *ret = el->ea_count;
- return 0;
-}
-
-static errcode_t
-ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
-{
- struct ea_refcount_el *el;
-
- el = get_refcount_el(refcount, blk, 0);
- if (!el || el->ea_count == 0)
- return EXT2_ET_INVALID_ARGUMENT;
-
- el->ea_count--;
-
- if (ret)
- *ret = el->ea_count;
- return 0;
-}
-
-static errcode_t
-ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
-{
- struct ea_refcount_el *el;
-
- /*
- * Get the refcount element
- */
- el = get_refcount_el(refcount, blk, count ? 1 : 0);
- if (!el)
- return count ? EXT2_ET_NO_MEMORY : 0;
- el->ea_count = count;
- return 0;
-}
-
-static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
-{
- refcount->cursor = 0;
-}
-
-
-static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
-{
- struct ea_refcount_el *list;
-
- while (1) {
- if (refcount->cursor >= refcount->count)
- return 0;
- list = refcount->list;
- if (list[refcount->cursor].ea_count) {
- if (ret)
- *ret = list[refcount->cursor].ea_count;
- return list[refcount->cursor++].ea_blk;
- }
- refcount->cursor++;
- }
-}
-
-
-/*
- * ehandler.c --- handle bad block errors which come up during the
- * course of an e2fsck session.
- */
-
-
-static const char *operation;
-
-static errcode_t
-e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
- void *data, size_t size FSCK_ATTR((unused)),
- int actual FSCK_ATTR((unused)), errcode_t error)
-{
- int i;
- char *p;
- ext2_filsys fs = (ext2_filsys) channel->app_data;
- e2fsck_t ctx;
-
- ctx = (e2fsck_t) fs->priv_data;
-
- /*
- * If more than one block was read, try reading each block
- * separately. We could use the actual bytes read to figure
- * out where to start, but we don't bother.
- */
- if (count > 1) {
- p = (char *) data;
- for (i=0; i < count; i++, p += channel->block_size, block++) {
- error = io_channel_read_blk(channel, block,
- 1, p);
- if (error)
- return error;
- }
- return 0;
- }
- if (operation)
- printf(_("Error reading block %lu (%s) while %s. "), block,
- error_message(error), operation);
- else
- printf(_("Error reading block %lu (%s). "), block,
- error_message(error));
- preenhalt(ctx);
- if (ask(ctx, _("Ignore error"), 1)) {
- if (ask(ctx, _("Force rewrite"), 1))
- io_channel_write_blk(channel, block, 1, data);
- return 0;
- }
-
- return error;
-}
-
-static errcode_t
-e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
- const void *data, size_t size FSCK_ATTR((unused)),
- int actual FSCK_ATTR((unused)), errcode_t error)
-{
- int i;
- const char *p;
- ext2_filsys fs = (ext2_filsys) channel->app_data;
- e2fsck_t ctx;
-
- ctx = (e2fsck_t) fs->priv_data;
-
- /*
- * If more than one block was written, try writing each block
- * separately. We could use the actual bytes read to figure
- * out where to start, but we don't bother.
- */
- if (count > 1) {
- p = (const char *) data;
- for (i=0; i < count; i++, p += channel->block_size, block++) {
- error = io_channel_write_blk(channel, block,
- 1, p);
- if (error)
- return error;
- }
- return 0;
- }
-
- if (operation)
- printf(_("Error writing block %lu (%s) while %s. "), block,
- error_message(error), operation);
- else
- printf(_("Error writing block %lu (%s). "), block,
- error_message(error));
- preenhalt(ctx);
- if (ask(ctx, _("Ignore error"), 1))
- return 0;
-
- return error;
-}
-
-static const char *ehandler_operation(const char *op)
-{
- const char *ret = operation;
-
- operation = op;
- return ret;
-}
-
-static void ehandler_init(io_channel channel)
-{
- channel->read_error = e2fsck_handle_read_error;
- channel->write_error = e2fsck_handle_write_error;
-}
-
-/*
- * journal.c --- code for handling the "ext3" journal
- *
- * Copyright (C) 2000 Andreas Dilger
- * Copyright (C) 2000 Theodore Ts'o
- *
- * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
- * Copyright (C) 1999 Red Hat Software
- *
- * This file may be redistributed under the terms of the
- * GNU General Public License version 2 or at your discretion
- * any later version.
- */
-
-/*
- * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
- * This creates a larger static binary, and a smaller binary using
- * shared libraries. It's also probably slightly less CPU-efficient,
- * which is why it's not on by default. But, it's a good way of
- * testing the functions in inode_io.c and fileio.c.
- */
-#undef USE_INODE_IO
-
-/* Kernel compatibility functions for handling the journal. These allow us
- * to use the recovery.c file virtually unchanged from the kernel, so we
- * don't have to do much to keep kernel and user recovery in sync.
- */
-static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
-{
-#ifdef USE_INODE_IO
- *phys = block;
- return 0;
-#else
- struct inode *inode = journal->j_inode;
- errcode_t retval;
- blk_t pblk;
-
- if (!inode) {
- *phys = block;
- return 0;
- }
-
- retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
- &inode->i_ext2, NULL, 0, block, &pblk);
- *phys = pblk;
- return retval;
-#endif
-}
-
-static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
-{
- struct buffer_head *bh;
-
- bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
- if (!bh)
- return NULL;
-
- bh->b_ctx = kdev->k_ctx;
- if (kdev->k_dev == K_DEV_FS)
- bh->b_io = kdev->k_ctx->fs->io;
- else
- bh->b_io = kdev->k_ctx->journal_io;
- bh->b_size = blocksize;
- bh->b_blocknr = blocknr;
-
- return bh;
-}
-
-static void sync_blockdev(kdev_t kdev)
-{
- io_channel io;
-
- if (kdev->k_dev == K_DEV_FS)
- io = kdev->k_ctx->fs->io;
- else
- io = kdev->k_ctx->journal_io;
-
- io_channel_flush(io);
-}
-
-static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
-{
- int retval;
- struct buffer_head *bh;
-
- for (; nr > 0; --nr) {
- bh = *bhp++;
- if (rw == READ && !bh->b_uptodate) {
- retval = io_channel_read_blk(bh->b_io,
- bh->b_blocknr,
- 1, bh->b_data);
- if (retval) {
- bb_error_msg("while reading block %lu",
- (unsigned long) bh->b_blocknr);
- bh->b_err = retval;
- continue;
- }
- bh->b_uptodate = 1;
- } else if (rw == WRITE && bh->b_dirty) {
- retval = io_channel_write_blk(bh->b_io,
- bh->b_blocknr,
- 1, bh->b_data);
- if (retval) {
- bb_error_msg("while writing block %lu",
- (unsigned long) bh->b_blocknr);
- bh->b_err = retval;
- continue;
- }
- bh->b_dirty = 0;
- bh->b_uptodate = 1;
- }
- }
-}
-
-static void mark_buffer_dirty(struct buffer_head *bh)
-{
- bh->b_dirty = 1;
-}
-
-static inline void mark_buffer_clean(struct buffer_head * bh)
-{
- bh->b_dirty = 0;
-}
-
-static void brelse(struct buffer_head *bh)
-{
- if (bh->b_dirty)
- ll_rw_block(WRITE, 1, &bh);
- ext2fs_free_mem(&bh);
-}
-
-static int buffer_uptodate(struct buffer_head *bh)
-{
- return bh->b_uptodate;
-}
-
-static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
-{
- bh->b_uptodate = val;
-}
-
-static void wait_on_buffer(struct buffer_head *bh)
-{
- if (!bh->b_uptodate)
- ll_rw_block(READ, 1, &bh);
-}
-
-
-static void e2fsck_clear_recover(e2fsck_t ctx, int error)
-{
- ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
-
- /* if we had an error doing journal recovery, we need a full fsck */
- if (error)
- ctx->fs->super->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(ctx->fs);
-}
-
-static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- struct ext2_super_block jsuper;
- struct problem_context pctx;
- struct buffer_head *bh;
- struct inode *j_inode = NULL;
- struct kdev_s *dev_fs = NULL, *dev_journal;
- const char *journal_name = NULL;
- journal_t *journal = NULL;
- errcode_t retval = 0;
- io_manager io_ptr = 0;
- unsigned long start = 0;
- blk_t blk;
- int ext_journal = 0;
- int tried_backup_jnl = 0;
- int i;
-
- clear_problem_context(&pctx);
-
- journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
- if (!journal) {
- return EXT2_ET_NO_MEMORY;
- }
-
- dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
- if (!dev_fs) {
- retval = EXT2_ET_NO_MEMORY;
- goto errout;
- }
- dev_journal = dev_fs+1;
-
- dev_fs->k_ctx = dev_journal->k_ctx = ctx;
- dev_fs->k_dev = K_DEV_FS;
- dev_journal->k_dev = K_DEV_JOURNAL;
-
- journal->j_dev = dev_journal;
- journal->j_fs_dev = dev_fs;
- journal->j_inode = NULL;
- journal->j_blocksize = ctx->fs->blocksize;
-
- if (uuid_is_null(sb->s_journal_uuid)) {
- if (!sb->s_journal_inum)
- return EXT2_ET_BAD_INODE_NUM;
- j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
- "journal inode");
- if (!j_inode) {
- retval = EXT2_ET_NO_MEMORY;
- goto errout;
- }
-
- j_inode->i_ctx = ctx;
- j_inode->i_ino = sb->s_journal_inum;
-
- if ((retval = ext2fs_read_inode(ctx->fs,
- sb->s_journal_inum,
- &j_inode->i_ext2))) {
- try_backup_journal:
- if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
- tried_backup_jnl)
- goto errout;
- memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
- memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
- EXT2_N_BLOCKS*4);
- j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
- j_inode->i_ext2.i_links_count = 1;
- j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
- tried_backup_jnl++;
- }
- if (!j_inode->i_ext2.i_links_count ||
- !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
- retval = EXT2_ET_NO_JOURNAL;
- goto try_backup_journal;
- }
- if (j_inode->i_ext2.i_size / journal->j_blocksize <
- JFS_MIN_JOURNAL_BLOCKS) {
- retval = EXT2_ET_JOURNAL_TOO_SMALL;
- goto try_backup_journal;
- }
- for (i=0; i < EXT2_N_BLOCKS; i++) {
- blk = j_inode->i_ext2.i_block[i];
- if (!blk) {
- if (i < EXT2_NDIR_BLOCKS) {
- retval = EXT2_ET_JOURNAL_TOO_SMALL;
- goto try_backup_journal;
- }
- continue;
- }
- if (blk < sb->s_first_data_block ||
- blk >= sb->s_blocks_count) {
- retval = EXT2_ET_BAD_BLOCK_NUM;
- goto try_backup_journal;
- }
- }
- journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
-
-#ifdef USE_INODE_IO
- retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
- &j_inode->i_ext2,
- &journal_name);
- if (retval)
- goto errout;
-
- io_ptr = inode_io_manager;
-#else
- journal->j_inode = j_inode;
- ctx->journal_io = ctx->fs->io;
- if ((retval = journal_bmap(journal, 0, &start)) != 0)
- goto errout;
-#endif
- } else {
- ext_journal = 1;
- if (!ctx->journal_name) {
- char uuid[37];
-
- uuid_unparse(sb->s_journal_uuid, uuid);
- ctx->journal_name = blkid_get_devname(ctx->blkid,
- "UUID", uuid);
- if (!ctx->journal_name)
- ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
- }
- journal_name = ctx->journal_name;
-
- if (!journal_name) {
- fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
- return EXT2_ET_LOAD_EXT_JOURNAL;
- }
-
- io_ptr = unix_io_manager;
- }
-
-#ifndef USE_INODE_IO
- if (ext_journal)
-#endif
- retval = io_ptr->open(journal_name, IO_FLAG_RW,
- &ctx->journal_io);
- if (retval)
- goto errout;
-
- io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
-
- if (ext_journal) {
- if (ctx->fs->blocksize == 1024)
- start = 1;
- bh = getblk(dev_journal, start, ctx->fs->blocksize);
- if (!bh) {
- retval = EXT2_ET_NO_MEMORY;
- goto errout;
- }
- ll_rw_block(READ, 1, &bh);
- if ((retval = bh->b_err) != 0)
- goto errout;
- memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
- sizeof(jsuper));
- brelse(bh);
-#if BB_BIG_ENDIAN
- if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
- ext2fs_swap_super(&jsuper);
-#endif
- if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
- !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
- fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
- retval = EXT2_ET_LOAD_EXT_JOURNAL;
- goto errout;
- }
- /* Make sure the journal UUID is correct */
- if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
- sizeof(jsuper.s_uuid))) {
- fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
- retval = EXT2_ET_LOAD_EXT_JOURNAL;
- goto errout;
- }
-
- journal->j_maxlen = jsuper.s_blocks_count;
- start++;
- }
-
- if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
- retval = EXT2_ET_NO_MEMORY;
- goto errout;
- }
-
- journal->j_sb_buffer = bh;
- journal->j_superblock = (journal_superblock_t *)bh->b_data;
-
-#ifdef USE_INODE_IO
- ext2fs_free_mem(&j_inode);
-#endif
-
- *ret_journal = journal;
- return 0;
-
-errout:
- ext2fs_free_mem(&dev_fs);
- ext2fs_free_mem(&j_inode);
- ext2fs_free_mem(&journal);
- return retval;
-}
-
-static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
- struct problem_context *pctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- int recover = ctx->fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_RECOVER;
- int has_journal = ctx->fs->super->s_feature_compat &
- EXT3_FEATURE_COMPAT_HAS_JOURNAL;
-
- if (has_journal || sb->s_journal_inum) {
- /* The journal inode is bogus, remove and force full fsck */
- pctx->ino = sb->s_journal_inum;
- if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
- if (has_journal && sb->s_journal_inum)
- printf("*** ext3 journal has been deleted - "
- "filesystem is now ext2 only ***\n\n");
- sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- sb->s_journal_inum = 0;
- ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
- e2fsck_clear_recover(ctx, 1);
- return 0;
- }
- return EXT2_ET_BAD_INODE_NUM;
- } else if (recover) {
- if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
- e2fsck_clear_recover(ctx, 1);
- return 0;
- }
- return EXT2_ET_UNSUPP_FEATURE;
- }
- return 0;
-}
-
-#define V1_SB_SIZE 0x0024
-static void clear_v2_journal_fields(journal_t *journal)
-{
- e2fsck_t ctx = journal->j_dev->k_ctx;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
- return;
-
- memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
- ctx->fs->blocksize-V1_SB_SIZE);
- mark_buffer_dirty(journal->j_sb_buffer);
-}
-
-
-static errcode_t e2fsck_journal_load(journal_t *journal)
-{
- e2fsck_t ctx = journal->j_dev->k_ctx;
- journal_superblock_t *jsb;
- struct buffer_head *jbh = journal->j_sb_buffer;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- ll_rw_block(READ, 1, &jbh);
- if (jbh->b_err) {
- bb_error_msg(_("reading journal superblock"));
- return jbh->b_err;
- }
-
- jsb = journal->j_superblock;
- /* If we don't even have JFS_MAGIC, we probably have a wrong inode */
- if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
- return e2fsck_journal_fix_bad_inode(ctx, &pctx);
-
- switch (ntohl(jsb->s_header.h_blocktype)) {
- case JFS_SUPERBLOCK_V1:
- journal->j_format_version = 1;
- if (jsb->s_feature_compat ||
- jsb->s_feature_incompat ||
- jsb->s_feature_ro_compat ||
- jsb->s_nr_users)
- clear_v2_journal_fields(journal);
- break;
-
- case JFS_SUPERBLOCK_V2:
- journal->j_format_version = 2;
- if (ntohl(jsb->s_nr_users) > 1 &&
- uuid_is_null(ctx->fs->super->s_journal_uuid))
- clear_v2_journal_fields(journal);
- if (ntohl(jsb->s_nr_users) > 1) {
- fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
- return EXT2_ET_JOURNAL_UNSUPP_VERSION;
- }
- break;
-
- /*
- * These should never appear in a journal super block, so if
- * they do, the journal is badly corrupted.
- */
- case JFS_DESCRIPTOR_BLOCK:
- case JFS_COMMIT_BLOCK:
- case JFS_REVOKE_BLOCK:
- return EXT2_ET_CORRUPT_SUPERBLOCK;
-
- /* If we don't understand the superblock major type, but there
- * is a magic number, then it is likely to be a new format we
- * just don't understand, so leave it alone. */
- default:
- return EXT2_ET_JOURNAL_UNSUPP_VERSION;
- }
-
- if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
- return EXT2_ET_UNSUPP_FEATURE;
-
- if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
- return EXT2_ET_RO_UNSUPP_FEATURE;
-
- /* We have now checked whether we know enough about the journal
- * format to be able to proceed safely, so any other checks that
- * fail we should attempt to recover from. */
- if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
- bb_error_msg(_("%s: no valid journal superblock found"),
- ctx->device_name);
- return EXT2_ET_CORRUPT_SUPERBLOCK;
- }
-
- if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
- journal->j_maxlen = ntohl(jsb->s_maxlen);
- else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
- bb_error_msg(_("%s: journal too short"),
- ctx->device_name);
- return EXT2_ET_CORRUPT_SUPERBLOCK;
- }
-
- journal->j_tail_sequence = ntohl(jsb->s_sequence);
- journal->j_transaction_sequence = journal->j_tail_sequence;
- journal->j_tail = ntohl(jsb->s_start);
- journal->j_first = ntohl(jsb->s_first);
- journal->j_last = ntohl(jsb->s_maxlen);
-
- return 0;
-}
-
-static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
- journal_t *journal)
-{
- char *p;
- union {
- uuid_t uuid;
- __u32 val[4];
- } u;
- __u32 new_seq = 0;
- int i;
-
- /* Leave a valid existing V1 superblock signature alone.
- * Anything unrecognizable we overwrite with a new V2
- * signature. */
-
- if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
- jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
- jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
- jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
- }
-
- /* Zero out everything else beyond the superblock header */
-
- p = ((char *) jsb) + sizeof(journal_header_t);
- memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
-
- jsb->s_blocksize = htonl(ctx->fs->blocksize);
- jsb->s_maxlen = htonl(journal->j_maxlen);
- jsb->s_first = htonl(1);
-
- /* Initialize the journal sequence number so that there is "no"
- * chance we will find old "valid" transactions in the journal.
- * This avoids the need to zero the whole journal (slow to do,
- * and risky when we are just recovering the filesystem).
- */
- uuid_generate(u.uuid);
- for (i = 0; i < 4; i ++)
- new_seq ^= u.val[i];
- jsb->s_sequence = htonl(new_seq);
-
- mark_buffer_dirty(journal->j_sb_buffer);
- ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
-}
-
-static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
- journal_t *journal,
- struct problem_context *pctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- int recover = ctx->fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_RECOVER;
-
- if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
- if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
- e2fsck_journal_reset_super(ctx, journal->j_superblock,
- journal);
- journal->j_transaction_sequence = 1;
- e2fsck_clear_recover(ctx, recover);
- return 0;
- }
- return EXT2_ET_CORRUPT_SUPERBLOCK;
- } else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
- return EXT2_ET_CORRUPT_SUPERBLOCK;
-
- return 0;
-}
-
-static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
- int reset, int drop)
-{
- journal_superblock_t *jsb;
-
- if (drop)
- mark_buffer_clean(journal->j_sb_buffer);
- else if (!(ctx->options & E2F_OPT_READONLY)) {
- jsb = journal->j_superblock;
- jsb->s_sequence = htonl(journal->j_transaction_sequence);
- if (reset)
- jsb->s_start = 0; /* this marks the journal as empty */
- mark_buffer_dirty(journal->j_sb_buffer);
- }
- brelse(journal->j_sb_buffer);
-
- if (ctx->journal_io) {
- if (ctx->fs && ctx->fs->io != ctx->journal_io)
- io_channel_close(ctx->journal_io);
- ctx->journal_io = 0;
- }
-
-#ifndef USE_INODE_IO
- ext2fs_free_mem(&journal->j_inode);
-#endif
- ext2fs_free_mem(&journal->j_fs_dev);
- ext2fs_free_mem(&journal);
-}
-
-/*
- * This function makes sure that the superblock fields regarding the
- * journal are consistent.
- */
-static int e2fsck_check_ext3_journal(e2fsck_t ctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- journal_t *journal;
- int recover = ctx->fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_RECOVER;
- struct problem_context pctx;
- problem_t problem;
- int reset = 0, force_fsck = 0;
- int retval;
-
- /* If we don't have any journal features, don't do anything more */
- if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
- !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
- uuid_is_null(sb->s_journal_uuid))
- return 0;
-
- clear_problem_context(&pctx);
- pctx.num = sb->s_journal_inum;
-
- retval = e2fsck_get_journal(ctx, &journal);
- if (retval) {
- if ((retval == EXT2_ET_BAD_INODE_NUM) ||
- (retval == EXT2_ET_BAD_BLOCK_NUM) ||
- (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
- (retval == EXT2_ET_NO_JOURNAL))
- return e2fsck_journal_fix_bad_inode(ctx, &pctx);
- return retval;
- }
-
- retval = e2fsck_journal_load(journal);
- if (retval) {
- if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
- ((retval == EXT2_ET_UNSUPP_FEATURE) &&
- (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
- &pctx))) ||
- ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
- (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
- &pctx))) ||
- ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
- (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
- retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
- &pctx);
- e2fsck_journal_release(ctx, journal, 0, 1);
- return retval;
- }
-
- /*
- * We want to make the flags consistent here. We will not leave with
- * needs_recovery set but has_journal clear. We can't get in a loop
- * with -y, -n, or -p, only if a user isn't making up their mind.
- */
-no_has_journal:
- if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
- recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
- pctx.str = "inode";
- if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
- if (recover &&
- !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
- goto no_has_journal;
- /*
- * Need a full fsck if we are releasing a
- * journal stored on a reserved inode.
- */
- force_fsck = recover ||
- (sb->s_journal_inum < EXT2_FIRST_INODE(sb));
- /* Clear all of the journal fields */
- sb->s_journal_inum = 0;
- sb->s_journal_dev = 0;
- memset(sb->s_journal_uuid, 0,
- sizeof(sb->s_journal_uuid));
- e2fsck_clear_recover(ctx, force_fsck);
- } else if (!(ctx->options & E2F_OPT_READONLY)) {
- sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- ext2fs_mark_super_dirty(ctx->fs);
- }
- }
-
- if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
- !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
- journal->j_superblock->s_start != 0) {
- /* Print status information */
- fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
- if (ctx->superblock)
- problem = PR_0_JOURNAL_RUN_DEFAULT;
- else
- problem = PR_0_JOURNAL_RUN;
- if (fix_problem(ctx, problem, &pctx)) {
- ctx->options |= E2F_OPT_FORCE;
- sb->s_feature_incompat |=
- EXT3_FEATURE_INCOMPAT_RECOVER;
- ext2fs_mark_super_dirty(ctx->fs);
- } else if (fix_problem(ctx,
- PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
- reset = 1;
- sb->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(ctx->fs);
- }
- /*
- * If the user answers no to the above question, we
- * ignore the fact that journal apparently has data;
- * accidentally replaying over valid data would be far
- * worse than skipping a questionable recovery.
- *
- * XXX should we abort with a fatal error here? What
- * will the ext3 kernel code do if a filesystem with
- * !NEEDS_RECOVERY but with a non-zero
- * journal->j_superblock->s_start is mounted?
- */
- }
-
- e2fsck_journal_release(ctx, journal, reset, 0);
- return retval;
-}
-
-static errcode_t recover_ext3_journal(e2fsck_t ctx)
-{
- journal_t *journal;
- int retval;
-
- journal_init_revoke_caches();
- retval = e2fsck_get_journal(ctx, &journal);
- if (retval)
- return retval;
-
- retval = e2fsck_journal_load(journal);
- if (retval)
- goto errout;
-
- retval = journal_init_revoke(journal, 1024);
- if (retval)
- goto errout;
-
- retval = -journal_recover(journal);
- if (retval)
- goto errout;
-
- if (journal->j_superblock->s_errno) {
- ctx->fs->super->s_state |= EXT2_ERROR_FS;
- ext2fs_mark_super_dirty(ctx->fs);
- journal->j_superblock->s_errno = 0;
- mark_buffer_dirty(journal->j_sb_buffer);
- }
-
-errout:
- journal_destroy_revoke(journal);
- journal_destroy_revoke_caches();
- e2fsck_journal_release(ctx, journal, 1, 0);
- return retval;
-}
-
-static int e2fsck_run_ext3_journal(e2fsck_t ctx)
-{
- io_manager io_ptr = ctx->fs->io->manager;
- int blocksize = ctx->fs->blocksize;
- errcode_t retval, recover_retval;
-
- printf(_("%s: recovering journal\n"), ctx->device_name);
- if (ctx->options & E2F_OPT_READONLY) {
- printf(_("%s: won't do journal recovery while read-only\n"),
- ctx->device_name);
- return EXT2_ET_FILE_RO;
- }
-
- if (ctx->fs->flags & EXT2_FLAG_DIRTY)
- ext2fs_flush(ctx->fs); /* Force out any modifications */
-
- recover_retval = recover_ext3_journal(ctx);
-
- /*
- * Reload the filesystem context to get up-to-date data from disk
- * because journal recovery will change the filesystem under us.
- */
- ext2fs_close(ctx->fs);
- retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
- ctx->superblock, blocksize, io_ptr,
- &ctx->fs);
-
- if (retval) {
- bb_error_msg(_("while trying to re-open %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
- ctx->fs->priv_data = ctx;
-
- /* Set the superblock flags */
- e2fsck_clear_recover(ctx, recover_retval);
- return recover_retval;
-}
-
-/*
- * This function will move the journal inode from a visible file in
- * the filesystem directory hierarchy to the reserved inode if necessary.
- */
-static const char *const journal_names[] = {
- ".journal", "journal", ".journal.dat", "journal.dat", 0 };
-
-static void e2fsck_move_ext3_journal(e2fsck_t ctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- struct problem_context pctx;
- struct ext2_inode inode;
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino;
- errcode_t retval;
- const char *const * cpp;
- int group, mount_flags;
-
- clear_problem_context(&pctx);
-
- /*
- * If the filesystem is opened read-only, or there is no
- * journal, then do nothing.
- */
- if ((ctx->options & E2F_OPT_READONLY) ||
- (sb->s_journal_inum == 0) ||
- !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
- return;
-
- /*
- * Read in the journal inode
- */
- if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
- return;
-
- /*
- * If it's necessary to backup the journal inode, do so.
- */
- if ((sb->s_jnl_backup_type == 0) ||
- ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
- memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
- if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
- memcpy(sb->s_jnl_blocks, inode.i_block,
- EXT2_N_BLOCKS*4);
- sb->s_jnl_blocks[16] = inode.i_size;
- sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
- ext2fs_mark_super_dirty(fs);
- fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
- }
- }
-
- /*
- * If the journal is already the hidden inode, then do nothing
- */
- if (sb->s_journal_inum == EXT2_JOURNAL_INO)
- return;
-
- /*
- * The journal inode had better have only one link and not be readable.
- */
- if (inode.i_links_count != 1)
- return;
-
- /*
- * If the filesystem is mounted, or we can't tell whether
- * or not it's mounted, do nothing.
- */
- retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
- if (retval || (mount_flags & EXT2_MF_MOUNTED))
- return;
-
- /*
- * If we can't find the name of the journal inode, then do
- * nothing.
- */
- for (cpp = journal_names; *cpp; cpp++) {
- retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
- strlen(*cpp), 0, &ino);
- if ((retval == 0) && (ino == sb->s_journal_inum))
- break;
- }
- if (*cpp == 0)
- return;
-
- /* We need the inode bitmap to be loaded */
- retval = ext2fs_read_bitmaps(fs);
- if (retval)
- return;
-
- pctx.str = *cpp;
- if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
- return;
-
- /*
- * OK, we've done all the checks, let's actually move the
- * journal inode. Errors at this point mean we need to force
- * an ext2 filesystem check.
- */
- if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
- goto err_out;
- if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
- goto err_out;
- sb->s_journal_inum = EXT2_JOURNAL_INO;
- ext2fs_mark_super_dirty(fs);
- fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
- inode.i_links_count = 0;
- inode.i_dtime = time(NULL);
- if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
- goto err_out;
-
- group = ext2fs_group_of_ino(fs, ino);
- ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
- ext2fs_mark_ib_dirty(fs);
- fs->group_desc[group].bg_free_inodes_count++;
- fs->super->s_free_inodes_count++;
- return;
-
-err_out:
- pctx.errcode = retval;
- fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
- fs->super->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(fs);
-}
-
-/*
- * message.c --- print e2fsck messages (with compression)
- *
- * print_e2fsck_message() prints a message to the user, using
- * compression techniques and expansions of abbreviations.
- *
- * The following % expansions are supported:
- *
- * %b <blk> block number
- * %B <blkcount> integer
- * %c <blk2> block number
- * %Di <dirent>->ino inode number
- * %Dn <dirent>->name string
- * %Dr <dirent>->rec_len
- * %Dl <dirent>->name_len
- * %Dt <dirent>->filetype
- * %d <dir> inode number
- * %g <group> integer
- * %i <ino> inode number
- * %Is <inode> -> i_size
- * %IS <inode> -> i_extra_isize
- * %Ib <inode> -> i_blocks
- * %Il <inode> -> i_links_count
- * %Im <inode> -> i_mode
- * %IM <inode> -> i_mtime
- * %IF <inode> -> i_faddr
- * %If <inode> -> i_file_acl
- * %Id <inode> -> i_dir_acl
- * %Iu <inode> -> i_uid
- * %Ig <inode> -> i_gid
- * %j <ino2> inode number
- * %m <com_err error message>
- * %N <num>
- * %p ext2fs_get_pathname of directory <ino>
- * %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
- * the containing directory. (If dirent is NULL
- * then return the pathname of directory <ino2>)
- * %q ext2fs_get_pathname of directory <dir>
- * %Q ext2fs_get_pathname of directory <ino> with <dir> as
- * the containing directory.
- * %s <str> miscellaneous string
- * %S backup superblock
- * %X <num> hexadecimal format
- *
- * The following '@' expansions are supported:
- *
- * @a extended attribute
- * @A error allocating
- * @b block
- * @B bitmap
- * @c compress
- * @C conflicts with some other fs block
- * @D deleted
- * @d directory
- * @e entry
- * @E Entry '%Dn' in %p (%i)
- * @f filesystem
- * @F for @i %i (%Q) is
- * @g group
- * @h HTREE directory inode
- * @i inode
- * @I illegal
- * @j journal
- * @l lost+found
- * @L is a link
- * @m multiply-claimed
- * @n invalid
- * @o orphaned
- * @p problem in
- * @r root inode
- * @s should be
- * @S superblock
- * @u unattached
- * @v device
- * @z zero-length
- */
-
-
-/*
- * This structure defines the abbreviations used by the text strings
- * below. The first character in the string is the index letter. An
- * abbreviation of the form '@<i>' is expanded by looking up the index
- * letter <i> in the table below.
- */
-static const char *const abbrevs[] = {
- N_("aextended attribute"),
- N_("Aerror allocating"),
- N_("bblock"),
- N_("Bbitmap"),
- N_("ccompress"),
- N_("Cconflicts with some other fs @b"),
- N_("iinode"),
- N_("Iillegal"),
- N_("jjournal"),
- N_("Ddeleted"),
- N_("ddirectory"),
- N_("eentry"),
- N_("E@e '%Dn' in %p (%i)"),
- N_("ffilesystem"),
- N_("Ffor @i %i (%Q) is"),
- N_("ggroup"),
- N_("hHTREE @d @i"),
- N_("llost+found"),
- N_("Lis a link"),
- N_("mmultiply-claimed"),
- N_("ninvalid"),
- N_("oorphaned"),
- N_("pproblem in"),
- N_("rroot @i"),
- N_("sshould be"),
- N_("Ssuper@b"),
- N_("uunattached"),
- N_("vdevice"),
- N_("zzero-length"),
- "@@",
- 0
- };
-
-/*
- * Give more user friendly names to the "special" inodes.
- */
-#define num_special_inodes 11
-static const char *const special_inode_name[] =
-{
- N_("<The NULL inode>"), /* 0 */
- N_("<The bad blocks inode>"), /* 1 */
- "/", /* 2 */
- N_("<The ACL index inode>"), /* 3 */
- N_("<The ACL data inode>"), /* 4 */
- N_("<The boot loader inode>"), /* 5 */
- N_("<The undelete directory inode>"), /* 6 */
- N_("<The group descriptor inode>"), /* 7 */
- N_("<The journal inode>"), /* 8 */
- N_("<Reserved inode 9>"), /* 9 */
- N_("<Reserved inode 10>"), /* 10 */
-};
-
-/*
- * This function does "safe" printing. It will convert non-printable
- * ASCII characters using '^' and M- notation.
- */
-static void safe_print(const char *cp, int len)
-{
- unsigned char ch;
-
- if (len < 0)
- len = strlen(cp);
-
- while (len--) {
- ch = *cp++;
- if (ch > 128) {
- fputs("M-", stdout);
- ch -= 128;
- }
- if ((ch < 32) || (ch == 0x7f)) {
- bb_putchar('^');
- ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
- }
- bb_putchar(ch);
- }
-}
-
-
-/*
- * This function prints a pathname, using the ext2fs_get_pathname
- * function
- */
-static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
-{
- errcode_t retval;
- char *path;
-
- if (!dir && (ino < num_special_inodes)) {
- fputs(_(special_inode_name[ino]), stdout);
- return;
- }
-
- retval = ext2fs_get_pathname(fs, dir, ino, &path);
- if (retval)
- fputs("???", stdout);
- else {
- safe_print(path, -1);
- ext2fs_free_mem(&path);
- }
-}
-
-static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
- struct problem_context *pctx, int first);
-/*
- * This function handles the '@' expansion. We allow recursive
- * expansion; an @ expression can contain further '@' and '%'
- * expressions.
- */
-static void expand_at_expression(e2fsck_t ctx, char ch,
- struct problem_context *pctx,
- int *first)
-{
- const char *const *cpp;
- const char *str;
-
- /* Search for the abbreviation */
- for (cpp = abbrevs; *cpp; cpp++) {
- if (ch == *cpp[0])
- break;
- }
- if (*cpp) {
- str = _(*cpp) + 1;
- if (*first && islower(*str)) {
- *first = 0;
- bb_putchar(toupper(*str++));
- }
- print_e2fsck_message(ctx, str, pctx, *first);
- } else
- printf("@%c", ch);
-}
-
-/*
- * This function expands '%IX' expressions
- */
-static void expand_inode_expression(char ch,
- struct problem_context *ctx)
-{
- struct ext2_inode *inode;
- struct ext2_inode_large *large_inode;
- char * time_str;
- time_t t;
- int do_gmt = -1;
-
- if (!ctx || !ctx->inode)
- goto no_inode;
-
- inode = ctx->inode;
- large_inode = (struct ext2_inode_large *) inode;
-
- switch (ch) {
- case 's':
- if (LINUX_S_ISDIR(inode->i_mode))
- printf("%u", inode->i_size);
- else {
- printf("%"PRIu64, (inode->i_size |
- ((uint64_t) inode->i_size_high << 32)));
- }
- break;
- case 'S':
- printf("%u", large_inode->i_extra_isize);
- break;
- case 'b':
- printf("%u", inode->i_blocks);
- break;
- case 'l':
- printf("%d", inode->i_links_count);
- break;
- case 'm':
- printf("0%o", inode->i_mode);
- break;
- case 'M':
- /* The diet libc doesn't respect the TZ environemnt variable */
- if (do_gmt == -1) {
- time_str = getenv("TZ");
- if (!time_str)
- time_str = "";
- do_gmt = !strcmp(time_str, "GMT");
- }
- t = inode->i_mtime;
- time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
- printf("%.24s", time_str);
- break;
- case 'F':
- printf("%u", inode->i_faddr);
- break;
- case 'f':
- printf("%u", inode->i_file_acl);
- break;
- case 'd':
- printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
- inode->i_dir_acl : 0));
- break;
- case 'u':
- printf("%d", (inode->i_uid |
- (inode->osd2.linux2.l_i_uid_high << 16)));
- break;
- case 'g':
- printf("%d", (inode->i_gid |
- (inode->osd2.linux2.l_i_gid_high << 16)));
- break;
- default:
- no_inode:
- printf("%%I%c", ch);
- break;
- }
-}
-
-/*
- * This function expands '%dX' expressions
- */
-static void expand_dirent_expression(char ch,
- struct problem_context *ctx)
-{
- struct ext2_dir_entry *dirent;
- int len;
-
- if (!ctx || !ctx->dirent)
- goto no_dirent;
-
- dirent = ctx->dirent;
-
- switch (ch) {
- case 'i':
- printf("%u", dirent->inode);
- break;
- case 'n':
- len = dirent->name_len & 0xFF;
- if (len > EXT2_NAME_LEN)
- len = EXT2_NAME_LEN;
- if (len > dirent->rec_len)
- len = dirent->rec_len;
- safe_print(dirent->name, len);
- break;
- case 'r':
- printf("%u", dirent->rec_len);
- break;
- case 'l':
- printf("%u", dirent->name_len & 0xFF);
- break;
- case 't':
- printf("%u", dirent->name_len >> 8);
- break;
- default:
- no_dirent:
- printf("%%D%c", ch);
- break;
- }
-}
-
-static void expand_percent_expression(ext2_filsys fs, char ch,
- struct problem_context *ctx)
-{
- if (!ctx)
- goto no_context;
-
- switch (ch) {
- case '%':
- bb_putchar('%');
- break;
- case 'b':
- printf("%u", ctx->blk);
- break;
- case 'B':
- printf("%"PRIi64, ctx->blkcount);
- break;
- case 'c':
- printf("%u", ctx->blk2);
- break;
- case 'd':
- printf("%u", ctx->dir);
- break;
- case 'g':
- printf("%d", ctx->group);
- break;
- case 'i':
- printf("%u", ctx->ino);
- break;
- case 'j':
- printf("%u", ctx->ino2);
- break;
- case 'm':
- fputs(error_message(ctx->errcode), stdout);
- break;
- case 'N':
- printf("%"PRIi64, ctx->num);
- break;
- case 'p':
- print_pathname(fs, ctx->ino, 0);
- break;
- case 'P':
- print_pathname(fs, ctx->ino2,
- ctx->dirent ? ctx->dirent->inode : 0);
- break;
- case 'q':
- print_pathname(fs, ctx->dir, 0);
- break;
- case 'Q':
- print_pathname(fs, ctx->dir, ctx->ino);
- break;
- case 'S':
- printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
- break;
- case 's':
- fputs((ctx->str ? ctx->str : "NULL"), stdout);
- break;
- case 'X':
- printf("0x%"PRIi64, ctx->num);
- break;
- default:
- no_context:
- printf("%%%c", ch);
- break;
- }
-}
-
-
-static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
- struct problem_context *pctx, int first)
-{
- ext2_filsys fs = ctx->fs;
- const char * cp;
- int i;
-
- e2fsck_clear_progbar(ctx);
- for (cp = msg; *cp; cp++) {
- if (cp[0] == '@') {
- cp++;
- expand_at_expression(ctx, *cp, pctx, &first);
- } else if (cp[0] == '%' && cp[1] == 'I') {
- cp += 2;
- expand_inode_expression(*cp, pctx);
- } else if (cp[0] == '%' && cp[1] == 'D') {
- cp += 2;
- expand_dirent_expression(*cp, pctx);
- } else if ((cp[0] == '%')) {
- cp++;
- expand_percent_expression(fs, *cp, pctx);
- } else {
- for (i=0; cp[i]; i++)
- if ((cp[i] == '@') || cp[i] == '%')
- break;
- printf("%.*s", i, cp);
- cp += i-1;
- }
- first = 0;
- }
-}
-
-
-/*
- * region.c --- code which manages allocations within a region.
- */
-
-struct region_el {
- region_addr_t start;
- region_addr_t end;
- struct region_el *next;
-};
-
-struct region_struct {
- region_addr_t min;
- region_addr_t max;
- struct region_el *allocated;
-};
-
-static region_t region_create(region_addr_t min, region_addr_t max)
-{
- region_t region;
-
- region = xzalloc(sizeof(struct region_struct));
- region->min = min;
- region->max = max;
- return region;
-}
-
-static void region_free(region_t region)
-{
- struct region_el *r, *next;
-
- for (r = region->allocated; r; r = next) {
- next = r->next;
- free(r);
- }
- memset(region, 0, sizeof(struct region_struct));
- free(region);
-}
-
-static int region_allocate(region_t region, region_addr_t start, int n)
-{
- struct region_el *r, *new_region, *prev, *next;
- region_addr_t end;
-
- end = start+n;
- if ((start < region->min) || (end > region->max))
- return -1;
- if (n == 0)
- return 1;
-
- /*
- * Search through the linked list. If we find that it
- * conflicts witih something that's already allocated, return
- * 1; if we can find an existing region which we can grow, do
- * so. Otherwise, stop when we find the appropriate place
- * insert a new region element into the linked list.
- */
- for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
- if (((start >= r->start) && (start < r->end)) ||
- ((end > r->start) && (end <= r->end)) ||
- ((start <= r->start) && (end >= r->end)))
- return 1;
- if (end == r->start) {
- r->start = start;
- return 0;
- }
- if (start == r->end) {
- if ((next = r->next)) {
- if (end > next->start)
- return 1;
- if (end == next->start) {
- r->end = next->end;
- r->next = next->next;
- free(next);
- return 0;
- }
- }
- r->end = end;
- return 0;
- }
- if (start < r->start)
- break;
- }
- /*
- * Insert a new region element structure into the linked list
- */
- new_region = xmalloc(sizeof(struct region_el));
- new_region->start = start;
- new_region->end = start + n;
- new_region->next = r;
- if (prev)
- prev->next = new_region;
- else
- region->allocated = new_region;
- return 0;
-}
-
-/*
- * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
- *
- * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
- * and applies the following tests to each inode:
- *
- * - The mode field of the inode must be legal.
- * - The size and block count fields of the inode are correct.
- * - A data block must not be used by another inode
- *
- * Pass 1 also gathers the collects the following information:
- *
- * - A bitmap of which inodes are in use. (inode_used_map)
- * - A bitmap of which inodes are directories. (inode_dir_map)
- * - A bitmap of which inodes are regular files. (inode_reg_map)
- * - A bitmap of which inodes have bad fields. (inode_bad_map)
- * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
- * - A bitmap of which blocks are in use. (block_found_map)
- * - A bitmap of which blocks are in use by two inodes (block_dup_map)
- * - The data blocks of the directory inodes. (dir_map)
- *
- * Pass 1 is designed to stash away enough information so that the
- * other passes should not need to read in the inode information
- * during the normal course of a filesystem check. (Althogh if an
- * inconsistency is detected, other passes may need to read in an
- * inode to fix it.)
- *
- * Note that pass 1B will be invoked if there are any duplicate blocks
- * found.
- */
-
-
-static int process_block(ext2_filsys fs, blk_t *blocknr,
- e2_blkcnt_t blockcnt, blk_t ref_blk,
- int ref_offset, void *priv_data);
-static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt, blk_t ref_blk,
- int ref_offset, void *priv_data);
-static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf);
-static void mark_table_blocks(e2fsck_t ctx);
-static void alloc_imagic_map(e2fsck_t ctx);
-static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
-static void handle_fs_bad_blocks(e2fsck_t ctx);
-static void process_inodes(e2fsck_t ctx, char *block_buf);
-static int process_inode_cmp(const void *a, const void *b);
-static errcode_t scan_callback(ext2_filsys fs,
- dgrp_t group, void * priv_data);
-static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
- char *block_buf, int adjust_sign);
-/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
-
-static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, int bufsize,
- const char *proc);
-
-struct process_block_struct_1 {
- ext2_ino_t ino;
- unsigned is_dir:1, is_reg:1, clear:1, suppress:1,
- fragmented:1, compressed:1, bbcheck:1;
- blk_t num_blocks;
- blk_t max_blocks;
- e2_blkcnt_t last_block;
- int num_illegal_blocks;
- blk_t previous_block;
- struct ext2_inode *inode;
- struct problem_context *pctx;
- ext2fs_block_bitmap fs_meta_blocks;
- e2fsck_t ctx;
-};
-
-struct process_inode_block {
- ext2_ino_t ino;
- struct ext2_inode inode;
-};
-
-struct scan_callback_struct {
- e2fsck_t ctx;
- char *block_buf;
-};
-
-/*
- * For the inodes to process list.
- */
-static struct process_inode_block *inodes_to_process;
-static int process_inode_count;
-
-static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
- EXT2_MIN_BLOCK_LOG_SIZE + 1];
-
-/*
- * Free all memory allocated by pass1 in preparation for restarting
- * things.
- */
-static void unwind_pass1(void)
-{
- ext2fs_free_mem(&inodes_to_process);
-}
-
-/*
- * Check to make sure a device inode is real. Returns 1 if the device
- * checks out, 0 if not.
- *
- * Note: this routine is now also used to check FIFO's and Sockets,
- * since they have the same requirement; the i_block fields should be
- * zero.
- */
-static int
-e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
-{
- int i;
-
- /*
- * If i_blocks is non-zero, or the index flag is set, then
- * this is a bogus device/fifo/socket
- */
- if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
- (inode->i_flags & EXT2_INDEX_FL))
- return 0;
-
- /*
- * We should be able to do the test below all the time, but
- * because the kernel doesn't forcibly clear the device
- * inode's additional i_block fields, there are some rare
- * occasions when a legitimate device inode will have non-zero
- * additional i_block fields. So for now, we only complain
- * when the immutable flag is set, which should never happen
- * for devices. (And that's when the problem is caused, since
- * you can't set or clear immutable flags for devices.) Once
- * the kernel has been fixed we can change this...
- */
- if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
- for (i=4; i < EXT2_N_BLOCKS; i++)
- if (inode->i_block[i])
- return 0;
- }
- return 1;
-}
-
-/*
- * Check to make sure a symlink inode is real. Returns 1 if the symlink
- * checks out, 0 if not.
- */
-static int
-e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
-{
- unsigned int len;
- int i;
- blk_t blocks;
-
- if ((inode->i_size_high || inode->i_size == 0) ||
- (inode->i_flags & EXT2_INDEX_FL))
- return 0;
-
- blocks = ext2fs_inode_data_blocks(fs, inode);
- if (blocks) {
- if ((inode->i_size >= fs->blocksize) ||
- (blocks != fs->blocksize >> 9) ||
- (inode->i_block[0] < fs->super->s_first_data_block) ||
- (inode->i_block[0] >= fs->super->s_blocks_count))
- return 0;
-
- for (i = 1; i < EXT2_N_BLOCKS; i++)
- if (inode->i_block[i])
- return 0;
-
- if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
- return 0;
-
- len = strnlen(buf, fs->blocksize);
- if (len == fs->blocksize)
- return 0;
- } else {
- if (inode->i_size >= sizeof(inode->i_block))
- return 0;
-
- len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
- if (len == sizeof(inode->i_block))
- return 0;
- }
- if (len != inode->i_size)
- return 0;
- return 1;
-}
-
-/*
- * If the immutable (or append-only) flag is set on the inode, offer
- * to clear it.
- */
-#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
-static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
-{
- if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
- return;
-
- if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
- return;
-
- pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
- e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
-}
-
-/*
- * If device, fifo or socket, check size is zero -- if not offer to
- * clear it
- */
-static void check_size(e2fsck_t ctx, struct problem_context *pctx)
-{
- struct ext2_inode *inode = pctx->inode;
-
- if ((inode->i_size == 0) && (inode->i_size_high == 0))
- return;
-
- if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
- return;
-
- inode->i_size = 0;
- inode->i_size_high = 0;
- e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
-}
-
-static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- struct ext2_inode_large *inode;
- struct ext2_ext_attr_entry *entry;
- char *start, *end;
- int storage_size, remain, offs;
- int problem = 0;
-
- inode = (struct ext2_inode_large *) pctx->inode;
- storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
- inode->i_extra_isize;
- start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
- inode->i_extra_isize + sizeof(__u32);
- end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
- entry = (struct ext2_ext_attr_entry *) start;
-
- /* scan all entry's headers first */
-
- /* take finish entry 0UL into account */
- remain = storage_size - sizeof(__u32);
- offs = end - start;
-
- while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
-
- /* header eats this space */
- remain -= sizeof(struct ext2_ext_attr_entry);
-
- /* is attribute name valid? */
- if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
- pctx->num = entry->e_name_len;
- problem = PR_1_ATTR_NAME_LEN;
- goto fix;
- }
-
- /* attribute len eats this space */
- remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
-
- /* check value size */
- if (entry->e_value_size == 0 || entry->e_value_size > remain) {
- pctx->num = entry->e_value_size;
- problem = PR_1_ATTR_VALUE_SIZE;
- goto fix;
- }
-
- /* check value placement */
- if (entry->e_value_offs +
- EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
- printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
- pctx->num = entry->e_value_offs;
- problem = PR_1_ATTR_VALUE_OFFSET;
- goto fix;
- }
-
- /* e_value_block must be 0 in inode's ea */
- if (entry->e_value_block != 0) {
- pctx->num = entry->e_value_block;
- problem = PR_1_ATTR_VALUE_BLOCK;
- goto fix;
- }
-
- /* e_hash must be 0 in inode's ea */
- if (entry->e_hash != 0) {
- pctx->num = entry->e_hash;
- problem = PR_1_ATTR_HASH;
- goto fix;
- }
-
- remain -= entry->e_value_size;
- offs -= EXT2_XATTR_SIZE(entry->e_value_size);
-
- entry = EXT2_EXT_ATTR_NEXT(entry);
- }
-fix:
- /*
- * it seems like a corruption. it's very unlikely we could repair
- * EA(s) in automatic fashion -bzzz
- */
- if (problem == 0 || !fix_problem(ctx, problem, pctx))
- return;
-
- /* simple remove all possible EA(s) */
- *((__u32 *)start) = 0UL;
- e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
- EXT2_INODE_SIZE(sb), "pass1");
-}
-
-static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- struct ext2_inode_large *inode;
- __u32 *eamagic;
- int min, max;
-
- inode = (struct ext2_inode_large *) pctx->inode;
- if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
- /* this isn't large inode. so, nothing to check */
- return;
- }
-
- /* i_extra_isize must cover i_extra_isize + i_pad1 at least */
- min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
- max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
- /*
- * For now we will allow i_extra_isize to be 0, but really
- * implementations should never allow i_extra_isize to be 0
- */
- if (inode->i_extra_isize &&
- (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
- if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
- return;
- inode->i_extra_isize = min;
- e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
- EXT2_INODE_SIZE(sb), "pass1");
- return;
- }
-
- eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
- inode->i_extra_isize);
- if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
- /* it seems inode has an extended attribute(s) in body */
- check_ea_in_inode(ctx, pctx);
- }
-}
-
-static void e2fsck_pass1(e2fsck_t ctx)
-{
- int i;
- __u64 max_sizes;
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino;
- struct ext2_inode *inode;
- ext2_inode_scan scan;
- char *block_buf;
- unsigned char frag, fsize;
- struct problem_context pctx;
- struct scan_callback_struct scan_struct;
- struct ext2_super_block *sb = ctx->fs->super;
- int imagic_fs;
- int busted_fs_time = 0;
- int inode_size;
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
-
- if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
- !(ctx->options & E2F_OPT_NO)) {
- if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
- ctx->dirs_to_hash = 0;
- }
-
- /* Pass 1 */
-
-#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
-
- for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
- max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
- max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
- max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
- max_sizes = (max_sizes * (1UL << i)) - 1;
- ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
- }
-#undef EXT2_BPP
-
- imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
-
- /*
- * Allocate bitmaps structures
- */
- pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
- &ctx->inode_used_map);
- if (pctx.errcode) {
- pctx.num = 1;
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
- _("directory inode map"), &ctx->inode_dir_map);
- if (pctx.errcode) {
- pctx.num = 2;
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
- _("regular file inode map"), &ctx->inode_reg_map);
- if (pctx.errcode) {
- pctx.num = 6;
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
- &ctx->block_found_map);
- if (pctx.errcode) {
- pctx.num = 1;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
- &ctx->inode_link_info);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- inode_size = EXT2_INODE_SIZE(fs->super);
- inode = (struct ext2_inode *)
- e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
-
- inodes_to_process = (struct process_inode_block *)
- e2fsck_allocate_memory(ctx,
- (ctx->process_inode_size *
- sizeof(struct process_inode_block)),
- "array of inodes to process");
- process_inode_count = 0;
-
- pctx.errcode = ext2fs_init_dblist(fs, 0);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- /*
- * If the last orphan field is set, clear it, since the pass1
- * processing will automatically find and clear the orphans.
- * In the future, we may want to try using the last_orphan
- * linked list ourselves, but for now, we clear it so that the
- * ext3 mount code won't get confused.
- */
- if (!(ctx->options & E2F_OPT_READONLY)) {
- if (fs->super->s_last_orphan) {
- fs->super->s_last_orphan = 0;
- ext2fs_mark_super_dirty(fs);
- }
- }
-
- mark_table_blocks(ctx);
- block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
- "block interate buffer");
- e2fsck_use_inode_shortcuts(ctx, 1);
- ehandler_operation(_("doing inode scan"));
- pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
- &scan);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
- ctx->stashed_inode = inode;
- scan_struct.ctx = ctx;
- scan_struct.block_buf = block_buf;
- ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
- if (ctx->progress)
- if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
- return;
- if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
- (fs->super->s_mtime < fs->super->s_inodes_count))
- busted_fs_time = 1;
-
- while (1) {
- pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
- inode, inode_size);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
- continue;
- }
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (!ino)
- break;
- pctx.ino = ino;
- pctx.inode = inode;
- ctx->stashed_ino = ino;
- if (inode->i_links_count) {
- pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
- ino, inode->i_links_count);
- if (pctx.errcode) {
- pctx.num = inode->i_links_count;
- fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
- if (ino == EXT2_BAD_INO) {
- struct process_block_struct_1 pb;
-
- pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
- &pb.fs_meta_blocks);
- if (pctx.errcode) {
- pctx.num = 4;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- pb.ino = EXT2_BAD_INO;
- pb.num_blocks = pb.last_block = 0;
- pb.num_illegal_blocks = 0;
- pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
- pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
- pb.inode = inode;
- pb.pctx = &pctx;
- pb.ctx = ctx;
- pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
- block_buf, process_bad_block, &pb);
- ext2fs_free_block_bitmap(pb.fs_meta_blocks);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (pb.bbcheck)
- if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
- clear_problem_context(&pctx);
- continue;
- } else if (ino == EXT2_ROOT_INO) {
- /*
- * Make sure the root inode is a directory; if
- * not, offer to clear it. It will be
- * regnerated in pass #3.
- */
- if (!LINUX_S_ISDIR(inode->i_mode)) {
- if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
- inode->i_dtime = time(NULL);
- inode->i_links_count = 0;
- ext2fs_icount_store(ctx->inode_link_info,
- ino, 0);
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- }
- /*
- * If dtime is set, offer to clear it. mke2fs
- * version 0.2b created filesystems with the
- * dtime field set for the root and lost+found
- * directories. We won't worry about
- * /lost+found, since that can be regenerated
- * easily. But we will fix the root directory
- * as a special case.
- */
- if (inode->i_dtime && inode->i_links_count) {
- if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
- inode->i_dtime = 0;
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- }
- } else if (ino == EXT2_JOURNAL_INO) {
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
- if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
- if (!LINUX_S_ISREG(inode->i_mode) &&
- fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
- &pctx)) {
- inode->i_mode = LINUX_S_IFREG;
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- check_blocks(ctx, &pctx, block_buf);
- continue;
- }
- if ((inode->i_links_count || inode->i_blocks ||
- inode->i_block[0]) &&
- fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
- &pctx)) {
- memset(inode, 0, inode_size);
- ext2fs_icount_store(ctx->inode_link_info,
- ino, 0);
- e2fsck_write_inode_full(ctx, ino, inode,
- inode_size, "pass1");
- }
- } else if (ino < EXT2_FIRST_INODE(fs->super)) {
- int problem = 0;
-
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
- if (ino == EXT2_BOOT_LOADER_INO) {
- if (LINUX_S_ISDIR(inode->i_mode))
- problem = PR_1_RESERVED_BAD_MODE;
- } else if (ino == EXT2_RESIZE_INO) {
- if (inode->i_mode &&
- !LINUX_S_ISREG(inode->i_mode))
- problem = PR_1_RESERVED_BAD_MODE;
- } else {
- if (inode->i_mode != 0)
- problem = PR_1_RESERVED_BAD_MODE;
- }
- if (problem) {
- if (fix_problem(ctx, problem, &pctx)) {
- inode->i_mode = 0;
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- }
- check_blocks(ctx, &pctx, block_buf);
- continue;
- }
- /*
- * Check for inodes who might have been part of the
- * orphaned list linked list. They should have gotten
- * dealt with by now, unless the list had somehow been
- * corrupted.
- *
- * FIXME: In the future, inodes which are still in use
- * (and which are therefore) pending truncation should
- * be handled specially. Right now we just clear the
- * dtime field, and the normal e2fsck handling of
- * inodes where i_size and the inode blocks are
- * inconsistent is to fix i_size, instead of releasing
- * the extra blocks. This won't catch the inodes that
- * was at the end of the orphan list, but it's better
- * than nothing. The right answer is that there
- * shouldn't be any bugs in the orphan list handling. :-)
- */
- if (inode->i_dtime && !busted_fs_time &&
- inode->i_dtime < ctx->fs->super->s_inodes_count) {
- if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
- inode->i_dtime = inode->i_links_count ?
- 0 : time(NULL);
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- }
-
- /*
- * This code assumes that deleted inodes have
- * i_links_count set to 0.
- */
- if (!inode->i_links_count) {
- if (!inode->i_dtime && inode->i_mode) {
- if (fix_problem(ctx,
- PR_1_ZERO_DTIME, &pctx)) {
- inode->i_dtime = time(NULL);
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- }
- }
- continue;
- }
- /*
- * n.b. 0.3c ext2fs code didn't clear i_links_count for
- * deleted files. Oops.
- *
- * Since all new ext2 implementations get this right,
- * we now assume that the case of non-zero
- * i_links_count and non-zero dtime means that we
- * should keep the file, not delete it.
- *
- */
- if (inode->i_dtime) {
- if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
- inode->i_dtime = 0;
- e2fsck_write_inode(ctx, ino, inode, "pass1");
- }
- }
-
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
- switch (fs->super->s_creator_os) {
- case EXT2_OS_LINUX:
- frag = inode->osd2.linux2.l_i_frag;
- fsize = inode->osd2.linux2.l_i_fsize;
- break;
- case EXT2_OS_HURD:
- frag = inode->osd2.hurd2.h_i_frag;
- fsize = inode->osd2.hurd2.h_i_fsize;
- break;
- case EXT2_OS_MASIX:
- frag = inode->osd2.masix2.m_i_frag;
- fsize = inode->osd2.masix2.m_i_fsize;
- break;
- default:
- frag = fsize = 0;
- }
-
- if (inode->i_faddr || frag || fsize ||
- (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
- mark_inode_bad(ctx, ino);
- if (inode->i_flags & EXT2_IMAGIC_FL) {
- if (imagic_fs) {
- if (!ctx->inode_imagic_map)
- alloc_imagic_map(ctx);
- ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
- ino);
- } else {
- if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
- inode->i_flags &= ~EXT2_IMAGIC_FL;
- e2fsck_write_inode(ctx, ino,
- inode, "pass1");
- }
- }
- }
-
- check_inode_extra_space(ctx, &pctx);
-
- if (LINUX_S_ISDIR(inode->i_mode)) {
- ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
- e2fsck_add_dir_info(ctx, ino, 0);
- ctx->fs_directory_count++;
- } else if (LINUX_S_ISREG (inode->i_mode)) {
- ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
- ctx->fs_regular_count++;
- } else if (LINUX_S_ISCHR (inode->i_mode) &&
- e2fsck_pass1_check_device_inode(fs, inode)) {
- check_immutable(ctx, &pctx);
- check_size(ctx, &pctx);
- ctx->fs_chardev_count++;
- } else if (LINUX_S_ISBLK (inode->i_mode) &&
- e2fsck_pass1_check_device_inode(fs, inode)) {
- check_immutable(ctx, &pctx);
- check_size(ctx, &pctx);
- ctx->fs_blockdev_count++;
- } else if (LINUX_S_ISLNK (inode->i_mode) &&
- e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
- check_immutable(ctx, &pctx);
- ctx->fs_symlinks_count++;
- if (ext2fs_inode_data_blocks(fs, inode) == 0) {
- ctx->fs_fast_symlinks_count++;
- check_blocks(ctx, &pctx, block_buf);
- continue;
- }
- }
- else if (LINUX_S_ISFIFO (inode->i_mode) &&
- e2fsck_pass1_check_device_inode(fs, inode)) {
- check_immutable(ctx, &pctx);
- check_size(ctx, &pctx);
- ctx->fs_fifo_count++;
- } else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
- e2fsck_pass1_check_device_inode(fs, inode)) {
- check_immutable(ctx, &pctx);
- check_size(ctx, &pctx);
- ctx->fs_sockets_count++;
- } else
- mark_inode_bad(ctx, ino);
- if (inode->i_block[EXT2_IND_BLOCK])
- ctx->fs_ind_count++;
- if (inode->i_block[EXT2_DIND_BLOCK])
- ctx->fs_dind_count++;
- if (inode->i_block[EXT2_TIND_BLOCK])
- ctx->fs_tind_count++;
- if (inode->i_block[EXT2_IND_BLOCK] ||
- inode->i_block[EXT2_DIND_BLOCK] ||
- inode->i_block[EXT2_TIND_BLOCK] ||
- inode->i_file_acl) {
- inodes_to_process[process_inode_count].ino = ino;
- inodes_to_process[process_inode_count].inode = *inode;
- process_inode_count++;
- } else
- check_blocks(ctx, &pctx, block_buf);
-
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
-
- if (process_inode_count >= ctx->process_inode_size) {
- process_inodes(ctx, block_buf);
-
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- }
- }
- process_inodes(ctx, block_buf);
- ext2fs_close_inode_scan(scan);
- ehandler_operation(0);
-
- /*
- * If any extended attribute blocks' reference counts need to
- * be adjusted, either up (ctx->refcount_extra), or down
- * (ctx->refcount), then fix them.
- */
- if (ctx->refcount) {
- adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
- ea_refcount_free(ctx->refcount);
- ctx->refcount = 0;
- }
- if (ctx->refcount_extra) {
- adjust_extattr_refcount(ctx, ctx->refcount_extra,
- block_buf, +1);
- ea_refcount_free(ctx->refcount_extra);
- ctx->refcount_extra = 0;
- }
-
- if (ctx->invalid_bitmaps)
- handle_fs_bad_blocks(ctx);
-
- /* We don't need the block_ea_map any more */
- ext2fs_free_block_bitmap(ctx->block_ea_map);
- ctx->block_ea_map = 0;
-
- if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
- ext2fs_block_bitmap save_bmap;
-
- save_bmap = fs->block_map;
- fs->block_map = ctx->block_found_map;
- clear_problem_context(&pctx);
- pctx.errcode = ext2fs_create_resize_inode(fs);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
- "recreate inode");
- inode->i_mtime = time(NULL);
- e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
- "recreate inode");
- fs->block_map = save_bmap;
- ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
- }
-
- if (ctx->flags & E2F_FLAG_RESTART) {
- /*
- * Only the master copy of the superblock and block
- * group descriptors are going to be written during a
- * restart, so set the superblock to be used to be the
- * master superblock.
- */
- ctx->use_superblock = 0;
- unwind_pass1();
- goto endit;
- }
-
- if (ctx->block_dup_map) {
- if (ctx->options & E2F_OPT_PREEN) {
- clear_problem_context(&pctx);
- fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
- }
- e2fsck_pass1_dupblocks(ctx, block_buf);
- }
- ext2fs_free_mem(&inodes_to_process);
-endit:
- e2fsck_use_inode_shortcuts(ctx, 0);
-
- ext2fs_free_mem(&block_buf);
- ext2fs_free_mem(&inode);
-}
-
-/*
- * When the inode_scan routines call this callback at the end of the
- * glock group, call process_inodes.
- */
-static errcode_t scan_callback(ext2_filsys fs,
- dgrp_t group, void * priv_data)
-{
- struct scan_callback_struct *scan_struct;
- e2fsck_t ctx;
-
- scan_struct = (struct scan_callback_struct *) priv_data;
- ctx = scan_struct->ctx;
-
- process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
-
- if (ctx->progress)
- if ((ctx->progress)(ctx, 1, group+1,
- ctx->fs->group_desc_count))
- return EXT2_ET_CANCEL_REQUESTED;
-
- return 0;
-}
-
-/*
- * Process the inodes in the "inodes to process" list.
- */
-static void process_inodes(e2fsck_t ctx, char *block_buf)
-{
- int i;
- struct ext2_inode *old_stashed_inode;
- ext2_ino_t old_stashed_ino;
- const char *old_operation;
- char buf[80];
- struct problem_context pctx;
-
- /* begin process_inodes */
- if (process_inode_count == 0)
- return;
- old_operation = ehandler_operation(0);
- old_stashed_inode = ctx->stashed_inode;
- old_stashed_ino = ctx->stashed_ino;
- qsort(inodes_to_process, process_inode_count,
- sizeof(struct process_inode_block), process_inode_cmp);
- clear_problem_context(&pctx);
- for (i=0; i < process_inode_count; i++) {
- pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
- pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
- sprintf(buf, _("reading indirect blocks of inode %u"),
- pctx.ino);
- ehandler_operation(buf);
- check_blocks(ctx, &pctx, block_buf);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- break;
- }
- ctx->stashed_inode = old_stashed_inode;
- ctx->stashed_ino = old_stashed_ino;
- process_inode_count = 0;
- /* end process inodes */
-
- ehandler_operation(old_operation);
-}
-
-static int process_inode_cmp(const void *a, const void *b)
-{
- const struct process_inode_block *ib_a =
- (const struct process_inode_block *) a;
- const struct process_inode_block *ib_b =
- (const struct process_inode_block *) b;
- int ret;
-
- ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
- ib_b->inode.i_block[EXT2_IND_BLOCK]);
- if (ret == 0)
- ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
- return ret;
-}
-
-/*
- * Mark an inode as being bad in some what
- */
-static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
-{
- struct problem_context pctx;
-
- if (!ctx->inode_bad_map) {
- clear_problem_context(&pctx);
-
- pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
- _("bad inode map"), &ctx->inode_bad_map);
- if (pctx.errcode) {
- pctx.num = 3;
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
- ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
-}
-
-
-/*
- * This procedure will allocate the inode imagic table
- */
-static void alloc_imagic_map(e2fsck_t ctx)
-{
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
- pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
- _("imagic inode map"),
- &ctx->inode_imagic_map);
- if (pctx.errcode) {
- pctx.num = 5;
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-}
-
-/*
- * Marks a block as in use, setting the dup_map if it's been set
- * already. Called by process_block and process_bad_block.
- *
- * WARNING: Assumes checks have already been done to make sure block
- * is valid. This is true in both process_block and process_bad_block.
- */
-static void mark_block_used(e2fsck_t ctx, blk_t block)
-{
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
- if (!ctx->block_dup_map) {
- pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
- _("multiply claimed block map"),
- &ctx->block_dup_map);
- if (pctx.errcode) {
- pctx.num = 3;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
- &pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
- ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
- } else {
- ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
- }
-}
-
-/*
- * Adjust the extended attribute block's reference counts at the end
- * of pass 1, either by subtracting out references for EA blocks that
- * are still referenced in ctx->refcount, or by adding references for
- * EA blocks that had extra references as accounted for in
- * ctx->refcount_extra.
- */
-static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
- char *block_buf, int adjust_sign)
-{
- struct ext2_ext_attr_header *header;
- struct problem_context pctx;
- ext2_filsys fs = ctx->fs;
- blk_t blk;
- __u32 should_be;
- int count;
-
- clear_problem_context(&pctx);
-
- ea_refcount_intr_begin(refcount);
- while (1) {
- if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
- break;
- pctx.blk = blk;
- pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
- return;
- }
- header = (struct ext2_ext_attr_header *) block_buf;
- pctx.blkcount = header->h_refcount;
- should_be = header->h_refcount + adjust_sign * count;
- pctx.num = should_be;
- if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
- header->h_refcount = should_be;
- pctx.errcode = ext2fs_write_ext_attr(fs, blk,
- block_buf);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
- continue;
- }
- }
- }
-}
-
-/*
- * Handle processing the extended attribute blocks
- */
-static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino = pctx->ino;
- struct ext2_inode *inode = pctx->inode;
- blk_t blk;
- char * end;
- struct ext2_ext_attr_header *header;
- struct ext2_ext_attr_entry *entry;
- int count;
- region_t region;
-
- blk = inode->i_file_acl;
- if (blk == 0)
- return 0;
-
- /*
- * If the Extended attribute flag isn't set, then a non-zero
- * file acl means that the inode is corrupted.
- *
- * Or if the extended attribute block is an invalid block,
- * then the inode is also corrupted.
- */
- if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
- (blk < fs->super->s_first_data_block) ||
- (blk >= fs->super->s_blocks_count)) {
- mark_inode_bad(ctx, ino);
- return 0;
- }
-
- /* If ea bitmap hasn't been allocated, create it */
- if (!ctx->block_ea_map) {
- pctx->errcode = ext2fs_allocate_block_bitmap(fs,
- _("ext attr block map"),
- &ctx->block_ea_map);
- if (pctx->errcode) {
- pctx->num = 2;
- fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return 0;
- }
- }
-
- /* Create the EA refcount structure if necessary */
- if (!ctx->refcount) {
- pctx->errcode = ea_refcount_create(0, &ctx->refcount);
- if (pctx->errcode) {
- pctx->num = 1;
- fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return 0;
- }
- }
-
- /* Have we seen this EA block before? */
- if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
- if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
- return 1;
- /* Ooops, this EA was referenced more than it stated */
- if (!ctx->refcount_extra) {
- pctx->errcode = ea_refcount_create(0,
- &ctx->refcount_extra);
- if (pctx->errcode) {
- pctx->num = 2;
- fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return 0;
- }
- }
- ea_refcount_increment(ctx->refcount_extra, blk, 0);
- return 1;
- }
-
- /*
- * OK, we haven't seen this EA block yet. So we need to
- * validate it
- */
- pctx->blk = blk;
- pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
- if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
- goto clear_extattr;
- header = (struct ext2_ext_attr_header *) block_buf;
- pctx->blk = inode->i_file_acl;
- if (((ctx->ext_attr_ver == 1) &&
- (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
- ((ctx->ext_attr_ver == 2) &&
- (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
- if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
- goto clear_extattr;
- }
-
- if (header->h_blocks != 1) {
- if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
- goto clear_extattr;
- }
-
- region = region_create(0, fs->blocksize);
- if (!region) {
- fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return 0;
- }
- if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
-
- entry = (struct ext2_ext_attr_entry *)(header+1);
- end = block_buf + fs->blocksize;
- while ((char *)entry < end && *(__u32 *)entry) {
- if (region_allocate(region, (char *)entry - (char *)header,
- EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
- if ((ctx->ext_attr_ver == 1 &&
- (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
- (ctx->ext_attr_ver == 2 &&
- entry->e_name_index == 0)) {
- if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
- goto clear_extattr;
- }
- if (entry->e_value_block != 0) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- }
- if (entry->e_value_size &&
- region_allocate(region, entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
- entry = EXT2_EXT_ATTR_NEXT(entry);
- }
- if (region_allocate(region, (char *)entry - (char *)header, 4)) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
- region_free(region);
-
- count = header->h_refcount - 1;
- if (count)
- ea_refcount_store(ctx->refcount, blk, count);
- mark_block_used(ctx, blk);
- ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
-
- return 1;
-
-clear_extattr:
- inode->i_file_acl = 0;
- e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
- return 0;
-}
-
-/* Returns 1 if bad htree, 0 if OK */
-static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
- ext2_ino_t ino FSCK_ATTR((unused)),
- struct ext2_inode *inode,
- char *block_buf)
-{
- struct ext2_dx_root_info *root;
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- blk_t blk;
-
- if ((!LINUX_S_ISDIR(inode->i_mode) &&
- fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
- (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
- fix_problem(ctx, PR_1_HTREE_SET, pctx)))
- return 1;
-
- blk = inode->i_block[0];
- if (((blk == 0) ||
- (blk < fs->super->s_first_data_block) ||
- (blk >= fs->super->s_blocks_count)) &&
- fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
- return 1;
-
- retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
- if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
- return 1;
-
- /* XXX should check that beginning matches a directory */
- root = (struct ext2_dx_root_info *) (block_buf + 24);
-
- if ((root->reserved_zero || root->info_length < 8) &&
- fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
- return 1;
-
- pctx->num = root->hash_version;
- if ((root->hash_version != EXT2_HASH_LEGACY) &&
- (root->hash_version != EXT2_HASH_HALF_MD4) &&
- (root->hash_version != EXT2_HASH_TEA) &&
- fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
- return 1;
-
- if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
- fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
- return 1;
-
- pctx->num = root->indirect_levels;
- if ((root->indirect_levels > 1) &&
- fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
- return 1;
-
- return 0;
-}
-
-/*
- * This subroutine is called on each inode to account for all of the
- * blocks used by that inode.
- */
-static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct process_block_struct_1 pb;
- ext2_ino_t ino = pctx->ino;
- struct ext2_inode *inode = pctx->inode;
- int bad_size = 0;
- int dirty_inode = 0;
- __u64 size;
-
- pb.ino = ino;
- pb.num_blocks = 0;
- pb.last_block = -1;
- pb.num_illegal_blocks = 0;
- pb.suppress = 0; pb.clear = 0;
- pb.fragmented = 0;
- pb.compressed = 0;
- pb.previous_block = 0;
- pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
- pb.is_reg = LINUX_S_ISREG(inode->i_mode);
- pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
- pb.inode = inode;
- pb.pctx = pctx;
- pb.ctx = ctx;
- pctx->ino = ino;
- pctx->errcode = 0;
-
- if (inode->i_flags & EXT2_COMPRBLK_FL) {
- if (fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_COMPRESSION)
- pb.compressed = 1;
- else {
- if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
- inode->i_flags &= ~EXT2_COMPRBLK_FL;
- dirty_inode++;
- }
- }
- }
-
- if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
- pb.num_blocks++;
-
- if (ext2fs_inode_has_valid_blocks(inode))
- pctx->errcode = ext2fs_block_iterate2(fs, ino,
- pb.is_dir ? BLOCK_FLAG_HOLE : 0,
- block_buf, process_block, &pb);
- end_problem_latch(ctx, PR_LATCH_BLOCK);
- end_problem_latch(ctx, PR_LATCH_TOOBIG);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- goto out;
- if (pctx->errcode)
- fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
-
- if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
- ctx->fs_fragmented++;
-
- if (pb.clear) {
- inode->i_links_count = 0;
- ext2fs_icount_store(ctx->inode_link_info, ino, 0);
- inode->i_dtime = time(NULL);
- dirty_inode++;
- ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
- /*
- * The inode was probably partially accounted for
- * before processing was aborted, so we need to
- * restart the pass 1 scan.
- */
- ctx->flags |= E2F_FLAG_RESTART;
- goto out;
- }
-
- if (inode->i_flags & EXT2_INDEX_FL) {
- if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
- inode->i_flags &= ~EXT2_INDEX_FL;
- dirty_inode++;
- } else {
-#ifdef ENABLE_HTREE
- e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
-#endif
- }
- }
- if (ctx->dirs_to_hash && pb.is_dir &&
- !(inode->i_flags & EXT2_INDEX_FL) &&
- ((inode->i_size / fs->blocksize) >= 3))
- ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
-
- if (!pb.num_blocks && pb.is_dir) {
- if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
- inode->i_links_count = 0;
- ext2fs_icount_store(ctx->inode_link_info, ino, 0);
- inode->i_dtime = time(NULL);
- dirty_inode++;
- ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
- ctx->fs_directory_count--;
- goto out;
- }
- }
-
- pb.num_blocks *= (fs->blocksize / 512);
-
- if (pb.is_dir) {
- int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
- if (nblock > (pb.last_block + 1))
- bad_size = 1;
- else if (nblock < (pb.last_block + 1)) {
- if (((pb.last_block + 1) - nblock) >
- fs->super->s_prealloc_dir_blocks)
- bad_size = 2;
- }
- } else {
- size = EXT2_I_SIZE(inode);
- if ((pb.last_block >= 0) &&
- (size < (__u64) pb.last_block * fs->blocksize))
- bad_size = 3;
- else if (size > ext2_max_sizes[fs->super->s_log_block_size])
- bad_size = 4;
- }
- /* i_size for symlinks is checked elsewhere */
- if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
- pctx->num = (pb.last_block+1) * fs->blocksize;
- if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
- inode->i_size = pctx->num;
- if (!LINUX_S_ISDIR(inode->i_mode))
- inode->i_size_high = pctx->num >> 32;
- dirty_inode++;
- }
- pctx->num = 0;
- }
- if (LINUX_S_ISREG(inode->i_mode) &&
- (inode->i_size_high || inode->i_size & 0x80000000UL))
- ctx->large_files++;
- if (pb.num_blocks != inode->i_blocks) {
- pctx->num = pb.num_blocks;
- if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
- inode->i_blocks = pb.num_blocks;
- dirty_inode++;
- }
- pctx->num = 0;
- }
-out:
- if (dirty_inode)
- e2fsck_write_inode(ctx, ino, inode, "check_blocks");
-}
-
-
-/*
- * This is a helper function for check_blocks().
- */
-static int process_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct process_block_struct_1 *p;
- struct problem_context *pctx;
- blk_t blk = *block_nr;
- int ret_code = 0;
- int problem = 0;
- e2fsck_t ctx;
-
- p = (struct process_block_struct_1 *) priv_data;
- pctx = p->pctx;
- ctx = p->ctx;
-
- if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
- /* todo: Check that the comprblk_fl is high, that the
- blkaddr pattern looks right (all non-holes up to
- first EXT2FS_COMPRESSED_BLKADDR, then all
- EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
- that the feature_incompat bit is high, and that the
- inode is a regular file. If we're doing a "full
- check" (a concept introduced to e2fsck by e2compr,
- meaning that we look at data blocks as well as
- metadata) then call some library routine that
- checks the compressed data. I'll have to think
- about this, because one particularly important
- problem to be able to fix is to recalculate the
- cluster size if necessary. I think that perhaps
- we'd better do most/all e2compr-specific checks
- separately, after the non-e2compr checks. If not
- doing a full check, it may be useful to test that
- the personality is linux; e.g. if it isn't then
- perhaps this really is just an illegal block. */
- return 0;
- }
-
- if (blk == 0) {
- if (p->is_dir == 0) {
- /*
- * Should never happen, since only directories
- * get called with BLOCK_FLAG_HOLE
- */
-#ifdef DEBUG_E2FSCK
- printf("process_block() called with blk == 0, "
- "blockcnt=%d, inode %lu???\n",
- blockcnt, p->ino);
-#endif
- return 0;
- }
- if (blockcnt < 0)
- return 0;
- if (blockcnt * fs->blocksize < p->inode->i_size) {
- goto mark_dir;
- }
- return 0;
- }
-
- /*
- * Simplistic fragmentation check. We merely require that the
- * file be contiguous. (Which can never be true for really
- * big files that are greater than a block group.)
- */
- if (!HOLE_BLKADDR(p->previous_block)) {
- if (p->previous_block+1 != blk)
- p->fragmented = 1;
- }
- p->previous_block = blk;
-
- if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
- problem = PR_1_TOOBIG_DIR;
- if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
- problem = PR_1_TOOBIG_REG;
- if (!p->is_dir && !p->is_reg && blockcnt > 0)
- problem = PR_1_TOOBIG_SYMLINK;
-
- if (blk < fs->super->s_first_data_block ||
- blk >= fs->super->s_blocks_count)
- problem = PR_1_ILLEGAL_BLOCK_NUM;
-
- if (problem) {
- p->num_illegal_blocks++;
- if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
- if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
- p->clear = 1;
- return BLOCK_ABORT;
- }
- if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
- p->suppress = 1;
- set_latch_flags(PR_LATCH_BLOCK,
- PRL_SUPPRESS, 0);
- }
- }
- pctx->blk = blk;
- pctx->blkcount = blockcnt;
- if (fix_problem(ctx, problem, pctx)) {
- blk = *block_nr = 0;
- ret_code = BLOCK_CHANGED;
- goto mark_dir;
- } else
- return 0;
- }
-
- if (p->ino == EXT2_RESIZE_INO) {
- /*
- * The resize inode has already be sanity checked
- * during pass #0 (the superblock checks). All we
- * have to do is mark the double indirect block as
- * being in use; all of the other blocks are handled
- * by mark_table_blocks()).
- */
- if (blockcnt == BLOCK_COUNT_DIND)
- mark_block_used(ctx, blk);
- } else
- mark_block_used(ctx, blk);
- p->num_blocks++;
- if (blockcnt >= 0)
- p->last_block = blockcnt;
-mark_dir:
- if (p->is_dir && (blockcnt >= 0)) {
- pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
- blk, blockcnt);
- if (pctx->errcode) {
- pctx->blk = blk;
- pctx->num = blockcnt;
- fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
- /* Should never get here */
- ctx->flags |= E2F_FLAG_ABORT;
- return BLOCK_ABORT;
- }
- }
- return ret_code;
-}
-
-static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data EXT2FS_ATTR((unused)))
-{
- /*
- * Note: This function processes blocks for the bad blocks
- * inode, which is never compressed. So we don't use HOLE_BLKADDR().
- */
-
- printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr);
- return BLOCK_ERROR;
-}
-
-/*
- * This routine gets called at the end of pass 1 if bad blocks are
- * detected in the superblock, group descriptors, inode_bitmaps, or
- * block bitmaps. At this point, all of the blocks have been mapped
- * out, so we can try to allocate new block(s) to replace the bad
- * blocks.
- */
-static void handle_fs_bad_blocks(e2fsck_t ctx)
-{
- printf("Bad blocks detected on your filesystem\n"
- "You should get your data off as the device will soon die\n");
-}
-
-/*
- * This routine marks all blocks which are used by the superblock,
- * group descriptors, inode bitmaps, and block bitmaps.
- */
-static void mark_table_blocks(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t block, b;
- dgrp_t i;
- int j;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- block = fs->super->s_first_data_block;
- for (i = 0; i < fs->group_desc_count; i++) {
- pctx.group = i;
-
- ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
-
- /*
- * Mark the blocks used for the inode table
- */
- if (fs->group_desc[i].bg_inode_table) {
- for (j = 0, b = fs->group_desc[i].bg_inode_table;
- j < fs->inode_blocks_per_group;
- j++, b++) {
- if (ext2fs_test_block_bitmap(ctx->block_found_map,
- b)) {
- pctx.blk = b;
- if (fix_problem(ctx,
- PR_1_ITABLE_CONFLICT, &pctx)) {
- ctx->invalid_inode_table_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- } else {
- ext2fs_mark_block_bitmap(ctx->block_found_map, b);
- }
- }
- }
-
- /*
- * Mark block used for the block bitmap
- */
- if (fs->group_desc[i].bg_block_bitmap) {
- if (ext2fs_test_block_bitmap(ctx->block_found_map,
- fs->group_desc[i].bg_block_bitmap)) {
- pctx.blk = fs->group_desc[i].bg_block_bitmap;
- if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
- ctx->invalid_block_bitmap_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- } else {
- ext2fs_mark_block_bitmap(ctx->block_found_map,
- fs->group_desc[i].bg_block_bitmap);
- }
- }
- /*
- * Mark block used for the inode bitmap
- */
- if (fs->group_desc[i].bg_inode_bitmap) {
- if (ext2fs_test_block_bitmap(ctx->block_found_map,
- fs->group_desc[i].bg_inode_bitmap)) {
- pctx.blk = fs->group_desc[i].bg_inode_bitmap;
- if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
- ctx->invalid_inode_bitmap_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- } else {
- ext2fs_mark_block_bitmap(ctx->block_found_map,
- fs->group_desc[i].bg_inode_bitmap);
- }
- }
- block += fs->super->s_blocks_per_group;
- }
-}
-
-/*
- * Thes subroutines short circuits ext2fs_get_blocks and
- * ext2fs_check_directory; we use them since we already have the inode
- * structure, so there's no point in letting the ext2fs library read
- * the inode again.
- */
-static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
- blk_t *blocks)
-{
- e2fsck_t ctx = (e2fsck_t) fs->priv_data;
- int i;
-
- if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
- return EXT2_ET_CALLBACK_NOTHANDLED;
-
- for (i=0; i < EXT2_N_BLOCKS; i++)
- blocks[i] = ctx->stashed_inode->i_block[i];
- return 0;
-}
-
-static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode)
-{
- e2fsck_t ctx = (e2fsck_t) fs->priv_data;
-
- if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
- return EXT2_ET_CALLBACK_NOTHANDLED;
- *inode = *ctx->stashed_inode;
- return 0;
-}
-
-static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode)
-{
- e2fsck_t ctx = (e2fsck_t) fs->priv_data;
-
- if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
- *ctx->stashed_inode = *inode;
- return EXT2_ET_CALLBACK_NOTHANDLED;
-}
-
-static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
-{
- e2fsck_t ctx = (e2fsck_t) fs->priv_data;
-
- if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
- return EXT2_ET_CALLBACK_NOTHANDLED;
-
- if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
- return EXT2_ET_NO_DIRECTORY;
- return 0;
-}
-
-void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
-{
- ext2_filsys fs = ctx->fs;
-
- if (bool) {
- fs->get_blocks = pass1_get_blocks;
- fs->check_directory = pass1_check_directory;
- fs->read_inode = pass1_read_inode;
- fs->write_inode = pass1_write_inode;
- ctx->stashed_ino = 0;
- } else {
- fs->get_blocks = 0;
- fs->check_directory = 0;
- fs->read_inode = 0;
- fs->write_inode = 0;
- }
-}
-
-/*
- * pass1b.c --- Pass #1b of e2fsck
- *
- * This file contains pass1B, pass1C, and pass1D of e2fsck. They are
- * only invoked if pass 1 discovered blocks which are in use by more
- * than one inode.
- *
- * Pass1B scans the data blocks of all the inodes again, generating a
- * complete list of duplicate blocks and which inodes have claimed
- * them.
- *
- * Pass1C does a tree-traversal of the filesystem, to determine the
- * parent directories of these inodes. This step is necessary so that
- * e2fsck can print out the pathnames of affected inodes.
- *
- * Pass1D is a reconciliation pass. For each inode with duplicate
- * blocks, the user is prompted if s/he would like to clone the file
- * (so that the file gets a fresh copy of the duplicated blocks) or
- * simply to delete the file.
- *
- */
-
-
-/* Needed for architectures where sizeof(int) != sizeof(void *) */
-#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
-#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
-
-/* Define an extension to the ext2 library's block count information */
-#define BLOCK_COUNT_EXTATTR (-5)
-
-struct block_el {
- blk_t block;
- struct block_el *next;
-};
-
-struct inode_el {
- ext2_ino_t inode;
- struct inode_el *next;
-};
-
-struct dup_block {
- int num_bad;
- struct inode_el *inode_list;
-};
-
-/*
- * This structure stores information about a particular inode which
- * is sharing blocks with other inodes. This information is collected
- * to display to the user, so that the user knows what files he or she
- * is dealing with, when trying to decide how to resolve the conflict
- * of multiply-claimed blocks.
- */
-struct dup_inode {
- ext2_ino_t dir;
- int num_dupblocks;
- struct ext2_inode inode;
- struct block_el *block_list;
-};
-
-static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
- e2_blkcnt_t blockcnt, blk_t ref_blk,
- int ref_offset, void *priv_data);
-static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
- struct dup_inode *dp, char *block_buf);
-static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
- struct dup_inode *dp, char* block_buf);
-static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
-
-static void pass1b(e2fsck_t ctx, char *block_buf);
-static void pass1c(e2fsck_t ctx, char *block_buf);
-static void pass1d(e2fsck_t ctx, char *block_buf);
-
-static int dup_inode_count = 0;
-
-static dict_t blk_dict, ino_dict;
-
-static ext2fs_inode_bitmap inode_dup_map;
-
-static int dict_int_cmp(const void *a, const void *b)
-{
- intptr_t ia, ib;
-
- ia = (intptr_t)a;
- ib = (intptr_t)b;
-
- return (ia-ib);
-}
-
-/*
- * Add a duplicate block record
- */
-static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
- struct ext2_inode *inode)
-{
- dnode_t *n;
- struct dup_block *db;
- struct dup_inode *di;
- struct block_el *blk_el;
- struct inode_el *ino_el;
-
- n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
- if (n)
- db = (struct dup_block *) dnode_get(n);
- else {
- db = (struct dup_block *) e2fsck_allocate_memory(ctx,
- sizeof(struct dup_block), "duplicate block header");
- db->num_bad = 0;
- db->inode_list = 0;
- dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
- }
- ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
- sizeof(struct inode_el), "inode element");
- ino_el->inode = ino;
- ino_el->next = db->inode_list;
- db->inode_list = ino_el;
- db->num_bad++;
-
- n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
- if (n)
- di = (struct dup_inode *) dnode_get(n);
- else {
- di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
- sizeof(struct dup_inode), "duplicate inode header");
- di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0;
- di->num_dupblocks = 0;
- di->block_list = 0;
- di->inode = *inode;
- dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
- }
- blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
- sizeof(struct block_el), "block element");
- blk_el->block = blk;
- blk_el->next = di->block_list;
- di->block_list = blk_el;
- di->num_dupblocks++;
-}
-
-/*
- * Free a duplicate inode record
- */
-static void inode_dnode_free(dnode_t *node)
-{
- struct dup_inode *di;
- struct block_el *p, *next;
-
- di = (struct dup_inode *) dnode_get(node);
- for (p = di->block_list; p; p = next) {
- next = p->next;
- free(p);
- }
- free(node);
-}
-
-/*
- * Free a duplicate block record
- */
-static void block_dnode_free(dnode_t *node)
-{
- struct dup_block *db;
- struct inode_el *p, *next;
-
- db = (struct dup_block *) dnode_get(node);
- for (p = db->inode_list; p; p = next) {
- next = p->next;
- free(p);
- }
- free(node);
-}
-
-
-/*
- * Main procedure for handling duplicate blocks
- */
-void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
- _("multiply claimed inode map"), &inode_dup_map);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
- dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
- dict_set_allocator(&ino_dict, inode_dnode_free);
- dict_set_allocator(&blk_dict, block_dnode_free);
-
- pass1b(ctx, block_buf);
- pass1c(ctx, block_buf);
- pass1d(ctx, block_buf);
-
- /*
- * Time to free all of the accumulated data structures that we
- * don't need anymore.
- */
- dict_free_nodes(&ino_dict);
- dict_free_nodes(&blk_dict);
-}
-
-/*
- * Scan the inodes looking for inodes that contain duplicate blocks.
- */
-struct process_block_struct_1b {
- e2fsck_t ctx;
- ext2_ino_t ino;
- int dup_blocks;
- struct ext2_inode *inode;
- struct problem_context *pctx;
-};
-
-static void pass1b(e2fsck_t ctx, char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino;
- struct ext2_inode inode;
- ext2_inode_scan scan;
- struct process_block_struct_1b pb;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
- pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
- &scan);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ctx->stashed_inode = &inode;
- pb.ctx = ctx;
- pb.pctx = &pctx;
- pctx.str = "pass1b";
- while (1) {
- pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
- if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
- continue;
- if (pctx.errcode) {
- fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (!ino)
- break;
- pctx.ino = ctx->stashed_ino = ino;
- if ((ino != EXT2_BAD_INO) &&
- !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
- continue;
-
- pb.ino = ino;
- pb.dup_blocks = 0;
- pb.inode = &inode;
-
- if (ext2fs_inode_has_valid_blocks(&inode) ||
- (ino == EXT2_BAD_INO))
- pctx.errcode = ext2fs_block_iterate2(fs, ino,
- 0, block_buf, process_pass1b_block, &pb);
- if (inode.i_file_acl)
- process_pass1b_block(fs, &inode.i_file_acl,
- BLOCK_COUNT_EXTATTR, 0, 0, &pb);
- if (pb.dup_blocks) {
- end_problem_latch(ctx, PR_LATCH_DBLOCK);
- if (ino >= EXT2_FIRST_INODE(fs->super) ||
- ino == EXT2_ROOT_INO)
- dup_inode_count++;
- }
- if (pctx.errcode)
- fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
- }
- ext2fs_close_inode_scan(scan);
- e2fsck_use_inode_shortcuts(ctx, 0);
-}
-
-static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
- blk_t *block_nr,
- e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
- blk_t ref_blk FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct process_block_struct_1b *p;
- e2fsck_t ctx;
-
- if (HOLE_BLKADDR(*block_nr))
- return 0;
- p = (struct process_block_struct_1b *) priv_data;
- ctx = p->ctx;
-
- if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
- return 0;
-
- /* OK, this is a duplicate block */
- if (p->ino != EXT2_BAD_INO) {
- p->pctx->blk = *block_nr;
- fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
- }
- p->dup_blocks++;
- ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
-
- add_dupe(ctx, p->ino, *block_nr, p->inode);
-
- return 0;
-}
-
-/*
- * Pass 1c: Scan directories for inodes with duplicate blocks. This
- * is used so that we can print pathnames when prompting the user for
- * what to do.
- */
-struct search_dir_struct {
- int count;
- ext2_ino_t first_inode;
- ext2_ino_t max_inode;
-};
-
-static int search_dirent_proc(ext2_ino_t dir, int entry,
- struct ext2_dir_entry *dirent,
- int offset FSCK_ATTR((unused)),
- int blocksize FSCK_ATTR((unused)),
- char *buf FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct search_dir_struct *sd;
- struct dup_inode *p;
- dnode_t *n;
-
- sd = (struct search_dir_struct *) priv_data;
-
- if (dirent->inode > sd->max_inode)
- /* Should abort this inode, but not everything */
- return 0;
-
- if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
- !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
- return 0;
-
- n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
- if (!n)
- return 0;
- p = (struct dup_inode *) dnode_get(n);
- p->dir = dir;
- sd->count--;
-
- return sd->count ? 0 : DIRENT_ABORT;
-}
-
-
-static void pass1c(e2fsck_t ctx, char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct search_dir_struct sd;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
-
- /*
- * Search through all directories to translate inodes to names
- * (by searching for the containing directory for that inode.)
- */
- sd.count = dup_inode_count;
- sd.first_inode = EXT2_FIRST_INODE(fs->super);
- sd.max_inode = fs->super->s_inodes_count;
- ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
- search_dirent_proc, &sd);
-}
-
-static void pass1d(e2fsck_t ctx, char *block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct dup_inode *p, *t;
- struct dup_block *q;
- ext2_ino_t *shared, ino;
- int shared_len;
- int i;
- int file_ok;
- int meta_data = 0;
- struct problem_context pctx;
- dnode_t *n, *m;
- struct block_el *s;
- struct inode_el *r;
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
- e2fsck_read_bitmaps(ctx);
-
- pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
- fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
- shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
- sizeof(ext2_ino_t) * dict_count(&ino_dict),
- "Shared inode list");
- for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
- p = (struct dup_inode *) dnode_get(n);
- shared_len = 0;
- file_ok = 1;
- ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
- if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
- continue;
-
- /*
- * Find all of the inodes which share blocks with this
- * one. First we find all of the duplicate blocks
- * belonging to this inode, and then search each block
- * get the list of inodes, and merge them together.
- */
- for (s = p->block_list; s; s = s->next) {
- m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
- if (!m)
- continue; /* Should never happen... */
- q = (struct dup_block *) dnode_get(m);
- if (q->num_bad > 1)
- file_ok = 0;
- if (check_if_fs_block(ctx, s->block)) {
- file_ok = 0;
- meta_data = 1;
- }
-
- /*
- * Add all inodes used by this block to the
- * shared[] --- which is a unique list, so
- * if an inode is already in shared[], don't
- * add it again.
- */
- for (r = q->inode_list; r; r = r->next) {
- if (r->inode == ino)
- continue;
- for (i = 0; i < shared_len; i++)
- if (shared[i] == r->inode)
- break;
- if (i == shared_len) {
- shared[shared_len++] = r->inode;
- }
- }
- }
-
- /*
- * Report the inode that we are working on
- */
- pctx.inode = &p->inode;
- pctx.ino = ino;
- pctx.dir = p->dir;
- pctx.blkcount = p->num_dupblocks;
- pctx.num = meta_data ? shared_len+1 : shared_len;
- fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
- pctx.blkcount = 0;
- pctx.num = 0;
-
- if (meta_data)
- fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
-
- for (i = 0; i < shared_len; i++) {
- m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
- if (!m)
- continue; /* should never happen */
- t = (struct dup_inode *) dnode_get(m);
- /*
- * Report the inode that we are sharing with
- */
- pctx.inode = &t->inode;
- pctx.ino = shared[i];
- pctx.dir = t->dir;
- fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
- }
- if (file_ok) {
- fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
- continue;
- }
- if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
- pctx.errcode = clone_file(ctx, ino, p, block_buf);
- if (pctx.errcode)
- fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
- else
- continue;
- }
- if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
- delete_file(ctx, ino, p, block_buf);
- else
- ext2fs_unmark_valid(fs);
- }
- ext2fs_free_mem(&shared);
-}
-
-/*
- * Drop the refcount on the dup_block structure, and clear the entry
- * in the block_dup_map if appropriate.
- */
-static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
-{
- p->num_bad--;
- if (p->num_bad <= 0 ||
- (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
- ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
-}
-
-static int delete_file_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct process_block_struct_1b *pb;
- struct dup_block *p;
- dnode_t *n;
- e2fsck_t ctx;
-
- pb = (struct process_block_struct_1b *) priv_data;
- ctx = pb->ctx;
-
- if (HOLE_BLKADDR(*block_nr))
- return 0;
-
- if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
- n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
- if (n) {
- p = (struct dup_block *) dnode_get(n);
- decrement_badcount(ctx, *block_nr, p);
- } else
- bb_error_msg(_("internal error; can't find dup_blk for %d"),
- *block_nr);
- } else {
- ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
- ext2fs_block_alloc_stats(fs, *block_nr, -1);
- }
-
- return 0;
-}
-
-static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
- struct dup_inode *dp, char* block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct process_block_struct_1b pb;
- struct ext2_inode inode;
- struct problem_context pctx;
- unsigned int count;
-
- clear_problem_context(&pctx);
- pctx.ino = pb.ino = ino;
- pb.dup_blocks = dp->num_dupblocks;
- pb.ctx = ctx;
- pctx.str = "delete_file";
-
- e2fsck_read_inode(ctx, ino, &inode, "delete_file");
- if (ext2fs_inode_has_valid_blocks(&inode))
- pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
- delete_file_block, &pb);
- if (pctx.errcode)
- fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
- ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
- if (ctx->inode_bad_map)
- ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
- ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
-
- /* Inode may have changed by block_iterate, so reread it */
- e2fsck_read_inode(ctx, ino, &inode, "delete_file");
- inode.i_links_count = 0;
- inode.i_dtime = time(NULL);
- if (inode.i_file_acl &&
- (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
- count = 1;
- pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
- block_buf, -1, &count);
- if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
- pctx.errcode = 0;
- count = 1;
- }
- if (pctx.errcode) {
- pctx.blk = inode.i_file_acl;
- fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
- }
- /*
- * If the count is zero, then arrange to have the
- * block deleted. If the block is in the block_dup_map,
- * also call delete_file_block since it will take care
- * of keeping the accounting straight.
- */
- if ((count == 0) ||
- ext2fs_test_block_bitmap(ctx->block_dup_map,
- inode.i_file_acl))
- delete_file_block(fs, &inode.i_file_acl,
- BLOCK_COUNT_EXTATTR, 0, 0, &pb);
- }
- e2fsck_write_inode(ctx, ino, &inode, "delete_file");
-}
-
-struct clone_struct {
- errcode_t errcode;
- ext2_ino_t dir;
- char *buf;
- e2fsck_t ctx;
-};
-
-static int clone_file_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct dup_block *p;
- blk_t new_block;
- errcode_t retval;
- struct clone_struct *cs = (struct clone_struct *) priv_data;
- dnode_t *n;
- e2fsck_t ctx;
-
- ctx = cs->ctx;
-
- if (HOLE_BLKADDR(*block_nr))
- return 0;
-
- if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
- n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
- if (n) {
- p = (struct dup_block *) dnode_get(n);
- retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
- &new_block);
- if (retval) {
- cs->errcode = retval;
- return BLOCK_ABORT;
- }
- if (cs->dir && (blockcnt >= 0)) {
- retval = ext2fs_set_dir_block(fs->dblist,
- cs->dir, new_block, blockcnt);
- if (retval) {
- cs->errcode = retval;
- return BLOCK_ABORT;
- }
- }
-
- retval = io_channel_read_blk(fs->io, *block_nr, 1,
- cs->buf);
- if (retval) {
- cs->errcode = retval;
- return BLOCK_ABORT;
- }
- retval = io_channel_write_blk(fs->io, new_block, 1,
- cs->buf);
- if (retval) {
- cs->errcode = retval;
- return BLOCK_ABORT;
- }
- decrement_badcount(ctx, *block_nr, p);
- *block_nr = new_block;
- ext2fs_mark_block_bitmap(ctx->block_found_map,
- new_block);
- ext2fs_mark_block_bitmap(fs->block_map, new_block);
- return BLOCK_CHANGED;
- } else
- bb_error_msg(_("internal error; can't find dup_blk for %d"),
- *block_nr);
- }
- return 0;
-}
-
-static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
- struct dup_inode *dp, char* block_buf)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- struct clone_struct cs;
- struct problem_context pctx;
- blk_t blk;
- dnode_t *n;
- struct inode_el *ino_el;
- struct dup_block *db;
- struct dup_inode *di;
-
- clear_problem_context(&pctx);
- cs.errcode = 0;
- cs.dir = 0;
- cs.ctx = ctx;
- retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
- if (retval)
- return retval;
-
- if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
- cs.dir = ino;
-
- pctx.ino = ino;
- pctx.str = "clone_file";
- if (ext2fs_inode_has_valid_blocks(&dp->inode))
- pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
- clone_file_block, &cs);
- ext2fs_mark_bb_dirty(fs);
- if (pctx.errcode) {
- fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
- retval = pctx.errcode;
- goto errout;
- }
- if (cs.errcode) {
- bb_error_msg(_("returned from clone_file_block"));
- retval = cs.errcode;
- goto errout;
- }
- /* The inode may have changed on disk, so we have to re-read it */
- e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
- blk = dp->inode.i_file_acl;
- if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
- BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
- BLOCK_CHANGED)) {
- e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
- /*
- * If we cloned the EA block, find all other inodes
- * which refered to that EA block, and modify
- * them to point to the new EA block.
- */
- n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
- db = (struct dup_block *) dnode_get(n);
- for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
- if (ino_el->inode == ino)
- continue;
- n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
- di = (struct dup_inode *) dnode_get(n);
- if (di->inode.i_file_acl == blk) {
- di->inode.i_file_acl = dp->inode.i_file_acl;
- e2fsck_write_inode(ctx, ino_el->inode,
- &di->inode, "clone file EA");
- decrement_badcount(ctx, blk, db);
- }
- }
- }
- retval = 0;
-errout:
- ext2fs_free_mem(&cs.buf);
- return retval;
-}
-
-/*
- * This routine returns 1 if a block overlaps with one of the superblocks,
- * group descriptors, inode bitmaps, or block bitmaps.
- */
-static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
-{
- ext2_filsys fs = ctx->fs;
- blk_t block;
- dgrp_t i;
-
- block = fs->super->s_first_data_block;
- for (i = 0; i < fs->group_desc_count; i++) {
-
- /* Check superblocks/block group descriptros */
- if (ext2fs_bg_has_super(fs, i)) {
- if (test_block >= block &&
- (test_block <= block + fs->desc_blocks))
- return 1;
- }
-
- /* Check the inode table */
- if ((fs->group_desc[i].bg_inode_table) &&
- (test_block >= fs->group_desc[i].bg_inode_table) &&
- (test_block < (fs->group_desc[i].bg_inode_table +
- fs->inode_blocks_per_group)))
- return 1;
-
- /* Check the bitmap blocks */
- if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
- (test_block == fs->group_desc[i].bg_inode_bitmap))
- return 1;
-
- block += fs->super->s_blocks_per_group;
- }
- return 0;
-}
-/*
- * pass2.c --- check directory structure
- *
- * Pass 2 of e2fsck iterates through all active directory inodes, and
- * applies to following tests to each directory entry in the directory
- * blocks in the inodes:
- *
- * - The length of the directory entry (rec_len) should be at
- * least 8 bytes, and no more than the remaining space
- * left in the directory block.
- * - The length of the name in the directory entry (name_len)
- * should be less than (rec_len - 8).
- * - The inode number in the directory entry should be within
- * legal bounds.
- * - The inode number should refer to a in-use inode.
- * - The first entry should be '.', and its inode should be
- * the inode of the directory.
- * - The second entry should be '..'.
- *
- * To minimize disk seek time, the directory blocks are processed in
- * sorted order of block numbers.
- *
- * Pass 2 also collects the following information:
- * - The inode numbers of the subdirectories for each directory.
- *
- * Pass 2 relies on the following information from previous passes:
- * - The directory information collected in pass 1.
- * - The inode_used_map bitmap
- * - The inode_bad_map bitmap
- * - The inode_dir_map bitmap
- *
- * Pass 2 frees the following data structures
- * - The inode_bad_map bitmap
- * - The inode_reg_map bitmap
- */
-
-/*
- * Keeps track of how many times an inode is referenced.
- */
-static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
-static int check_dir_block(ext2_filsys fs,
- struct ext2_db_entry *dir_blocks_info,
- void *priv_data);
-static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
- struct problem_context *pctx);
-static int update_dir_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block,
- int ref_offset,
- void *priv_data);
-static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
-static int htree_depth(struct dx_dir_info *dx_dir,
- struct dx_dirblock_info *dx_db);
-static int special_dir_block_cmp(const void *a, const void *b);
-
-struct check_dir_struct {
- char *buf;
- struct problem_context pctx;
- int count, max;
- e2fsck_t ctx;
-};
-
-static void e2fsck_pass2(e2fsck_t ctx)
-{
- struct ext2_super_block *sb = ctx->fs->super;
- struct problem_context pctx;
- ext2_filsys fs = ctx->fs;
- char *buf;
- struct dir_info *dir;
- struct check_dir_struct cd;
- struct dx_dir_info *dx_dir;
- struct dx_dirblock_info *dx_db, *dx_parent;
- int b;
- int i, depth;
- problem_t code;
- int bad_dir;
-
- clear_problem_context(&cd.pctx);
-
- /* Pass 2 */
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
-
- cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
- 0, ctx->inode_link_info,
- &ctx->inode_count);
- if (cd.pctx.errcode) {
- fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
- "directory scan buffer");
-
- /*
- * Set up the parent pointer for the root directory, if
- * present. (If the root directory is not present, we will
- * create it in pass 3.)
- */
- dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
- if (dir)
- dir->parent = EXT2_ROOT_INO;
-
- cd.buf = buf;
- cd.ctx = ctx;
- cd.count = 1;
- cd.max = ext2fs_dblist_count(fs->dblist);
-
- if (ctx->progress)
- (void) (ctx->progress)(ctx, 2, 0, cd.max);
-
- if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
- ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
-
- cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
- &cd);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if (cd.pctx.errcode) {
- fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
-#ifdef ENABLE_HTREE
- for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if (dx_dir->numblocks == 0)
- continue;
- clear_problem_context(&pctx);
- bad_dir = 0;
- pctx.dir = dx_dir->ino;
- dx_db = dx_dir->dx_block;
- if (dx_db->flags & DX_FLAG_REFERENCED)
- dx_db->flags |= DX_FLAG_DUP_REF;
- else
- dx_db->flags |= DX_FLAG_REFERENCED;
- /*
- * Find all of the first and last leaf blocks, and
- * update their parent's min and max hash values
- */
- for (b=0, dx_db = dx_dir->dx_block;
- b < dx_dir->numblocks;
- b++, dx_db++) {
- if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
- !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
- continue;
- dx_parent = &dx_dir->dx_block[dx_db->parent];
- /*
- * XXX Make sure dx_parent->min_hash > dx_db->min_hash
- */
- if (dx_db->flags & DX_FLAG_FIRST)
- dx_parent->min_hash = dx_db->min_hash;
- /*
- * XXX Make sure dx_parent->max_hash < dx_db->max_hash
- */
- if (dx_db->flags & DX_FLAG_LAST)
- dx_parent->max_hash = dx_db->max_hash;
- }
-
- for (b=0, dx_db = dx_dir->dx_block;
- b < dx_dir->numblocks;
- b++, dx_db++) {
- pctx.blkcount = b;
- pctx.group = dx_db->parent;
- code = 0;
- if (!(dx_db->flags & DX_FLAG_FIRST) &&
- (dx_db->min_hash < dx_db->node_min_hash)) {
- pctx.blk = dx_db->min_hash;
- pctx.blk2 = dx_db->node_min_hash;
- code = PR_2_HTREE_MIN_HASH;
- fix_problem(ctx, code, &pctx);
- bad_dir++;
- }
- if (dx_db->type == DX_DIRBLOCK_LEAF) {
- depth = htree_depth(dx_dir, dx_db);
- if (depth != dx_dir->depth) {
- code = PR_2_HTREE_BAD_DEPTH;
- fix_problem(ctx, code, &pctx);
- bad_dir++;
- }
- }
- /*
- * This test doesn't apply for the root block
- * at block #0
- */
- if (b &&
- (dx_db->max_hash > dx_db->node_max_hash)) {
- pctx.blk = dx_db->max_hash;
- pctx.blk2 = dx_db->node_max_hash;
- code = PR_2_HTREE_MAX_HASH;
- fix_problem(ctx, code, &pctx);
- bad_dir++;
- }
- if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
- code = PR_2_HTREE_NOTREF;
- fix_problem(ctx, code, &pctx);
- bad_dir++;
- } else if (dx_db->flags & DX_FLAG_DUP_REF) {
- code = PR_2_HTREE_DUPREF;
- fix_problem(ctx, code, &pctx);
- bad_dir++;
- }
- if (code == 0)
- continue;
- }
- if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
- clear_htree(ctx, dx_dir->ino);
- dx_dir->numblocks = 0;
- }
- }
-#endif
- ext2fs_free_mem(&buf);
- ext2fs_free_dblist(fs->dblist);
-
- ext2fs_free_inode_bitmap(ctx->inode_bad_map);
- ctx->inode_bad_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_reg_map);
- ctx->inode_reg_map = 0;
-
- clear_problem_context(&pctx);
- if (ctx->large_files) {
- if (!(sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
- fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
- sb->s_feature_ro_compat |=
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
- ext2fs_mark_super_dirty(fs);
- }
- if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
- fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
- ext2fs_update_dynamic_rev(fs);
- ext2fs_mark_super_dirty(fs);
- }
- } else if (!ctx->large_files &&
- (sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
- if (fs->flags & EXT2_FLAG_RW) {
- sb->s_feature_ro_compat &=
- ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
- ext2fs_mark_super_dirty(fs);
- }
- }
-}
-
-#define MAX_DEPTH 32000
-static int htree_depth(struct dx_dir_info *dx_dir,
- struct dx_dirblock_info *dx_db)
-{
- int depth = 0;
-
- while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
- dx_db = &dx_dir->dx_block[dx_db->parent];
- depth++;
- }
- return depth;
-}
-
-static int dict_de_cmp(const void *a, const void *b)
-{
- const struct ext2_dir_entry *de_a, *de_b;
- int a_len, b_len;
-
- de_a = (const struct ext2_dir_entry *) a;
- a_len = de_a->name_len & 0xFF;
- de_b = (const struct ext2_dir_entry *) b;
- b_len = de_b->name_len & 0xFF;
-
- if (a_len != b_len)
- return (a_len - b_len);
-
- return strncmp(de_a->name, de_b->name, a_len);
-}
-
-/*
- * This is special sort function that makes sure that directory blocks
- * with a dirblock of zero are sorted to the beginning of the list.
- * This guarantees that the root node of the htree directories are
- * processed first, so we know what hash version to use.
- */
-static int special_dir_block_cmp(const void *a, const void *b)
-{
- const struct ext2_db_entry *db_a =
- (const struct ext2_db_entry *) a;
- const struct ext2_db_entry *db_b =
- (const struct ext2_db_entry *) b;
-
- if (db_a->blockcnt && !db_b->blockcnt)
- return 1;
-
- if (!db_a->blockcnt && db_b->blockcnt)
- return -1;
-
- if (db_a->blk != db_b->blk)
- return (int) (db_a->blk - db_b->blk);
-
- if (db_a->ino != db_b->ino)
- return (int) (db_a->ino - db_b->ino);
-
- return (int) (db_a->blockcnt - db_b->blockcnt);
-}
-
-
-/*
- * Make sure the first entry in the directory is '.', and that the
- * directory entry is sane.
- */
-static int check_dot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
- ext2_ino_t ino, struct problem_context *pctx)
-{
- struct ext2_dir_entry *nextdir;
- int status = 0;
- int created = 0;
- int new_len;
- int problem = 0;
-
- if (!dirent->inode)
- problem = PR_2_MISSING_DOT;
- else if (((dirent->name_len & 0xFF) != 1) ||
- (dirent->name[0] != '.'))
- problem = PR_2_1ST_NOT_DOT;
- else if (dirent->name[1] != '\0')
- problem = PR_2_DOT_NULL_TERM;
-
- if (problem) {
- if (fix_problem(ctx, problem, pctx)) {
- if (dirent->rec_len < 12)
- dirent->rec_len = 12;
- dirent->inode = ino;
- dirent->name_len = 1;
- dirent->name[0] = '.';
- dirent->name[1] = '\0';
- status = 1;
- created = 1;
- }
- }
- if (dirent->inode != ino) {
- if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
- dirent->inode = ino;
- status = 1;
- }
- }
- if (dirent->rec_len > 12) {
- new_len = dirent->rec_len - 12;
- if (new_len > 12) {
- if (created ||
- fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
- nextdir = (struct ext2_dir_entry *)
- ((char *) dirent + 12);
- dirent->rec_len = 12;
- nextdir->rec_len = new_len;
- nextdir->inode = 0;
- nextdir->name_len = 0;
- status = 1;
- }
- }
- }
- return status;
-}
-
-/*
- * Make sure the second entry in the directory is '..', and that the
- * directory entry is sane. We do not check the inode number of '..'
- * here; this gets done in pass 3.
- */
-static int check_dotdot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
- struct dir_info *dir, struct problem_context *pctx)
-{
- int problem = 0;
-
- if (!dirent->inode)
- problem = PR_2_MISSING_DOT_DOT;
- else if (((dirent->name_len & 0xFF) != 2) ||
- (dirent->name[0] != '.') ||
- (dirent->name[1] != '.'))
- problem = PR_2_2ND_NOT_DOT_DOT;
- else if (dirent->name[2] != '\0')
- problem = PR_2_DOT_DOT_NULL_TERM;
-
- if (problem) {
- if (fix_problem(ctx, problem, pctx)) {
- if (dirent->rec_len < 12)
- dirent->rec_len = 12;
- /*
- * Note: we don't have the parent inode just
- * yet, so we will fill it in with the root
- * inode. This will get fixed in pass 3.
- */
- dirent->inode = EXT2_ROOT_INO;
- dirent->name_len = 2;
- dirent->name[0] = '.';
- dirent->name[1] = '.';
- dirent->name[2] = '\0';
- return 1;
- }
- return 0;
- }
- dir->dotdot = dirent->inode;
- return 0;
-}
-
-/*
- * Check to make sure a directory entry doesn't contain any illegal
- * characters.
- */
-static int check_name(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
- struct problem_context *pctx)
-{
- int i;
- int fixup = -1;
- int ret = 0;
-
- for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
- if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
- if (fixup < 0) {
- fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
- }
- if (fixup) {
- dirent->name[i] = '.';
- ret = 1;
- }
- }
- }
- return ret;
-}
-
-/*
- * Check the directory filetype (if present)
- */
-
-/*
- * Given a mode, return the ext2 file type
- */
-static int ext2_file_type(unsigned int mode)
-{
- if (LINUX_S_ISREG(mode))
- return EXT2_FT_REG_FILE;
-
- if (LINUX_S_ISDIR(mode))
- return EXT2_FT_DIR;
-
- if (LINUX_S_ISCHR(mode))
- return EXT2_FT_CHRDEV;
-
- if (LINUX_S_ISBLK(mode))
- return EXT2_FT_BLKDEV;
-
- if (LINUX_S_ISLNK(mode))
- return EXT2_FT_SYMLINK;
-
- if (LINUX_S_ISFIFO(mode))
- return EXT2_FT_FIFO;
-
- if (LINUX_S_ISSOCK(mode))
- return EXT2_FT_SOCK;
-
- return 0;
-}
-
-static int check_filetype(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
- struct problem_context *pctx)
-{
- int filetype = dirent->name_len >> 8;
- int should_be = EXT2_FT_UNKNOWN;
- struct ext2_inode inode;
-
- if (!(ctx->fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- if (filetype == 0 ||
- !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
- return 0;
- dirent->name_len = dirent->name_len & 0xFF;
- return 1;
- }
-
- if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
- should_be = EXT2_FT_DIR;
- } else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
- dirent->inode)) {
- should_be = EXT2_FT_REG_FILE;
- } else if (ctx->inode_bad_map &&
- ext2fs_test_inode_bitmap(ctx->inode_bad_map,
- dirent->inode))
- should_be = 0;
- else {
- e2fsck_read_inode(ctx, dirent->inode, &inode,
- "check_filetype");
- should_be = ext2_file_type(inode.i_mode);
- }
- if (filetype == should_be)
- return 0;
- pctx->num = should_be;
-
- if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
- pctx) == 0)
- return 0;
-
- dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
- return 1;
-}
-
-#ifdef ENABLE_HTREE
-static void parse_int_node(ext2_filsys fs,
- struct ext2_db_entry *db,
- struct check_dir_struct *cd,
- struct dx_dir_info *dx_dir,
- char *block_buf)
-{
- struct ext2_dx_root_info *root;
- struct ext2_dx_entry *ent;
- struct ext2_dx_countlimit *limit;
- struct dx_dirblock_info *dx_db;
- int i, expect_limit, count;
- blk_t blk;
- ext2_dirhash_t min_hash = 0xffffffff;
- ext2_dirhash_t max_hash = 0;
- ext2_dirhash_t hash = 0, prev_hash;
-
- if (db->blockcnt == 0) {
- root = (struct ext2_dx_root_info *) (block_buf + 24);
- ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
- } else {
- ent = (struct ext2_dx_entry *) (block_buf+8);
- }
- limit = (struct ext2_dx_countlimit *) ent;
-
- count = ext2fs_le16_to_cpu(limit->count);
- expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
- sizeof(struct ext2_dx_entry);
- if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
- cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
- if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
- goto clear_and_exit;
- }
- if (count > expect_limit) {
- cd->pctx.num = count;
- if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
- goto clear_and_exit;
- count = expect_limit;
- }
-
- for (i=0; i < count; i++) {
- prev_hash = hash;
- hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
- blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
- /* Check to make sure the block is valid */
- if (blk > (blk_t) dx_dir->numblocks) {
- cd->pctx.blk = blk;
- if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
- &cd->pctx))
- goto clear_and_exit;
- }
- if (hash < prev_hash &&
- fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
- goto clear_and_exit;
- dx_db = &dx_dir->dx_block[blk];
- if (dx_db->flags & DX_FLAG_REFERENCED) {
- dx_db->flags |= DX_FLAG_DUP_REF;
- } else {
- dx_db->flags |= DX_FLAG_REFERENCED;
- dx_db->parent = db->blockcnt;
- }
- if (hash < min_hash)
- min_hash = hash;
- if (hash > max_hash)
- max_hash = hash;
- dx_db->node_min_hash = hash;
- if ((i+1) < count)
- dx_db->node_max_hash =
- ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
- else {
- dx_db->node_max_hash = 0xfffffffe;
- dx_db->flags |= DX_FLAG_LAST;
- }
- if (i == 0)
- dx_db->flags |= DX_FLAG_FIRST;
- }
- dx_db = &dx_dir->dx_block[db->blockcnt];
- dx_db->min_hash = min_hash;
- dx_db->max_hash = max_hash;
- return;
-
-clear_and_exit:
- clear_htree(cd->ctx, cd->pctx.ino);
- dx_dir->numblocks = 0;
-}
-#endif /* ENABLE_HTREE */
-
-/*
- * Given a busted directory, try to salvage it somehow.
- *
- */
-static void salvage_directory(ext2_filsys fs,
- struct ext2_dir_entry *dirent,
- struct ext2_dir_entry *prev,
- unsigned int *offset)
-{
- char *cp = (char *) dirent;
- int left = fs->blocksize - *offset - dirent->rec_len;
- int name_len = dirent->name_len & 0xFF;
-
- /*
- * Special case of directory entry of size 8: copy what's left
- * of the directory block up to cover up the invalid hole.
- */
- if ((left >= 12) && (dirent->rec_len == 8)) {
- memmove(cp, cp+8, left);
- memset(cp + left, 0, 8);
- return;
- }
- /*
- * If the directory entry overruns the end of the directory
- * block, and the name is small enough to fit, then adjust the
- * record length.
- */
- if ((left < 0) &&
- (name_len + 8 <= dirent->rec_len + left) &&
- dirent->inode <= fs->super->s_inodes_count &&
- strnlen(dirent->name, name_len) == name_len) {
- dirent->rec_len += left;
- return;
- }
- /*
- * If the directory entry is a multiple of four, so it is
- * valid, let the previous directory entry absorb the invalid
- * one.
- */
- if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
- prev->rec_len += dirent->rec_len;
- *offset += dirent->rec_len;
- return;
- }
- /*
- * Default salvage method --- kill all of the directory
- * entries for the rest of the block. We will either try to
- * absorb it into the previous directory entry, or create a
- * new empty directory entry the rest of the directory block.
- */
- if (prev) {
- prev->rec_len += fs->blocksize - *offset;
- *offset = fs->blocksize;
- } else {
- dirent->rec_len = fs->blocksize - *offset;
- dirent->name_len = 0;
- dirent->inode = 0;
- }
-}
-
-static int check_dir_block(ext2_filsys fs,
- struct ext2_db_entry *db,
- void *priv_data)
-{
- struct dir_info *subdir, *dir;
- struct dx_dir_info *dx_dir;
-#ifdef ENABLE_HTREE
- struct dx_dirblock_info *dx_db = NULL;
-#endif /* ENABLE_HTREE */
- struct ext2_dir_entry *dirent, *prev;
- ext2_dirhash_t hash;
- unsigned int offset = 0;
- int dir_modified = 0;
- int dot_state;
- blk_t block_nr = db->blk;
- ext2_ino_t ino = db->ino;
- __u16 links;
- struct check_dir_struct *cd;
- char *buf;
- e2fsck_t ctx;
- int problem;
- struct ext2_dx_root_info *root;
- struct ext2_dx_countlimit *limit;
- static dict_t de_dict;
- struct problem_context pctx;
- int dups_found = 0;
-
- cd = (struct check_dir_struct *) priv_data;
- buf = cd->buf;
- ctx = cd->ctx;
-
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return DIRENT_ABORT;
-
- if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
- return DIRENT_ABORT;
-
- /*
- * Make sure the inode is still in use (could have been
- * deleted in the duplicate/bad blocks pass.
- */
- if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
- return 0;
-
- cd->pctx.ino = ino;
- cd->pctx.blk = block_nr;
- cd->pctx.blkcount = db->blockcnt;
- cd->pctx.ino2 = 0;
- cd->pctx.dirent = 0;
- cd->pctx.num = 0;
-
- if (db->blk == 0) {
- if (allocate_dir_block(ctx, db, &cd->pctx))
- return 0;
- block_nr = db->blk;
- }
-
- if (db->blockcnt)
- dot_state = 2;
- else
- dot_state = 0;
-
- if (ctx->dirs_to_hash &&
- ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
- dups_found++;
-
- cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
- if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
- cd->pctx.errcode = 0; /* We'll handle this ourselves */
- if (cd->pctx.errcode) {
- if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
- ctx->flags |= E2F_FLAG_ABORT;
- return DIRENT_ABORT;
- }
- memset(buf, 0, fs->blocksize);
- }
-#ifdef ENABLE_HTREE
- dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
- if (dx_dir && dx_dir->numblocks) {
- if (db->blockcnt >= dx_dir->numblocks) {
- printf("XXX should never happen!!!\n");
- abort();
- }
- dx_db = &dx_dir->dx_block[db->blockcnt];
- dx_db->type = DX_DIRBLOCK_LEAF;
- dx_db->phys = block_nr;
- dx_db->min_hash = ~0;
- dx_db->max_hash = 0;
-
- dirent = (struct ext2_dir_entry *) buf;
- limit = (struct ext2_dx_countlimit *) (buf+8);
- if (db->blockcnt == 0) {
- root = (struct ext2_dx_root_info *) (buf + 24);
- dx_db->type = DX_DIRBLOCK_ROOT;
- dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
- if ((root->reserved_zero ||
- root->info_length < 8 ||
- root->indirect_levels > 1) &&
- fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
- clear_htree(ctx, ino);
- dx_dir->numblocks = 0;
- dx_db = 0;
- }
- dx_dir->hashversion = root->hash_version;
- dx_dir->depth = root->indirect_levels + 1;
- } else if ((dirent->inode == 0) &&
- (dirent->rec_len == fs->blocksize) &&
- (dirent->name_len == 0) &&
- (ext2fs_le16_to_cpu(limit->limit) ==
- ((fs->blocksize-8) /
- sizeof(struct ext2_dx_entry))))
- dx_db->type = DX_DIRBLOCK_NODE;
- }
-#endif /* ENABLE_HTREE */
-
- dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
- prev = 0;
- do {
- problem = 0;
- dirent = (struct ext2_dir_entry *) (buf + offset);
- cd->pctx.dirent = dirent;
- cd->pctx.num = offset;
- if (((offset + dirent->rec_len) > fs->blocksize) ||
- (dirent->rec_len < 12) ||
- ((dirent->rec_len % 4) != 0) ||
- (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
- if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
- salvage_directory(fs, dirent, prev, &offset);
- dir_modified++;
- continue;
- } else
- goto abort_free_dict;
- }
- if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
- if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
- dirent->name_len = EXT2_NAME_LEN;
- dir_modified++;
- }
- }
-
- if (dot_state == 0) {
- if (check_dot(ctx, dirent, ino, &cd->pctx))
- dir_modified++;
- } else if (dot_state == 1) {
- dir = e2fsck_get_dir_info(ctx, ino);
- if (!dir) {
- fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
- goto abort_free_dict;
- }
- if (check_dotdot(ctx, dirent, dir, &cd->pctx))
- dir_modified++;
- } else if (dirent->inode == ino) {
- problem = PR_2_LINK_DOT;
- if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
- dirent->inode = 0;
- dir_modified++;
- goto next;
- }
- }
- if (!dirent->inode)
- goto next;
-
- /*
- * Make sure the inode listed is a legal one.
- */
- if (((dirent->inode != EXT2_ROOT_INO) &&
- (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
- (dirent->inode > fs->super->s_inodes_count)) {
- problem = PR_2_BAD_INO;
- } else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
- dirent->inode))) {
- /*
- * If the inode is unused, offer to clear it.
- */
- problem = PR_2_UNUSED_INODE;
- } else if ((dot_state > 1) &&
- ((dirent->name_len & 0xFF) == 1) &&
- (dirent->name[0] == '.')) {
- /*
- * If there's a '.' entry in anything other
- * than the first directory entry, it's a
- * duplicate entry that should be removed.
- */
- problem = PR_2_DUP_DOT;
- } else if ((dot_state > 1) &&
- ((dirent->name_len & 0xFF) == 2) &&
- (dirent->name[0] == '.') &&
- (dirent->name[1] == '.')) {
- /*
- * If there's a '..' entry in anything other
- * than the second directory entry, it's a
- * duplicate entry that should be removed.
- */
- problem = PR_2_DUP_DOT_DOT;
- } else if ((dot_state > 1) &&
- (dirent->inode == EXT2_ROOT_INO)) {
- /*
- * Don't allow links to the root directory.
- * We check this specially to make sure we
- * catch this error case even if the root
- * directory hasn't been created yet.
- */
- problem = PR_2_LINK_ROOT;
- } else if ((dot_state > 1) &&
- (dirent->name_len & 0xFF) == 0) {
- /*
- * Don't allow zero-length directory names.
- */
- problem = PR_2_NULL_NAME;
- }
-
- if (problem) {
- if (fix_problem(ctx, problem, &cd->pctx)) {
- dirent->inode = 0;
- dir_modified++;
- goto next;
- } else {
- ext2fs_unmark_valid(fs);
- if (problem == PR_2_BAD_INO)
- goto next;
- }
- }
-
- /*
- * If the inode was marked as having bad fields in
- * pass1, process it and offer to fix/clear it.
- * (We wait until now so that we can display the
- * pathname to the user.)
- */
- if (ctx->inode_bad_map &&
- ext2fs_test_inode_bitmap(ctx->inode_bad_map,
- dirent->inode)) {
- if (e2fsck_process_bad_inode(ctx, ino,
- dirent->inode,
- buf + fs->blocksize)) {
- dirent->inode = 0;
- dir_modified++;
- goto next;
- }
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return DIRENT_ABORT;
- }
-
- if (check_name(ctx, dirent, &cd->pctx))
- dir_modified++;
-
- if (check_filetype(ctx, dirent, &cd->pctx))
- dir_modified++;
-
-#ifdef ENABLE_HTREE
- if (dx_db) {
- ext2fs_dirhash(dx_dir->hashversion, dirent->name,
- (dirent->name_len & 0xFF),
- fs->super->s_hash_seed, &hash, 0);
- if (hash < dx_db->min_hash)
- dx_db->min_hash = hash;
- if (hash > dx_db->max_hash)
- dx_db->max_hash = hash;
- }
-#endif
-
- /*
- * If this is a directory, then mark its parent in its
- * dir_info structure. If the parent field is already
- * filled in, then this directory has more than one
- * hard link. We assume the first link is correct,
- * and ask the user if he/she wants to clear this one.
- */
- if ((dot_state > 1) &&
- (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
- dirent->inode))) {
- subdir = e2fsck_get_dir_info(ctx, dirent->inode);
- if (!subdir) {
- cd->pctx.ino = dirent->inode;
- fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
- goto abort_free_dict;
- }
- if (subdir->parent) {
- cd->pctx.ino2 = subdir->parent;
- if (fix_problem(ctx, PR_2_LINK_DIR,
- &cd->pctx)) {
- dirent->inode = 0;
- dir_modified++;
- goto next;
- }
- cd->pctx.ino2 = 0;
- } else
- subdir->parent = ino;
- }
-
- if (dups_found) {
- ;
- } else if (dict_lookup(&de_dict, dirent)) {
- clear_problem_context(&pctx);
- pctx.ino = ino;
- pctx.dirent = dirent;
- fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
- if (!ctx->dirs_to_hash)
- ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
- if (ctx->dirs_to_hash)
- ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
- dups_found++;
- } else
- dict_alloc_insert(&de_dict, dirent, dirent);
-
- ext2fs_icount_increment(ctx->inode_count, dirent->inode,
- &links);
- if (links > 1)
- ctx->fs_links_count++;
- ctx->fs_total_count++;
- next:
- prev = dirent;
- offset += dirent->rec_len;
- dot_state++;
- } while (offset < fs->blocksize);
-#ifdef ENABLE_HTREE
- if (dx_db) {
- cd->pctx.dir = cd->pctx.ino;
- if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
- (dx_db->type == DX_DIRBLOCK_NODE))
- parse_int_node(fs, db, cd, dx_dir, buf);
- }
-#endif /* ENABLE_HTREE */
- if (offset != fs->blocksize) {
- cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
- if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
- dirent->rec_len = cd->pctx.num;
- dir_modified++;
- }
- }
- if (dir_modified) {
- cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
- if (cd->pctx.errcode) {
- if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
- &cd->pctx))
- goto abort_free_dict;
- }
- ext2fs_mark_changed(fs);
- }
- dict_free_nodes(&de_dict);
- return 0;
-abort_free_dict:
- dict_free_nodes(&de_dict);
- ctx->flags |= E2F_FLAG_ABORT;
- return DIRENT_ABORT;
-}
-
-/*
- * This function is called to deallocate a block, and is an interator
- * functioned called by deallocate inode via ext2fs_iterate_block().
- */
-static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- e2fsck_t ctx = (e2fsck_t) priv_data;
-
- if (HOLE_BLKADDR(*block_nr))
- return 0;
- if ((*block_nr < fs->super->s_first_data_block) ||
- (*block_nr >= fs->super->s_blocks_count))
- return 0;
- ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
- ext2fs_block_alloc_stats(fs, *block_nr, -1);
- return 0;
-}
-
-/*
- * This fuction deallocates an inode
- */
-static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
-{
- ext2_filsys fs = ctx->fs;
- struct ext2_inode inode;
- struct problem_context pctx;
- __u32 count;
-
- ext2fs_icount_store(ctx->inode_link_info, ino, 0);
- e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
- inode.i_links_count = 0;
- inode.i_dtime = time(NULL);
- e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
- clear_problem_context(&pctx);
- pctx.ino = ino;
-
- /*
- * Fix up the bitmaps...
- */
- e2fsck_read_bitmaps(ctx);
- ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
- ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
- if (ctx->inode_bad_map)
- ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
- ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
-
- if (inode.i_file_acl &&
- (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
- pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
- block_buf, -1, &count);
- if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
- pctx.errcode = 0;
- count = 1;
- }
- if (pctx.errcode) {
- pctx.blk = inode.i_file_acl;
- fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (count == 0) {
- ext2fs_unmark_block_bitmap(ctx->block_found_map,
- inode.i_file_acl);
- ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
- }
- inode.i_file_acl = 0;
- }
-
- if (!ext2fs_inode_has_valid_blocks(&inode))
- return;
-
- if (LINUX_S_ISREG(inode.i_mode) &&
- (inode.i_size_high || inode.i_size & 0x80000000UL))
- ctx->large_files--;
-
- pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
- deallocate_inode_block, ctx);
- if (pctx.errcode) {
- fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-}
-
-/*
- * This fuction clears the htree flag on an inode
- */
-static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
-{
- struct ext2_inode inode;
-
- e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
- inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
- e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
- if (ctx->dirs_to_hash)
- ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
-}
-
-
-static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
- ext2_ino_t ino, char *buf)
-{
- ext2_filsys fs = ctx->fs;
- struct ext2_inode inode;
- int inode_modified = 0;
- int not_fixed = 0;
- unsigned char *frag, *fsize;
- struct problem_context pctx;
- int problem = 0;
-
- e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
-
- clear_problem_context(&pctx);
- pctx.ino = ino;
- pctx.dir = dir;
- pctx.inode = &inode;
-
- if (inode.i_file_acl &&
- !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
- fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
- inode.i_file_acl = 0;
-#if BB_BIG_ENDIAN
- /*
- * This is a special kludge to deal with long symlinks
- * on big endian systems. i_blocks had already been
- * decremented earlier in pass 1, but since i_file_acl
- * hadn't yet been cleared, ext2fs_read_inode()
- * assumed that the file was short symlink and would
- * not have byte swapped i_block[0]. Hence, we have
- * to byte-swap it here.
- */
- if (LINUX_S_ISLNK(inode.i_mode) &&
- (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
- (inode.i_blocks == fs->blocksize >> 9))
- inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
-#endif
- inode_modified++;
- } else
- not_fixed++;
-
- if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
- !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
- !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
- !(LINUX_S_ISSOCK(inode.i_mode)))
- problem = PR_2_BAD_MODE;
- else if (LINUX_S_ISCHR(inode.i_mode)
- && !e2fsck_pass1_check_device_inode(fs, &inode))
- problem = PR_2_BAD_CHAR_DEV;
- else if (LINUX_S_ISBLK(inode.i_mode)
- && !e2fsck_pass1_check_device_inode(fs, &inode))
- problem = PR_2_BAD_BLOCK_DEV;
- else if (LINUX_S_ISFIFO(inode.i_mode)
- && !e2fsck_pass1_check_device_inode(fs, &inode))
- problem = PR_2_BAD_FIFO;
- else if (LINUX_S_ISSOCK(inode.i_mode)
- && !e2fsck_pass1_check_device_inode(fs, &inode))
- problem = PR_2_BAD_SOCKET;
- else if (LINUX_S_ISLNK(inode.i_mode)
- && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
- problem = PR_2_INVALID_SYMLINK;
- }
-
- if (problem) {
- if (fix_problem(ctx, problem, &pctx)) {
- deallocate_inode(ctx, ino, 0);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return 0;
- return 1;
- } else
- not_fixed++;
- problem = 0;
- }
-
- if (inode.i_faddr) {
- if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
- inode.i_faddr = 0;
- inode_modified++;
- } else
- not_fixed++;
- }
-
- switch (fs->super->s_creator_os) {
- case EXT2_OS_LINUX:
- frag = &inode.osd2.linux2.l_i_frag;
- fsize = &inode.osd2.linux2.l_i_fsize;
- break;
- case EXT2_OS_HURD:
- frag = &inode.osd2.hurd2.h_i_frag;
- fsize = &inode.osd2.hurd2.h_i_fsize;
- break;
- case EXT2_OS_MASIX:
- frag = &inode.osd2.masix2.m_i_frag;
- fsize = &inode.osd2.masix2.m_i_fsize;
- break;
- default:
- frag = fsize = 0;
- }
- if (frag && *frag) {
- pctx.num = *frag;
- if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
- *frag = 0;
- inode_modified++;
- } else
- not_fixed++;
- pctx.num = 0;
- }
- if (fsize && *fsize) {
- pctx.num = *fsize;
- if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
- *fsize = 0;
- inode_modified++;
- } else
- not_fixed++;
- pctx.num = 0;
- }
-
- if (inode.i_file_acl &&
- ((inode.i_file_acl < fs->super->s_first_data_block) ||
- (inode.i_file_acl >= fs->super->s_blocks_count))) {
- if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
- inode.i_file_acl = 0;
- inode_modified++;
- } else
- not_fixed++;
- }
- if (inode.i_dir_acl &&
- LINUX_S_ISDIR(inode.i_mode)) {
- if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
- inode.i_dir_acl = 0;
- inode_modified++;
- } else
- not_fixed++;
- }
-
- if (inode_modified)
- e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
- if (!not_fixed)
- ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
- return 0;
-}
-
-
-/*
- * allocate_dir_block --- this function allocates a new directory
- * block for a particular inode; this is done if a directory has
- * a "hole" in it, or if a directory has a illegal block number
- * that was zeroed out and now needs to be replaced.
- */
-static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
- struct problem_context *pctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t blk;
- char *block;
- struct ext2_inode inode;
-
- if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
- return 1;
-
- /*
- * Read the inode and block bitmaps in; we'll be messing with
- * them.
- */
- e2fsck_read_bitmaps(ctx);
-
- /*
- * First, find a free block
- */
- pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
- if (pctx->errcode) {
- pctx->str = "ext2fs_new_block";
- fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
- return 1;
- }
- ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
- ext2fs_mark_block_bitmap(fs->block_map, blk);
- ext2fs_mark_bb_dirty(fs);
-
- /*
- * Now let's create the actual data block for the inode
- */
- if (db->blockcnt)
- pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
- else
- pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
- EXT2_ROOT_INO, &block);
-
- if (pctx->errcode) {
- pctx->str = "ext2fs_new_dir_block";
- fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
- return 1;
- }
-
- pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
- ext2fs_free_mem(&block);
- if (pctx->errcode) {
- pctx->str = "ext2fs_write_dir_block";
- fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
- return 1;
- }
-
- /*
- * Update the inode block count
- */
- e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
- inode.i_blocks += fs->blocksize / 512;
- if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
- inode.i_size = (db->blockcnt+1) * fs->blocksize;
- e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
-
- /*
- * Finally, update the block pointers for the inode
- */
- db->blk = blk;
- pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
- 0, update_dir_block, db);
- if (pctx->errcode) {
- pctx->str = "ext2fs_block_iterate";
- fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
- return 1;
- }
-
- return 0;
-}
-
-/*
- * This is a helper function for allocate_dir_block().
- */
-static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct ext2_db_entry *db;
-
- db = (struct ext2_db_entry *) priv_data;
- if (db->blockcnt == (int) blockcnt) {
- *block_nr = db->blk;
- return BLOCK_CHANGED;
- }
- return 0;
-}
-
-/*
- * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
- *
- * Pass #3 assures that all directories are connected to the
- * filesystem tree, using the following algorithm:
- *
- * First, the root directory is checked to make sure it exists; if
- * not, e2fsck will offer to create a new one. It is then marked as
- * "done".
- *
- * Then, pass3 interates over all directory inodes; for each directory
- * it attempts to trace up the filesystem tree, using dirinfo.parent
- * until it reaches a directory which has been marked "done". If it
- * cannot do so, then the directory must be disconnected, and e2fsck
- * will offer to reconnect it to /lost+found. While it is chasing
- * parent pointers up the filesystem tree, if pass3 sees a directory
- * twice, then it has detected a filesystem loop, and it will again
- * offer to reconnect the directory to /lost+found in to break the
- * filesystem loop.
- *
- * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
- * reconnect inodes to /lost+found; this subroutine is also used by
- * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
- * is responsible for creating /lost+found if it does not exist.
- *
- * Pass 3 frees the following data structures:
- * - The dirinfo directory information cache.
- */
-
-static void check_root(e2fsck_t ctx);
-static int check_directory(e2fsck_t ctx, struct dir_info *dir,
- struct problem_context *pctx);
-static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
-
-static ext2fs_inode_bitmap inode_loop_detect;
-static ext2fs_inode_bitmap inode_done_map;
-
-static void e2fsck_pass3(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- int i;
- struct problem_context pctx;
- struct dir_info *dir;
- unsigned long maxdirs, count;
-
- clear_problem_context(&pctx);
-
- /* Pass 3 */
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
-
- /*
- * Allocate some bitmaps to do loop detection.
- */
- pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
- &inode_done_map);
- if (pctx.errcode) {
- pctx.num = 2;
- fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- goto abort_exit;
- }
- check_root(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- goto abort_exit;
-
- ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
-
- maxdirs = e2fsck_get_num_dirinfo(ctx);
- count = 1;
-
- if (ctx->progress)
- if ((ctx->progress)(ctx, 3, 0, maxdirs))
- goto abort_exit;
-
- for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- goto abort_exit;
- if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
- goto abort_exit;
- if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
- if (check_directory(ctx, dir, &pctx))
- goto abort_exit;
- }
-
- /*
- * Force the creation of /lost+found if not present
- */
- if ((ctx->flags & E2F_OPT_READONLY) == 0)
- e2fsck_get_lost_and_found(ctx, 1);
-
- /*
- * If there are any directories that need to be indexed or
- * optimized, do it here.
- */
- e2fsck_rehash_directories(ctx);
-
-abort_exit:
- e2fsck_free_dir_info(ctx);
- ext2fs_free_inode_bitmap(inode_loop_detect);
- inode_loop_detect = 0;
- ext2fs_free_inode_bitmap(inode_done_map);
- inode_done_map = 0;
-}
-
-/*
- * This makes sure the root inode is present; if not, we ask if the
- * user wants us to create it. Not creating it is a fatal error.
- */
-static void check_root(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t blk;
- struct ext2_inode inode;
- char * block;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
- /*
- * If the root inode is not a directory, die here. The
- * user must have answered 'no' in pass1 when we
- * offered to clear it.
- */
- if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
- EXT2_ROOT_INO))) {
- fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- }
- return;
- }
-
- if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
- fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- e2fsck_read_bitmaps(ctx);
-
- /*
- * First, find a free block
- */
- pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
- if (pctx.errcode) {
- pctx.str = "ext2fs_new_block";
- fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
- ext2fs_mark_block_bitmap(fs->block_map, blk);
- ext2fs_mark_bb_dirty(fs);
-
- /*
- * Now let's create the actual data block for the inode
- */
- pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
- &block);
- if (pctx.errcode) {
- pctx.str = "ext2fs_new_dir_block";
- fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
- if (pctx.errcode) {
- pctx.str = "ext2fs_write_dir_block";
- fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_free_mem(&block);
-
- /*
- * Set up the inode structure
- */
- memset(&inode, 0, sizeof(inode));
- inode.i_mode = 040755;
- inode.i_size = fs->blocksize;
- inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
- inode.i_links_count = 2;
- inode.i_blocks = fs->blocksize / 512;
- inode.i_block[0] = blk;
-
- /*
- * Write out the inode.
- */
- pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
- if (pctx.errcode) {
- pctx.str = "ext2fs_write_inode";
- fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- /*
- * Miscellaneous bookkeeping...
- */
- e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
- ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
- ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
-
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
- ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
- ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
- ext2fs_mark_ib_dirty(fs);
-}
-
-/*
- * This subroutine is responsible for making sure that a particular
- * directory is connected to the root; if it isn't we trace it up as
- * far as we can go, and then offer to connect the resulting parent to
- * the lost+found. We have to do loop detection; if we ever discover
- * a loop, we treat that as a disconnected directory and offer to
- * reparent it to lost+found.
- *
- * However, loop detection is expensive, because for very large
- * filesystems, the inode_loop_detect bitmap is huge, and clearing it
- * is non-trivial. Loops in filesystems are also a rare error case,
- * and we shouldn't optimize for error cases. So we try two passes of
- * the algorithm. The first time, we ignore loop detection and merely
- * increment a counter; if the counter exceeds some extreme threshold,
- * then we try again with the loop detection bitmap enabled.
- */
-static int check_directory(e2fsck_t ctx, struct dir_info *dir,
- struct problem_context *pctx)
-{
- ext2_filsys fs = ctx->fs;
- struct dir_info *p = dir;
- int loop_pass = 0, parent_count = 0;
-
- if (!p)
- return 0;
-
- while (1) {
- /*
- * Mark this inode as being "done"; by the time we
- * return from this function, the inode we either be
- * verified as being connected to the directory tree,
- * or we will have offered to reconnect this to
- * lost+found.
- *
- * If it was marked done already, then we've reached a
- * parent we've already checked.
- */
- if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
- break;
-
- /*
- * If this directory doesn't have a parent, or we've
- * seen the parent once already, then offer to
- * reparent it to lost+found
- */
- if (!p->parent ||
- (loop_pass &&
- (ext2fs_test_inode_bitmap(inode_loop_detect,
- p->parent)))) {
- pctx->ino = p->ino;
- if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
- if (e2fsck_reconnect_file(ctx, pctx->ino))
- ext2fs_unmark_valid(fs);
- else {
- p = e2fsck_get_dir_info(ctx, pctx->ino);
- p->parent = ctx->lost_and_found;
- fix_dotdot(ctx, p, ctx->lost_and_found);
- }
- }
- break;
- }
- p = e2fsck_get_dir_info(ctx, p->parent);
- if (!p) {
- fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
- return 0;
- }
- if (loop_pass) {
- ext2fs_mark_inode_bitmap(inode_loop_detect,
- p->ino);
- } else if (parent_count++ > 2048) {
- /*
- * If we've run into a path depth that's
- * greater than 2048, try again with the inode
- * loop bitmap turned on and start from the
- * top.
- */
- loop_pass = 1;
- if (inode_loop_detect)
- ext2fs_clear_inode_bitmap(inode_loop_detect);
- else {
- pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
- if (pctx->errcode) {
- pctx->num = 1;
- fix_problem(ctx,
- PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return -1;
- }
- }
- p = dir;
- }
- }
-
- /*
- * Make sure that .. and the parent directory are the same;
- * offer to fix it if not.
- */
- if (dir->parent != dir->dotdot) {
- pctx->ino = dir->ino;
- pctx->ino2 = dir->dotdot;
- pctx->dir = dir->parent;
- if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
- fix_dotdot(ctx, dir, dir->parent);
- }
- return 0;
-}
-
-/*
- * This routine gets the lost_and_found inode, making it a directory
- * if necessary
- */
-ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino;
- blk_t blk;
- errcode_t retval;
- struct ext2_inode inode;
- char * block;
- static const char name[] = "lost+found";
- struct problem_context pctx;
- struct dir_info *dirinfo;
-
- if (ctx->lost_and_found)
- return ctx->lost_and_found;
-
- clear_problem_context(&pctx);
-
- retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
- sizeof(name)-1, 0, &ino);
- if (retval && !fix)
- return 0;
- if (!retval) {
- if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
- ctx->lost_and_found = ino;
- return ino;
- }
-
- /* Lost+found isn't a directory! */
- if (!fix)
- return 0;
- pctx.ino = ino;
- if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
- return 0;
-
- /* OK, unlink the old /lost+found file. */
- pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
- if (pctx.errcode) {
- pctx.str = "ext2fs_unlink";
- fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
- return 0;
- }
- dirinfo = e2fsck_get_dir_info(ctx, ino);
- if (dirinfo)
- dirinfo->parent = 0;
- e2fsck_adjust_inode_count(ctx, ino, -1);
- } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
- }
- if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
- return 0;
-
- /*
- * Read the inode and block bitmaps in; we'll be messing with
- * them.
- */
- e2fsck_read_bitmaps(ctx);
-
- /*
- * First, find a free block
- */
- retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
- return 0;
- }
- ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
- ext2fs_block_alloc_stats(fs, blk, +1);
-
- /*
- * Next find a free inode.
- */
- retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
- ctx->inode_used_map, &ino);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
- return 0;
- }
- ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
- ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
- ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
-
- /*
- * Now let's create the actual data block for the inode
- */
- retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
- return 0;
- }
-
- retval = ext2fs_write_dir_block(fs, blk, block);
- ext2fs_free_mem(&block);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
- return 0;
- }
-
- /*
- * Set up the inode structure
- */
- memset(&inode, 0, sizeof(inode));
- inode.i_mode = 040700;
- inode.i_size = fs->blocksize;
- inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
- inode.i_links_count = 2;
- inode.i_blocks = fs->blocksize / 512;
- inode.i_block[0] = blk;
-
- /*
- * Next, write out the inode.
- */
- pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
- if (pctx.errcode) {
- pctx.str = "ext2fs_write_inode";
- fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
- return 0;
- }
- /*
- * Finally, create the directory link
- */
- pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
- if (pctx.errcode) {
- pctx.str = "ext2fs_link";
- fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
- return 0;
- }
-
- /*
- * Miscellaneous bookkeeping that needs to be kept straight.
- */
- e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
- e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
- ext2fs_icount_store(ctx->inode_count, ino, 2);
- ext2fs_icount_store(ctx->inode_link_info, ino, 2);
- ctx->lost_and_found = ino;
- return ino;
-}
-
-/*
- * This routine will connect a file to lost+found
- */
-int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- char name[80];
- struct problem_context pctx;
- struct ext2_inode inode;
- int file_type = 0;
-
- clear_problem_context(&pctx);
- pctx.ino = ino;
-
- if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
- if (e2fsck_get_lost_and_found(ctx, 1) == 0)
- ctx->bad_lost_and_found++;
- }
- if (ctx->bad_lost_and_found) {
- fix_problem(ctx, PR_3_NO_LPF, &pctx);
- return 1;
- }
-
- sprintf(name, "#%u", ino);
- if (ext2fs_read_inode(fs, ino, &inode) == 0)
- file_type = ext2_file_type(inode.i_mode);
- retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
- if (retval == EXT2_ET_DIR_NO_SPACE) {
- if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
- return 1;
- retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
- 1, 0);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
- return 1;
- }
- retval = ext2fs_link(fs, ctx->lost_and_found, name,
- ino, file_type);
- }
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
- return 1;
- }
- e2fsck_adjust_inode_count(ctx, ino, 1);
-
- return 0;
-}
-
-/*
- * Utility routine to adjust the inode counts on an inode.
- */
-errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- struct ext2_inode inode;
-
- if (!ino)
- return 0;
-
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
- return retval;
-
- if (adj == 1) {
- ext2fs_icount_increment(ctx->inode_count, ino, 0);
- if (inode.i_links_count == (__u16) ~0)
- return 0;
- ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
- inode.i_links_count++;
- } else if (adj == -1) {
- ext2fs_icount_decrement(ctx->inode_count, ino, 0);
- if (inode.i_links_count == 0)
- return 0;
- ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
- inode.i_links_count--;
- }
-
- retval = ext2fs_write_inode(fs, ino, &inode);
- if (retval)
- return retval;
-
- return 0;
-}
-
-/*
- * Fix parent --- this routine fixes up the parent of a directory.
- */
-struct fix_dotdot_struct {
- ext2_filsys fs;
- ext2_ino_t parent;
- int done;
- e2fsck_t ctx;
-};
-
-static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
- int offset FSCK_ATTR((unused)),
- int blocksize FSCK_ATTR((unused)),
- char *buf FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
- errcode_t retval;
- struct problem_context pctx;
-
- if ((dirent->name_len & 0xFF) != 2)
- return 0;
- if (strncmp(dirent->name, "..", 2))
- return 0;
-
- clear_problem_context(&pctx);
-
- retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
- }
- retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
- }
- dirent->inode = fp->parent;
-
- fp->done++;
- return DIRENT_ABORT | DIRENT_CHANGED;
-}
-
-static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- struct fix_dotdot_struct fp;
- struct problem_context pctx;
-
- fp.fs = fs;
- fp.parent = parent;
- fp.done = 0;
- fp.ctx = ctx;
-
- retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
- 0, fix_dotdot_proc, &fp);
- if (retval || !fp.done) {
- clear_problem_context(&pctx);
- pctx.ino = dir->ino;
- pctx.errcode = retval;
- fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
- PR_3_FIX_PARENT_NOFIND, &pctx);
- ext2fs_unmark_valid(fs);
- }
- dir->dotdot = parent;
-}
-
-/*
- * These routines are responsible for expanding a /lost+found if it is
- * too small.
- */
-
-struct expand_dir_struct {
- int num;
- int guaranteed_size;
- int newblocks;
- int last_block;
- errcode_t err;
- e2fsck_t ctx;
-};
-
-static int expand_dir_proc(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
- blk_t new_blk;
- static blk_t last_blk = 0;
- char *block;
- errcode_t retval;
- e2fsck_t ctx;
-
- ctx = es->ctx;
-
- if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
- return BLOCK_ABORT;
-
- if (blockcnt > 0)
- es->last_block = blockcnt;
- if (*blocknr) {
- last_blk = *blocknr;
- return 0;
- }
- retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
- &new_blk);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- if (blockcnt > 0) {
- retval = ext2fs_new_dir_block(fs, 0, 0, &block);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- es->num--;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
- } else {
- retval = ext2fs_get_mem(fs->blocksize, &block);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- memset(block, 0, fs->blocksize);
- retval = io_channel_write_blk(fs->io, new_blk, 1, block);
- }
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- ext2fs_free_mem(&block);
- *blocknr = new_blk;
- ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
- ext2fs_block_alloc_stats(fs, new_blk, +1);
- es->newblocks++;
-
- if (es->num == 0)
- return (BLOCK_CHANGED | BLOCK_ABORT);
- else
- return BLOCK_CHANGED;
-}
-
-errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
- int num, int guaranteed_size)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- struct expand_dir_struct es;
- struct ext2_inode inode;
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- /*
- * Read the inode and block bitmaps in; we'll be messing with
- * them.
- */
- e2fsck_read_bitmaps(ctx);
-
- retval = ext2fs_check_directory(fs, dir);
- if (retval)
- return retval;
-
- es.num = num;
- es.guaranteed_size = guaranteed_size;
- es.last_block = 0;
- es.err = 0;
- es.newblocks = 0;
- es.ctx = ctx;
-
- retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
- 0, expand_dir_proc, &es);
-
- if (es.err)
- return es.err;
-
- /*
- * Update the size and block count fields in the inode.
- */
- retval = ext2fs_read_inode(fs, dir, &inode);
- if (retval)
- return retval;
-
- inode.i_size = (es.last_block + 1) * fs->blocksize;
- inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
-
- e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
-
- return 0;
-}
-
-/*
- * pass4.c -- pass #4 of e2fsck: Check reference counts
- *
- * Pass 4 frees the following data structures:
- * - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
- */
-
-/*
- * This routine is called when an inode is not connected to the
- * directory tree.
- *
- * This subroutine returns 1 then the caller shouldn't bother with the
- * rest of the pass 4 tests.
- */
-static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
-{
- ext2_filsys fs = ctx->fs;
- struct ext2_inode inode;
- struct problem_context pctx;
-
- e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
- clear_problem_context(&pctx);
- pctx.ino = i;
- pctx.inode = &inode;
-
- /*
- * Offer to delete any zero-length files that does not have
- * blocks. If there is an EA block, it might have useful
- * information, so we won't prompt to delete it, but let it be
- * reconnected to lost+found.
- */
- if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
- LINUX_S_ISDIR(inode.i_mode))) {
- if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
- ext2fs_icount_store(ctx->inode_link_info, i, 0);
- inode.i_links_count = 0;
- inode.i_dtime = time(NULL);
- e2fsck_write_inode(ctx, i, &inode,
- "disconnect_inode");
- /*
- * Fix up the bitmaps...
- */
- e2fsck_read_bitmaps(ctx);
- ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
- ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
- ext2fs_inode_alloc_stats2(fs, i, -1,
- LINUX_S_ISDIR(inode.i_mode));
- return 0;
- }
- }
-
- /*
- * Prompt to reconnect.
- */
- if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
- if (e2fsck_reconnect_file(ctx, i))
- ext2fs_unmark_valid(fs);
- } else {
- /*
- * If we don't attach the inode, then skip the
- * i_links_test since there's no point in trying to
- * force i_links_count to zero.
- */
- ext2fs_unmark_valid(fs);
- return 1;
- }
- return 0;
-}
-
-
-static void e2fsck_pass4(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t i;
- struct ext2_inode inode;
- struct problem_context pctx;
- __u16 link_count, link_counted;
- char *buf = NULL;
- int group, maxgroup;
-
- /* Pass 4 */
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
-
- group = 0;
- maxgroup = fs->group_desc_count;
- if (ctx->progress)
- if ((ctx->progress)(ctx, 4, 0, maxgroup))
- return;
-
- for (i=1; i <= fs->super->s_inodes_count; i++) {
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if ((i % fs->super->s_inodes_per_group) == 0) {
- group++;
- if (ctx->progress)
- if ((ctx->progress)(ctx, 4, group, maxgroup))
- return;
- }
- if (i == EXT2_BAD_INO ||
- (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
- continue;
- if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
- (ctx->inode_imagic_map &&
- ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
- continue;
- ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
- ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
- if (link_counted == 0) {
- if (!buf)
- buf = e2fsck_allocate_memory(ctx,
- fs->blocksize, "bad_inode buffer");
- if (e2fsck_process_bad_inode(ctx, 0, i, buf))
- continue;
- if (disconnect_inode(ctx, i))
- continue;
- ext2fs_icount_fetch(ctx->inode_link_info, i,
- &link_count);
- ext2fs_icount_fetch(ctx->inode_count, i,
- &link_counted);
- }
- if (link_counted != link_count) {
- e2fsck_read_inode(ctx, i, &inode, "pass4");
- pctx.ino = i;
- pctx.inode = &inode;
- if (link_count != inode.i_links_count) {
- pctx.num = link_count;
- fix_problem(ctx,
- PR_4_INCONSISTENT_COUNT, &pctx);
- }
- pctx.num = link_counted;
- if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
- inode.i_links_count = link_counted;
- e2fsck_write_inode(ctx, i, &inode, "pass4");
- }
- }
- }
- ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
- ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
- ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
- ctx->inode_imagic_map = 0;
- ext2fs_free_mem(&buf);
-}
-
-/*
- * pass5.c --- check block and inode bitmaps against on-disk bitmaps
- */
-
-#define NO_BLK ((blk_t) -1)
-
-static void print_bitmap_problem(e2fsck_t ctx, int problem,
- struct problem_context *pctx)
-{
- switch (problem) {
- case PR_5_BLOCK_UNUSED:
- if (pctx->blk == pctx->blk2)
- pctx->blk2 = 0;
- else
- problem = PR_5_BLOCK_RANGE_UNUSED;
- break;
- case PR_5_BLOCK_USED:
- if (pctx->blk == pctx->blk2)
- pctx->blk2 = 0;
- else
- problem = PR_5_BLOCK_RANGE_USED;
- break;
- case PR_5_INODE_UNUSED:
- if (pctx->ino == pctx->ino2)
- pctx->ino2 = 0;
- else
- problem = PR_5_INODE_RANGE_UNUSED;
- break;
- case PR_5_INODE_USED:
- if (pctx->ino == pctx->ino2)
- pctx->ino2 = 0;
- else
- problem = PR_5_INODE_RANGE_USED;
- break;
- }
- fix_problem(ctx, problem, pctx);
- pctx->blk = pctx->blk2 = NO_BLK;
- pctx->ino = pctx->ino2 = 0;
-}
-
-static void check_block_bitmaps(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t i;
- int *free_array;
- int group = 0;
- unsigned int blocks = 0;
- unsigned int free_blocks = 0;
- int group_free = 0;
- int actual, bitmap;
- struct problem_context pctx;
- int problem, save_problem, fixit, had_problem;
- errcode_t retval;
-
- clear_problem_context(&pctx);
- free_array = (int *) e2fsck_allocate_memory(ctx,
- fs->group_desc_count * sizeof(int), "free block count array");
-
- if ((fs->super->s_first_data_block <
- ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
- (fs->super->s_blocks_count-1 >
- ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
- pctx.num = 1;
- pctx.blk = fs->super->s_first_data_block;
- pctx.blk2 = fs->super->s_blocks_count -1;
- pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
- pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
- fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
-
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
-
- if ((fs->super->s_first_data_block <
- ext2fs_get_block_bitmap_start(fs->block_map)) ||
- (fs->super->s_blocks_count-1 >
- ext2fs_get_block_bitmap_end(fs->block_map))) {
- pctx.num = 2;
- pctx.blk = fs->super->s_first_data_block;
- pctx.blk2 = fs->super->s_blocks_count -1;
- pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
- pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
- fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
-
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
-
-redo_counts:
- had_problem = 0;
- save_problem = 0;
- pctx.blk = pctx.blk2 = NO_BLK;
- for (i = fs->super->s_first_data_block;
- i < fs->super->s_blocks_count;
- i++) {
- actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
- bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
-
- if (actual == bitmap)
- goto do_counts;
-
- if (!actual && bitmap) {
- /*
- * Block not used, but marked in use in the bitmap.
- */
- problem = PR_5_BLOCK_UNUSED;
- } else {
- /*
- * Block used, but not marked in use in the bitmap.
- */
- problem = PR_5_BLOCK_USED;
- }
- if (pctx.blk == NO_BLK) {
- pctx.blk = pctx.blk2 = i;
- save_problem = problem;
- } else {
- if ((problem == save_problem) &&
- (pctx.blk2 == i-1))
- pctx.blk2++;
- else {
- print_bitmap_problem(ctx, save_problem, &pctx);
- pctx.blk = pctx.blk2 = i;
- save_problem = problem;
- }
- }
- ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
- had_problem++;
-
- do_counts:
- if (!bitmap) {
- group_free++;
- free_blocks++;
- }
- blocks ++;
- if ((blocks == fs->super->s_blocks_per_group) ||
- (i == fs->super->s_blocks_count-1)) {
- free_array[group] = group_free;
- group ++;
- blocks = 0;
- group_free = 0;
- if (ctx->progress)
- if ((ctx->progress)(ctx, 5, group,
- fs->group_desc_count*2))
- return;
- }
- }
- if (pctx.blk != NO_BLK)
- print_bitmap_problem(ctx, save_problem, &pctx);
- if (had_problem)
- fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
- else
- fixit = -1;
- ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
-
- if (fixit == 1) {
- ext2fs_free_block_bitmap(fs->block_map);
- retval = ext2fs_copy_bitmap(ctx->block_found_map,
- &fs->block_map);
- if (retval) {
- clear_problem_context(&pctx);
- fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_set_bitmap_padding(fs->block_map);
- ext2fs_mark_bb_dirty(fs);
-
- /* Redo the counts */
- blocks = 0; free_blocks = 0; group_free = 0; group = 0;
- memset(free_array, 0, fs->group_desc_count * sizeof(int));
- goto redo_counts;
- } else if (fixit == 0)
- ext2fs_unmark_valid(fs);
-
- for (i = 0; i < fs->group_desc_count; i++) {
- if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
- pctx.group = i;
- pctx.blk = fs->group_desc[i].bg_free_blocks_count;
- pctx.blk2 = free_array[i];
-
- if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
- &pctx)) {
- fs->group_desc[i].bg_free_blocks_count =
- free_array[i];
- ext2fs_mark_super_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- }
- }
- if (free_blocks != fs->super->s_free_blocks_count) {
- pctx.group = 0;
- pctx.blk = fs->super->s_free_blocks_count;
- pctx.blk2 = free_blocks;
-
- if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
- fs->super->s_free_blocks_count = free_blocks;
- ext2fs_mark_super_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- }
- ext2fs_free_mem(&free_array);
-}
-
-static void check_inode_bitmaps(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t i;
- unsigned int free_inodes = 0;
- int group_free = 0;
- int dirs_count = 0;
- int group = 0;
- unsigned int inodes = 0;
- int *free_array;
- int *dir_array;
- int actual, bitmap;
- errcode_t retval;
- struct problem_context pctx;
- int problem, save_problem, fixit, had_problem;
-
- clear_problem_context(&pctx);
- free_array = (int *) e2fsck_allocate_memory(ctx,
- fs->group_desc_count * sizeof(int), "free inode count array");
-
- dir_array = (int *) e2fsck_allocate_memory(ctx,
- fs->group_desc_count * sizeof(int), "directory count array");
-
- if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
- (fs->super->s_inodes_count >
- ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
- pctx.num = 3;
- pctx.blk = 1;
- pctx.blk2 = fs->super->s_inodes_count;
- pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
- pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
- fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
-
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
- if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
- (fs->super->s_inodes_count >
- ext2fs_get_inode_bitmap_end(fs->inode_map))) {
- pctx.num = 4;
- pctx.blk = 1;
- pctx.blk2 = fs->super->s_inodes_count;
- pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
- pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
- fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
-
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
-
-redo_counts:
- had_problem = 0;
- save_problem = 0;
- pctx.ino = pctx.ino2 = 0;
- for (i = 1; i <= fs->super->s_inodes_count; i++) {
- actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
- bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
-
- if (actual == bitmap)
- goto do_counts;
-
- if (!actual && bitmap) {
- /*
- * Inode wasn't used, but marked in bitmap
- */
- problem = PR_5_INODE_UNUSED;
- } else /* if (actual && !bitmap) */ {
- /*
- * Inode used, but not in bitmap
- */
- problem = PR_5_INODE_USED;
- }
- if (pctx.ino == 0) {
- pctx.ino = pctx.ino2 = i;
- save_problem = problem;
- } else {
- if ((problem == save_problem) &&
- (pctx.ino2 == i-1))
- pctx.ino2++;
- else {
- print_bitmap_problem(ctx, save_problem, &pctx);
- pctx.ino = pctx.ino2 = i;
- save_problem = problem;
- }
- }
- ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
- had_problem++;
-
-do_counts:
- if (!bitmap) {
- group_free++;
- free_inodes++;
- } else {
- if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
- dirs_count++;
- }
- inodes++;
- if ((inodes == fs->super->s_inodes_per_group) ||
- (i == fs->super->s_inodes_count)) {
- free_array[group] = group_free;
- dir_array[group] = dirs_count;
- group ++;
- inodes = 0;
- group_free = 0;
- dirs_count = 0;
- if (ctx->progress)
- if ((ctx->progress)(ctx, 5,
- group + fs->group_desc_count,
- fs->group_desc_count*2))
- return;
- }
- }
- if (pctx.ino)
- print_bitmap_problem(ctx, save_problem, &pctx);
-
- if (had_problem)
- fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
- else
- fixit = -1;
- ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
-
- if (fixit == 1) {
- ext2fs_free_inode_bitmap(fs->inode_map);
- retval = ext2fs_copy_bitmap(ctx->inode_used_map,
- &fs->inode_map);
- if (retval) {
- clear_problem_context(&pctx);
- fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- ext2fs_set_bitmap_padding(fs->inode_map);
- ext2fs_mark_ib_dirty(fs);
-
- /* redo counts */
- inodes = 0; free_inodes = 0; group_free = 0;
- dirs_count = 0; group = 0;
- memset(free_array, 0, fs->group_desc_count * sizeof(int));
- memset(dir_array, 0, fs->group_desc_count * sizeof(int));
- goto redo_counts;
- } else if (fixit == 0)
- ext2fs_unmark_valid(fs);
-
- for (i = 0; i < fs->group_desc_count; i++) {
- if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
- pctx.group = i;
- pctx.ino = fs->group_desc[i].bg_free_inodes_count;
- pctx.ino2 = free_array[i];
- if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
- &pctx)) {
- fs->group_desc[i].bg_free_inodes_count =
- free_array[i];
- ext2fs_mark_super_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- }
- if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
- pctx.group = i;
- pctx.ino = fs->group_desc[i].bg_used_dirs_count;
- pctx.ino2 = dir_array[i];
-
- if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
- &pctx)) {
- fs->group_desc[i].bg_used_dirs_count =
- dir_array[i];
- ext2fs_mark_super_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- }
- }
- if (free_inodes != fs->super->s_free_inodes_count) {
- pctx.group = -1;
- pctx.ino = fs->super->s_free_inodes_count;
- pctx.ino2 = free_inodes;
-
- if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
- fs->super->s_free_inodes_count = free_inodes;
- ext2fs_mark_super_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- }
- ext2fs_free_mem(&free_array);
- ext2fs_free_mem(&dir_array);
-}
-
-static void check_inode_end(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t end, save_inodes_count, i;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
- pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
- &save_inodes_count);
- if (pctx.errcode) {
- pctx.num = 1;
- fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
- if (save_inodes_count == end)
- return;
-
- for (i = save_inodes_count + 1; i <= end; i++) {
- if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
- if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
- for (i = save_inodes_count + 1; i <= end; i++)
- ext2fs_mark_inode_bitmap(fs->inode_map,
- i);
- ext2fs_mark_ib_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- break;
- }
- }
-
- pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
- save_inodes_count, 0);
- if (pctx.errcode) {
- pctx.num = 2;
- fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
-}
-
-static void check_block_end(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t end, save_blocks_count, i;
- struct problem_context pctx;
-
- clear_problem_context(&pctx);
-
- end = fs->block_map->start +
- (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
- pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
- &save_blocks_count);
- if (pctx.errcode) {
- pctx.num = 3;
- fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
- if (save_blocks_count == end)
- return;
-
- for (i = save_blocks_count + 1; i <= end; i++) {
- if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
- if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
- for (i = save_blocks_count + 1; i <= end; i++)
- ext2fs_mark_block_bitmap(fs->block_map,
- i);
- ext2fs_mark_bb_dirty(fs);
- } else
- ext2fs_unmark_valid(fs);
- break;
- }
- }
-
- pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
- save_blocks_count, 0);
- if (pctx.errcode) {
- pctx.num = 4;
- fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* fatal */
- return;
- }
-}
-
-static void e2fsck_pass5(e2fsck_t ctx)
-{
- struct problem_context pctx;
-
- /* Pass 5 */
-
- clear_problem_context(&pctx);
-
- if (!(ctx->options & E2F_OPT_PREEN))
- fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
-
- if (ctx->progress)
- if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
- return;
-
- e2fsck_read_bitmaps(ctx);
-
- check_block_bitmaps(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- check_inode_bitmaps(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- check_inode_end(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- check_block_end(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
-
- ext2fs_free_inode_bitmap(ctx->inode_used_map);
- ctx->inode_used_map = 0;
- ext2fs_free_inode_bitmap(ctx->inode_dir_map);
- ctx->inode_dir_map = 0;
- ext2fs_free_block_bitmap(ctx->block_found_map);
- ctx->block_found_map = 0;
-}
-
-/*
- * problem.c --- report filesystem problems to the user
- */
-
-#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
-#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
-#define PR_NO_DEFAULT 0x000004 /* Default to no */
-#define PR_MSG_ONLY 0x000008 /* Print message only */
-
-/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
-
-#define PR_FATAL 0x001000 /* Fatal error */
-#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
- /* ask another */
-#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
-#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
-#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
-#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
-#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
-
-
-#define PROMPT_NONE 0
-#define PROMPT_FIX 1
-#define PROMPT_CLEAR 2
-#define PROMPT_RELOCATE 3
-#define PROMPT_ALLOCATE 4
-#define PROMPT_EXPAND 5
-#define PROMPT_CONNECT 6
-#define PROMPT_CREATE 7
-#define PROMPT_SALVAGE 8
-#define PROMPT_TRUNCATE 9
-#define PROMPT_CLEAR_INODE 10
-#define PROMPT_ABORT 11
-#define PROMPT_SPLIT 12
-#define PROMPT_CONTINUE 13
-#define PROMPT_CLONE 14
-#define PROMPT_DELETE 15
-#define PROMPT_SUPPRESS 16
-#define PROMPT_UNLINK 17
-#define PROMPT_CLEAR_HTREE 18
-#define PROMPT_RECREATE 19
-#define PROMPT_NULL 20
-
-struct e2fsck_problem {
- problem_t e2p_code;
- const char * e2p_description;
- char prompt;
- int flags;
- problem_t second_code;
-};
-
-struct latch_descr {
- int latch_code;
- problem_t question;
- problem_t end_message;
- int flags;
-};
-
-/*
- * These are the prompts which are used to ask the user if they want
- * to fix a problem.
- */
-static const char *const prompt[] = {
- N_("(no prompt)"), /* 0 */
- N_("Fix"), /* 1 */
- N_("Clear"), /* 2 */
- N_("Relocate"), /* 3 */
- N_("Allocate"), /* 4 */
- N_("Expand"), /* 5 */
- N_("Connect to /lost+found"), /* 6 */
- N_("Create"), /* 7 */
- N_("Salvage"), /* 8 */
- N_("Truncate"), /* 9 */
- N_("Clear inode"), /* 10 */
- N_("Abort"), /* 11 */
- N_("Split"), /* 12 */
- N_("Continue"), /* 13 */
- N_("Clone multiply-claimed blocks"), /* 14 */
- N_("Delete file"), /* 15 */
- N_("Suppress messages"),/* 16 */
- N_("Unlink"), /* 17 */
- N_("Clear HTree index"),/* 18 */
- N_("Recreate"), /* 19 */
- "", /* 20 */
-};
-
-/*
- * These messages are printed when we are preen mode and we will be
- * automatically fixing the problem.
- */
-static const char *const preen_msg[] = {
- N_("(NONE)"), /* 0 */
- N_("FIXED"), /* 1 */
- N_("CLEARED"), /* 2 */
- N_("RELOCATED"), /* 3 */
- N_("ALLOCATED"), /* 4 */
- N_("EXPANDED"), /* 5 */
- N_("RECONNECTED"), /* 6 */
- N_("CREATED"), /* 7 */
- N_("SALVAGED"), /* 8 */
- N_("TRUNCATED"), /* 9 */
- N_("INODE CLEARED"), /* 10 */
- N_("ABORTED"), /* 11 */
- N_("SPLIT"), /* 12 */
- N_("CONTINUING"), /* 13 */
- N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
- N_("FILE DELETED"), /* 15 */
- N_("SUPPRESSED"), /* 16 */
- N_("UNLINKED"), /* 17 */
- N_("HTREE INDEX CLEARED"),/* 18 */
- N_("WILL RECREATE"), /* 19 */
- "", /* 20 */
-};
-
-static const struct e2fsck_problem problem_table[] = {
-
- /* Pre-Pass 1 errors */
-
- /* Block bitmap not in group */
- { PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g. (@b %b)\n"),
- PROMPT_RELOCATE, PR_LATCH_RELOC },
-
- /* Inode bitmap not in group */
- { PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g. (@b %b)\n"),
- PROMPT_RELOCATE, PR_LATCH_RELOC },
-
- /* Inode table not in group */
- { PR_0_ITABLE_NOT_GROUP,
- N_("@i table for @g %g is not in @g. (@b %b)\n"
- "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
- PROMPT_RELOCATE, PR_LATCH_RELOC },
-
- /* Superblock corrupt */
- { PR_0_SB_CORRUPT,
- N_("\nThe @S could not be read or does not describe a correct ext2\n"
- "@f. If the @v is valid and it really contains an ext2\n"
- "@f (and not swap or ufs or something else), then the @S\n"
- "is corrupt, and you might try running e2fsck with an alternate @S:\n"
- " e2fsck -b %S <@v>\n\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Filesystem size is wrong */
- { PR_0_FS_SIZE_WRONG,
- N_("The @f size (according to the @S) is %b @bs\n"
- "The physical size of the @v is %c @bs\n"
- "Either the @S or the partition table is likely to be corrupt!\n"),
- PROMPT_ABORT, 0 },
-
- /* Fragments not supported */
- { PR_0_NO_FRAGMENTS,
- N_("@S @b_size = %b, fragsize = %c.\n"
- "This version of e2fsck does not support fragment sizes different\n"
- "from the @b size.\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Bad blocks_per_group */
- { PR_0_BLOCKS_PER_GROUP,
- N_("@S @bs_per_group = %b, should have been %c\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
-
- /* Bad first_data_block */
- { PR_0_FIRST_DATA_BLOCK,
- N_("@S first_data_@b = %b, should have been %c\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
-
- /* Adding UUID to filesystem */
- { PR_0_ADD_UUID,
- N_("@f did not have a UUID; generating one.\n\n"),
- PROMPT_NONE, 0 },
-
- /* Relocate hint */
- { PR_0_RELOCATE_HINT,
- N_("Note: if several inode or block bitmap blocks or part\n"
- "of the inode table require relocation, you may wish to try\n"
- "running e2fsck with the '-b %S' option first. The problem\n"
- "may lie only with the primary block group descriptors, and\n"
- "the backup block group descriptors may be OK.\n\n"),
- PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
-
- /* Miscellaneous superblock corruption */
- { PR_0_MISC_CORRUPT_SUPER,
- N_("Corruption found in @S. (%s = %N).\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
-
- /* Error determing physical device size of filesystem */
- { PR_0_GETSIZE_ERROR,
- N_("Error determining size of the physical @v: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Inode count in superblock is incorrect */
- { PR_0_INODE_COUNT_WRONG,
- N_("@i count in @S is %i, @s %j.\n"),
- PROMPT_FIX, 0 },
-
- { PR_0_HURD_CLEAR_FILETYPE,
- N_("The Hurd does not support the filetype feature.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Journal inode is invalid */
- { PR_0_JOURNAL_BAD_INODE,
- N_("@S has an @n ext3 @j (@i %i).\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* The external journal has (unsupported) multiple filesystems */
- { PR_0_JOURNAL_UNSUPP_MULTIFS,
- N_("External @j has multiple @f users (unsupported).\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Can't find external journal */
- { PR_0_CANT_FIND_JOURNAL,
- N_("Can't find external @j\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* External journal has bad superblock */
- { PR_0_EXT_JOURNAL_BAD_SUPER,
- N_("External @j has bad @S\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Superblock has a bad journal UUID */
- { PR_0_JOURNAL_BAD_UUID,
- N_("External @j does not support this @f\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Journal has an unknown superblock type */
- { PR_0_JOURNAL_UNSUPP_SUPER,
- N_("Ext3 @j @S is unknown type %N (unsupported).\n"
- "It is likely that your copy of e2fsck is old and/or doesn't "
- "support this @j format.\n"
- "It is also possible the @j @S is corrupt.\n"),
- PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
-
- /* Journal superblock is corrupt */
- { PR_0_JOURNAL_BAD_SUPER,
- N_("Ext3 @j @S is corrupt.\n"),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Superblock flag should be cleared */
- { PR_0_JOURNAL_HAS_JOURNAL,
- N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Superblock flag is incorrect */
- { PR_0_JOURNAL_RECOVER_SET,
- N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Journal has data, but recovery flag is clear */
- { PR_0_JOURNAL_RECOVERY_CLEAR,
- N_("ext3 recovery flag is clear, but @j has data.\n"),
- PROMPT_NONE, 0 },
-
- /* Ask if we should clear the journal */
- { PR_0_JOURNAL_RESET_JOURNAL,
- N_("Clear @j"),
- PROMPT_NULL, PR_PREEN_NOMSG },
-
- /* Ask if we should run the journal anyway */
- { PR_0_JOURNAL_RUN,
- N_("Run @j anyway"),
- PROMPT_NULL, 0 },
-
- /* Run the journal by default */
- { PR_0_JOURNAL_RUN_DEFAULT,
- N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
- PROMPT_NONE, 0 },
-
- /* Clearing orphan inode */
- { PR_0_ORPHAN_CLEAR_INODE,
- N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
- PROMPT_NONE, 0 },
-
- /* Illegal block found in orphaned inode */
- { PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
- N_("@I @b #%B (%b) found in @o @i %i.\n"),
- PROMPT_NONE, 0 },
-
- /* Already cleared block found in orphaned inode */
- { PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
- N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
- PROMPT_NONE, 0 },
-
- /* Illegal orphan inode in superblock */
- { PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
- N_("@I @o @i %i in @S.\n"),
- PROMPT_NONE, 0 },
-
- /* Illegal inode in orphaned inode list */
- { PR_0_ORPHAN_ILLEGAL_INODE,
- N_("@I @i %i in @o @i list.\n"),
- PROMPT_NONE, 0 },
-
- /* Filesystem revision is 0, but feature flags are set */
- { PR_0_FS_REV_LEVEL,
- N_("@f has feature flag(s) set, but is a revision 0 @f. "),
- PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
-
- /* Journal superblock has an unknown read-only feature flag set */
- { PR_0_JOURNAL_UNSUPP_ROCOMPAT,
- N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
- PROMPT_ABORT, 0 },
-
- /* Journal superblock has an unknown incompatible feature flag set */
- { PR_0_JOURNAL_UNSUPP_INCOMPAT,
- N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
- PROMPT_ABORT, 0 },
-
- /* Journal has unsupported version number */
- { PR_0_JOURNAL_UNSUPP_VERSION,
- N_("@j version not supported by this e2fsck.\n"),
- PROMPT_ABORT, 0 },
-
- /* Moving journal to hidden file */
- { PR_0_MOVE_JOURNAL,
- N_("Moving @j from /%s to hidden @i.\n\n"),
- PROMPT_NONE, 0 },
-
- /* Error moving journal to hidden file */
- { PR_0_ERR_MOVE_JOURNAL,
- N_("Error moving @j: %m\n\n"),
- PROMPT_NONE, 0 },
-
- /* Clearing V2 journal superblock */
- { PR_0_CLEAR_V2_JOURNAL,
- N_("Found @n V2 @j @S fields (from V1 @j).\n"
- "Clearing fields beyond the V1 @j @S...\n\n"),
- PROMPT_NONE, 0 },
-
- /* Backup journal inode blocks */
- { PR_0_BACKUP_JNL,
- N_("Backing up @j @i @b information.\n\n"),
- PROMPT_NONE, 0 },
-
- /* Reserved blocks w/o resize_inode */
- { PR_0_NONZERO_RESERVED_GDT_BLOCKS,
- N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
- "is %N; @s zero. "),
- PROMPT_FIX, 0 },
-
- /* Resize_inode not enabled, but resize inode is non-zero */
- { PR_0_CLEAR_RESIZE_INODE,
- N_("Resize_@i not enabled, but the resize @i is non-zero. "),
- PROMPT_CLEAR, 0 },
-
- /* Resize inode invalid */
- { PR_0_RESIZE_INODE_INVALID,
- N_("Resize @i not valid. "),
- PROMPT_RECREATE, 0 },
-
- /* Pass 1 errors */
-
- /* Pass 1: Checking inodes, blocks, and sizes */
- { PR_1_PASS_HEADER,
- N_("Pass 1: Checking @is, @bs, and sizes\n"),
- PROMPT_NONE, 0 },
-
- /* Root directory is not an inode */
- { PR_1_ROOT_NO_DIR, N_("@r is not a @d. "),
- PROMPT_CLEAR, 0 },
-
- /* Root directory has dtime set */
- { PR_1_ROOT_DTIME,
- N_("@r has dtime set (probably due to old mke2fs). "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Reserved inode has bad mode */
- { PR_1_RESERVED_BAD_MODE,
- N_("Reserved @i %i (%Q) has @n mode. "),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Deleted inode has zero dtime */
- { PR_1_ZERO_DTIME,
- N_("@D @i %i has zero dtime. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Inode in use, but dtime set */
- { PR_1_SET_DTIME,
- N_("@i %i is in use, but has dtime set. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Zero-length directory */
- { PR_1_ZERO_LENGTH_DIR,
- N_("@i %i is a @z @d. "),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Block bitmap conflicts with some other fs block */
- { PR_1_BB_CONFLICT,
- N_("@g %g's @b @B at %b @C.\n"),
- PROMPT_RELOCATE, 0 },
-
- /* Inode bitmap conflicts with some other fs block */
- { PR_1_IB_CONFLICT,
- N_("@g %g's @i @B at %b @C.\n"),
- PROMPT_RELOCATE, 0 },
-
- /* Inode table conflicts with some other fs block */
- { PR_1_ITABLE_CONFLICT,
- N_("@g %g's @i table at %b @C.\n"),
- PROMPT_RELOCATE, 0 },
-
- /* Block bitmap is on a bad block */
- { PR_1_BB_BAD_BLOCK,
- N_("@g %g's @b @B (%b) is bad. "),
- PROMPT_RELOCATE, 0 },
-
- /* Inode bitmap is on a bad block */
- { PR_1_IB_BAD_BLOCK,
- N_("@g %g's @i @B (%b) is bad. "),
- PROMPT_RELOCATE, 0 },
-
- /* Inode has incorrect i_size */
- { PR_1_BAD_I_SIZE,
- N_("@i %i, i_size is %Is, @s %N. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Inode has incorrect i_blocks */
- { PR_1_BAD_I_BLOCKS,
- N_("@i %i, i_@bs is %Ib, @s %N. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Illegal blocknumber in inode */
- { PR_1_ILLEGAL_BLOCK_NUM,
- N_("@I @b #%B (%b) in @i %i. "),
- PROMPT_CLEAR, PR_LATCH_BLOCK },
-
- /* Block number overlaps fs metadata */
- { PR_1_BLOCK_OVERLAPS_METADATA,
- N_("@b #%B (%b) overlaps @f metadata in @i %i. "),
- PROMPT_CLEAR, PR_LATCH_BLOCK },
-
- /* Inode has illegal blocks (latch question) */
- { PR_1_INODE_BLOCK_LATCH,
- N_("@i %i has illegal @b(s). "),
- PROMPT_CLEAR, 0 },
-
- /* Too many bad blocks in inode */
- { PR_1_TOO_MANY_BAD_BLOCKS,
- N_("Too many illegal @bs in @i %i.\n"),
- PROMPT_CLEAR_INODE, PR_NO_OK },
-
- /* Illegal block number in bad block inode */
- { PR_1_BB_ILLEGAL_BLOCK_NUM,
- N_("@I @b #%B (%b) in bad @b @i. "),
- PROMPT_CLEAR, PR_LATCH_BBLOCK },
-
- /* Bad block inode has illegal blocks (latch question) */
- { PR_1_INODE_BBLOCK_LATCH,
- N_("Bad @b @i has illegal @b(s). "),
- PROMPT_CLEAR, 0 },
-
- /* Duplicate or bad blocks in use! */
- { PR_1_DUP_BLOCKS_PREENSTOP,
- N_("Duplicate or bad @b in use!\n"),
- PROMPT_NONE, 0 },
-
- /* Bad block used as bad block indirect block */
- { PR_1_BBINODE_BAD_METABLOCK,
- N_("Bad @b %b used as bad @b @i indirect @b. "),
- PROMPT_CLEAR, PR_LATCH_BBLOCK },
-
- /* Inconsistency can't be fixed prompt */
- { PR_1_BBINODE_BAD_METABLOCK_PROMPT,
- N_("\nThe bad @b @i has probably been corrupted. You probably\n"
- "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
- "in the @f.\n"),
- PROMPT_CONTINUE, PR_PREEN_NOMSG },
-
- /* Bad primary block */
- { PR_1_BAD_PRIMARY_BLOCK,
- N_("\nIf the @b is really bad, the @f cannot be fixed.\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
-
- /* Bad primary block prompt */
- { PR_1_BAD_PRIMARY_BLOCK_PROMPT,
- N_("You can remove this @b from the bad @b list and hope\n"
- "that the @b is really OK. But there are no guarantees.\n\n"),
- PROMPT_CLEAR, PR_PREEN_NOMSG },
-
- /* Bad primary superblock */
- { PR_1_BAD_PRIMARY_SUPERBLOCK,
- N_("The primary @S (%b) is on the bad @b list.\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
-
- /* Bad primary block group descriptors */
- { PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
- N_("Block %b in the primary @g descriptors "
- "is on the bad @b list\n"),
- PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
-
- /* Bad superblock in group */
- { PR_1_BAD_SUPERBLOCK,
- N_("Warning: Group %g's @S (%b) is bad.\n"),
- PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Bad block group descriptors in group */
- { PR_1_BAD_GROUP_DESCRIPTORS,
- N_("Warning: Group %g's copy of the @g descriptors has a bad "
- "@b (%b).\n"),
- PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Block claimed for no reason */
- { PR_1_PROGERR_CLAIMED_BLOCK,
- N_("Programming error? @b #%b claimed for no reason in "
- "process_bad_@b.\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Error allocating blocks for relocating metadata */
- { PR_1_RELOC_BLOCK_ALLOCATE,
- N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Error allocating block buffer during relocation process */
- { PR_1_RELOC_MEMORY_ALLOCATE,
- N_("@A @b buffer for relocating %s\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Relocating metadata group information from X to Y */
- { PR_1_RELOC_FROM_TO,
- N_("Relocating @g %g's %s from %b to %c...\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Relocating metatdata group information to X */
- { PR_1_RELOC_TO,
- N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Block read error during relocation process */
- { PR_1_RELOC_READ_ERR,
- N_("Warning: could not read @b %b of %s: %m\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Block write error during relocation process */
- { PR_1_RELOC_WRITE_ERR,
- N_("Warning: could not write @b %b for %s: %m\n"),
- PROMPT_NONE, PR_PREEN_OK },
-
- /* Error allocating inode bitmap */
- { PR_1_ALLOCATE_IBITMAP_ERROR,
- N_("@A @i @B (%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error allocating block bitmap */
- { PR_1_ALLOCATE_BBITMAP_ERROR,
- N_("@A @b @B (%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error allocating icount structure */
- { PR_1_ALLOCATE_ICOUNT,
- N_("@A icount link information: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error allocating dbcount */
- { PR_1_ALLOCATE_DBCOUNT,
- N_("@A @d @b array: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while scanning inodes */
- { PR_1_ISCAN_ERROR,
- N_("Error while scanning @is (%i): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while iterating over blocks */
- { PR_1_BLOCK_ITERATE,
- N_("Error while iterating over @bs in @i %i: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while storing inode count information */
- { PR_1_ICOUNT_STORE,
- N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while storing directory block information */
- { PR_1_ADD_DBLOCK,
- N_("Error storing @d @b information "
- "(@i=%i, @b=%b, num=%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while reading inode (for clearing) */
- { PR_1_READ_INODE,
- N_("Error reading @i %i: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Suppress messages prompt */
- { PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
-
- /* Imagic flag set on an inode when filesystem doesn't support it */
- { PR_1_SET_IMAGIC,
- N_("@i %i has imagic flag set. "),
- PROMPT_CLEAR, 0 },
-
- /* Immutable flag set on a device or socket inode */
- { PR_1_SET_IMMUTABLE,
- N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
- "or append-only flag set. "),
- PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
-
- /* Compression flag set on an inode when filesystem doesn't support it */
- { PR_1_COMPR_SET,
- N_("@i %i has @cion flag set on @f without @cion support. "),
- PROMPT_CLEAR, 0 },
-
- /* Non-zero size for device, fifo or socket inode */
- { PR_1_SET_NONZSIZE,
- N_("Special (@v/socket/fifo) @i %i has non-zero size. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Filesystem revision is 0, but feature flags are set */
- { PR_1_FS_REV_LEVEL,
- N_("@f has feature flag(s) set, but is a revision 0 @f. "),
- PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
-
- /* Journal inode is not in use, but contains data */
- { PR_1_JOURNAL_INODE_NOT_CLEAR,
- N_("@j @i is not in use, but contains data. "),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Journal has bad mode */
- { PR_1_JOURNAL_BAD_MODE,
- N_("@j is not regular file. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Deal with inodes that were part of orphan linked list */
- { PR_1_LOW_DTIME,
- N_("@i %i was part of the @o @i list. "),
- PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
-
- /* Deal with inodes that were part of corrupted orphan linked
- list (latch question) */
- { PR_1_ORPHAN_LIST_REFUGEES,
- N_("@is that were part of a corrupted orphan linked list found. "),
- PROMPT_FIX, 0 },
-
- /* Error allocating refcount structure */
- { PR_1_ALLOCATE_REFCOUNT,
- N_("@A refcount structure (%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error reading extended attribute block */
- { PR_1_READ_EA_BLOCK,
- N_("Error reading @a @b %b for @i %i. "),
- PROMPT_CLEAR, 0 },
-
- /* Invalid extended attribute block */
- { PR_1_BAD_EA_BLOCK,
- N_("@i %i has a bad @a @b %b. "),
- PROMPT_CLEAR, 0 },
-
- /* Error reading Extended Attribute block while fixing refcount */
- { PR_1_EXTATTR_READ_ABORT,
- N_("Error reading @a @b %b (%m). "),
- PROMPT_ABORT, 0 },
-
- /* Extended attribute reference count incorrect */
- { PR_1_EXTATTR_REFCOUNT,
- N_("@a @b %b has reference count %B, @s %N. "),
- PROMPT_FIX, 0 },
-
- /* Error writing Extended Attribute block while fixing refcount */
- { PR_1_EXTATTR_WRITE,
- N_("Error writing @a @b %b (%m). "),
- PROMPT_ABORT, 0 },
-
- /* Multiple EA blocks not supported */
- { PR_1_EA_MULTI_BLOCK,
- N_("@a @b %b has h_@bs > 1. "),
- PROMPT_CLEAR, 0},
-
- /* Error allocating EA region allocation structure */
- { PR_1_EA_ALLOC_REGION,
- N_("@A @a @b %b. "),
- PROMPT_ABORT, 0},
-
- /* Error EA allocation collision */
- { PR_1_EA_ALLOC_COLLISION,
- N_("@a @b %b is corrupt (allocation collision). "),
- PROMPT_CLEAR, 0},
-
- /* Bad extended attribute name */
- { PR_1_EA_BAD_NAME,
- N_("@a @b %b is corrupt (@n name). "),
- PROMPT_CLEAR, 0},
-
- /* Bad extended attribute value */
- { PR_1_EA_BAD_VALUE,
- N_("@a @b %b is corrupt (@n value). "),
- PROMPT_CLEAR, 0},
-
- /* Inode too big (latch question) */
- { PR_1_INODE_TOOBIG,
- N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
-
- /* Directory too big */
- { PR_1_TOOBIG_DIR,
- N_("@b #%B (%b) causes @d to be too big. "),
- PROMPT_CLEAR, PR_LATCH_TOOBIG },
-
- /* Regular file too big */
- { PR_1_TOOBIG_REG,
- N_("@b #%B (%b) causes file to be too big. "),
- PROMPT_CLEAR, PR_LATCH_TOOBIG },
-
- /* Symlink too big */
- { PR_1_TOOBIG_SYMLINK,
- N_("@b #%B (%b) causes symlink to be too big. "),
- PROMPT_CLEAR, PR_LATCH_TOOBIG },
-
- /* INDEX_FL flag set on a non-HTREE filesystem */
- { PR_1_HTREE_SET,
- N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* INDEX_FL flag set on a non-directory */
- { PR_1_HTREE_NODIR,
- N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Invalid root node in HTREE directory */
- { PR_1_HTREE_BADROOT,
- N_("@h %i has an @n root node.\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Unsupported hash version in HTREE directory */
- { PR_1_HTREE_HASHV,
- N_("@h %i has an unsupported hash version (%N)\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Incompatible flag in HTREE root node */
- { PR_1_HTREE_INCOMPAT,
- N_("@h %i uses an incompatible htree root node flag.\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* HTREE too deep */
- { PR_1_HTREE_DEPTH,
- N_("@h %i has a tree depth (%N) which is too big\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Bad block has indirect block that conflicts with filesystem block */
- { PR_1_BB_FS_BLOCK,
- N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
- "@f metadata. "),
- PROMPT_CLEAR, PR_LATCH_BBLOCK },
-
- /* Resize inode failed */
- { PR_1_RESIZE_INODE_CREATE,
- N_("Resize @i (re)creation failed: %m."),
- PROMPT_ABORT, 0 },
-
- /* invalid inode->i_extra_isize */
- { PR_1_EXTRA_ISIZE,
- N_("@i %i has a extra size (%IS) which is @n\n"),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* invalid ea entry->e_name_len */
- { PR_1_ATTR_NAME_LEN,
- N_("@a in @i %i has a namelen (%N) which is @n\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* invalid ea entry->e_value_size */
- { PR_1_ATTR_VALUE_SIZE,
- N_("@a in @i %i has a value size (%N) which is @n\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* invalid ea entry->e_value_offs */
- { PR_1_ATTR_VALUE_OFFSET,
- N_("@a in @i %i has a value offset (%N) which is @n\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* invalid ea entry->e_value_block */
- { PR_1_ATTR_VALUE_BLOCK,
- N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* invalid ea entry->e_hash */
- { PR_1_ATTR_HASH,
- N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Pass 1b errors */
-
- /* Pass 1B: Rescan for duplicate/bad blocks */
- { PR_1B_PASS_HEADER,
- N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
- "Pass 1B: Rescanning for @m @bs\n"),
- PROMPT_NONE, 0 },
-
- /* Duplicate/bad block(s) header */
- { PR_1B_DUP_BLOCK_HEADER,
- N_("@m @b(s) in @i %i:"),
- PROMPT_NONE, 0 },
-
- /* Duplicate/bad block(s) in inode */
- { PR_1B_DUP_BLOCK,
- " %b",
- PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
-
- /* Duplicate/bad block(s) end */
- { PR_1B_DUP_BLOCK_END,
- "\n",
- PROMPT_NONE, PR_PREEN_NOHDR },
-
- /* Error while scanning inodes */
- { PR_1B_ISCAN_ERROR,
- N_("Error while scanning inodes (%i): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error allocating inode bitmap */
- { PR_1B_ALLOCATE_IBITMAP_ERROR,
- N_("@A @i @B (@i_dup_map): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error while iterating over blocks */
- { PR_1B_BLOCK_ITERATE,
- N_("Error while iterating over @bs in @i %i (%s): %m\n"),
- PROMPT_NONE, 0 },
-
- /* Error adjusting EA refcount */
- { PR_1B_ADJ_EA_REFCOUNT,
- N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
- PROMPT_NONE, 0 },
-
-
- /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
- { PR_1C_PASS_HEADER,
- N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
- PROMPT_NONE, 0 },
-
-
- /* Pass 1D: Reconciling multiply-claimed blocks */
- { PR_1D_PASS_HEADER,
- N_("Pass 1D: Reconciling @m @bs\n"),
- PROMPT_NONE, 0 },
-
- /* File has duplicate blocks */
- { PR_1D_DUP_FILE,
- N_("File %Q (@i #%i, mod time %IM)\n"
- " has %B @m @b(s), shared with %N file(s):\n"),
- PROMPT_NONE, 0 },
-
- /* List of files sharing duplicate blocks */
- { PR_1D_DUP_FILE_LIST,
- N_("\t%Q (@i #%i, mod time %IM)\n"),
- PROMPT_NONE, 0 },
-
- /* File sharing blocks with filesystem metadata */
- { PR_1D_SHARE_METADATA,
- N_("\t<@f metadata>\n"),
- PROMPT_NONE, 0 },
-
- /* Report of how many duplicate/bad inodes */
- { PR_1D_NUM_DUP_INODES,
- N_("(There are %N @is containing @m @bs.)\n\n"),
- PROMPT_NONE, 0 },
-
- /* Duplicated blocks already reassigned or cloned. */
- { PR_1D_DUP_BLOCKS_DEALT,
- N_("@m @bs already reassigned or cloned.\n\n"),
- PROMPT_NONE, 0 },
-
- /* Clone duplicate/bad blocks? */
- { PR_1D_CLONE_QUESTION,
- "", PROMPT_CLONE, PR_NO_OK },
-
- /* Delete file? */
- { PR_1D_DELETE_QUESTION,
- "", PROMPT_DELETE, 0 },
-
- /* Couldn't clone file (error) */
- { PR_1D_CLONE_ERROR,
- N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
-
- /* Pass 2 errors */
-
- /* Pass 2: Checking directory structure */
- { PR_2_PASS_HEADER,
- N_("Pass 2: Checking @d structure\n"),
- PROMPT_NONE, 0 },
-
- /* Bad inode number for '.' */
- { PR_2_BAD_INODE_DOT,
- N_("@n @i number for '.' in @d @i %i.\n"),
- PROMPT_FIX, 0 },
-
- /* Directory entry has bad inode number */
- { PR_2_BAD_INO,
- N_("@E has @n @i #: %Di.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Directory entry has deleted or unused inode */
- { PR_2_UNUSED_INODE,
- N_("@E has @D/unused @i %Di. "),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Directry entry is link to '.' */
- { PR_2_LINK_DOT,
- N_("@E @L to '.' "),
- PROMPT_CLEAR, 0 },
-
- /* Directory entry points to inode now located in a bad block */
- { PR_2_BB_INODE,
- N_("@E points to @i (%Di) located in a bad @b.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Directory entry contains a link to a directory */
- { PR_2_LINK_DIR,
- N_("@E @L to @d %P (%Di).\n"),
- PROMPT_CLEAR, 0 },
-
- /* Directory entry contains a link to the root directry */
- { PR_2_LINK_ROOT,
- N_("@E @L to the @r.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Directory entry has illegal characters in its name */
- { PR_2_BAD_NAME,
- N_("@E has illegal characters in its name.\n"),
- PROMPT_FIX, 0 },
-
- /* Missing '.' in directory inode */
- { PR_2_MISSING_DOT,
- N_("Missing '.' in @d @i %i.\n"),
- PROMPT_FIX, 0 },
-
- /* Missing '..' in directory inode */
- { PR_2_MISSING_DOT_DOT,
- N_("Missing '..' in @d @i %i.\n"),
- PROMPT_FIX, 0 },
-
- /* First entry in directory inode doesn't contain '.' */
- { PR_2_1ST_NOT_DOT,
- N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
- PROMPT_FIX, 0 },
-
- /* Second entry in directory inode doesn't contain '..' */
- { PR_2_2ND_NOT_DOT_DOT,
- N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
- PROMPT_FIX, 0 },
-
- /* i_faddr should be zero */
- { PR_2_FADDR_ZERO,
- N_("i_faddr @F %IF, @s zero.\n"),
- PROMPT_CLEAR, 0 },
-
- /* i_file_acl should be zero */
- { PR_2_FILE_ACL_ZERO,
- N_("i_file_acl @F %If, @s zero.\n"),
- PROMPT_CLEAR, 0 },
-
- /* i_dir_acl should be zero */
- { PR_2_DIR_ACL_ZERO,
- N_("i_dir_acl @F %Id, @s zero.\n"),
- PROMPT_CLEAR, 0 },
-
- /* i_frag should be zero */
- { PR_2_FRAG_ZERO,
- N_("i_frag @F %N, @s zero.\n"),
- PROMPT_CLEAR, 0 },
-
- /* i_fsize should be zero */
- { PR_2_FSIZE_ZERO,
- N_("i_fsize @F %N, @s zero.\n"),
- PROMPT_CLEAR, 0 },
-
- /* inode has bad mode */
- { PR_2_BAD_MODE,
- N_("@i %i (%Q) has @n mode (%Im).\n"),
- PROMPT_CLEAR, 0 },
-
- /* directory corrupted */
- { PR_2_DIR_CORRUPTED,
- N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
- PROMPT_SALVAGE, 0 },
-
- /* filename too long */
- { PR_2_FILENAME_LONG,
- N_("@d @i %i, @b %B, offset %N: filename too long\n"),
- PROMPT_TRUNCATE, 0 },
-
- /* Directory inode has a missing block (hole) */
- { PR_2_DIRECTORY_HOLE,
- N_("@d @i %i has an unallocated @b #%B. "),
- PROMPT_ALLOCATE, 0 },
-
- /* '.' is not NULL terminated */
- { PR_2_DOT_NULL_TERM,
- N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
- PROMPT_FIX, 0 },
-
- /* '..' is not NULL terminated */
- { PR_2_DOT_DOT_NULL_TERM,
- N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
- PROMPT_FIX, 0 },
-
- /* Illegal character device inode */
- { PR_2_BAD_CHAR_DEV,
- N_("@i %i (%Q) is an @I character @v.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Illegal block device inode */
- { PR_2_BAD_BLOCK_DEV,
- N_("@i %i (%Q) is an @I @b @v.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Duplicate '.' entry */
- { PR_2_DUP_DOT,
- N_("@E is duplicate '.' @e.\n"),
- PROMPT_FIX, 0 },
-
- /* Duplicate '..' entry */
- { PR_2_DUP_DOT_DOT,
- N_("@E is duplicate '..' @e.\n"),
- PROMPT_FIX, 0 },
-
- /* Internal error: couldn't find dir_info */
- { PR_2_NO_DIRINFO,
- N_("Internal error: cannot find dir_info for %i.\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Final rec_len is wrong */
- { PR_2_FINAL_RECLEN,
- N_("@E has rec_len of %Dr, @s %N.\n"),
- PROMPT_FIX, 0 },
-
- /* Error allocating icount structure */
- { PR_2_ALLOCATE_ICOUNT,
- N_("@A icount structure: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error iterating over directory blocks */
- { PR_2_DBLIST_ITERATE,
- N_("Error iterating over @d @bs: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error reading directory block */
- { PR_2_READ_DIRBLOCK,
- N_("Error reading @d @b %b (@i %i): %m\n"),
- PROMPT_CONTINUE, 0 },
-
- /* Error writing directory block */
- { PR_2_WRITE_DIRBLOCK,
- N_("Error writing @d @b %b (@i %i): %m\n"),
- PROMPT_CONTINUE, 0 },
-
- /* Error allocating new directory block */
- { PR_2_ALLOC_DIRBOCK,
- N_("@A new @d @b for @i %i (%s): %m\n"),
- PROMPT_NONE, 0 },
-
- /* Error deallocating inode */
- { PR_2_DEALLOC_INODE,
- N_("Error deallocating @i %i: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Directory entry for '.' is big. Split? */
- { PR_2_SPLIT_DOT,
- N_("@d @e for '.' is big. "),
- PROMPT_SPLIT, PR_NO_OK },
-
- /* Illegal FIFO inode */
- { PR_2_BAD_FIFO,
- N_("@i %i (%Q) is an @I FIFO.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Illegal socket inode */
- { PR_2_BAD_SOCKET,
- N_("@i %i (%Q) is an @I socket.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Directory filetype not set */
- { PR_2_SET_FILETYPE,
- N_("Setting filetype for @E to %N.\n"),
- PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
-
- /* Directory filetype incorrect */
- { PR_2_BAD_FILETYPE,
- N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
- PROMPT_FIX, 0 },
-
- /* Directory filetype set on filesystem */
- { PR_2_CLEAR_FILETYPE,
- N_("@E has filetype set.\n"),
- PROMPT_CLEAR, PR_PREEN_OK },
-
- /* Directory filename is null */
- { PR_2_NULL_NAME,
- N_("@E has a @z name.\n"),
- PROMPT_CLEAR, 0 },
-
- /* Invalid symlink */
- { PR_2_INVALID_SYMLINK,
- N_("Symlink %Q (@i #%i) is @n.\n"),
- PROMPT_CLEAR, 0 },
-
- /* i_file_acl (extended attribute block) is bad */
- { PR_2_FILE_ACL_BAD,
- N_("@a @b @F @n (%If).\n"),
- PROMPT_CLEAR, 0 },
-
- /* Filesystem contains large files, but has no such flag in sb */
- { PR_2_FEATURE_LARGE_FILES,
- N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
- PROMPT_FIX, 0 },
-
- /* Node in HTREE directory not referenced */
- { PR_2_HTREE_NOTREF,
- N_("@p @h %d: node (%B) not referenced\n"),
- PROMPT_NONE, 0 },
-
- /* Node in HTREE directory referenced twice */
- { PR_2_HTREE_DUPREF,
- N_("@p @h %d: node (%B) referenced twice\n"),
- PROMPT_NONE, 0 },
-
- /* Node in HTREE directory has bad min hash */
- { PR_2_HTREE_MIN_HASH,
- N_("@p @h %d: node (%B) has bad min hash\n"),
- PROMPT_NONE, 0 },
-
- /* Node in HTREE directory has bad max hash */
- { PR_2_HTREE_MAX_HASH,
- N_("@p @h %d: node (%B) has bad max hash\n"),
- PROMPT_NONE, 0 },
-
- /* Clear invalid HTREE directory */
- { PR_2_HTREE_CLEAR,
- N_("@n @h %d (%q). "), PROMPT_CLEAR, 0 },
-
- /* Bad block in htree interior node */
- { PR_2_HTREE_BADBLK,
- N_("@p @h %d (%q): bad @b number %b.\n"),
- PROMPT_CLEAR_HTREE, 0 },
-
- /* Error adjusting EA refcount */
- { PR_2_ADJ_EA_REFCOUNT,
- N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Invalid HTREE root node */
- { PR_2_HTREE_BAD_ROOT,
- N_("@p @h %d: root node is @n\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Invalid HTREE limit */
- { PR_2_HTREE_BAD_LIMIT,
- N_("@p @h %d: node (%B) has @n limit (%N)\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Invalid HTREE count */
- { PR_2_HTREE_BAD_COUNT,
- N_("@p @h %d: node (%B) has @n count (%N)\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* HTREE interior node has out-of-order hashes in table */
- { PR_2_HTREE_HASH_ORDER,
- N_("@p @h %d: node (%B) has an unordered hash table\n"),
- PROMPT_CLEAR_HTREE, PR_PREEN_OK },
-
- /* Node in HTREE directory has invalid depth */
- { PR_2_HTREE_BAD_DEPTH,
- N_("@p @h %d: node (%B) has @n depth\n"),
- PROMPT_NONE, 0 },
-
- /* Duplicate directory entry found */
- { PR_2_DUPLICATE_DIRENT,
- N_("Duplicate @E found. "),
- PROMPT_CLEAR, 0 },
-
- /* Non-unique filename found */
- { PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
- N_("@E has a non-unique filename.\nRename to %s"),
- PROMPT_NULL, 0 },
-
- /* Duplicate directory entry found */
- { PR_2_REPORT_DUP_DIRENT,
- N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
- PROMPT_NONE, 0 },
-
- /* Pass 3 errors */
-
- /* Pass 3: Checking directory connectivity */
- { PR_3_PASS_HEADER,
- N_("Pass 3: Checking @d connectivity\n"),
- PROMPT_NONE, 0 },
-
- /* Root inode not allocated */
- { PR_3_NO_ROOT_INODE,
- N_("@r not allocated. "),
- PROMPT_ALLOCATE, 0 },
-
- /* No room in lost+found */
- { PR_3_EXPAND_LF_DIR,
- N_("No room in @l @d. "),
- PROMPT_EXPAND, 0 },
-
- /* Unconnected directory inode */
- { PR_3_UNCONNECTED_DIR,
- N_("Unconnected @d @i %i (%p)\n"),
- PROMPT_CONNECT, 0 },
-
- /* /lost+found not found */
- { PR_3_NO_LF_DIR,
- N_("/@l not found. "),
- PROMPT_CREATE, PR_PREEN_OK },
-
- /* .. entry is incorrect */
- { PR_3_BAD_DOT_DOT,
- N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
- PROMPT_FIX, 0 },
-
- /* Bad or non-existent /lost+found. Cannot reconnect */
- { PR_3_NO_LPF,
- N_("Bad or non-existent /@l. Cannot reconnect.\n"),
- PROMPT_NONE, 0 },
-
- /* Could not expand /lost+found */
- { PR_3_CANT_EXPAND_LPF,
- N_("Could not expand /@l: %m\n"),
- PROMPT_NONE, 0 },
-
- /* Could not reconnect inode */
- { PR_3_CANT_RECONNECT,
- N_("Could not reconnect %i: %m\n"),
- PROMPT_NONE, 0 },
-
- /* Error while trying to find /lost+found */
- { PR_3_ERR_FIND_LPF,
- N_("Error while trying to find /@l: %m\n"),
- PROMPT_NONE, 0 },
-
- /* Error in ext2fs_new_block while creating /lost+found */
- { PR_3_ERR_LPF_NEW_BLOCK,
- N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
- PROMPT_NONE, 0 },
-
- /* Error in ext2fs_new_inode while creating /lost+found */
- { PR_3_ERR_LPF_NEW_INODE,
- N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
- PROMPT_NONE, 0 },
-
- /* Error in ext2fs_new_dir_block while creating /lost+found */
- { PR_3_ERR_LPF_NEW_DIR_BLOCK,
- N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
- PROMPT_NONE, 0 },
-
- /* Error while writing directory block for /lost+found */
- { PR_3_ERR_LPF_WRITE_BLOCK,
- N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
- PROMPT_NONE, 0 },
-
- /* Error while adjusting inode count */
- { PR_3_ADJUST_INODE,
- N_("Error while adjusting @i count on @i %i\n"),
- PROMPT_NONE, 0 },
-
- /* Couldn't fix parent directory -- error */
- { PR_3_FIX_PARENT_ERR,
- N_("Couldn't fix parent of @i %i: %m\n\n"),
- PROMPT_NONE, 0 },
-
- /* Couldn't fix parent directory -- couldn't find it */
- { PR_3_FIX_PARENT_NOFIND,
- N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"),
- PROMPT_NONE, 0 },
-
- /* Error allocating inode bitmap */
- { PR_3_ALLOCATE_IBITMAP_ERROR,
- N_("@A @i @B (%N): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error creating root directory */
- { PR_3_CREATE_ROOT_ERROR,
- N_("Error creating root @d (%s): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error creating lost and found directory */
- { PR_3_CREATE_LPF_ERROR,
- N_("Error creating /@l @d (%s): %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Root inode is not directory; aborting */
- { PR_3_ROOT_NOT_DIR_ABORT,
- N_("@r is not a @d; aborting.\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Cannot proceed without a root inode. */
- { PR_3_NO_ROOT_INODE_ABORT,
- N_("can't proceed without a @r.\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Internal error: couldn't find dir_info */
- { PR_3_NO_DIRINFO,
- N_("Internal error: cannot find dir_info for %i.\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Lost+found not a directory */
- { PR_3_LPF_NOTDIR,
- N_("/@l is not a @d (ino=%i)\n"),
- PROMPT_UNLINK, 0 },
-
- /* Pass 3A Directory Optimization */
-
- /* Pass 3A: Optimizing directories */
- { PR_3A_PASS_HEADER,
- N_("Pass 3A: Optimizing directories\n"),
- PROMPT_NONE, PR_PREEN_NOMSG },
-
- /* Error iterating over directories */
- { PR_3A_OPTIMIZE_ITER,
- N_("Failed to create dirs_to_hash iterator: %m"),
- PROMPT_NONE, 0 },
-
- /* Error rehash directory */
- { PR_3A_OPTIMIZE_DIR_ERR,
- N_("Failed to optimize directory %q (%d): %m"),
- PROMPT_NONE, 0 },
-
- /* Rehashing dir header */
- { PR_3A_OPTIMIZE_DIR_HEADER,
- N_("Optimizing directories: "),
- PROMPT_NONE, PR_MSG_ONLY },
-
- /* Rehashing directory %d */
- { PR_3A_OPTIMIZE_DIR,
- " %d",
- PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
-
- /* Rehashing dir end */
- { PR_3A_OPTIMIZE_DIR_END,
- "\n",
- PROMPT_NONE, PR_PREEN_NOHDR },
-
- /* Pass 4 errors */
-
- /* Pass 4: Checking reference counts */
- { PR_4_PASS_HEADER,
- N_("Pass 4: Checking reference counts\n"),
- PROMPT_NONE, 0 },
-
- /* Unattached zero-length inode */
- { PR_4_ZERO_LEN_INODE,
- N_("@u @z @i %i. "),
- PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
-
- /* Unattached inode */
- { PR_4_UNATTACHED_INODE,
- N_("@u @i %i\n"),
- PROMPT_CONNECT, 0 },
-
- /* Inode ref count wrong */
- { PR_4_BAD_REF_COUNT,
- N_("@i %i ref count is %Il, @s %N. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- { PR_4_INCONSISTENT_COUNT,
- N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
- "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
- "@i_link_info[%i] is %N, @i.i_links_count is %Il. "
- "They @s the same!\n"),
- PROMPT_NONE, 0 },
-
- /* Pass 5 errors */
-
- /* Pass 5: Checking group summary information */
- { PR_5_PASS_HEADER,
- N_("Pass 5: Checking @g summary information\n"),
- PROMPT_NONE, 0 },
-
- /* Padding at end of inode bitmap is not set. */
- { PR_5_INODE_BMAP_PADDING,
- N_("Padding at end of @i @B is not set. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Padding at end of block bitmap is not set. */
- { PR_5_BLOCK_BMAP_PADDING,
- N_("Padding at end of @b @B is not set. "),
- PROMPT_FIX, PR_PREEN_OK },
-
- /* Block bitmap differences header */
- { PR_5_BLOCK_BITMAP_HEADER,
- N_("@b @B differences: "),
- PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
-
- /* Block not used, but marked in bitmap */
- { PR_5_BLOCK_UNUSED,
- " -%b",
- PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Block used, but not marked used in bitmap */
- { PR_5_BLOCK_USED,
- " +%b",
- PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Block bitmap differences end */
- { PR_5_BLOCK_BITMAP_END,
- "\n",
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode bitmap differences header */
- { PR_5_INODE_BITMAP_HEADER,
- N_("@i @B differences: "),
- PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode not used, but marked in bitmap */
- { PR_5_INODE_UNUSED,
- " -%i",
- PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode used, but not marked used in bitmap */
- { PR_5_INODE_USED,
- " +%i",
- PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode bitmap differences end */
- { PR_5_INODE_BITMAP_END,
- "\n",
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Free inodes count for group wrong */
- { PR_5_FREE_INODE_COUNT_GROUP,
- N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Directories count for group wrong */
- { PR_5_FREE_DIR_COUNT_GROUP,
- N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Free inodes count wrong */
- { PR_5_FREE_INODE_COUNT,
- N_("Free @is count wrong (%i, counted=%j).\n"),
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Free blocks count for group wrong */
- { PR_5_FREE_BLOCK_COUNT_GROUP,
- N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Free blocks count wrong */
- { PR_5_FREE_BLOCK_COUNT,
- N_("Free @bs count wrong (%b, counted=%c).\n"),
- PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Programming error: bitmap endpoints don't match */
- { PR_5_BMAP_ENDPOINTS,
- N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
- "match calculated @B endpoints (%i, %j)\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Internal error: fudging end of bitmap */
- { PR_5_FUDGE_BITMAP_ERROR,
- N_("Internal error: fudging end of bitmap (%N)\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error copying in replacement inode bitmap */
- { PR_5_COPY_IBITMAP_ERROR,
- N_("Error copying in replacement @i @B: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Error copying in replacement block bitmap */
- { PR_5_COPY_BBITMAP_ERROR,
- N_("Error copying in replacement @b @B: %m\n"),
- PROMPT_NONE, PR_FATAL },
-
- /* Block range not used, but marked in bitmap */
- { PR_5_BLOCK_RANGE_UNUSED,
- " -(%b--%c)",
- PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Block range used, but not marked used in bitmap */
- { PR_5_BLOCK_RANGE_USED,
- " +(%b--%c)",
- PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode range not used, but marked in bitmap */
- { PR_5_INODE_RANGE_UNUSED,
- " -(%i--%j)",
- PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- /* Inode range used, but not marked used in bitmap */
- { PR_5_INODE_RANGE_USED,
- " +(%i--%j)",
- PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
-
- { 0 }
-};
-
-/*
- * This is the latch flags register. It allows several problems to be
- * "latched" together. This means that the user has to answer but one
- * question for the set of problems, and all of the associated
- * problems will be either fixed or not fixed.
- */
-static struct latch_descr pr_latch_info[] = {
- { PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
- { PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
- { PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
- { PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
- { PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
- { PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
- { PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
- { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
- { PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
- { -1, 0, 0 },
-};
-
-static const struct e2fsck_problem *find_problem(problem_t code)
-{
- int i;
-
- for (i=0; problem_table[i].e2p_code; i++) {
- if (problem_table[i].e2p_code == code)
- return &problem_table[i];
- }
- return 0;
-}
-
-static struct latch_descr *find_latch(int code)
-{
- int i;
-
- for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
- if (pr_latch_info[i].latch_code == code)
- return &pr_latch_info[i];
- }
- return 0;
-}
-
-int end_problem_latch(e2fsck_t ctx, int mask)
-{
- struct latch_descr *ldesc;
- struct problem_context pctx;
- int answer = -1;
-
- ldesc = find_latch(mask);
- if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
- clear_problem_context(&pctx);
- answer = fix_problem(ctx, ldesc->end_message, &pctx);
- }
- ldesc->flags &= ~(PRL_VARIABLE);
- return answer;
-}
-
-int set_latch_flags(int mask, int setflags, int clearflags)
-{
- struct latch_descr *ldesc;
-
- ldesc = find_latch(mask);
- if (!ldesc)
- return -1;
- ldesc->flags |= setflags;
- ldesc->flags &= ~clearflags;
- return 0;
-}
-
-void clear_problem_context(struct problem_context *ctx)
-{
- memset(ctx, 0, sizeof(struct problem_context));
- ctx->blkcount = -1;
- ctx->group = -1;
-}
-
-int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
-{
- ext2_filsys fs = ctx->fs;
- const struct e2fsck_problem *ptr;
- struct latch_descr *ldesc = NULL;
- const char *message;
- int def_yn, answer, ans;
- int print_answer = 0;
- int suppress = 0;
-
- ptr = find_problem(code);
- if (!ptr) {
- printf(_("Unhandled error code (0x%x)!\n"), code);
- return 0;
- }
- def_yn = 1;
- if ((ptr->flags & PR_NO_DEFAULT) ||
- ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
- (ctx->options & E2F_OPT_NO))
- def_yn= 0;
-
- /*
- * Do special latch processing. This is where we ask the
- * latch question, if it exists
- */
- if (ptr->flags & PR_LATCH_MASK) {
- ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
- if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
- ans = fix_problem(ctx, ldesc->question, pctx);
- if (ans == 1)
- ldesc->flags |= PRL_YES;
- if (ans == 0)
- ldesc->flags |= PRL_NO;
- ldesc->flags |= PRL_LATCHED;
- }
- if (ldesc->flags & PRL_SUPPRESS)
- suppress++;
- }
- if ((ptr->flags & PR_PREEN_NOMSG) &&
- (ctx->options & E2F_OPT_PREEN))
- suppress++;
- if ((ptr->flags & PR_NO_NOMSG) &&
- (ctx->options & E2F_OPT_NO))
- suppress++;
- if (!suppress) {
- message = ptr->e2p_description;
- if ((ctx->options & E2F_OPT_PREEN) &&
- !(ptr->flags & PR_PREEN_NOHDR)) {
- printf("%s: ", ctx->device_name ?
- ctx->device_name : ctx->filesystem_name);
- }
- if (*message)
- print_e2fsck_message(ctx, _(message), pctx, 1);
- }
- if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
- preenhalt(ctx);
-
- if (ptr->flags & PR_FATAL)
- bb_error_msg_and_die(0);
-
- if (ptr->prompt == PROMPT_NONE) {
- if (ptr->flags & PR_NOCOLLATE)
- answer = -1;
- else
- answer = def_yn;
- } else {
- if (ctx->options & E2F_OPT_PREEN) {
- answer = def_yn;
- if (!(ptr->flags & PR_PREEN_NOMSG))
- print_answer = 1;
- } else if ((ptr->flags & PR_LATCH_MASK) &&
- (ldesc->flags & (PRL_YES | PRL_NO))) {
- if (!suppress)
- print_answer = 1;
- if (ldesc->flags & PRL_YES)
- answer = 1;
- else
- answer = 0;
- } else
- answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
- if (!answer && !(ptr->flags & PR_NO_OK))
- ext2fs_unmark_valid(fs);
-
- if (print_answer)
- printf("%s.\n", answer ?
- _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
- }
-
- if ((ptr->prompt == PROMPT_ABORT) && answer)
- bb_error_msg_and_die(0);
-
- if (ptr->flags & PR_AFTER_CODE)
- answer = fix_problem(ctx, ptr->second_code, pctx);
-
- return answer;
-}
-
-/*
- * linux/fs/recovery.c
- *
- * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
- */
-
-/*
- * Maintain information about the progress of the recovery job, so that
- * the different passes can carry information between them.
- */
-struct recovery_info
-{
- tid_t start_transaction;
- tid_t end_transaction;
-
- int nr_replays;
- int nr_revokes;
- int nr_revoke_hits;
-};
-
-enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
-static int do_one_pass(journal_t *journal,
- struct recovery_info *info, enum passtype pass);
-static int scan_revoke_records(journal_t *, struct buffer_head *,
- tid_t, struct recovery_info *);
-
-/*
- * Read a block from the journal
- */
-
-static int jread(struct buffer_head **bhp, journal_t *journal,
- unsigned int offset)
-{
- int err;
- unsigned long blocknr;
- struct buffer_head *bh;
-
- *bhp = NULL;
-
- err = journal_bmap(journal, offset, &blocknr);
-
- if (err) {
- printf("JBD: bad block at offset %u\n", offset);
- return err;
- }
-
- bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
- if (!bh)
- return -ENOMEM;
-
- if (!buffer_uptodate(bh)) {
- /* If this is a brand new buffer, start readahead.
- Otherwise, we assume we are already reading it. */
- if (!buffer_req(bh))
- do_readahead(journal, offset);
- wait_on_buffer(bh);
- }
-
- if (!buffer_uptodate(bh)) {
- printf("JBD: Failed to read block at offset %u\n", offset);
- brelse(bh);
- return -EIO;
- }
-
- *bhp = bh;
- return 0;
-}
-
-
-/*
- * Count the number of in-use tags in a journal descriptor block.
- */
-
-static int count_tags(struct buffer_head *bh, int size)
-{
- char * tagp;
- journal_block_tag_t * tag;
- int nr = 0;
-
- tagp = &bh->b_data[sizeof(journal_header_t)];
-
- while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
- tag = (journal_block_tag_t *) tagp;
-
- nr++;
- tagp += sizeof(journal_block_tag_t);
- if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
- tagp += 16;
-
- if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
- break;
- }
-
- return nr;
-}
-
-
-/* Make sure we wrap around the log correctly! */
-#define wrap(journal, var) \
-do { \
- if (var >= (journal)->j_last) \
- var -= ((journal)->j_last - (journal)->j_first); \
-} while (0)
-
-/**
- * int journal_recover(journal_t *journal) - recovers a on-disk journal
- * @journal: the journal to recover
- *
- * The primary function for recovering the log contents when mounting a
- * journaled device.
- *
- * Recovery is done in three passes. In the first pass, we look for the
- * end of the log. In the second, we assemble the list of revoke
- * blocks. In the third and final pass, we replay any un-revoked blocks
- * in the log.
- */
-int journal_recover(journal_t *journal)
-{
- int err;
- journal_superblock_t * sb;
-
- struct recovery_info info;
-
- memset(&info, 0, sizeof(info));
- sb = journal->j_superblock;
-
- /*
- * The journal superblock's s_start field (the current log head)
- * is always zero if, and only if, the journal was cleanly
- * unmounted.
- */
-
- if (!sb->s_start) {
- journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
- return 0;
- }
-
- err = do_one_pass(journal, &info, PASS_SCAN);
- if (!err)
- err = do_one_pass(journal, &info, PASS_REVOKE);
- if (!err)
- err = do_one_pass(journal, &info, PASS_REPLAY);
-
- /* Restart the log at the next transaction ID, thus invalidating
- * any existing commit records in the log. */
- journal->j_transaction_sequence = ++info.end_transaction;
-
- journal_clear_revoke(journal);
- sync_blockdev(journal->j_fs_dev);
- return err;
-}
-
-static int do_one_pass(journal_t *journal,
- struct recovery_info *info, enum passtype pass)
-{
- unsigned int first_commit_ID, next_commit_ID;
- unsigned long next_log_block;
- int err, success = 0;
- journal_superblock_t * sb;
- journal_header_t * tmp;
- struct buffer_head * bh;
- unsigned int sequence;
- int blocktype;
-
- /* Precompute the maximum metadata descriptors in a descriptor block */
- int MAX_BLOCKS_PER_DESC;
- MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
- / sizeof(journal_block_tag_t));
-
- /*
- * First thing is to establish what we expect to find in the log
- * (in terms of transaction IDs), and where (in terms of log
- * block offsets): query the superblock.
- */
-
- sb = journal->j_superblock;
- next_commit_ID = ntohl(sb->s_sequence);
- next_log_block = ntohl(sb->s_start);
-
- first_commit_ID = next_commit_ID;
- if (pass == PASS_SCAN)
- info->start_transaction = first_commit_ID;
-
- /*
- * Now we walk through the log, transaction by transaction,
- * making sure that each transaction has a commit block in the
- * expected place. Each complete transaction gets replayed back
- * into the main filesystem.
- */
-
- while (1) {
- int flags;
- char * tagp;
- journal_block_tag_t * tag;
- struct buffer_head * obh;
- struct buffer_head * nbh;
-
- /* If we already know where to stop the log traversal,
- * check right now that we haven't gone past the end of
- * the log. */
-
- if (pass != PASS_SCAN)
- if (tid_geq(next_commit_ID, info->end_transaction))
- break;
-
- /* Skip over each chunk of the transaction looking
- * either the next descriptor block or the final commit
- * record. */
-
- err = jread(&bh, journal, next_log_block);
- if (err)
- goto failed;
-
- next_log_block++;
- wrap(journal, next_log_block);
-
- /* What kind of buffer is it?
- *
- * If it is a descriptor block, check that it has the
- * expected sequence number. Otherwise, we're all done
- * here. */
-
- tmp = (journal_header_t *)bh->b_data;
-
- if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
- brelse(bh);
- break;
- }
-
- blocktype = ntohl(tmp->h_blocktype);
- sequence = ntohl(tmp->h_sequence);
-
- if (sequence != next_commit_ID) {
- brelse(bh);
- break;
- }
-
- /* OK, we have a valid descriptor block which matches
- * all of the sequence number checks. What are we going
- * to do with it? That depends on the pass... */
-
- switch (blocktype) {
- case JFS_DESCRIPTOR_BLOCK:
- /* If it is a valid descriptor block, replay it
- * in pass REPLAY; otherwise, just skip over the
- * blocks it describes. */
- if (pass != PASS_REPLAY) {
- next_log_block +=
- count_tags(bh, journal->j_blocksize);
- wrap(journal, next_log_block);
- brelse(bh);
- continue;
- }
-
- /* A descriptor block: we can now write all of
- * the data blocks. Yay, useful work is finally
- * getting done here! */
-
- tagp = &bh->b_data[sizeof(journal_header_t)];
- while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
- <= journal->j_blocksize) {
- unsigned long io_block;
-
- tag = (journal_block_tag_t *) tagp;
- flags = ntohl(tag->t_flags);
-
- io_block = next_log_block++;
- wrap(journal, next_log_block);
- err = jread(&obh, journal, io_block);
- if (err) {
- /* Recover what we can, but
- * report failure at the end. */
- success = err;
- printf("JBD: IO error %d recovering "
- "block %ld in log\n",
- err, io_block);
- } else {
- unsigned long blocknr;
-
- blocknr = ntohl(tag->t_blocknr);
-
- /* If the block has been
- * revoked, then we're all done
- * here. */
- if (journal_test_revoke
- (journal, blocknr,
- next_commit_ID)) {
- brelse(obh);
- ++info->nr_revoke_hits;
- goto skip_write;
- }
-
- /* Find a buffer for the new
- * data being restored */
- nbh = getblk(journal->j_fs_dev,
- blocknr,
- journal->j_blocksize);
- if (nbh == NULL) {
- printf("JBD: Out of memory "
- "during recovery.\n");
- err = -ENOMEM;
- brelse(bh);
- brelse(obh);
- goto failed;
- }
-
- lock_buffer(nbh);
- memcpy(nbh->b_data, obh->b_data,
- journal->j_blocksize);
- if (flags & JFS_FLAG_ESCAPE) {
- *((unsigned int *)bh->b_data) =
- htonl(JFS_MAGIC_NUMBER);
- }
-
- mark_buffer_uptodate(nbh, 1);
- mark_buffer_dirty(nbh);
- ++info->nr_replays;
- /* ll_rw_block(WRITE, 1, &nbh); */
- unlock_buffer(nbh);
- brelse(obh);
- brelse(nbh);
- }
-
- skip_write:
- tagp += sizeof(journal_block_tag_t);
- if (!(flags & JFS_FLAG_SAME_UUID))
- tagp += 16;
-
- if (flags & JFS_FLAG_LAST_TAG)
- break;
- }
-
- brelse(bh);
- continue;
-
- case JFS_COMMIT_BLOCK:
- /* Found an expected commit block: not much to
- * do other than move on to the next sequence
- * number. */
- brelse(bh);
- next_commit_ID++;
- continue;
-
- case JFS_REVOKE_BLOCK:
- /* If we aren't in the REVOKE pass, then we can
- * just skip over this block. */
- if (pass != PASS_REVOKE) {
- brelse(bh);
- continue;
- }
-
- err = scan_revoke_records(journal, bh,
- next_commit_ID, info);
- brelse(bh);
- if (err)
- goto failed;
- continue;
-
- default:
- goto done;
- }
- }
-
- done:
- /*
- * We broke out of the log scan loop: either we came to the
- * known end of the log or we found an unexpected block in the
- * log. If the latter happened, then we know that the "current"
- * transaction marks the end of the valid log.
- */
-
- if (pass == PASS_SCAN)
- info->end_transaction = next_commit_ID;
- else {
- /* It's really bad news if different passes end up at
- * different places (but possible due to IO errors). */
- if (info->end_transaction != next_commit_ID) {
- printf("JBD: recovery pass %d ended at "
- "transaction %u, expected %u\n",
- pass, next_commit_ID, info->end_transaction);
- if (!success)
- success = -EIO;
- }
- }
-
- return success;
-
- failed:
- return err;
-}
-
-
-/* Scan a revoke record, marking all blocks mentioned as revoked. */
-
-static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
- tid_t sequence, struct recovery_info *info)
-{
- journal_revoke_header_t *header;
- int offset, max;
-
- header = (journal_revoke_header_t *) bh->b_data;
- offset = sizeof(journal_revoke_header_t);
- max = ntohl(header->r_count);
-
- while (offset < max) {
- unsigned long blocknr;
- int err;
-
- blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
- offset += 4;
- err = journal_set_revoke(journal, blocknr, sequence);
- if (err)
- return err;
- ++info->nr_revokes;
- }
- return 0;
-}
-
-
-/*
- * rehash.c --- rebuild hash tree directories
- *
- * This algorithm is designed for simplicity of implementation and to
- * pack the directory as much as possible. It however requires twice
- * as much memory as the size of the directory. The maximum size
- * directory supported using a 4k blocksize is roughly a gigabyte, and
- * so there may very well be problems with machines that don't have
- * virtual memory, and obscenely large directories.
- *
- * An alternate algorithm which is much more disk intensive could be
- * written, and probably will need to be written in the future. The
- * design goals of such an algorithm are: (a) use (roughly) constant
- * amounts of memory, no matter how large the directory, (b) the
- * directory must be safe at all times, even if e2fsck is interrupted
- * in the middle, (c) we must use minimal amounts of extra disk
- * blocks. This pretty much requires an incremental approach, where
- * we are reading from one part of the directory, and inserting into
- * the front half. So the algorithm will have to keep track of a
- * moving block boundary between the new tree and the old tree, and
- * files will need to be moved from the old directory and inserted
- * into the new tree. If the new directory requires space which isn't
- * yet available, blocks from the beginning part of the old directory
- * may need to be moved to the end of the directory to make room for
- * the new tree:
- *
- * --------------------------------------------------------
- * | new tree | | old tree |
- * --------------------------------------------------------
- * ^ ptr ^ptr
- * tail new head old
- *
- * This is going to be a pain in the tuckus to implement, and will
- * require a lot more disk accesses. So I'm going to skip it for now;
- * it's only really going to be an issue for really, really big
- * filesystems (when we reach the level of tens of millions of files
- * in a single directory). It will probably be easier to simply
- * require that e2fsck use VM first.
- */
-
-struct fill_dir_struct {
- char *buf;
- struct ext2_inode *inode;
- int err;
- e2fsck_t ctx;
- struct hash_entry *harray;
- int max_array, num_array;
- int dir_size;
- int compress;
- ino_t parent;
-};
-
-struct hash_entry {
- ext2_dirhash_t hash;
- ext2_dirhash_t minor_hash;
- struct ext2_dir_entry *dir;
-};
-
-struct out_dir {
- int num;
- int max;
- char *buf;
- ext2_dirhash_t *hashes;
-};
-
-static int fill_dir_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
- struct hash_entry *new_array, *ent;
- struct ext2_dir_entry *dirent;
- char *dir;
- unsigned int offset, dir_offset;
-
- if (blockcnt < 0)
- return 0;
-
- offset = blockcnt * fs->blocksize;
- if (offset + fs->blocksize > fd->inode->i_size) {
- fd->err = EXT2_ET_DIR_CORRUPTED;
- return BLOCK_ABORT;
- }
- dir = (fd->buf+offset);
- if (HOLE_BLKADDR(*block_nr)) {
- memset(dir, 0, fs->blocksize);
- dirent = (struct ext2_dir_entry *) dir;
- dirent->rec_len = fs->blocksize;
- } else {
- fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
- if (fd->err)
- return BLOCK_ABORT;
- }
- /* While the directory block is "hot", index it. */
- dir_offset = 0;
- while (dir_offset < fs->blocksize) {
- dirent = (struct ext2_dir_entry *) (dir + dir_offset);
- if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
- (dirent->rec_len < 8) ||
- ((dirent->rec_len % 4) != 0) ||
- (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
- fd->err = EXT2_ET_DIR_CORRUPTED;
- return BLOCK_ABORT;
- }
- dir_offset += dirent->rec_len;
- if (dirent->inode == 0)
- continue;
- if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
- (dirent->name[0] == '.'))
- continue;
- if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
- (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
- fd->parent = dirent->inode;
- continue;
- }
- if (fd->num_array >= fd->max_array) {
- new_array = xrealloc(fd->harray,
- sizeof(struct hash_entry) * (fd->max_array+500));
- fd->harray = new_array;
- fd->max_array += 500;
- }
- ent = fd->harray + fd->num_array++;
- ent->dir = dirent;
- fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
- if (fd->compress)
- ent->hash = ent->minor_hash = 0;
- else {
- fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
- dirent->name,
- dirent->name_len & 0xFF,
- fs->super->s_hash_seed,
- &ent->hash, &ent->minor_hash);
- if (fd->err)
- return BLOCK_ABORT;
- }
- }
-
- return 0;
-}
-
-/* Used for sorting the hash entry */
-static int name_cmp(const void *a, const void *b)
-{
- const struct hash_entry *he_a = (const struct hash_entry *) a;
- const struct hash_entry *he_b = (const struct hash_entry *) b;
- int ret;
- int min_len;
-
- min_len = he_a->dir->name_len;
- if (min_len > he_b->dir->name_len)
- min_len = he_b->dir->name_len;
-
- ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
- if (ret == 0) {
- if (he_a->dir->name_len > he_b->dir->name_len)
- ret = 1;
- else if (he_a->dir->name_len < he_b->dir->name_len)
- ret = -1;
- else
- ret = he_b->dir->inode - he_a->dir->inode;
- }
- return ret;
-}
-
-/* Used for sorting the hash entry */
-static int hash_cmp(const void *a, const void *b)
-{
- const struct hash_entry *he_a = (const struct hash_entry *) a;
- const struct hash_entry *he_b = (const struct hash_entry *) b;
- int ret;
-
- if (he_a->hash > he_b->hash)
- ret = 1;
- else if (he_a->hash < he_b->hash)
- ret = -1;
- else {
- if (he_a->minor_hash > he_b->minor_hash)
- ret = 1;
- else if (he_a->minor_hash < he_b->minor_hash)
- ret = -1;
- else
- ret = name_cmp(a, b);
- }
- return ret;
-}
-
-static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
- int blocks)
-{
- void *new_mem;
-
- if (outdir->max) {
- new_mem = xrealloc(outdir->buf, blocks * fs->blocksize);
- outdir->buf = new_mem;
- new_mem = xrealloc(outdir->hashes,
- blocks * sizeof(ext2_dirhash_t));
- outdir->hashes = new_mem;
- } else {
- outdir->buf = xmalloc(blocks * fs->blocksize);
- outdir->hashes = xmalloc(blocks * sizeof(ext2_dirhash_t));
- outdir->num = 0;
- }
- outdir->max = blocks;
- return 0;
-}
-
-static void free_out_dir(struct out_dir *outdir)
-{
- free(outdir->buf);
- free(outdir->hashes);
- outdir->max = 0;
- outdir->num =0;
-}
-
-static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
- char ** ret)
-{
- errcode_t retval;
-
- if (outdir->num >= outdir->max) {
- retval = alloc_size_dir(fs, outdir, outdir->max + 50);
- if (retval)
- return retval;
- }
- *ret = outdir->buf + (outdir->num++ * fs->blocksize);
- memset(*ret, 0, fs->blocksize);
- return 0;
-}
-
-/*
- * This function is used to make a unique filename. We do this by
- * appending ~0, and then incrementing the number. However, we cannot
- * expand the length of the filename beyond the padding available in
- * the directory entry.
- */
-static void mutate_name(char *str, __u16 *len)
-{
- int i;
- __u16 l = *len & 0xFF, h = *len & 0xff00;
-
- /*
- * First check to see if it looks the name has been mutated
- * already
- */
- for (i = l-1; i > 0; i--) {
- if (!isdigit(str[i]))
- break;
- }
- if ((i == l-1) || (str[i] != '~')) {
- if (((l-1) & 3) < 2)
- l += 2;
- else
- l = (l+3) & ~3;
- str[l-2] = '~';
- str[l-1] = '0';
- *len = l | h;
- return;
- }
- for (i = l-1; i >= 0; i--) {
- if (isdigit(str[i])) {
- if (str[i] == '9')
- str[i] = '0';
- else {
- str[i]++;
- return;
- }
- continue;
- }
- if (i == 1) {
- if (str[0] == 'z')
- str[0] = 'A';
- else if (str[0] == 'Z') {
- str[0] = '~';
- str[1] = '0';
- } else
- str[0]++;
- } else if (i > 0) {
- str[i] = '1';
- str[i-1] = '~';
- } else {
- if (str[0] == '~')
- str[0] = 'a';
- else
- str[0]++;
- }
- break;
- }
-}
-
-static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
- ext2_ino_t ino,
- struct fill_dir_struct *fd)
-{
- struct problem_context pctx;
- struct hash_entry *ent, *prev;
- int i, j;
- int fixed = 0;
- char new_name[256];
- __u16 new_len;
-
- clear_problem_context(&pctx);
- pctx.ino = ino;
-
- for (i=1; i < fd->num_array; i++) {
- ent = fd->harray + i;
- prev = ent - 1;
- if (!ent->dir->inode ||
- ((ent->dir->name_len & 0xFF) !=
- (prev->dir->name_len & 0xFF)) ||
- (strncmp(ent->dir->name, prev->dir->name,
- ent->dir->name_len & 0xFF)))
- continue;
- pctx.dirent = ent->dir;
- if ((ent->dir->inode == prev->dir->inode) &&
- fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
- e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
- ent->dir->inode = 0;
- fixed++;
- continue;
- }
- memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
- new_len = ent->dir->name_len;
- mutate_name(new_name, &new_len);
- for (j=0; j < fd->num_array; j++) {
- if ((i==j) ||
- ((ent->dir->name_len & 0xFF) !=
- (fd->harray[j].dir->name_len & 0xFF)) ||
- (strncmp(new_name, fd->harray[j].dir->name,
- new_len & 0xFF)))
- continue;
- mutate_name(new_name, &new_len);
-
- j = -1;
- }
- new_name[new_len & 0xFF] = 0;
- pctx.str = new_name;
- if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
- memcpy(ent->dir->name, new_name, new_len & 0xFF);
- ent->dir->name_len = new_len;
- ext2fs_dirhash(fs->super->s_def_hash_version,
- ent->dir->name,
- ent->dir->name_len & 0xFF,
- fs->super->s_hash_seed,
- &ent->hash, &ent->minor_hash);
- fixed++;
- }
- }
- return fixed;
-}
-
-
-static errcode_t copy_dir_entries(ext2_filsys fs,
- struct fill_dir_struct *fd,
- struct out_dir *outdir)
-{
- errcode_t retval;
- char *block_start;
- struct hash_entry *ent;
- struct ext2_dir_entry *dirent;
- int i, rec_len, left;
- ext2_dirhash_t prev_hash;
- int offset;
-
- outdir->max = 0;
- retval = alloc_size_dir(fs, outdir,
- (fd->dir_size / fs->blocksize) + 2);
- if (retval)
- return retval;
- outdir->num = fd->compress ? 0 : 1;
- offset = 0;
- outdir->hashes[0] = 0;
- prev_hash = 1;
- if ((retval = get_next_block(fs, outdir, &block_start)))
- return retval;
- dirent = (struct ext2_dir_entry *) block_start;
- left = fs->blocksize;
- for (i=0; i < fd->num_array; i++) {
- ent = fd->harray + i;
- if (ent->dir->inode == 0)
- continue;
- rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
- if (rec_len > left) {
- if (left)
- dirent->rec_len += left;
- if ((retval = get_next_block(fs, outdir,
- &block_start)))
- return retval;
- offset = 0;
- }
- left = fs->blocksize - offset;
- dirent = (struct ext2_dir_entry *) (block_start + offset);
- if (offset == 0) {
- if (ent->hash == prev_hash)
- outdir->hashes[outdir->num-1] = ent->hash | 1;
- else
- outdir->hashes[outdir->num-1] = ent->hash;
- }
- dirent->inode = ent->dir->inode;
- dirent->name_len = ent->dir->name_len;
- dirent->rec_len = rec_len;
- memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
- offset += rec_len;
- left -= rec_len;
- if (left < 12) {
- dirent->rec_len += left;
- offset += left;
- left = 0;
- }
- prev_hash = ent->hash;
- }
- if (left)
- dirent->rec_len += left;
-
- return 0;
-}
-
-
-static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
- ext2_ino_t ino, ext2_ino_t parent)
-{
- struct ext2_dir_entry *dir;
- struct ext2_dx_root_info *root;
- struct ext2_dx_countlimit *limits;
- int filetype = 0;
-
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
- filetype = EXT2_FT_DIR << 8;
-
- memset(buf, 0, fs->blocksize);
- dir = (struct ext2_dir_entry *) buf;
- dir->inode = ino;
- dir->name[0] = '.';
- dir->name_len = 1 | filetype;
- dir->rec_len = 12;
- dir = (struct ext2_dir_entry *) (buf + 12);
- dir->inode = parent;
- dir->name[0] = '.';
- dir->name[1] = '.';
- dir->name_len = 2 | filetype;
- dir->rec_len = fs->blocksize - 12;
-
- root = (struct ext2_dx_root_info *) (buf+24);
- root->reserved_zero = 0;
- root->hash_version = fs->super->s_def_hash_version;
- root->info_length = 8;
- root->indirect_levels = 0;
- root->unused_flags = 0;
-
- limits = (struct ext2_dx_countlimit *) (buf+32);
- limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
- limits->count = 0;
-
- return root;
-}
-
-
-static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
-{
- struct ext2_dir_entry *dir;
- struct ext2_dx_countlimit *limits;
-
- memset(buf, 0, fs->blocksize);
- dir = (struct ext2_dir_entry *) buf;
- dir->inode = 0;
- dir->rec_len = fs->blocksize;
-
- limits = (struct ext2_dx_countlimit *) (buf+8);
- limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
- limits->count = 0;
-
- return (struct ext2_dx_entry *) limits;
-}
-
-/*
- * This function takes the leaf nodes which have been written in
- * outdir, and populates the root node and any necessary interior nodes.
- */
-static errcode_t calculate_tree(ext2_filsys fs,
- struct out_dir *outdir,
- ext2_ino_t ino,
- ext2_ino_t parent)
-{
- struct ext2_dx_root_info *root_info;
- struct ext2_dx_entry *root, *dx_ent = NULL;
- struct ext2_dx_countlimit *root_limit, *limit;
- errcode_t retval;
- char * block_start;
- int i, c1, c2, nblks;
- int limit_offset, root_offset;
-
- root_info = set_root_node(fs, outdir->buf, ino, parent);
- root_offset = limit_offset = ((char *) root_info - outdir->buf) +
- root_info->info_length;
- root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
- c1 = root_limit->limit;
- nblks = outdir->num;
-
- /* Write out the pointer blocks */
- if (nblks-1 <= c1) {
- /* Just write out the root block, and we're done */
- root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
- for (i=1; i < nblks; i++) {
- root->block = ext2fs_cpu_to_le32(i);
- if (i != 1)
- root->hash =
- ext2fs_cpu_to_le32(outdir->hashes[i]);
- root++;
- c1--;
- }
- } else {
- c2 = 0;
- limit = 0;
- root_info->indirect_levels = 1;
- for (i=1; i < nblks; i++) {
- if (c1 == 0)
- return ENOSPC;
- if (c2 == 0) {
- if (limit)
- limit->limit = limit->count =
- ext2fs_cpu_to_le16(limit->limit);
- root = (struct ext2_dx_entry *)
- (outdir->buf + root_offset);
- root->block = ext2fs_cpu_to_le32(outdir->num);
- if (i != 1)
- root->hash =
- ext2fs_cpu_to_le32(outdir->hashes[i]);
- if ((retval = get_next_block(fs, outdir,
- &block_start)))
- return retval;
- dx_ent = set_int_node(fs, block_start);
- limit = (struct ext2_dx_countlimit *) dx_ent;
- c2 = limit->limit;
- root_offset += sizeof(struct ext2_dx_entry);
- c1--;
- }
- dx_ent->block = ext2fs_cpu_to_le32(i);
- if (c2 != limit->limit)
- dx_ent->hash =
- ext2fs_cpu_to_le32(outdir->hashes[i]);
- dx_ent++;
- c2--;
- }
- limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
- limit->limit = ext2fs_cpu_to_le16(limit->limit);
- }
- root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
- root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
- root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
-
- return 0;
-}
-
-struct write_dir_struct {
- struct out_dir *outdir;
- errcode_t err;
- e2fsck_t ctx;
- int cleared;
-};
-
-/*
- * Helper function which writes out a directory block.
- */
-static int write_dir_block(ext2_filsys fs,
- blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
- blk_t blk;
- char *dir;
-
- if (*block_nr == 0)
- return 0;
- if (blockcnt >= wd->outdir->num) {
- e2fsck_read_bitmaps(wd->ctx);
- blk = *block_nr;
- ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
- ext2fs_block_alloc_stats(fs, blk, -1);
- *block_nr = 0;
- wd->cleared++;
- return BLOCK_CHANGED;
- }
- if (blockcnt < 0)
- return 0;
-
- dir = wd->outdir->buf + (blockcnt * fs->blocksize);
- wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
- if (wd->err)
- return BLOCK_ABORT;
- return 0;
-}
-
-static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
- struct out_dir *outdir,
- ext2_ino_t ino, int compress)
-{
- struct write_dir_struct wd;
- errcode_t retval;
- struct ext2_inode inode;
-
- retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
- if (retval)
- return retval;
-
- wd.outdir = outdir;
- wd.err = 0;
- wd.ctx = ctx;
- wd.cleared = 0;
-
- retval = ext2fs_block_iterate2(fs, ino, 0, 0,
- write_dir_block, &wd);
- if (retval)
- return retval;
- if (wd.err)
- return wd.err;
-
- e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
- if (compress)
- inode.i_flags &= ~EXT2_INDEX_FL;
- else
- inode.i_flags |= EXT2_INDEX_FL;
- inode.i_size = outdir->num * fs->blocksize;
- inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
- e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
-
- return 0;
-}
-
-static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- struct ext2_inode inode;
- char *dir_buf = NULL;
- struct fill_dir_struct fd;
- struct out_dir outdir;
-
- outdir.max = outdir.num = 0;
- outdir.buf = 0;
- outdir.hashes = 0;
- e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
-
- retval = ENOMEM;
- fd.harray = 0;
- dir_buf = xmalloc(inode.i_size);
-
- fd.max_array = inode.i_size / 32;
- fd.num_array = 0;
- fd.harray = xmalloc(fd.max_array * sizeof(struct hash_entry));
-
- fd.ctx = ctx;
- fd.buf = dir_buf;
- fd.inode = &inode;
- fd.err = 0;
- fd.dir_size = 0;
- fd.compress = 0;
- if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
- (inode.i_size / fs->blocksize) < 2)
- fd.compress = 1;
- fd.parent = 0;
-
- /* Read in the entire directory into memory */
- retval = ext2fs_block_iterate2(fs, ino, 0, 0,
- fill_dir_block, &fd);
- if (fd.err) {
- retval = fd.err;
- goto errout;
- }
-
- /* Sort the list */
-resort:
- if (fd.compress)
- qsort(fd.harray+2, fd.num_array-2,
- sizeof(struct hash_entry), name_cmp);
- else
- qsort(fd.harray, fd.num_array,
- sizeof(struct hash_entry), hash_cmp);
-
- /*
- * Look for duplicates
- */
- if (duplicate_search_and_fix(ctx, fs, ino, &fd))
- goto resort;
-
- if (ctx->options & E2F_OPT_NO) {
- retval = 0;
- goto errout;
- }
-
- /*
- * Copy the directory entries. In a htree directory these
- * will become the leaf nodes.
- */
- retval = copy_dir_entries(fs, &fd, &outdir);
- if (retval)
- goto errout;
-
- free(dir_buf); dir_buf = 0;
-
- if (!fd.compress) {
- /* Calculate the interior nodes */
- retval = calculate_tree(fs, &outdir, ino, fd.parent);
- if (retval)
- goto errout;
- }
-
- retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
-
-errout:
- free(dir_buf);
- free(fd.harray);
-
- free_out_dir(&outdir);
- return retval;
-}
-
-void e2fsck_rehash_directories(e2fsck_t ctx)
-{
- struct problem_context pctx;
- struct dir_info *dir;
- ext2_u32_iterate iter;
- ext2_ino_t ino;
- errcode_t retval;
- int i, cur, max, all_dirs, dir_index, first = 1;
-
- all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
-
- if (!ctx->dirs_to_hash && !all_dirs)
- return;
-
- e2fsck_get_lost_and_found(ctx, 0);
-
- clear_problem_context(&pctx);
-
- dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
- cur = 0;
- if (all_dirs) {
- i = 0;
- max = e2fsck_get_num_dirinfo(ctx);
- } else {
- retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
- &iter);
- if (retval) {
- pctx.errcode = retval;
- fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
- return;
- }
- max = ext2fs_u32_list_count(ctx->dirs_to_hash);
- }
- while (1) {
- if (all_dirs) {
- if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
- break;
- ino = dir->ino;
- } else {
- if (!ext2fs_u32_list_iterate(iter, &ino))
- break;
- }
- if (ino == ctx->lost_and_found)
- continue;
- pctx.dir = ino;
- if (first) {
- fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
- first = 0;
- }
- pctx.errcode = e2fsck_rehash_dir(ctx, ino);
- if (pctx.errcode) {
- end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
- fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
- }
- if (ctx->progress && !ctx->progress_fd)
- e2fsck_simple_progress(ctx, "Rebuilding directory",
- 100.0 * (float) (++cur) / (float) max, ino);
- }
- end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
- if (!all_dirs)
- ext2fs_u32_list_iterate_end(iter);
-
- ext2fs_u32_list_free(ctx->dirs_to_hash);
- ctx->dirs_to_hash = 0;
-}
-
-/*
- * linux/fs/revoke.c
- *
- * Journal revoke routines for the generic filesystem journaling code;
- * part of the ext2fs journaling system.
- *
- * Revoke is the mechanism used to prevent old log records for deleted
- * metadata from being replayed on top of newer data using the same
- * blocks. The revoke mechanism is used in two separate places:
- *
- * + Commit: during commit we write the entire list of the current
- * transaction's revoked blocks to the journal
- *
- * + Recovery: during recovery we record the transaction ID of all
- * revoked blocks. If there are multiple revoke records in the log
- * for a single block, only the last one counts, and if there is a log
- * entry for a block beyond the last revoke, then that log entry still
- * gets replayed.
- *
- * We can get interactions between revokes and new log data within a
- * single transaction:
- *
- * Block is revoked and then journaled:
- * The desired end result is the journaling of the new block, so we
- * cancel the revoke before the transaction commits.
- *
- * Block is journaled and then revoked:
- * The revoke must take precedence over the write of the block, so we
- * need either to cancel the journal entry or to write the revoke
- * later in the log than the log block. In this case, we choose the
- * latter: journaling a block cancels any revoke record for that block
- * in the current transaction, so any revoke for that block in the
- * transaction must have happened after the block was journaled and so
- * the revoke must take precedence.
- *
- * Block is revoked and then written as data:
- * The data write is allowed to succeed, but the revoke is _not_
- * cancelled. We still need to prevent old log records from
- * overwriting the new data. We don't even need to clear the revoke
- * bit here.
- *
- * Revoke information on buffers is a tri-state value:
- *
- * RevokeValid clear: no cached revoke status, need to look it up
- * RevokeValid set, Revoked clear:
- * buffer has not been revoked, and cancel_revoke
- * need do nothing.
- * RevokeValid set, Revoked set:
- * buffer has been revoked.
- */
-
-static kmem_cache_t *revoke_record_cache;
-static kmem_cache_t *revoke_table_cache;
-
-/* Each revoke record represents one single revoked block. During
- journal replay, this involves recording the transaction ID of the
- last transaction to revoke this block. */
-
-struct jbd_revoke_record_s
-{
- struct list_head hash;
- tid_t sequence; /* Used for recovery only */
- unsigned long blocknr;
-};
-
-
-/* The revoke table is just a simple hash table of revoke records. */
-struct jbd_revoke_table_s
-{
- /* It is conceivable that we might want a larger hash table
- * for recovery. Must be a power of two. */
- int hash_size;
- int hash_shift;
- struct list_head *hash_table;
-};
-
-
-/* Utility functions to maintain the revoke table */
-
-/* Borrowed from buffer.c: this is a tried and tested block hash function */
-static int hash(journal_t *journal, unsigned long block)
-{
- struct jbd_revoke_table_s *table = journal->j_revoke;
- int hash_shift = table->hash_shift;
-
- return ((block << (hash_shift - 6)) ^
- (block >> 13) ^
- (block << (hash_shift - 12))) & (table->hash_size - 1);
-}
-
-static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
- tid_t seq)
-{
- struct list_head *hash_list;
- struct jbd_revoke_record_s *record;
-
- record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
- if (!record)
- goto oom;
-
- record->sequence = seq;
- record->blocknr = blocknr;
- hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
- list_add(&record->hash, hash_list);
- return 0;
-
-oom:
- return -ENOMEM;
-}
-
-/* Find a revoke record in the journal's hash table. */
-
-static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
- unsigned long blocknr)
-{
- struct list_head *hash_list;
- struct jbd_revoke_record_s *record;
-
- hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
-
- record = (struct jbd_revoke_record_s *) hash_list->next;
- while (&(record->hash) != hash_list) {
- if (record->blocknr == blocknr)
- return record;
- record = (struct jbd_revoke_record_s *) record->hash.next;
- }
- return NULL;
-}
-
-int journal_init_revoke_caches(void)
-{
- revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
- if (revoke_record_cache == 0)
- return -ENOMEM;
-
- revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
- if (revoke_table_cache == 0) {
- do_cache_destroy(revoke_record_cache);
- revoke_record_cache = NULL;
- return -ENOMEM;
- }
- return 0;
-}
-
-void journal_destroy_revoke_caches(void)
-{
- do_cache_destroy(revoke_record_cache);
- revoke_record_cache = 0;
- do_cache_destroy(revoke_table_cache);
- revoke_table_cache = 0;
-}
-
-/* Initialise the revoke table for a given journal to a given size. */
-
-int journal_init_revoke(journal_t *journal, int hash_size)
-{
- int shift, tmp;
-
- journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
- if (!journal->j_revoke)
- return -ENOMEM;
-
- /* Check that the hash_size is a power of two */
- journal->j_revoke->hash_size = hash_size;
-
- shift = 0;
- tmp = hash_size;
- while ((tmp >>= 1UL) != 0UL)
- shift++;
- journal->j_revoke->hash_shift = shift;
-
- journal->j_revoke->hash_table = xmalloc(hash_size * sizeof(struct list_head));
-
- for (tmp = 0; tmp < hash_size; tmp++)
- INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
-
- return 0;
-}
-
-/* Destoy a journal's revoke table. The table must already be empty! */
-
-void journal_destroy_revoke(journal_t *journal)
-{
- struct jbd_revoke_table_s *table;
- struct list_head *hash_list;
- int i;
-
- table = journal->j_revoke;
- if (!table)
- return;
-
- for (i=0; i<table->hash_size; i++) {
- hash_list = &table->hash_table[i];
- }
-
- free(table->hash_table);
- free(table);
- journal->j_revoke = NULL;
-}
-
-/*
- * Revoke support for recovery.
- *
- * Recovery needs to be able to:
- *
- * record all revoke records, including the tid of the latest instance
- * of each revoke in the journal
- *
- * check whether a given block in a given transaction should be replayed
- * (ie. has not been revoked by a revoke record in that or a subsequent
- * transaction)
- *
- * empty the revoke table after recovery.
- */
-
-/*
- * First, setting revoke records. We create a new revoke record for
- * every block ever revoked in the log as we scan it for recovery, and
- * we update the existing records if we find multiple revokes for a
- * single block.
- */
-
-int journal_set_revoke(journal_t *journal, unsigned long blocknr,
- tid_t sequence)
-{
- struct jbd_revoke_record_s *record;
-
- record = find_revoke_record(journal, blocknr);
- if (record) {
- /* If we have multiple occurences, only record the
- * latest sequence number in the hashed record */
- if (tid_gt(sequence, record->sequence))
- record->sequence = sequence;
- return 0;
- }
- return insert_revoke_hash(journal, blocknr, sequence);
-}
-
-/*
- * Test revoke records. For a given block referenced in the log, has
- * that block been revoked? A revoke record with a given transaction
- * sequence number revokes all blocks in that transaction and earlier
- * ones, but later transactions still need replayed.
- */
-
-int journal_test_revoke(journal_t *journal, unsigned long blocknr,
- tid_t sequence)
-{
- struct jbd_revoke_record_s *record;
-
- record = find_revoke_record(journal, blocknr);
- if (!record)
- return 0;
- if (tid_gt(sequence, record->sequence))
- return 0;
- return 1;
-}
-
-/*
- * Finally, once recovery is over, we need to clear the revoke table so
- * that it can be reused by the running filesystem.
- */
-
-void journal_clear_revoke(journal_t *journal)
-{
- int i;
- struct list_head *hash_list;
- struct jbd_revoke_record_s *record;
- struct jbd_revoke_table_s *revoke_var;
-
- revoke_var = journal->j_revoke;
-
- for (i = 0; i < revoke_var->hash_size; i++) {
- hash_list = &revoke_var->hash_table[i];
- while (!list_empty(hash_list)) {
- record = (struct jbd_revoke_record_s*) hash_list->next;
- list_del(&record->hash);
- free(record);
- }
- }
-}
-
-/*
- * e2fsck.c - superblock checks
- */
-
-#define MIN_CHECK 1
-#define MAX_CHECK 2
-
-static void check_super_value(e2fsck_t ctx, const char *descr,
- unsigned long value, int flags,
- unsigned long min_val, unsigned long max_val)
-{
- struct problem_context pctx;
-
- if (((flags & MIN_CHECK) && (value < min_val)) ||
- ((flags & MAX_CHECK) && (value > max_val))) {
- clear_problem_context(&pctx);
- pctx.num = value;
- pctx.str = descr;
- fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
- }
-}
-
-/*
- * This routine may get stubbed out in special compilations of the
- * e2fsck code..
- */
-#ifndef EXT2_SPECIAL_DEVICE_SIZE
-static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
-{
- return (ext2fs_get_device_size(ctx->filesystem_name,
- EXT2_BLOCK_SIZE(ctx->fs->super),
- &ctx->num_blocks));
-}
-#endif
-
-/*
- * helper function to release an inode
- */
-struct process_block_struct {
- e2fsck_t ctx;
- char *buf;
- struct problem_context *pctx;
- int truncating;
- int truncate_offset;
- e2_blkcnt_t truncate_block;
- int truncated_blocks;
- int abort;
- errcode_t errcode;
-};
-
-static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_blk FSCK_ATTR((unused)),
- int ref_offset FSCK_ATTR((unused)),
- void *priv_data)
-{
- struct process_block_struct *pb;
- e2fsck_t ctx;
- struct problem_context *pctx;
- blk_t blk = *block_nr;
- int retval = 0;
-
- pb = (struct process_block_struct *) priv_data;
- ctx = pb->ctx;
- pctx = pb->pctx;
-
- pctx->blk = blk;
- pctx->blkcount = blockcnt;
-
- if (HOLE_BLKADDR(blk))
- return 0;
-
- if ((blk < fs->super->s_first_data_block) ||
- (blk >= fs->super->s_blocks_count)) {
- fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
- return_abort:
- pb->abort = 1;
- return BLOCK_ABORT;
- }
-
- if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
- fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
- goto return_abort;
- }
-
- /*
- * If we are deleting an orphan, then we leave the fields alone.
- * If we are truncating an orphan, then update the inode fields
- * and clean up any partial block data.
- */
- if (pb->truncating) {
- /*
- * We only remove indirect blocks if they are
- * completely empty.
- */
- if (blockcnt < 0) {
- int i, limit;
- blk_t *bp;
-
- pb->errcode = io_channel_read_blk(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
-
- limit = fs->blocksize >> 2;
- for (i = 0, bp = (blk_t *) pb->buf;
- i < limit; i++, bp++)
- if (*bp)
- return 0;
- }
- /*
- * We don't remove direct blocks until we've reached
- * the truncation block.
- */
- if (blockcnt >= 0 && blockcnt < pb->truncate_block)
- return 0;
- /*
- * If part of the last block needs truncating, we do
- * it here.
- */
- if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
- pb->errcode = io_channel_read_blk(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
- memset(pb->buf + pb->truncate_offset, 0,
- fs->blocksize - pb->truncate_offset);
- pb->errcode = io_channel_write_blk(fs->io, blk, 1,
- pb->buf);
- if (pb->errcode)
- goto return_abort;
- }
- pb->truncated_blocks++;
- *block_nr = 0;
- retval |= BLOCK_CHANGED;
- }
-
- ext2fs_block_alloc_stats(fs, blk, -1);
- return retval;
-}
-
-/*
- * This function releases an inode. Returns 1 if an inconsistency was
- * found. If the inode has a link count, then it is being truncated and
- * not deleted.
- */
-static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
- struct ext2_inode *inode, char *block_buf,
- struct problem_context *pctx)
-{
- struct process_block_struct pb;
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
- __u32 count;
-
- if (!ext2fs_inode_has_valid_blocks(inode))
- return 0;
-
- pb.buf = block_buf + 3 * ctx->fs->blocksize;
- pb.ctx = ctx;
- pb.abort = 0;
- pb.errcode = 0;
- pb.pctx = pctx;
- if (inode->i_links_count) {
- pb.truncating = 1;
- pb.truncate_block = (e2_blkcnt_t)
- ((((long long)inode->i_size_high << 32) +
- inode->i_size + fs->blocksize - 1) /
- fs->blocksize);
- pb.truncate_offset = inode->i_size % fs->blocksize;
- } else {
- pb.truncating = 0;
- pb.truncate_block = 0;
- pb.truncate_offset = 0;
- }
- pb.truncated_blocks = 0;
- retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
- block_buf, release_inode_block, &pb);
- if (retval) {
- bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
- ino);
- return 1;
- }
- if (pb.abort)
- return 1;
-
- /* Refresh the inode since ext2fs_block_iterate may have changed it */
- e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
-
- if (pb.truncated_blocks)
- inode->i_blocks -= pb.truncated_blocks *
- (fs->blocksize / 512);
-
- if (inode->i_file_acl) {
- retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
- block_buf, -1, &count);
- if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
- retval = 0;
- count = 1;
- }
- if (retval) {
- bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
- ino);
- return 1;
- }
- if (count == 0)
- ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
- inode->i_file_acl = 0;
- }
- return 0;
-}
-
-/*
- * This function releases all of the orphan inodes. It returns 1 if
- * it hit some error, and 0 on success.
- */
-static int release_orphan_inodes(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- ext2_ino_t ino, next_ino;
- struct ext2_inode inode;
- struct problem_context pctx;
- char *block_buf;
-
- if ((ino = fs->super->s_last_orphan) == 0)
- return 0;
-
- /*
- * Win or lose, we won't be using the head of the orphan inode
- * list again.
- */
- fs->super->s_last_orphan = 0;
- ext2fs_mark_super_dirty(fs);
-
- /*
- * If the filesystem contains errors, don't run the orphan
- * list, since the orphan list can't be trusted; and we're
- * going to be running a full e2fsck run anyway...
- */
- if (fs->super->s_state & EXT2_ERROR_FS)
- return 0;
-
- if ((ino < EXT2_FIRST_INODE(fs->super)) ||
- (ino > fs->super->s_inodes_count)) {
- clear_problem_context(&pctx);
- pctx.ino = ino;
- fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
- return 1;
- }
-
- block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
- "block iterate buffer");
- e2fsck_read_bitmaps(ctx);
-
- while (ino) {
- e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
- clear_problem_context(&pctx);
- pctx.ino = ino;
- pctx.inode = &inode;
- pctx.str = inode.i_links_count ? _("Truncating") :
- _("Clearing");
-
- fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
-
- next_ino = inode.i_dtime;
- if (next_ino &&
- ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
- (next_ino > fs->super->s_inodes_count))) {
- pctx.ino = next_ino;
- fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
- goto return_abort;
- }
-
- if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
- goto return_abort;
-
- if (!inode.i_links_count) {
- ext2fs_inode_alloc_stats2(fs, ino, -1,
- LINUX_S_ISDIR(inode.i_mode));
- inode.i_dtime = time(NULL);
- } else {
- inode.i_dtime = 0;
- }
- e2fsck_write_inode(ctx, ino, &inode, "delete_file");
- ino = next_ino;
- }
- ext2fs_free_mem(&block_buf);
- return 0;
- return_abort:
- ext2fs_free_mem(&block_buf);
- return 1;
-}
-
-/*
- * Check the resize inode to make sure it is sane. We check both for
- * the case where on-line resizing is not enabled (in which case the
- * resize inode should be cleared) as well as the case where on-line
- * resizing is enabled.
- */
-static void check_resize_inode(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- struct ext2_inode inode;
- struct problem_context pctx;
- int i, j, gdt_off, ind_off;
- blk_t blk, pblk, expect;
- __u32 *dind_buf = NULL, *ind_buf;
- errcode_t retval;
-
- clear_problem_context(&pctx);
-
- /*
- * If the resize inode feature isn't set, then
- * s_reserved_gdt_blocks must be zero.
- */
- if (!(fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INO)) {
- if (fs->super->s_reserved_gdt_blocks) {
- pctx.num = fs->super->s_reserved_gdt_blocks;
- if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
- &pctx)) {
- fs->super->s_reserved_gdt_blocks = 0;
- ext2fs_mark_super_dirty(fs);
- }
- }
- }
-
- /* Read the resize inode */
- pctx.ino = EXT2_RESIZE_INO;
- retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
- if (retval) {
- if (fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INO)
- ctx->flags |= E2F_FLAG_RESIZE_INODE;
- return;
- }
-
- /*
- * If the resize inode feature isn't set, check to make sure
- * the resize inode is cleared; then we're done.
- */
- if (!(fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INO)) {
- for (i=0; i < EXT2_N_BLOCKS; i++) {
- if (inode.i_block[i])
- break;
- }
- if ((i < EXT2_N_BLOCKS) &&
- fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
- memset(&inode, 0, sizeof(inode));
- e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
- "clear_resize");
- }
- return;
- }
-
- /*
- * The resize inode feature is enabled; check to make sure the
- * only block in use is the double indirect block
- */
- blk = inode.i_block[EXT2_DIND_BLOCK];
- for (i=0; i < EXT2_N_BLOCKS; i++) {
- if (i != EXT2_DIND_BLOCK && inode.i_block[i])
- break;
- }
- if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
- !(inode.i_mode & LINUX_S_IFREG) ||
- (blk < fs->super->s_first_data_block ||
- blk >= fs->super->s_blocks_count)) {
- resize_inode_invalid:
- if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
- memset(&inode, 0, sizeof(inode));
- e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
- "clear_resize");
- ctx->flags |= E2F_FLAG_RESIZE_INODE;
- }
- if (!(ctx->options & E2F_OPT_READONLY)) {
- fs->super->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(fs);
- }
- goto cleanup;
- }
- dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
- "resize dind buffer");
- ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
-
- retval = ext2fs_read_ind_block(fs, blk, dind_buf);
- if (retval)
- goto resize_inode_invalid;
-
- gdt_off = fs->desc_blocks;
- pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
- for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
- i++, gdt_off++, pblk++) {
- gdt_off %= fs->blocksize/4;
- if (dind_buf[gdt_off] != pblk)
- goto resize_inode_invalid;
- retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
- if (retval)
- goto resize_inode_invalid;
- ind_off = 0;
- for (j = 1; j < fs->group_desc_count; j++) {
- if (!ext2fs_bg_has_super(fs, j))
- continue;
- expect = pblk + (j * fs->super->s_blocks_per_group);
- if (ind_buf[ind_off] != expect)
- goto resize_inode_invalid;
- ind_off++;
- }
- }
-
- cleanup:
- ext2fs_free_mem(&dind_buf);
-}
-
-static void check_super_block(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- blk_t first_block, last_block;
- struct ext2_super_block *sb = fs->super;
- struct ext2_group_desc *gd;
- blk_t blocks_per_group = fs->super->s_blocks_per_group;
- blk_t bpg_max;
- int inodes_per_block;
- int ipg_max;
- int inode_size;
- dgrp_t i;
- blk_t should_be;
- struct problem_context pctx;
- __u32 free_blocks = 0, free_inodes = 0;
-
- inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
- ipg_max = inodes_per_block * (blocks_per_group - 4);
- if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
- ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
- bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
- if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
- bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
-
- ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
- sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
- ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
- sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
- ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
- sizeof(int) * fs->group_desc_count, "invalid_inode_table");
-
- clear_problem_context(&pctx);
-
- /*
- * Verify the super block constants...
- */
- check_super_value(ctx, "inodes_count", sb->s_inodes_count,
- MIN_CHECK, 1, 0);
- check_super_value(ctx, "blocks_count", sb->s_blocks_count,
- MIN_CHECK, 1, 0);
- check_super_value(ctx, "first_data_block", sb->s_first_data_block,
- MAX_CHECK, 0, sb->s_blocks_count);
- check_super_value(ctx, "log_block_size", sb->s_log_block_size,
- MIN_CHECK | MAX_CHECK, 0,
- EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
- check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
- MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
- check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
- MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
- bpg_max);
- check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
- MIN_CHECK | MAX_CHECK, 8, bpg_max);
- check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
- MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
- check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
- MAX_CHECK, 0, sb->s_blocks_count / 2);
- check_super_value(ctx, "reserved_gdt_blocks",
- sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
- fs->blocksize/4);
- inode_size = EXT2_INODE_SIZE(sb);
- check_super_value(ctx, "inode_size",
- inode_size, MIN_CHECK | MAX_CHECK,
- EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
- if (inode_size & (inode_size - 1)) {
- pctx.num = inode_size;
- pctx.str = "inode_size";
- fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
- ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
- return;
- }
-
- if (!ctx->num_blocks) {
- pctx.errcode = e2fsck_get_device_size(ctx);
- if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
- fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
- (ctx->num_blocks < sb->s_blocks_count)) {
- pctx.blk = sb->s_blocks_count;
- pctx.blk2 = ctx->num_blocks;
- if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
- }
-
- if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
- pctx.blk = EXT2_BLOCK_SIZE(sb);
- pctx.blk2 = EXT2_FRAG_SIZE(sb);
- fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- should_be = sb->s_frags_per_group >>
- (sb->s_log_block_size - sb->s_log_frag_size);
- if (sb->s_blocks_per_group != should_be) {
- pctx.blk = sb->s_blocks_per_group;
- pctx.blk2 = should_be;
- fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- should_be = (sb->s_log_block_size == 0) ? 1 : 0;
- if (sb->s_first_data_block != should_be) {
- pctx.blk = sb->s_first_data_block;
- pctx.blk2 = should_be;
- fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-
- should_be = sb->s_inodes_per_group * fs->group_desc_count;
- if (sb->s_inodes_count != should_be) {
- pctx.ino = sb->s_inodes_count;
- pctx.ino2 = should_be;
- if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
- sb->s_inodes_count = should_be;
- ext2fs_mark_super_dirty(fs);
- }
- }
-
- /*
- * Verify the group descriptors....
- */
- first_block = sb->s_first_data_block;
- last_block = first_block + blocks_per_group;
-
- for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
- pctx.group = i;
-
- if (i == fs->group_desc_count - 1)
- last_block = sb->s_blocks_count;
- if ((gd->bg_block_bitmap < first_block) ||
- (gd->bg_block_bitmap >= last_block)) {
- pctx.blk = gd->bg_block_bitmap;
- if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
- gd->bg_block_bitmap = 0;
- }
- if (gd->bg_block_bitmap == 0) {
- ctx->invalid_block_bitmap_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- if ((gd->bg_inode_bitmap < first_block) ||
- (gd->bg_inode_bitmap >= last_block)) {
- pctx.blk = gd->bg_inode_bitmap;
- if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
- gd->bg_inode_bitmap = 0;
- }
- if (gd->bg_inode_bitmap == 0) {
- ctx->invalid_inode_bitmap_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- if ((gd->bg_inode_table < first_block) ||
- ((gd->bg_inode_table +
- fs->inode_blocks_per_group - 1) >= last_block)) {
- pctx.blk = gd->bg_inode_table;
- if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
- gd->bg_inode_table = 0;
- }
- if (gd->bg_inode_table == 0) {
- ctx->invalid_inode_table_flag[i]++;
- ctx->invalid_bitmaps++;
- }
- free_blocks += gd->bg_free_blocks_count;
- free_inodes += gd->bg_free_inodes_count;
- first_block += sb->s_blocks_per_group;
- last_block += sb->s_blocks_per_group;
-
- if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
- (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
- (gd->bg_used_dirs_count > sb->s_inodes_per_group))
- ext2fs_unmark_valid(fs);
- }
-
- /*
- * Update the global counts from the block group counts. This
- * is needed for an experimental patch which eliminates
- * locking the entire filesystem when allocating blocks or
- * inodes; if the filesystem is not unmounted cleanly, the
- * global counts may not be accurate.
- */
- if ((free_blocks != sb->s_free_blocks_count) ||
- (free_inodes != sb->s_free_inodes_count)) {
- if (ctx->options & E2F_OPT_READONLY)
- ext2fs_unmark_valid(fs);
- else {
- sb->s_free_blocks_count = free_blocks;
- sb->s_free_inodes_count = free_inodes;
- ext2fs_mark_super_dirty(fs);
- }
- }
-
- if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
- (sb->s_free_inodes_count > sb->s_inodes_count))
- ext2fs_unmark_valid(fs);
-
-
- /*
- * If we have invalid bitmaps, set the error state of the
- * filesystem.
- */
- if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
- sb->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(fs);
- }
-
- clear_problem_context(&pctx);
-
- /*
- * If the UUID field isn't assigned, assign it.
- */
- if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
- if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
- uuid_generate(sb->s_uuid);
- ext2fs_mark_super_dirty(fs);
- fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
- }
- }
-
- /* FIXME - HURD support?
- * For the Hurd, check to see if the filetype option is set,
- * since it doesn't support it.
- */
- if (!(ctx->options & E2F_OPT_READONLY) &&
- fs->super->s_creator_os == EXT2_OS_HURD &&
- (fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
- if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
- fs->super->s_feature_incompat &=
- ~EXT2_FEATURE_INCOMPAT_FILETYPE;
- ext2fs_mark_super_dirty(fs);
- }
- }
-
- /*
- * If we have any of the compatibility flags set, we need to have a
- * revision 1 filesystem. Most kernels will not check the flags on
- * a rev 0 filesystem and we may have corruption issues because of
- * the incompatible changes to the filesystem.
- */
- if (!(ctx->options & E2F_OPT_READONLY) &&
- fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
- (fs->super->s_feature_compat ||
- fs->super->s_feature_ro_compat ||
- fs->super->s_feature_incompat) &&
- fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
- ext2fs_update_dynamic_rev(fs);
- ext2fs_mark_super_dirty(fs);
- }
-
- check_resize_inode(ctx);
-
- /*
- * Clean up any orphan inodes, if present.
- */
- if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
- fs->super->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(fs);
- }
-
- /*
- * Move the ext3 journal file, if necessary.
- */
- e2fsck_move_ext3_journal(ctx);
-}
-
-/*
- * swapfs.c --- byte-swap an ext2 filesystem
- */
-
-#ifdef ENABLE_SWAPFS
-
-struct swap_block_struct {
- ext2_ino_t ino;
- int isdir;
- errcode_t errcode;
- char *dir_buf;
- struct ext2_inode *inode;
-};
-
-/*
- * This is a helper function for block_iterate. We mark all of the
- * indirect and direct blocks as changed, so that block_iterate will
- * write them out.
- */
-static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
- void *priv_data)
-{
- errcode_t retval;
-
- struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
-
- if (sb->isdir && (blockcnt >= 0) && *block_nr) {
- retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
- if (retval) {
- sb->errcode = retval;
- return BLOCK_ABORT;
- }
- retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
- if (retval) {
- sb->errcode = retval;
- return BLOCK_ABORT;
- }
- }
- if (blockcnt >= 0) {
- if (blockcnt < EXT2_NDIR_BLOCKS)
- return 0;
- return BLOCK_CHANGED;
- }
- if (blockcnt == BLOCK_COUNT_IND) {
- if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
- return 0;
- return BLOCK_CHANGED;
- }
- if (blockcnt == BLOCK_COUNT_DIND) {
- if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
- return 0;
- return BLOCK_CHANGED;
- }
- if (blockcnt == BLOCK_COUNT_TIND) {
- if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
- return 0;
- return BLOCK_CHANGED;
- }
- return BLOCK_CHANGED;
-}
-
-/*
- * This function is responsible for byte-swapping all of the indirect,
- * block pointers. It is also responsible for byte-swapping directories.
- */
-static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
- struct ext2_inode *inode)
-{
- errcode_t retval;
- struct swap_block_struct sb;
-
- sb.ino = ino;
- sb.inode = inode;
- sb.dir_buf = block_buf + ctx->fs->blocksize*3;
- sb.errcode = 0;
- sb.isdir = 0;
- if (LINUX_S_ISDIR(inode->i_mode))
- sb.isdir = 1;
-
- retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
- swap_block, &sb);
- if (retval) {
- bb_error_msg(_("while calling ext2fs_block_iterate"));
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (sb.errcode) {
- bb_error_msg(_("while calling iterator function"));
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
-}
-
-static void swap_inodes(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- dgrp_t group;
- unsigned int i;
- ext2_ino_t ino = 1;
- char *buf, *block_buf;
- errcode_t retval;
- struct ext2_inode * inode;
-
- e2fsck_use_inode_shortcuts(ctx, 1);
-
- retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
- &buf);
- if (retval) {
- bb_error_msg(_("while allocating inode buffer"));
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
- "block interate buffer");
- for (group = 0; group < fs->group_desc_count; group++) {
- retval = io_channel_read_blk(fs->io,
- fs->group_desc[group].bg_inode_table,
- fs->inode_blocks_per_group, buf);
- if (retval) {
- bb_error_msg(_("while reading inode table (group %d)"),
- group);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- inode = (struct ext2_inode *) buf;
- for (i=0; i < fs->super->s_inodes_per_group;
- i++, ino++, inode++) {
- ctx->stashed_ino = ino;
- ctx->stashed_inode = inode;
-
- if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
- ext2fs_swap_inode(fs, inode, inode, 0);
-
- /*
- * Skip deleted files.
- */
- if (inode->i_links_count == 0)
- continue;
-
- if (LINUX_S_ISDIR(inode->i_mode) ||
- ((inode->i_block[EXT2_IND_BLOCK] ||
- inode->i_block[EXT2_DIND_BLOCK] ||
- inode->i_block[EXT2_TIND_BLOCK]) &&
- ext2fs_inode_has_valid_blocks(inode)))
- swap_inode_blocks(ctx, ino, block_buf, inode);
-
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
-
- if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
- ext2fs_swap_inode(fs, inode, inode, 1);
- }
- retval = io_channel_write_blk(fs->io,
- fs->group_desc[group].bg_inode_table,
- fs->inode_blocks_per_group, buf);
- if (retval) {
- bb_error_msg(_("while writing inode table (group %d)"),
- group);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- }
- ext2fs_free_mem(&buf);
- ext2fs_free_mem(&block_buf);
- e2fsck_use_inode_shortcuts(ctx, 0);
- ext2fs_flush_icache(fs);
-}
-
-#if defined(__powerpc__) && BB_BIG_ENDIAN
-/*
- * On the PowerPC, the big-endian variant of the ext2 filesystem
- * has its bitmaps stored as 32-bit words with bit 0 as the LSB
- * of each word. Thus a bitmap with only bit 0 set would be, as
- * a string of bytes, 00 00 00 01 00 ...
- * To cope with this, we byte-reverse each word of a bitmap if
- * we have a big-endian filesystem, that is, if we are *not*
- * byte-swapping other word-sized numbers.
- */
-#define EXT2_BIG_ENDIAN_BITMAPS
-#endif
-
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
-static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
-{
- __u32 *p = (__u32 *) bmap->bitmap;
- int n, nbytes = (bmap->end - bmap->start + 7) / 8;
-
- for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
- *p = ext2fs_swab32(*p);
-}
-#endif
-
-
-#ifdef ENABLE_SWAPFS
-static void swap_filesys(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- if (!(ctx->options & E2F_OPT_PREEN))
- printf(_("Pass 0: Doing byte-swap of filesystem\n"));
-
- /* Byte swap */
-
- if (fs->super->s_mnt_count) {
- fprintf(stderr, _("%s: the filesystem must be freshly "
- "checked using fsck\n"
- "and not mounted before trying to "
- "byte-swap it.\n"), ctx->device_name);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
- EXT2_FLAG_SWAP_BYTES_WRITE);
- fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
- } else {
- fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
- fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
- }
- swap_inodes(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
- fs->flags |= EXT2_FLAG_SWAP_BYTES;
- fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
- EXT2_FLAG_SWAP_BYTES_WRITE);
-
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
- e2fsck_read_bitmaps(ctx);
- ext2fs_swap_bitmap(fs->inode_map);
- ext2fs_swap_bitmap(fs->block_map);
- fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
-#endif
- fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
- ext2fs_flush(fs);
- fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
-}
-#endif /* ENABLE_SWAPFS */
-
-#endif
-
-/*
- * util.c --- miscellaneous utilities
- */
-
-
-void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
- const char *description)
-{
- return xzalloc(size);
-}
-
-static char *string_copy(const char *str, int len)
-{
- char *ret;
-
- if (!str)
- return NULL;
- if (!len)
- len = strlen(str);
- ret = xmalloc(len+1);
- strncpy(ret, str, len);
- ret[len] = 0;
- return ret;
-}
-
-#ifndef HAVE_CONIO_H
-static int read_a_char(void)
-{
- char c;
- int r;
- int fail = 0;
-
- while (1) {
- if (e2fsck_global_ctx &&
- (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
- return 3;
- }
- r = read(0, &c, 1);
- if (r == 1)
- return c;
- if (fail++ > 100)
- break;
- }
- return EOF;
-}
-#endif
-
-static int ask_yn(const char * string, int def)
-{
- int c;
- const char *defstr;
- static const char short_yes[] = "yY";
- static const char short_no[] = "nN";
-
-#ifdef HAVE_TERMIOS_H
- struct termios termios, tmp;
-
- tcgetattr (0, &termios);
- tmp = termios;
- tmp.c_lflag &= ~(ICANON | ECHO);
- tmp.c_cc[VMIN] = 1;
- tmp.c_cc[VTIME] = 0;
- tcsetattr_stdin_TCSANOW(&tmp);
-#endif
-
- if (def == 1)
- defstr = "<y>";
- else if (def == 0)
- defstr = "<n>";
- else
- defstr = " (y/n)";
- printf("%s%s? ", string, defstr);
- while (1) {
- fflush (stdout);
- if ((c = read_a_char()) == EOF)
- break;
- if (c == 3) {
-#ifdef HAVE_TERMIOS_H
- tcsetattr_stdin_TCSANOW(&termios);
-#endif
- if (e2fsck_global_ctx &&
- e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
- puts("\n");
- longjmp(e2fsck_global_ctx->abort_loc, 1);
- }
- puts(_("cancelled!\n"));
- return 0;
- }
- if (strchr(short_yes, (char) c)) {
- def = 1;
- break;
- }
- else if (strchr(short_no, (char) c)) {
- def = 0;
- break;
- }
- else if ((c == ' ' || c == '\n') && (def != -1))
- break;
- }
- if (def)
- puts("yes\n");
- else
- puts ("no\n");
-#ifdef HAVE_TERMIOS_H
- tcsetattr_stdin_TCSANOW(&termios);
-#endif
- return def;
-}
-
-int ask (e2fsck_t ctx, const char * string, int def)
-{
- if (ctx->options & E2F_OPT_NO) {
- printf(_("%s? no\n\n"), string);
- return 0;
- }
- if (ctx->options & E2F_OPT_YES) {
- printf(_("%s? yes\n\n"), string);
- return 1;
- }
- if (ctx->options & E2F_OPT_PREEN) {
- printf("%s? %s\n\n", string, def ? _("yes") : _("no"));
- return def;
- }
- return ask_yn(string, def);
-}
-
-void e2fsck_read_bitmaps(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
-
- if (ctx->invalid_bitmaps) {
- bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
-
- ehandler_operation(_("reading inode and block bitmaps"));
- retval = ext2fs_read_bitmaps(fs);
- ehandler_operation(0);
- if (retval) {
- bb_error_msg(_("while retrying to read bitmaps for %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
-}
-
-static void e2fsck_write_bitmaps(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- errcode_t retval;
-
- if (ext2fs_test_bb_dirty(fs)) {
- ehandler_operation(_("writing block bitmaps"));
- retval = ext2fs_write_block_bitmap(fs);
- ehandler_operation(0);
- if (retval) {
- bb_error_msg(_("while retrying to write block bitmaps for %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
- }
-
- if (ext2fs_test_ib_dirty(fs)) {
- ehandler_operation(_("writing inode bitmaps"));
- retval = ext2fs_write_inode_bitmap(fs);
- ehandler_operation(0);
- if (retval) {
- bb_error_msg(_("while retrying to write inode bitmaps for %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
- }
-}
-
-void preenhalt(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
-
- if (!(ctx->options & E2F_OPT_PREEN))
- return;
- fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
- "RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
- ctx->device_name);
- if (fs != NULL) {
- fs->super->s_state |= EXT2_ERROR_FS;
- ext2fs_mark_super_dirty(fs);
- ext2fs_close(fs);
- }
- exit(EXIT_UNCORRECTED);
-}
-
-void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, const char *proc)
-{
- int retval;
-
- retval = ext2fs_read_inode(ctx->fs, ino, inode);
- if (retval) {
- bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
- bb_error_msg_and_die(0);
- }
-}
-
-extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, int bufsize,
- const char *proc)
-{
- int retval;
-
- retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
- if (retval) {
- bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
- bb_error_msg_and_die(0);
- }
-}
-
-extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
- struct ext2_inode * inode, const char *proc)
-{
- int retval;
-
- retval = ext2fs_write_inode(ctx->fs, ino, inode);
- if (retval) {
- bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
- bb_error_msg_and_die(0);
- }
-}
-
-blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
- io_manager manager)
-{
- struct ext2_super_block *sb;
- io_channel io = NULL;
- void *buf = NULL;
- int blocksize;
- blk_t superblock, ret_sb = 8193;
-
- if (fs && fs->super) {
- ret_sb = (fs->super->s_blocks_per_group +
- fs->super->s_first_data_block);
- if (ctx) {
- ctx->superblock = ret_sb;
- ctx->blocksize = fs->blocksize;
- }
- return ret_sb;
- }
-
- if (ctx) {
- if (ctx->blocksize) {
- ret_sb = ctx->blocksize * 8;
- if (ctx->blocksize == 1024)
- ret_sb++;
- ctx->superblock = ret_sb;
- return ret_sb;
- }
- ctx->superblock = ret_sb;
- ctx->blocksize = 1024;
- }
-
- if (!name || !manager)
- goto cleanup;
-
- if (manager->open(name, 0, &io) != 0)
- goto cleanup;
-
- if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
- goto cleanup;
- sb = (struct ext2_super_block *) buf;
-
- for (blocksize = EXT2_MIN_BLOCK_SIZE;
- blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
- superblock = blocksize*8;
- if (blocksize == 1024)
- superblock++;
- io_channel_set_blksize(io, blocksize);
- if (io_channel_read_blk(io, superblock,
- -SUPERBLOCK_SIZE, buf))
- continue;
-#if BB_BIG_ENDIAN
- if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
- ext2fs_swap_super(sb);
-#endif
- if (sb->s_magic == EXT2_SUPER_MAGIC) {
- ret_sb = superblock;
- if (ctx) {
- ctx->superblock = superblock;
- ctx->blocksize = blocksize;
- }
- break;
- }
- }
-
-cleanup:
- if (io)
- io_channel_close(io);
- ext2fs_free_mem(&buf);
- return ret_sb;
-}
-
-
-/*
- * This function runs through the e2fsck passes and calls them all,
- * returning restart, abort, or cancel as necessary...
- */
-typedef void (*pass_t)(e2fsck_t ctx);
-
-static const pass_t e2fsck_passes[] = {
- e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
- e2fsck_pass5, 0 };
-
-#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
-
-static int e2fsck_run(e2fsck_t ctx)
-{
- int i;
- pass_t e2fsck_pass;
-
- if (setjmp(ctx->abort_loc)) {
- ctx->flags &= ~E2F_FLAG_SETJMP_OK;
- return (ctx->flags & E2F_FLAG_RUN_RETURN);
- }
- ctx->flags |= E2F_FLAG_SETJMP_OK;
-
- for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
- if (ctx->flags & E2F_FLAG_RUN_RETURN)
- break;
- e2fsck_pass(ctx);
- if (ctx->progress)
- (void) (ctx->progress)(ctx, 0, 0, 0);
- }
- ctx->flags &= ~E2F_FLAG_SETJMP_OK;
-
- if (ctx->flags & E2F_FLAG_RUN_RETURN)
- return (ctx->flags & E2F_FLAG_RUN_RETURN);
- return 0;
-}
-
-
-/*
- * unix.c - The unix-specific code for e2fsck
- */
-
-
-/* Command line options */
-static int swapfs;
-#ifdef ENABLE_SWAPFS
-static int normalize_swapfs;
-#endif
-static int cflag; /* check disk */
-static int show_version_only;
-static int verbose;
-
-#define P_E2(singular, plural, n) n, ((n) == 1 ? singular : plural)
-
-static void show_stats(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- int inodes, inodes_used, blocks, blocks_used;
- int dir_links;
- int num_files, num_links;
- int frag_percent;
-
- dir_links = 2 * ctx->fs_directory_count - 1;
- num_files = ctx->fs_total_count - dir_links;
- num_links = ctx->fs_links_count - dir_links;
- inodes = fs->super->s_inodes_count;
- inodes_used = (fs->super->s_inodes_count -
- fs->super->s_free_inodes_count);
- blocks = fs->super->s_blocks_count;
- blocks_used = (fs->super->s_blocks_count -
- fs->super->s_free_blocks_count);
-
- frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
- frag_percent = (frag_percent + 5) / 10;
-
- if (!verbose) {
- printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
- ctx->device_name, inodes_used, inodes,
- frag_percent / 10, frag_percent % 10,
- blocks_used, blocks);
- return;
- }
- printf("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
- 100 * inodes_used / inodes);
- printf("%8d non-contiguous inode%s (%0d.%d%%)\n",
- P_E2("", "s", ctx->fs_fragmented),
- frag_percent / 10, frag_percent % 10);
- printf(_(" # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
- ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
- printf("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
- (int) ((long long) 100 * blocks_used / blocks));
- printf("%8d large file%s\n", P_E2("", "s", ctx->large_files));
- printf("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
- printf("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
- printf("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
- printf("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
- printf("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
- printf("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
- printf("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
- printf(" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
- printf("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
- printf("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
-}
-
-static void check_mount(e2fsck_t ctx)
-{
- errcode_t retval;
- int cont;
-
- retval = ext2fs_check_if_mounted(ctx->filesystem_name,
- &ctx->mount_flags);
- if (retval) {
- bb_error_msg(_("while determining whether %s is mounted"),
- ctx->filesystem_name);
- return;
- }
-
- /*
- * If the filesystem isn't mounted, or it's the root filesystem
- * and it's mounted read-only, then everything's fine.
- */
- if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
- ((ctx->mount_flags & EXT2_MF_ISROOT) &&
- (ctx->mount_flags & EXT2_MF_READONLY)))
- return;
-
- if (ctx->options & E2F_OPT_READONLY) {
- printf(_("Warning! %s is mounted.\n"), ctx->filesystem_name);
- return;
- }
-
- printf(_("%s is mounted. "), ctx->filesystem_name);
- if (!ctx->interactive)
- bb_error_msg_and_die(_("can't continue, aborting"));
- printf(_("\n\n\007\007\007\007WARNING!!! "
- "Running e2fsck on a mounted filesystem may cause\n"
- "SEVERE filesystem damage.\007\007\007\n\n"));
- cont = ask_yn(_("Do you really want to continue"), -1);
- if (!cont) {
- printf(_("check aborted.\n"));
- exit(0);
- }
-}
-
-static int is_on_batt(void)
-{
- FILE *f;
- DIR *d;
- char tmp[80], tmp2[80], fname[80];
- unsigned int acflag;
- struct dirent* de;
-
- f = fopen_for_read("/proc/apm");
- if (f) {
- if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
- acflag = 1;
- fclose(f);
- return (acflag != 1);
- }
- d = opendir("/proc/acpi/ac_adapter");
- if (d) {
- while ((de=readdir(d)) != NULL) {
- if (!strncmp(".", de->d_name, 1))
- continue;
- snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
- de->d_name);
- f = fopen_for_read(fname);
- if (!f)
- continue;
- if (fscanf(f, "%s %s", tmp2, tmp) != 2)
- tmp[0] = 0;
- fclose(f);
- if (strncmp(tmp, "off-line", 8) == 0) {
- closedir(d);
- return 1;
- }
- }
- closedir(d);
- }
- return 0;
-}
-
-/*
- * This routine checks to see if a filesystem can be skipped; if so,
- * it will exit with EXIT_OK. Under some conditions it will print a
- * message explaining why a check is being forced.
- */
-static void check_if_skip(e2fsck_t ctx)
-{
- ext2_filsys fs = ctx->fs;
- const char *reason = NULL;
- unsigned int reason_arg = 0;
- long next_check;
- int batt = is_on_batt();
- time_t now = time(NULL);
-
- if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
- return;
-
- if ((fs->super->s_state & EXT2_ERROR_FS) ||
- !ext2fs_test_valid(fs))
- reason = _(" contains a file system with errors");
- else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
- reason = _(" was not cleanly unmounted");
- else if ((fs->super->s_max_mnt_count > 0) &&
- (fs->super->s_mnt_count >=
- (unsigned) fs->super->s_max_mnt_count)) {
- reason = _(" has been mounted %u times without being checked");
- reason_arg = fs->super->s_mnt_count;
- if (batt && (fs->super->s_mnt_count <
- (unsigned) fs->super->s_max_mnt_count*2))
- reason = 0;
- } else if (fs->super->s_checkinterval &&
- ((now - fs->super->s_lastcheck) >=
- fs->super->s_checkinterval)) {
- reason = _(" has gone %u days without being checked");
- reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
- if (batt && ((now - fs->super->s_lastcheck) <
- fs->super->s_checkinterval*2))
- reason = 0;
- }
- if (reason) {
- fputs(ctx->device_name, stdout);
- printf(reason, reason_arg);
- fputs(_(", check forced.\n"), stdout);
- return;
- }
- printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
- fs->super->s_inodes_count - fs->super->s_free_inodes_count,
- fs->super->s_inodes_count,
- fs->super->s_blocks_count - fs->super->s_free_blocks_count,
- fs->super->s_blocks_count);
- next_check = 100000;
- if (fs->super->s_max_mnt_count > 0) {
- next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
- if (next_check <= 0)
- next_check = 1;
- }
- if (fs->super->s_checkinterval &&
- ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
- next_check = 1;
- if (next_check <= 5) {
- if (next_check == 1)
- fputs(_(" (check after next mount)"), stdout);
- else
- printf(_(" (check in %ld mounts)"), next_check);
- }
- bb_putchar('\n');
- ext2fs_close(fs);
- ctx->fs = NULL;
- e2fsck_free_context(ctx);
- exit(EXIT_OK);
-}
-
-/*
- * For completion notice
- */
-struct percent_tbl {
- int max_pass;
- int table[32];
-};
-static const struct percent_tbl e2fsck_tbl = {
- 5, { 0, 70, 90, 92, 95, 100 }
-};
-
-static char bar[128], spaces[128];
-
-static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
- int max)
-{
- float percent;
-
- if (pass <= 0)
- return 0.0;
- if (pass > tbl->max_pass || max == 0)
- return 100.0;
- percent = ((float) curr) / ((float) max);
- return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
- + tbl->table[pass-1]);
-}
-
-void e2fsck_clear_progbar(e2fsck_t ctx)
-{
- if (!(ctx->flags & E2F_FLAG_PROG_BAR))
- return;
-
- printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
- ctx->stop_meta);
- fflush(stdout);
- ctx->flags &= ~E2F_FLAG_PROG_BAR;
-}
-
-int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
- unsigned int dpynum)
-{
- static const char spinner[] = "\\|/-";
- int i;
- unsigned int tick;
- struct timeval tv;
- int dpywidth;
- int fixed_percent;
-
- if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
- return 0;
-
- /*
- * Calculate the new progress position. If the
- * percentage hasn't changed, then we skip out right
- * away.
- */
- fixed_percent = (int) ((10 * percent) + 0.5);
- if (ctx->progress_last_percent == fixed_percent)
- return 0;
- ctx->progress_last_percent = fixed_percent;
-
- /*
- * If we've already updated the spinner once within
- * the last 1/8th of a second, no point doing it
- * again.
- */
- gettimeofday(&tv, NULL);
- tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
- if ((tick == ctx->progress_last_time) &&
- (fixed_percent != 0) && (fixed_percent != 1000))
- return 0;
- ctx->progress_last_time = tick;
-
- /*
- * Advance the spinner, and note that the progress bar
- * will be on the screen
- */
- ctx->progress_pos = (ctx->progress_pos+1) & 3;
- ctx->flags |= E2F_FLAG_PROG_BAR;
-
- dpywidth = 66 - strlen(label);
- dpywidth = 8 * (dpywidth / 8);
- if (dpynum)
- dpywidth -= 8;
-
- i = ((percent * dpywidth) + 50) / 100;
- printf("%s%s: |%s%s", ctx->start_meta, label,
- bar + (sizeof(bar) - (i+1)),
- spaces + (sizeof(spaces) - (dpywidth - i + 1)));
- if (fixed_percent == 1000)
- bb_putchar('|');
- else
- bb_putchar(spinner[ctx->progress_pos & 3]);
- printf(" %4.1f%% ", percent);
- if (dpynum)
- printf("%u\r", dpynum);
- else
- fputs(" \r", stdout);
- fputs(ctx->stop_meta, stdout);
-
- if (fixed_percent == 1000)
- e2fsck_clear_progbar(ctx);
- fflush(stdout);
-
- return 0;
-}
-
-static int e2fsck_update_progress(e2fsck_t ctx, int pass,
- unsigned long cur, unsigned long max)
-{
- char buf[80];
- float percent;
-
- if (pass == 0)
- return 0;
-
- if (ctx->progress_fd) {
- sprintf(buf, "%d %lu %lu\n", pass, cur, max);
- xwrite_str(ctx->progress_fd, buf);
- } else {
- percent = calc_percent(&e2fsck_tbl, pass, cur, max);
- e2fsck_simple_progress(ctx, ctx->device_name,
- percent, 0);
- }
- return 0;
-}
-
-static void reserve_stdio_fds(void)
-{
- int fd;
-
- while (1) {
- fd = open(bb_dev_null, O_RDWR);
- if (fd > 2)
- break;
- if (fd < 0) {
- fprintf(stderr, _("ERROR: Cannot open "
- "/dev/null (%s)\n"),
- strerror(errno));
- break;
- }
- }
- close(fd);
-}
-
-static void signal_progress_on(int sig FSCK_ATTR((unused)))
-{
- e2fsck_t ctx = e2fsck_global_ctx;
-
- if (!ctx)
- return;
-
- ctx->progress = e2fsck_update_progress;
- ctx->progress_fd = 0;
-}
-
-static void signal_progress_off(int sig FSCK_ATTR((unused)))
-{
- e2fsck_t ctx = e2fsck_global_ctx;
-
- if (!ctx)
- return;
-
- e2fsck_clear_progbar(ctx);
- ctx->progress = 0;
-}
-
-static void signal_cancel(int sig FSCK_ATTR((unused)))
-{
- e2fsck_t ctx = e2fsck_global_ctx;
-
- if (!ctx)
- exit(FSCK_CANCELED);
-
- ctx->flags |= E2F_FLAG_CANCEL;
-}
-
-static void parse_extended_opts(e2fsck_t ctx, const char *opts)
-{
- char *buf, *token, *next, *p, *arg;
- int ea_ver;
- int extended_usage = 0;
-
- buf = string_copy(opts, 0);
- for (token = buf; token && *token; token = next) {
- p = strchr(token, ',');
- next = 0;
- if (p) {
- *p = 0;
- next = p+1;
- }
- arg = strchr(token, '=');
- if (arg) {
- *arg = 0;
- arg++;
- }
- if (strcmp(token, "ea_ver") == 0) {
- if (!arg) {
- extended_usage++;
- continue;
- }
- ea_ver = strtoul(arg, &p, 0);
- if (*p ||
- ((ea_ver != 1) && (ea_ver != 2))) {
- fprintf(stderr,
- _("Invalid EA version.\n"));
- extended_usage++;
- continue;
- }
- ctx->ext_attr_ver = ea_ver;
- } else {
- fprintf(stderr, _("Unknown extended option: %s\n"),
- token);
- extended_usage++;
- }
- }
- if (extended_usage) {
- bb_error_msg_and_die(
- "Extended options are separated by commas, "
- "and may take an argument which\n"
- "is set off by an equals ('=') sign. "
- "Valid extended options are:\n"
- "\tea_ver=<ea_version (1 or 2)>\n\n");
- }
-}
-
-
-static errcode_t PRS(int argc, char **argv, e2fsck_t *ret_ctx)
-{
- int flush = 0;
- int c, fd;
- e2fsck_t ctx;
- errcode_t retval;
- struct sigaction sa;
- char *extended_opts = NULL;
-
- retval = e2fsck_allocate_context(&ctx);
- if (retval)
- return retval;
-
- *ret_ctx = ctx;
-
- setvbuf(stdout, NULL, _IONBF, BUFSIZ);
- setvbuf(stderr, NULL, _IONBF, BUFSIZ);
- if (isatty(0) && isatty(1)) {
- ctx->interactive = 1;
- } else {
- ctx->start_meta[0] = '\001';
- ctx->stop_meta[0] = '\002';
- }
- memset(bar, '=', sizeof(bar)-1);
- memset(spaces, ' ', sizeof(spaces)-1);
- blkid_get_cache(&ctx->blkid, NULL);
-
- if (argc && *argv)
- ctx->program_name = *argv;
- else
- ctx->program_name = "e2fsck";
- while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
- switch (c) {
- case 'C':
- ctx->progress = e2fsck_update_progress;
- ctx->progress_fd = atoi(optarg);
- if (!ctx->progress_fd)
- break;
- /* Validate the file descriptor to avoid disasters */
- fd = dup(ctx->progress_fd);
- if (fd < 0) {
- fprintf(stderr,
- _("Error validating file descriptor %d: %s\n"),
- ctx->progress_fd,
- error_message(errno));
- bb_error_msg_and_die(_("Invalid completion information file descriptor"));
- } else
- close(fd);
- break;
- case 'D':
- ctx->options |= E2F_OPT_COMPRESS_DIRS;
- break;
- case 'E':
- extended_opts = optarg;
- break;
- case 'p':
- case 'a':
- if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
- conflict_opt:
- bb_error_msg_and_die(_("only one the options -p/-a, -n or -y may be specified"));
- }
- ctx->options |= E2F_OPT_PREEN;
- break;
- case 'n':
- if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
- goto conflict_opt;
- ctx->options |= E2F_OPT_NO;
- break;
- case 'y':
- if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
- goto conflict_opt;
- ctx->options |= E2F_OPT_YES;
- break;
- case 't':
- /* FIXME - This needs to go away in a future path - will change binary */
- fprintf(stderr, _("The -t option is not "
- "supported on this version of e2fsck.\n"));
- break;
- case 'c':
- if (cflag++)
- ctx->options |= E2F_OPT_WRITECHECK;
- ctx->options |= E2F_OPT_CHECKBLOCKS;
- break;
- case 'r':
- /* What we do by default, anyway! */
- break;
- case 'b':
- ctx->use_superblock = atoi(optarg);
- ctx->flags |= E2F_FLAG_SB_SPECIFIED;
- break;
- case 'B':
- ctx->blocksize = atoi(optarg);
- break;
- case 'I':
- ctx->inode_buffer_blocks = atoi(optarg);
- break;
- case 'j':
- ctx->journal_name = string_copy(optarg, 0);
- break;
- case 'P':
- ctx->process_inode_size = atoi(optarg);
- break;
- case 'd':
- ctx->options |= E2F_OPT_DEBUG;
- break;
- case 'f':
- ctx->options |= E2F_OPT_FORCE;
- break;
- case 'F':
- flush = 1;
- break;
- case 'v':
- verbose = 1;
- break;
- case 'V':
- show_version_only = 1;
- break;
- case 'N':
- ctx->device_name = optarg;
- break;
-#ifdef ENABLE_SWAPFS
- case 's':
- normalize_swapfs = 1;
- case 'S':
- swapfs = 1;
- break;
-#else
- case 's':
- case 'S':
- fprintf(stderr, _("Byte-swapping filesystems "
- "not compiled in this version "
- "of e2fsck\n"));
- exit(1);
-#endif
- default:
- bb_show_usage();
- }
- if (show_version_only)
- return 0;
- if (optind != argc - 1)
- bb_show_usage();
- if ((ctx->options & E2F_OPT_NO) &&
- !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
- ctx->options |= E2F_OPT_READONLY;
- ctx->io_options = strchr(argv[optind], '?');
- if (ctx->io_options)
- *ctx->io_options++ = 0;
- ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
- if (!ctx->filesystem_name) {
- bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
- bb_error_msg_and_die(0);
- }
- if (extended_opts)
- parse_extended_opts(ctx, extended_opts);
-
- if (flush) {
- fd = open(ctx->filesystem_name, O_RDONLY, 0);
- if (fd < 0) {
- bb_error_msg(_("while opening %s for flushing"),
- ctx->filesystem_name);
- bb_error_msg_and_die(0);
- }
- if ((retval = ext2fs_sync_device(fd, 1))) {
- bb_error_msg(_("while trying to flush %s"),
- ctx->filesystem_name);
- bb_error_msg_and_die(0);
- }
- close(fd);
- }
-#ifdef ENABLE_SWAPFS
- if (swapfs && cflag) {
- fprintf(stderr, _("Incompatible options not "
- "allowed when byte-swapping.\n"));
- exit(EXIT_USAGE);
- }
-#endif
- /*
- * Set up signal action
- */
- memset(&sa, 0, sizeof(struct sigaction));
- sa.sa_handler = signal_cancel;
- sigaction(SIGINT, &sa, 0);
- sigaction(SIGTERM, &sa, 0);
-#ifdef SA_RESTART
- sa.sa_flags = SA_RESTART;
-#endif
- e2fsck_global_ctx = ctx;
- sa.sa_handler = signal_progress_on;
- sigaction(SIGUSR1, &sa, 0);
- sa.sa_handler = signal_progress_off;
- sigaction(SIGUSR2, &sa, 0);
-
- /* Update our PATH to include /sbin if we need to run badblocks */
- if (cflag)
- e2fs_set_sbin_path();
- return 0;
-}
-
-static const char my_ver_string[] = E2FSPROGS_VERSION;
-static const char my_ver_date[] = E2FSPROGS_DATE;
-
-int e2fsck_main (int argc, char **argv);
-int e2fsck_main (int argc, char **argv)
-{
- errcode_t retval;
- int exit_value = EXIT_OK;
- ext2_filsys fs = 0;
- io_manager io_ptr;
- struct ext2_super_block *sb;
- const char *lib_ver_date;
- int my_ver, lib_ver;
- e2fsck_t ctx;
- struct problem_context pctx;
- int flags, run_result;
-
- clear_problem_context(&pctx);
-
- my_ver = ext2fs_parse_version_string(my_ver_string);
- lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
- if (my_ver > lib_ver) {
- fprintf( stderr, _("Error: ext2fs library version "
- "out of date!\n"));
- show_version_only++;
- }
-
- retval = PRS(argc, argv, &ctx);
- if (retval) {
- bb_error_msg(_("while trying to initialize program"));
- exit(EXIT_ERROR);
- }
- reserve_stdio_fds();
-
- if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
- fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
- my_ver_date);
-
- if (show_version_only) {
- fprintf(stderr, _("\tUsing %s, %s\n"),
- error_message(EXT2_ET_BASE), lib_ver_date);
- exit(EXIT_OK);
- }
-
- check_mount(ctx);
-
- if (!(ctx->options & E2F_OPT_PREEN) &&
- !(ctx->options & E2F_OPT_NO) &&
- !(ctx->options & E2F_OPT_YES)) {
- if (!ctx->interactive)
- bb_error_msg_and_die(_("need terminal for interactive repairs"));
- }
- ctx->superblock = ctx->use_superblock;
-restart:
-#ifdef CONFIG_TESTIO_DEBUG
- io_ptr = test_io_manager;
- test_io_backing_manager = unix_io_manager;
-#else
- io_ptr = unix_io_manager;
-#endif
- flags = 0;
- if ((ctx->options & E2F_OPT_READONLY) == 0)
- flags |= EXT2_FLAG_RW;
-
- if (ctx->superblock && ctx->blocksize) {
- retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
- flags, ctx->superblock, ctx->blocksize,
- io_ptr, &fs);
- } else if (ctx->superblock) {
- int blocksize;
- for (blocksize = EXT2_MIN_BLOCK_SIZE;
- blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
- retval = ext2fs_open2(ctx->filesystem_name,
- ctx->io_options, flags,
- ctx->superblock, blocksize,
- io_ptr, &fs);
- if (!retval)
- break;
- }
- } else
- retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
- flags, 0, 0, io_ptr, &fs);
- if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
- !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
- ((retval == EXT2_ET_BAD_MAGIC) ||
- ((retval == 0) && ext2fs_check_desc(fs)))) {
- if (!fs || (fs->group_desc_count > 1)) {
- printf(_("%s trying backup blocks...\n"),
- retval ? _("Couldn't find ext2 superblock,") :
- _("Group descriptors look bad..."));
- get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
- if (fs)
- ext2fs_close(fs);
- goto restart;
- }
- }
- if (retval) {
- bb_error_msg(_("while trying to open %s"),
- ctx->filesystem_name);
- if (retval == EXT2_ET_REV_TOO_HIGH) {
- printf(_("The filesystem revision is apparently "
- "too high for this version of e2fsck.\n"
- "(Or the filesystem superblock "
- "is corrupt)\n\n"));
- fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
- } else if (retval == EXT2_ET_SHORT_READ)
- printf(_("Could this be a zero-length partition?\n"));
- else if ((retval == EPERM) || (retval == EACCES))
- printf(_("You must have %s access to the "
- "filesystem or be root\n"),
- (ctx->options & E2F_OPT_READONLY) ?
- "r/o" : "r/w");
- else if (retval == ENXIO)
- printf(_("Possibly non-existent or swap device?\n"));
-#ifdef EROFS
- else if (retval == EROFS)
- printf(_("Disk write-protected; use the -n option "
- "to do a read-only\n"
- "check of the device.\n"));
-#endif
- else
- fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
- bb_error_msg_and_die(0);
- }
- ctx->fs = fs;
- fs->priv_data = ctx;
- sb = fs->super;
- if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
- bb_error_msg(_("while trying to open %s"),
- ctx->filesystem_name);
- get_newer:
- bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
- }
-
- /*
- * Set the device name, which is used whenever we print error
- * or informational messages to the user.
- */
- if (ctx->device_name == 0 &&
- (sb->s_volume_name[0] != 0)) {
- ctx->device_name = string_copy(sb->s_volume_name,
- sizeof(sb->s_volume_name));
- }
- if (ctx->device_name == 0)
- ctx->device_name = ctx->filesystem_name;
-
- /*
- * Make sure the ext3 superblock fields are consistent.
- */
- retval = e2fsck_check_ext3_journal(ctx);
- if (retval) {
- bb_error_msg(_("while checking ext3 journal for %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
-
- /*
- * Check to see if we need to do ext3-style recovery. If so,
- * do it, and then restart the fsck.
- */
- if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
- if (ctx->options & E2F_OPT_READONLY) {
- printf(_("Warning: skipping journal recovery "
- "because doing a read-only filesystem "
- "check.\n"));
- io_channel_flush(ctx->fs->io);
- } else {
- if (ctx->flags & E2F_FLAG_RESTARTED) {
- /*
- * Whoops, we attempted to run the
- * journal twice. This should never
- * happen, unless the hardware or
- * device driver is being bogus.
- */
- bb_error_msg(_("can't set superblock flags on %s"), ctx->device_name);
- bb_error_msg_and_die(0);
- }
- retval = e2fsck_run_ext3_journal(ctx);
- if (retval) {
- bb_error_msg(_("while recovering ext3 journal of %s"),
- ctx->device_name);
- bb_error_msg_and_die(0);
- }
- ext2fs_close(ctx->fs);
- ctx->fs = 0;
- ctx->flags |= E2F_FLAG_RESTARTED;
- goto restart;
- }
- }
-
- /*
- * Check for compatibility with the feature sets. We need to
- * be more stringent than ext2fs_open().
- */
- if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
- (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
- bb_error_msg("(%s)", ctx->device_name);
- goto get_newer;
- }
- if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
- bb_error_msg("(%s)", ctx->device_name);
- goto get_newer;
- }
-#ifdef ENABLE_COMPRESSION
- /* FIXME - do we support this at all? */
- if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
- bb_error_msg(_("warning: compression support is experimental"));
-#endif
-#ifndef ENABLE_HTREE
- if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
- bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
- "but filesystem %s has HTREE directories."),
- ctx->device_name);
- goto get_newer;
- }
-#endif
-
- /*
- * If the user specified a specific superblock, presumably the
- * master superblock has been trashed. So we mark the
- * superblock as dirty, so it can be written out.
- */
- if (ctx->superblock &&
- !(ctx->options & E2F_OPT_READONLY))
- ext2fs_mark_super_dirty(fs);
-
- /*
- * We only update the master superblock because (a) paranoia;
- * we don't want to corrupt the backup superblocks, and (b) we
- * don't need to update the mount count and last checked
- * fields in the backup superblock (the kernel doesn't
- * update the backup superblocks anyway).
- */
- fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
-
- ehandler_init(fs->io);
-
- if (ctx->superblock)
- set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
- ext2fs_mark_valid(fs);
- check_super_block(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- bb_error_msg_and_die(0);
- check_if_skip(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- bb_error_msg_and_die(0);
-#ifdef ENABLE_SWAPFS
-
-#ifdef WORDS_BIGENDIAN
-#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
-#else
-#define NATIVE_FLAG 0
-#endif
-
-
- if (normalize_swapfs) {
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
- fprintf(stderr, _("%s: Filesystem byte order "
- "already normalized.\n"), ctx->device_name);
- bb_error_msg_and_die(0);
- }
- }
- if (swapfs) {
- swap_filesys(ctx);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- bb_error_msg_and_die(0);
- }
-#endif
-
- /*
- * Mark the system as valid, 'til proven otherwise
- */
- ext2fs_mark_valid(fs);
-
- retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
- if (retval) {
- bb_error_msg(_("while reading bad blocks inode"));
- preenhalt(ctx);
- printf(_("This doesn't bode well,"
- " but we'll try to go on...\n"));
- }
-
- run_result = e2fsck_run(ctx);
- e2fsck_clear_progbar(ctx);
- if (run_result == E2F_FLAG_RESTART) {
- printf(_("Restarting e2fsck from the beginning...\n"));
- retval = e2fsck_reset_context(ctx);
- if (retval) {
- bb_error_msg(_("while resetting context"));
- bb_error_msg_and_die(0);
- }
- ext2fs_close(fs);
- goto restart;
- }
- if (run_result & E2F_FLAG_CANCEL) {
- printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
- ctx->device_name : ctx->filesystem_name);
- exit_value |= FSCK_CANCELED;
- }
- if (run_result & E2F_FLAG_ABORT)
- bb_error_msg_and_die(_("aborted"));
-
- /* Cleanup */
- if (ext2fs_test_changed(fs)) {
- exit_value |= EXIT_NONDESTRUCT;
- if (!(ctx->options & E2F_OPT_PREEN))
- printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
- ctx->device_name);
- if (ctx->mount_flags & EXT2_MF_ISROOT) {
- printf(_("%s: ***** REBOOT LINUX *****\n"),
- ctx->device_name);
- exit_value |= EXIT_DESTRUCT;
- }
- }
- if (!ext2fs_test_valid(fs)) {
- printf(_("\n%s: ********** WARNING: Filesystem still has "
- "errors **********\n\n"), ctx->device_name);
- exit_value |= EXIT_UNCORRECTED;
- exit_value &= ~EXIT_NONDESTRUCT;
- }
- if (exit_value & FSCK_CANCELED)
- exit_value &= ~EXIT_NONDESTRUCT;
- else {
- show_stats(ctx);
- if (!(ctx->options & E2F_OPT_READONLY)) {
- if (ext2fs_test_valid(fs)) {
- if (!(sb->s_state & EXT2_VALID_FS))
- exit_value |= EXIT_NONDESTRUCT;
- sb->s_state = EXT2_VALID_FS;
- } else
- sb->s_state &= ~EXT2_VALID_FS;
- sb->s_mnt_count = 0;
- sb->s_lastcheck = time(NULL);
- ext2fs_mark_super_dirty(fs);
- }
- }
-
- e2fsck_write_bitmaps(ctx);
-
- ext2fs_close(fs);
- ctx->fs = NULL;
- free(ctx->filesystem_name);
- free(ctx->journal_name);
- e2fsck_free_context(ctx);
-
- return exit_value;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2fsck.h b/e2fsprogs/old_e2fsprogs/e2fsck.h
deleted file mode 100644
index c159fab..0000000
--- a/e2fsprogs/old_e2fsprogs/e2fsck.h
+++ b/dev/null
@@ -1,638 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#include <sys/types.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <time.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <setjmp.h>
-#include <errno.h>
-#include <getopt.h>
-#include <limits.h>
-#include <stddef.h>
-#include <assert.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <mntent.h>
-#include <dirent.h>
-#include "ext2fs/kernel-list.h"
-#include <sys/types.h>
-#include <linux/types.h>
-
-/*
- * Now pull in the real linux/jfs.h definitions.
- */
-#include "ext2fs/kernel-jbd.h"
-
-
-
-#include "fsck.h"
-
-#include "ext2fs/ext2_fs.h"
-#include "blkid/blkid.h"
-#include "ext2fs/ext2_ext_attr.h"
-#include "uuid/uuid.h"
-#include "libbb.h"
-
-#ifdef HAVE_CONIO_H
-#undef HAVE_TERMIOS_H
-#include <conio.h>
-#define read_a_char() getch()
-#else
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-#endif
-
-
-/*
- * The last ext2fs revision level that this version of e2fsck is able to
- * support
- */
-#define E2FSCK_CURRENT_REV 1
-
-/* Used by the region allocation code */
-typedef __u32 region_addr_t;
-typedef struct region_struct *region_t;
-
-struct dx_dirblock_info {
- int type;
- blk_t phys;
- int flags;
- blk_t parent;
- ext2_dirhash_t min_hash;
- ext2_dirhash_t max_hash;
- ext2_dirhash_t node_min_hash;
- ext2_dirhash_t node_max_hash;
-};
-
-/*
-These defines are used in the type field of dx_dirblock_info
-*/
-
-#define DX_DIRBLOCK_ROOT 1
-#define DX_DIRBLOCK_LEAF 2
-#define DX_DIRBLOCK_NODE 3
-
-
-/*
-The following defines are used in the 'flags' field of a dx_dirblock_info
-*/
-#define DX_FLAG_REFERENCED 1
-#define DX_FLAG_DUP_REF 2
-#define DX_FLAG_FIRST 4
-#define DX_FLAG_LAST 8
-
-/*
- * E2fsck options
- */
-#define E2F_OPT_READONLY 0x0001
-#define E2F_OPT_PREEN 0x0002
-#define E2F_OPT_YES 0x0004
-#define E2F_OPT_NO 0x0008
-#define E2F_OPT_TIME 0x0010
-#define E2F_OPT_CHECKBLOCKS 0x0040
-#define E2F_OPT_DEBUG 0x0080
-#define E2F_OPT_FORCE 0x0100
-#define E2F_OPT_WRITECHECK 0x0200
-#define E2F_OPT_COMPRESS_DIRS 0x0400
-
-/*
- * E2fsck flags
- */
-#define E2F_FLAG_ABORT 0x0001 /* Abort signaled */
-#define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */
-#define E2F_FLAG_SIGNAL_MASK 0x0003
-#define E2F_FLAG_RESTART 0x0004 /* Restart signaled */
-
-#define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */
-
-#define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */
-#define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */
-#define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */
-#define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly
- * specified by the user */
-#define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */
-#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */
-
-
-/*Don't know where these come from*/
-#define READ 0
-#define WRITE 1
-#define cpu_to_be32(n) htonl(n)
-#define be32_to_cpu(n) ntohl(n)
-
-/*
- * We define a set of "latch groups"; these are problems which are
- * handled as a set. The user answers once for a particular latch
- * group.
- */
-#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
-#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
-#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
-#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
-#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
-#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
-#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
-#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
-#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
-#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
-
-#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
-
-/*
- * Latch group descriptor flags
- */
-#define PRL_YES 0x0001 /* Answer yes */
-#define PRL_NO 0x0002 /* Answer no */
-#define PRL_LATCHED 0x0004 /* The latch group is latched */
-#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
-
-#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
-
-/*
- * Pre-Pass 1 errors
- */
-
-#define PR_0_BB_NOT_GROUP 0x000001 /* Block bitmap not in group */
-#define PR_0_IB_NOT_GROUP 0x000002 /* Inode bitmap not in group */
-#define PR_0_ITABLE_NOT_GROUP 0x000003 /* Inode table not in group */
-#define PR_0_SB_CORRUPT 0x000004 /* Superblock corrupt */
-#define PR_0_FS_SIZE_WRONG 0x000005 /* Filesystem size is wrong */
-#define PR_0_NO_FRAGMENTS 0x000006 /* Fragments not supported */
-#define PR_0_BLOCKS_PER_GROUP 0x000007 /* Bad blocks_per_group */
-#define PR_0_FIRST_DATA_BLOCK 0x000008 /* Bad first_data_block */
-#define PR_0_ADD_UUID 0x000009 /* Adding UUID to filesystem */
-#define PR_0_RELOCATE_HINT 0x00000A /* Relocate hint */
-#define PR_0_MISC_CORRUPT_SUPER 0x00000B /* Miscellaneous superblock corruption */
-#define PR_0_GETSIZE_ERROR 0x00000C /* Error determing physical device size of filesystem */
-#define PR_0_INODE_COUNT_WRONG 0x00000D /* Inode count in the superblock incorrect */
-#define PR_0_HURD_CLEAR_FILETYPE 0x00000E /* The Hurd does not support the filetype feature */
-#define PR_0_JOURNAL_BAD_INODE 0x00000F /* The Hurd does not support the filetype feature */
-#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010 /* The external journal has multiple filesystems (which we can't handle yet) */
-#define PR_0_CANT_FIND_JOURNAL 0x000011 /* Can't find external journal */
-#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012/* External journal has bad superblock */
-#define PR_0_JOURNAL_BAD_UUID 0x000013 /* Superblock has a bad journal UUID */
-#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014 /* Journal has an unknown superblock type */
-#define PR_0_JOURNAL_BAD_SUPER 0x000015 /* Journal superblock is corrupt */
-#define PR_0_JOURNAL_HAS_JOURNAL 0x000016 /* Journal superblock is corrupt */
-#define PR_0_JOURNAL_RECOVER_SET 0x000017 /* Superblock has recovery flag set but no journal */
-#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018 /* Journal has data, but recovery flag is clear */
-#define PR_0_JOURNAL_RESET_JOURNAL 0x000019 /* Ask if we should clear the journal */
-#define PR_0_FS_REV_LEVEL 0x00001A /* Filesystem revision is 0, but feature flags are set */
-#define PR_0_ORPHAN_CLEAR_INODE 0x000020 /* Clearing orphan inode */
-#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021 /* Illegal block found in orphaned inode */
-#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022 /* Already cleared block found in orphaned inode */
-#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023 /* Illegal orphan inode in superblock */
-#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024 /* Illegal inode in orphaned inode list */
-#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025 /* Journal has unsupported read-only feature - abort */
-#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026 /* Journal has unsupported incompatible feature - abort */
-#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027 /* Journal has unsupported version number */
-#define PR_0_MOVE_JOURNAL 0x000028 /* Moving journal to hidden file */
-#define PR_0_ERR_MOVE_JOURNAL 0x000029 /* Error moving journal */
-#define PR_0_CLEAR_V2_JOURNAL 0x00002A /* Clearing V2 journal superblock */
-#define PR_0_JOURNAL_RUN 0x00002B /* Run journal anyway */
-#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C /* Run journal anyway by default */
-#define PR_0_BACKUP_JNL 0x00002D /* Backup journal inode blocks */
-#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E /* Reserved blocks w/o resize_inode */
-#define PR_0_CLEAR_RESIZE_INODE 0x00002F /* Resize_inode not enabled, but resize inode is non-zero */
-#define PR_0_RESIZE_INODE_INVALID 0x000030 /* Resize inode invalid */
-
-/*
- * Pass 1 errors
- */
-
-#define PR_1_PASS_HEADER 0x010000 /* Pass 1: Checking inodes, blocks, and sizes */
-#define PR_1_ROOT_NO_DIR 0x010001 /* Root directory is not an inode */
-#define PR_1_ROOT_DTIME 0x010002 /* Root directory has dtime set */
-#define PR_1_RESERVED_BAD_MODE 0x010003 /* Reserved inode has bad mode */
-#define PR_1_ZERO_DTIME 0x010004 /* Deleted inode has zero dtime */
-#define PR_1_SET_DTIME 0x010005 /* Inode in use, but dtime set */
-#define PR_1_ZERO_LENGTH_DIR 0x010006 /* Zero-length directory */
-#define PR_1_BB_CONFLICT 0x010007 /* Block bitmap conflicts with some other fs block */
-#define PR_1_IB_CONFLICT 0x010008 /* Inode bitmap conflicts with some other fs block */
-#define PR_1_ITABLE_CONFLICT 0x010009 /* Inode table conflicts with some other fs block */
-#define PR_1_BB_BAD_BLOCK 0x01000A /* Block bitmap is on a bad block */
-#define PR_1_IB_BAD_BLOCK 0x01000B /* Inode bitmap is on a bad block */
-#define PR_1_BAD_I_SIZE 0x01000C /* Inode has incorrect i_size */
-#define PR_1_BAD_I_BLOCKS 0x01000D /* Inode has incorrect i_blocks */
-#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E /* Illegal block number in inode */
-#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F /* Block number overlaps fs metadata */
-#define PR_1_INODE_BLOCK_LATCH 0x010010 /* Inode has illegal blocks (latch question) */
-#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011 /* Too many bad blocks in inode */
-#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012 /* Illegal block number in bad block inode */
-#define PR_1_INODE_BBLOCK_LATCH 0x010013 /* Bad block inode has illegal blocks (latch question) */
-#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014 /* Duplicate or bad blocks in use! */
-#define PR_1_BBINODE_BAD_METABLOCK 0x010015 /* Bad block used as bad block indirect block */
-#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016 /* Inconsistency can't be fixed prompt */
-#define PR_1_BAD_PRIMARY_BLOCK 0x010017 /* Bad primary block */
-#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018 /* Bad primary block prompt */
-#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019 /* Bad primary superblock */
-#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A /* Bad primary block group descriptors */
-#define PR_1_BAD_SUPERBLOCK 0x01001B /* Bad superblock in group */
-#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C /* Bad block group descriptors in group */
-#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D /* Block claimed for no reason */
-#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E /* Error allocating blocks for relocating metadata */
-#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F /* Error allocating block buffer during relocation process */
-#define PR_1_RELOC_FROM_TO 0x010020 /* Relocating metadata group information from X to Y */
-#define PR_1_RELOC_TO 0x010021 /* Relocating metatdata group information to X */
-#define PR_1_RELOC_READ_ERR 0x010022 /* Block read error during relocation process */
-#define PR_1_RELOC_WRITE_ERR 0x010023 /* Block write error during relocation process */
-#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024 /* Error allocating inode bitmap */
-#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025 /* Error allocating block bitmap */
-#define PR_1_ALLOCATE_ICOUNT 0x010026 /* Error allocating icount structure */
-#define PR_1_ALLOCATE_DBCOUNT 0x010027 /* Error allocating dbcount */
-#define PR_1_ISCAN_ERROR 0x010028 /* Error while scanning inodes */
-#define PR_1_BLOCK_ITERATE 0x010029 /* Error while iterating over blocks */
-#define PR_1_ICOUNT_STORE 0x01002A /* Error while storing inode count information */
-#define PR_1_ADD_DBLOCK 0x01002B /* Error while storing directory block information */
-#define PR_1_READ_INODE 0x01002C /* Error while reading inode (for clearing) */
-#define PR_1_SUPPRESS_MESSAGES 0x01002D /* Suppress messages prompt */
-#define PR_1_SET_IMAGIC 0x01002F /* Imagic flag set on an inode when filesystem doesn't support it */
-#define PR_1_SET_IMMUTABLE 0x010030 /* Immutable flag set on a device or socket inode */
-#define PR_1_COMPR_SET 0x010031 /* Compression flag set on a non-compressed filesystem */
-#define PR_1_SET_NONZSIZE 0x010032 /* Non-zero size on device, fifo or socket inode */
-#define PR_1_FS_REV_LEVEL 0x010033 /* Filesystem revision is 0, but feature flags are set */
-#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034 /* Journal inode not in use, needs clearing */
-#define PR_1_JOURNAL_BAD_MODE 0x010035 /* Journal inode has wrong mode */
-#define PR_1_LOW_DTIME 0x010036 /* Inode that was part of orphan linked list */
-#define PR_1_ORPHAN_LIST_REFUGEES 0x010037 /* Latch question which asks how to deal with low dtime inodes */
-#define PR_1_ALLOCATE_REFCOUNT 0x010038 /* Error allocating refcount structure */
-#define PR_1_READ_EA_BLOCK 0x010039 /* Error reading Extended Attribute block */
-#define PR_1_BAD_EA_BLOCK 0x01003A /* Invalid Extended Attribute block */
-#define PR_1_EXTATTR_READ_ABORT 0x01003B /* Error reading Extended Attribute block while fixing refcount -- abort */
-#define PR_1_EXTATTR_REFCOUNT 0x01003C /* Extended attribute reference count incorrect */
-#define PR_1_EXTATTR_WRITE 0x01003D /* Error writing Extended Attribute block while fixing refcount */
-#define PR_1_EA_MULTI_BLOCK 0x01003E /* Multiple EA blocks not supported */
-#define PR_1_EA_ALLOC_REGION 0x01003F /* Error allocating EA region allocation structure */
-#define PR_1_EA_ALLOC_COLLISION 0x010040 /* Error EA allocation collision */
-#define PR_1_EA_BAD_NAME 0x010041 /* Bad extended attribute name */
-#define PR_1_EA_BAD_VALUE 0x010042 /* Bad extended attribute value */
-#define PR_1_INODE_TOOBIG 0x010043 /* Inode too big (latch question) */
-#define PR_1_TOOBIG_DIR 0x010044 /* Directory too big */
-#define PR_1_TOOBIG_REG 0x010045 /* Regular file too big */
-#define PR_1_TOOBIG_SYMLINK 0x010046 /* Symlink too big */
-#define PR_1_HTREE_SET 0x010047 /* INDEX_FL flag set on a non-HTREE filesystem */
-#define PR_1_HTREE_NODIR 0x010048 /* INDEX_FL flag set on a non-directory */
-#define PR_1_HTREE_BADROOT 0x010049 /* Invalid root node in HTREE directory */
-#define PR_1_HTREE_HASHV 0x01004A /* Unsupported hash version in HTREE directory */
-#define PR_1_HTREE_INCOMPAT 0x01004B /* Incompatible flag in HTREE root node */
-#define PR_1_HTREE_DEPTH 0x01004C /* HTREE too deep */
-#define PR_1_BB_FS_BLOCK 0x01004D /* Bad block has indirect block that conflicts with filesystem block */
-#define PR_1_RESIZE_INODE_CREATE 0x01004E /* Resize inode failed */
-#define PR_1_EXTRA_ISIZE 0x01004F /* inode->i_size is too long */
-#define PR_1_ATTR_NAME_LEN 0x010050 /* attribute name is too long */
-#define PR_1_ATTR_VALUE_OFFSET 0x010051 /* wrong EA value offset */
-#define PR_1_ATTR_VALUE_BLOCK 0x010052 /* wrong EA blocknumber */
-#define PR_1_ATTR_VALUE_SIZE 0x010053 /* wrong EA value size */
-#define PR_1_ATTR_HASH 0x010054 /* wrong EA hash value */
-
-/*
- * Pass 1b errors
- */
-
-#define PR_1B_PASS_HEADER 0x011000 /* Pass 1B: Rescan for duplicate/bad blocks */
-#define PR_1B_DUP_BLOCK_HEADER 0x011001 /* Duplicate/bad block(s) header */
-#define PR_1B_DUP_BLOCK 0x011002 /* Duplicate/bad block(s) in inode */
-#define PR_1B_DUP_BLOCK_END 0x011003 /* Duplicate/bad block(s) end */
-#define PR_1B_ISCAN_ERROR 0x011004 /* Error while scanning inodes */
-#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005 /* Error allocating inode bitmap */
-#define PR_1B_BLOCK_ITERATE 0x0110006 /* Error while iterating over blocks */
-#define PR_1B_ADJ_EA_REFCOUNT 0x0110007 /* Error adjusting EA refcount */
-#define PR_1C_PASS_HEADER 0x012000 /* Pass 1C: Scan directories for inodes with dup blocks. */
-#define PR_1D_PASS_HEADER 0x013000 /* Pass 1D: Reconciling duplicate blocks */
-#define PR_1D_DUP_FILE 0x013001 /* File has duplicate blocks */
-#define PR_1D_DUP_FILE_LIST 0x013002 /* List of files sharing duplicate blocks */
-#define PR_1D_SHARE_METADATA 0x013003 /* File sharing blocks with filesystem metadata */
-#define PR_1D_NUM_DUP_INODES 0x013004 /* Report of how many duplicate/bad inodes */
-#define PR_1D_DUP_BLOCKS_DEALT 0x013005 /* Duplicated blocks already reassigned or cloned. */
-#define PR_1D_CLONE_QUESTION 0x013006 /* Clone duplicate/bad blocks? */
-#define PR_1D_DELETE_QUESTION 0x013007 /* Delete file? */
-#define PR_1D_CLONE_ERROR 0x013008 /* Couldn't clone file (error) */
-
-/*
- * Pass 2 errors
- */
-
-#define PR_2_PASS_HEADER 0x020000 /* Pass 2: Checking directory structure */
-#define PR_2_BAD_INODE_DOT 0x020001 /* Bad inode number for '.' */
-#define PR_2_BAD_INO 0x020002 /* Directory entry has bad inode number */
-#define PR_2_UNUSED_INODE 0x020003 /* Directory entry has deleted or unused inode */
-#define PR_2_LINK_DOT 0x020004 /* Directry entry is link to '.' */
-#define PR_2_BB_INODE 0x020005 /* Directory entry points to inode now located in a bad block */
-#define PR_2_LINK_DIR 0x020006 /* Directory entry contains a link to a directory */
-#define PR_2_LINK_ROOT 0x020007 /* Directory entry contains a link to the root directry */
-#define PR_2_BAD_NAME 0x020008 /* Directory entry has illegal characters in its name */
-#define PR_2_MISSING_DOT 0x020009 /* Missing '.' in directory inode */
-#define PR_2_MISSING_DOT_DOT 0x02000A /* Missing '..' in directory inode */
-#define PR_2_1ST_NOT_DOT 0x02000B /* First entry in directory inode doesn't contain '.' */
-#define PR_2_2ND_NOT_DOT_DOT 0x02000C /* Second entry in directory inode doesn't contain '..' */
-#define PR_2_FADDR_ZERO 0x02000D /* i_faddr should be zero */
-#define PR_2_FILE_ACL_ZERO 0x02000E /* i_file_acl should be zero */
-#define PR_2_DIR_ACL_ZERO 0x02000F /* i_dir_acl should be zero */
-#define PR_2_FRAG_ZERO 0x020010 /* i_frag should be zero */
-#define PR_2_FSIZE_ZERO 0x020011 /* i_fsize should be zero */
-#define PR_2_BAD_MODE 0x020012 /* inode has bad mode */
-#define PR_2_DIR_CORRUPTED 0x020013 /* directory corrupted */
-#define PR_2_FILENAME_LONG 0x020014 /* filename too long */
-#define PR_2_DIRECTORY_HOLE 0x020015 /* Directory inode has a missing block (hole) */
-#define PR_2_DOT_NULL_TERM 0x020016 /* '.' is not NULL terminated */
-#define PR_2_DOT_DOT_NULL_TERM 0x020017 /* '..' is not NULL terminated */
-#define PR_2_BAD_CHAR_DEV 0x020018 /* Illegal character device in inode */
-#define PR_2_BAD_BLOCK_DEV 0x020019 /* Illegal block device in inode */
-#define PR_2_DUP_DOT 0x02001A /* Duplicate '.' entry */
-#define PR_2_DUP_DOT_DOT 0x02001B /* Duplicate '..' entry */
-#define PR_2_NO_DIRINFO 0x02001C /* Internal error: couldn't find dir_info */
-#define PR_2_FINAL_RECLEN 0x02001D /* Final rec_len is wrong */
-#define PR_2_ALLOCATE_ICOUNT 0x02001E /* Error allocating icount structure */
-#define PR_2_DBLIST_ITERATE 0x02001F /* Error iterating over directory blocks */
-#define PR_2_READ_DIRBLOCK 0x020020 /* Error reading directory block */
-#define PR_2_WRITE_DIRBLOCK 0x020021 /* Error writing directory block */
-#define PR_2_ALLOC_DIRBOCK 0x020022 /* Error allocating new directory block */
-#define PR_2_DEALLOC_INODE 0x020023 /* Error deallocating inode */
-#define PR_2_SPLIT_DOT 0x020024 /* Directory entry for '.' is big. Split? */
-#define PR_2_BAD_FIFO 0x020025 /* Illegal FIFO */
-#define PR_2_BAD_SOCKET 0x020026 /* Illegal socket */
-#define PR_2_SET_FILETYPE 0x020027 /* Directory filetype not set */
-#define PR_2_BAD_FILETYPE 0x020028 /* Directory filetype incorrect */
-#define PR_2_CLEAR_FILETYPE 0x020029 /* Directory filetype set when it shouldn't be */
-#define PR_2_NULL_NAME 0x020030 /* Directory filename can't be zero-length */
-#define PR_2_INVALID_SYMLINK 0x020031 /* Invalid symlink */
-#define PR_2_FILE_ACL_BAD 0x020032 /* i_file_acl (extended attribute) is bad */
-#define PR_2_FEATURE_LARGE_FILES 0x020033 /* Filesystem contains large files, but has no such flag in sb */
-#define PR_2_HTREE_NOTREF 0x020034 /* Node in HTREE directory not referenced */
-#define PR_2_HTREE_DUPREF 0x020035 /* Node in HTREE directory referenced twice */
-#define PR_2_HTREE_MIN_HASH 0x020036 /* Node in HTREE directory has bad min hash */
-#define PR_2_HTREE_MAX_HASH 0x020037 /* Node in HTREE directory has bad max hash */
-#define PR_2_HTREE_CLEAR 0x020038 /* Clear invalid HTREE directory */
-#define PR_2_HTREE_BADBLK 0x02003A /* Bad block in htree interior node */
-#define PR_2_ADJ_EA_REFCOUNT 0x02003B /* Error adjusting EA refcount */
-#define PR_2_HTREE_BAD_ROOT 0x02003C /* Invalid HTREE root node */
-#define PR_2_HTREE_BAD_LIMIT 0x02003D /* Invalid HTREE limit */
-#define PR_2_HTREE_BAD_COUNT 0x02003E /* Invalid HTREE count */
-#define PR_2_HTREE_HASH_ORDER 0x02003F /* HTREE interior node has out-of-order hashes in table */
-#define PR_2_HTREE_BAD_DEPTH 0x020040 /* Node in HTREE directory has bad depth */
-#define PR_2_DUPLICATE_DIRENT 0x020041 /* Duplicate directory entry found */
-#define PR_2_NON_UNIQUE_FILE 0x020042 /* Non-unique filename found */
-#define PR_2_REPORT_DUP_DIRENT 0x020043 /* Duplicate directory entry found */
-
-/*
- * Pass 3 errors
- */
-
-#define PR_3_PASS_HEADER 0x030000 /* Pass 3: Checking directory connectivity */
-#define PR_3_NO_ROOT_INODE 0x030001 /* Root inode not allocated */
-#define PR_3_EXPAND_LF_DIR 0x030002 /* No room in lost+found */
-#define PR_3_UNCONNECTED_DIR 0x030003 /* Unconnected directory inode */
-#define PR_3_NO_LF_DIR 0x030004 /* /lost+found not found */
-#define PR_3_BAD_DOT_DOT 0x030005 /* .. entry is incorrect */
-#define PR_3_NO_LPF 0x030006 /* Bad or non-existent /lost+found. Cannot reconnect */
-#define PR_3_CANT_EXPAND_LPF 0x030007 /* Could not expand /lost+found */
-#define PR_3_CANT_RECONNECT 0x030008 /* Could not reconnect inode */
-#define PR_3_ERR_FIND_LPF 0x030009 /* Error while trying to find /lost+found */
-#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A /* Error in ext2fs_new_block while creating /lost+found */
-#define PR_3_ERR_LPF_NEW_INODE 0x03000B /* Error in ext2fs_new_inode while creating /lost+found */
-#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C /* Error in ext2fs_new_dir_block while creating /lost+found */
-#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D /* Error while writing directory block for /lost+found */
-#define PR_3_ADJUST_INODE 0x03000E /* Error while adjusting inode count */
-#define PR_3_FIX_PARENT_ERR 0x03000F /* Couldn't fix parent directory -- error */
-#define PR_3_FIX_PARENT_NOFIND 0x030010 /* Couldn't fix parent directory -- couldn't find it */
-#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011 /* Error allocating inode bitmap */
-#define PR_3_CREATE_ROOT_ERROR 0x030012 /* Error creating root directory */
-#define PR_3_CREATE_LPF_ERROR 0x030013 /* Error creating lost and found directory */
-#define PR_3_ROOT_NOT_DIR_ABORT 0x030014 /* Root inode is not directory; aborting */
-#define PR_3_NO_ROOT_INODE_ABORT 0x030015 /* Cannot proceed without a root inode. */
-#define PR_3_NO_DIRINFO 0x030016 /* Internal error: couldn't find dir_info */
-#define PR_3_LPF_NOTDIR 0x030017 /* Lost+found is not a directory */
-
-/*
- * Pass 3a --- rehashing diretories
- */
-#define PR_3A_PASS_HEADER 0x031000 /* Pass 3a: Reindexing directories */
-#define PR_3A_OPTIMIZE_ITER 0x031001 /* Error iterating over directories */
-#define PR_3A_OPTIMIZE_DIR_ERR 0x031002 /* Error rehash directory */
-#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003 /* Rehashing dir header */
-#define PR_3A_OPTIMIZE_DIR 0x031004 /* Rehashing directory %d */
-#define PR_3A_OPTIMIZE_DIR_END 0x031005 /* Rehashing dir end */
-
-/*
- * Pass 4 errors
- */
-
-#define PR_4_PASS_HEADER 0x040000 /* Pass 4: Checking reference counts */
-#define PR_4_ZERO_LEN_INODE 0x040001 /* Unattached zero-length inode */
-#define PR_4_UNATTACHED_INODE 0x040002 /* Unattached inode */
-#define PR_4_BAD_REF_COUNT 0x040003 /* Inode ref count wrong */
-#define PR_4_INCONSISTENT_COUNT 0x040004 /* Inconsistent inode count information cached */
-
-/*
- * Pass 5 errors
- */
-
-#define PR_5_PASS_HEADER 0x050000 /* Pass 5: Checking group summary information */
-#define PR_5_INODE_BMAP_PADDING 0x050001 /* Padding at end of inode bitmap is not set. */
-#define PR_5_BLOCK_BMAP_PADDING 0x050002 /* Padding at end of block bitmap is not set. */
-#define PR_5_BLOCK_BITMAP_HEADER 0x050003 /* Block bitmap differences header */
-#define PR_5_BLOCK_UNUSED 0x050004 /* Block not used, but marked in bitmap */
-#define PR_5_BLOCK_USED 0x050005 /* Block used, but not marked used in bitmap */
-#define PR_5_BLOCK_BITMAP_END 0x050006 /* Block bitmap differences end */
-#define PR_5_INODE_BITMAP_HEADER 0x050007 /* Inode bitmap differences header */
-#define PR_5_INODE_UNUSED 0x050008 /* Inode not used, but marked in bitmap */
-#define PR_5_INODE_USED 0x050009 /* Inode used, but not marked used in bitmap */
-#define PR_5_INODE_BITMAP_END 0x05000A /* Inode bitmap differences end */
-#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B /* Free inodes count for group wrong */
-#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C /* Directories count for group wrong */
-#define PR_5_FREE_INODE_COUNT 0x05000D /* Free inodes count wrong */
-#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E /* Free blocks count for group wrong */
-#define PR_5_FREE_BLOCK_COUNT 0x05000F /* Free blocks count wrong */
-#define PR_5_BMAP_ENDPOINTS 0x050010 /* Programming error: bitmap endpoints don't match */
-#define PR_5_FUDGE_BITMAP_ERROR 0x050011 /* Internal error: fudging end of bitmap */
-#define PR_5_COPY_IBITMAP_ERROR 0x050012 /* Error copying in replacement inode bitmap */
-#define PR_5_COPY_BBITMAP_ERROR 0x050013 /* Error copying in replacement block bitmap */
-#define PR_5_BLOCK_RANGE_UNUSED 0x050014 /* Block range not used, but marked in bitmap */
-#define PR_5_BLOCK_RANGE_USED 0x050015 /* Block range used, but not marked used in bitmap */
-#define PR_5_INODE_RANGE_UNUSED 0x050016 /* Inode range not used, but marked in bitmap */
-#define PR_5_INODE_RANGE_USED 0x050017 /* Inode rangeused, but not marked used in bitmap */
-
-
-/*
- * The directory information structure; stores directory information
- * collected in earlier passes, to avoid disk i/o in fetching the
- * directory information.
- */
-struct dir_info {
- ext2_ino_t ino; /* Inode number */
- ext2_ino_t dotdot; /* Parent according to '..' */
- ext2_ino_t parent; /* Parent according to treewalk */
-};
-
-
-
-/*
- * The indexed directory information structure; stores information for
- * directories which contain a hash tree index.
- */
-struct dx_dir_info {
- ext2_ino_t ino; /* Inode number */
- int numblocks; /* number of blocks */
- int hashversion;
- short depth; /* depth of tree */
- struct dx_dirblock_info *dx_block; /* Array of size numblocks */
-};
-
-/*
- * Define the extended attribute refcount structure
- */
-typedef struct ea_refcount *ext2_refcount_t;
-
-struct e2fsck_struct {
- ext2_filsys fs;
- const char *program_name;
- char *filesystem_name;
- char *device_name;
- char *io_options;
- int flags; /* E2fsck internal flags */
- int options;
- blk_t use_superblock; /* sb requested by user */
- blk_t superblock; /* sb used to open fs */
- int blocksize; /* blocksize */
- blk_t num_blocks; /* Total number of blocks */
- int mount_flags;
- blkid_cache blkid; /* blkid cache */
-
- jmp_buf abort_loc;
-
- unsigned long abort_code;
-
- int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
- unsigned long max);
-
- ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
- ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
- ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
- ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
- ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
-
- ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
- ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
- ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
-
- /*
- * Inode count arrays
- */
- ext2_icount_t inode_count;
- ext2_icount_t inode_link_info;
-
- ext2_refcount_t refcount;
- ext2_refcount_t refcount_extra;
-
- /*
- * Array of flags indicating whether an inode bitmap, block
- * bitmap, or inode table is invalid
- */
- int *invalid_inode_bitmap_flag;
- int *invalid_block_bitmap_flag;
- int *invalid_inode_table_flag;
- int invalid_bitmaps; /* There are invalid bitmaps/itable */
-
- /*
- * Block buffer
- */
- char *block_buf;
-
- /*
- * For pass1_check_directory and pass1_get_blocks
- */
- ext2_ino_t stashed_ino;
- struct ext2_inode *stashed_inode;
-
- /*
- * Location of the lost and found directory
- */
- ext2_ino_t lost_and_found;
- int bad_lost_and_found;
-
- /*
- * Directory information
- */
- int dir_info_count;
- int dir_info_size;
- struct dir_info *dir_info;
-
- /*
- * Indexed directory information
- */
- int dx_dir_info_count;
- int dx_dir_info_size;
- struct dx_dir_info *dx_dir_info;
-
- /*
- * Directories to hash
- */
- ext2_u32_list dirs_to_hash;
-
- /*
- * Tuning parameters
- */
- int process_inode_size;
- int inode_buffer_blocks;
-
- /*
- * ext3 journal support
- */
- io_channel journal_io;
- char *journal_name;
-
- /*
- * How we display the progress update (for unix)
- */
- int progress_fd;
- int progress_pos;
- int progress_last_percent;
- unsigned int progress_last_time;
- int interactive; /* Are we connected directly to a tty? */
- char start_meta[2], stop_meta[2];
-
- /* File counts */
- int fs_directory_count;
- int fs_regular_count;
- int fs_blockdev_count;
- int fs_chardev_count;
- int fs_links_count;
- int fs_symlinks_count;
- int fs_fast_symlinks_count;
- int fs_fifo_count;
- int fs_total_count;
- int fs_sockets_count;
- int fs_ind_count;
- int fs_dind_count;
- int fs_tind_count;
- int fs_fragmented;
- int large_files;
- int fs_ext_attr_inodes;
- int fs_ext_attr_blocks;
-
- int ext_attr_ver;
-
- /*
- * For the use of callers of the e2fsck functions; not used by
- * e2fsck functions themselves.
- */
- void *priv_data;
-};
-
-
-#define tid_gt(x, y) ((x - y) > 0)
-
-static inline int tid_geq(tid_t x, tid_t y)
-{
- int difference = (x - y);
- return (difference >= 0);
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src b/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src
deleted file mode 100644
index 482630c..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src
+++ b/dev/null
@@ -1,18 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under GPLv2, see file LICENSE in this source tree.
-
-NEEDED-$(CONFIG_CHATTR) = y
-NEEDED-$(CONFIG_LSATTR) = y
-NEEDED-$(CONFIG_MKE2FS) = y
-NEEDED-$(CONFIG_TUNE2FS) = y
-
-lib-y:=
-
-INSERT
-
-lib-$(NEEDED-y) += fgetsetflags.o fgetsetversion.o pf.o iod.o mntopts.o \
- feature.o ls.o uuid.o pe.o ostype.o ps.o hashstr.o \
- parse_num.o
diff --git a/e2fsprogs/old_e2fsprogs/e2p/e2p.h b/e2fsprogs/old_e2fsprogs/e2p/e2p.h
deleted file mode 100644
index bad2d6a..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/e2p.h
+++ b/dev/null
@@ -1,64 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#include "libbb.h"
-#include <sys/types.h> /* Needed by dirent.h on netbsd */
-#include <stdio.h>
-#include <dirent.h>
-
-#include "../ext2fs/ext2_fs.h"
-
-#define E2P_FEATURE_COMPAT 0
-#define E2P_FEATURE_INCOMPAT 1
-#define E2P_FEATURE_RO_INCOMPAT 2
-#ifndef EXT3_FEATURE_INCOMPAT_EXTENTS
-#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
-#endif
-
-/* `options' for print_e2flags() */
-
-#define PFOPT_LONG 1 /* Must be 1 for compatibility with `int long_format'. */
-
-/*int fgetversion (const char * name, unsigned long * version);*/
-/*int fsetversion (const char * name, unsigned long version);*/
-int fgetsetversion(const char * name, unsigned long * get_version, unsigned long set_version);
-#define fgetversion(name, version) fgetsetversion(name, version, 0)
-#define fsetversion(name, version) fgetsetversion(name, NULL, version)
-
-/*int fgetflags (const char * name, unsigned long * flags);*/
-/*int fsetflags (const char * name, unsigned long flags);*/
-int fgetsetflags(const char * name, unsigned long * get_flags, unsigned long set_flags);
-#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
-#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
-
-int getflags (int fd, unsigned long * flags);
-int getversion (int fd, unsigned long * version);
-int iterate_on_dir (const char * dir_name,
- int (*func) (const char *, struct dirent *, void *),
- void * private);
-/*void list_super(struct ext2_super_block * s);*/
-void list_super2(struct ext2_super_block * s, FILE *f);
-#define list_super(s) list_super2(s, stdout)
-void print_fs_errors (FILE *f, unsigned short errors);
-void print_flags (FILE *f, unsigned long flags, unsigned options);
-void print_fs_state (FILE *f, unsigned short state);
-int setflags (int fd, unsigned long flags);
-int setversion (int fd, unsigned long version);
-
-const char *e2p_feature2string(int compat, unsigned int mask);
-int e2p_string2feature(char *string, int *compat, unsigned int *mask);
-int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array);
-
-int e2p_is_null_uuid(void *uu);
-void e2p_uuid_to_str(void *uu, char *out);
-const char *e2p_uuid2str(void *uu);
-
-const char *e2p_hash2string(int num);
-int e2p_string2hash(char *string);
-
-const char *e2p_mntopt2string(unsigned int mask);
-int e2p_string2mntopt(char *string, unsigned int *mask);
-int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok);
-
-unsigned long parse_num_blocks(const char *arg, int log_block_size);
-
-char *e2p_os2string(int os_type);
-int e2p_string2os(char *str);
diff --git a/e2fsprogs/old_e2fsprogs/e2p/feature.c b/e2fsprogs/old_e2fsprogs/e2p/feature.c
deleted file mode 100644
index 2102ed8..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/feature.c
+++ b/dev/null
@@ -1,187 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * feature.c --- convert between features and strings
- *
- * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "e2p.h"
-
-struct feature {
- int compat;
- unsigned int mask;
- const char *string;
-};
-
-static const struct feature feature_list[] = {
- { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC,
- "dir_prealloc" },
- { E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL,
- "has_journal" },
- { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES,
- "imagic_inodes" },
- { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR,
- "ext_attr" },
- { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX,
- "dir_index" },
- { E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INO,
- "resize_inode" },
- { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
- "sparse_super" },
- { E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE,
- "large_file" },
- { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
- "compression" },
- { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE,
- "filetype" },
- { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER,
- "needs_recovery" },
- { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
- "journal_dev" },
- { E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS,
- "extents" },
- { E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG,
- "meta_bg" },
- { 0, 0, 0 },
-};
-
-const char *e2p_feature2string(int compat, unsigned int mask)
-{
- const struct feature *f;
- static char buf[20];
- char fchar;
- int fnum;
-
- for (f = feature_list; f->string; f++) {
- if ((compat == f->compat) &&
- (mask == f->mask))
- return f->string;
- }
- switch (compat) {
- case E2P_FEATURE_COMPAT:
- fchar = 'C';
- break;
- case E2P_FEATURE_INCOMPAT:
- fchar = 'I';
- break;
- case E2P_FEATURE_RO_INCOMPAT:
- fchar = 'R';
- break;
- default:
- fchar = '?';
- break;
- }
- for (fnum = 0; mask >>= 1; fnum++);
- sprintf(buf, "FEATURE_%c%d", fchar, fnum);
- return buf;
-}
-
-int e2p_string2feature(char *string, int *compat_type, unsigned int *mask)
-{
- const struct feature *f;
- char *eptr;
- int num;
-
- for (f = feature_list; f->string; f++) {
- if (!strcasecmp(string, f->string)) {
- *compat_type = f->compat;
- *mask = f->mask;
- return 0;
- }
- }
- if (strncasecmp(string, "FEATURE_", 8))
- return 1;
-
- switch (string[8]) {
- case 'c':
- case 'C':
- *compat_type = E2P_FEATURE_COMPAT;
- break;
- case 'i':
- case 'I':
- *compat_type = E2P_FEATURE_INCOMPAT;
- break;
- case 'r':
- case 'R':
- *compat_type = E2P_FEATURE_RO_INCOMPAT;
- break;
- default:
- return 1;
- }
- if (string[9] == 0)
- return 1;
- num = strtol(string+9, &eptr, 10);
- if (num > 32 || num < 0)
- return 1;
- if (*eptr)
- return 1;
- *mask = 1 << num;
- return 0;
-}
-
-static inline char *skip_over_blanks(char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
- return cp;
-}
-
-static inline char *skip_over_word(char *cp)
-{
- while (*cp && !isspace(*cp) && *cp != ',')
- cp++;
- return cp;
-}
-
-/*
- * Edit a feature set array as requested by the user. The ok_array,
- * if set, allows the application to limit what features the user is
- * allowed to set or clear using this function.
- */
-int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array)
-{
- char *cp, *buf, *next;
- int neg;
- unsigned int mask;
- int compat_type;
-
- buf = xstrdup(str);
- cp = buf;
- while (cp && *cp) {
- neg = 0;
- cp = skip_over_blanks(cp);
- next = skip_over_word(cp);
- if (*next == 0)
- next = 0;
- else
- *next = 0;
- switch (*cp) {
- case '-':
- case '^':
- neg++;
- case '+':
- cp++;
- break;
- }
- if (e2p_string2feature(cp, &compat_type, &mask))
- return 1;
- if (ok_array && !(ok_array[compat_type] & mask))
- return 1;
- if (neg)
- compat_array[compat_type] &= ~mask;
- else
- compat_array[compat_type] |= mask;
- cp = next ? next+1 : 0;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c b/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
deleted file mode 100644
index 008b798..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
+++ b/dev/null
@@ -1,70 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * fgetflags.c - Get a file flags on an ext2 file system
- * fsetflags.c - Set a file flags on an ext2 file system
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- */
-
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_EXT2_IOCTLS
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#endif
-
-#include "e2p.h"
-
-#ifdef O_LARGEFILE
-#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
-#else
-#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
-#endif
-
-int fgetsetflags (const char * name, unsigned long * get_flags, unsigned long set_flags)
-{
-#ifdef HAVE_EXT2_IOCTLS
- struct stat buf;
- int fd, r, f, save_errno = 0;
-
- if (!stat(name, &buf) &&
- !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
- goto notsupp;
- }
- fd = open (name, OPEN_FLAGS);
- if (fd == -1)
- return -1;
- if (!get_flags) {
- f = (int) set_flags;
- r = ioctl (fd, EXT2_IOC_SETFLAGS, &f);
- } else {
- r = ioctl (fd, EXT2_IOC_GETFLAGS, &f);
- *get_flags = f;
- }
- if (r == -1)
- save_errno = errno;
- close (fd);
- if (save_errno)
- errno = save_errno;
- return r;
-notsupp:
-#endif /* HAVE_EXT2_IOCTLS */
- errno = EOPNOTSUPP;
- return -1;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c b/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
deleted file mode 100644
index 8d79054..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
+++ b/dev/null
@@ -1,70 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * fgetversion.c - Get a file version on an ext2 file system
- * fsetversion.c - Set a file version on an ext2 file system
- *
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- */
-
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <sys/ioctl.h>
-
-#include "e2p.h"
-
-#ifdef O_LARGEFILE
-#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
-#else
-#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
-#endif
-
-/*
- To do fsetversion: unsigned long *ptr_version must be set to NULL.
- and unsigned long version must be set to a value
- To do fgetversion: unsigned long *ptr_version must NOT be set to NULL
- and unsigned long version is ignored.
- TITO.
-*/
-
-int fgetsetversion (const char * name, unsigned long * get_version, unsigned long set_version)
-{
-#ifdef HAVE_EXT2_IOCTLS
- int fd, r, ver, save_errno = 0;
-
- fd = open (name, OPEN_FLAGS);
- if (fd == -1)
- return -1;
- if (!get_version) {
- ver = (int) set_version;
- r = ioctl (fd, EXT2_IOC_SETVERSION, &ver);
- } else {
- r = ioctl (fd, EXT2_IOC_GETVERSION, &ver);
- *get_version = ver;
- }
- if (r == -1)
- save_errno = errno;
- close (fd);
- if (save_errno)
- errno = save_errno;
- return r;
-#else /* ! HAVE_EXT2_IOCTLS */
- errno = EOPNOTSUPP;
- return -1;
-#endif /* ! HAVE_EXT2_IOCTLS */
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/hashstr.c b/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
deleted file mode 100644
index 697ffad..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
+++ b/dev/null
@@ -1,70 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * feature.c --- convert between features and strings
- *
- * Copyright (C) 1999 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "e2p.h"
-
-struct hash {
- int num;
- const char *string;
-};
-
-static const struct hash hash_list[] = {
- { EXT2_HASH_LEGACY, "legacy" },
- { EXT2_HASH_HALF_MD4, "half_md4" },
- { EXT2_HASH_TEA, "tea" },
- { 0, 0 },
-};
-
-const char *e2p_hash2string(int num)
-{
- const struct hash *p;
- static char buf[20];
-
- for (p = hash_list; p->string; p++) {
- if (num == p->num)
- return p->string;
- }
- sprintf(buf, "HASHALG_%d", num);
- return buf;
-}
-
-/*
- * Returns the hash algorithm, or -1 on error
- */
-int e2p_string2hash(char *string)
-{
- const struct hash *p;
- char *eptr;
- int num;
-
- for (p = hash_list; p->string; p++) {
- if (!strcasecmp(string, p->string)) {
- return p->num;
- }
- }
- if (strncasecmp(string, "HASHALG_", 8))
- return -1;
-
- if (string[8] == 0)
- return -1;
- num = strtol(string+8, &eptr, 10);
- if (num > 255 || num < 0)
- return -1;
- if (*eptr)
- return -1;
- return num;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/iod.c b/e2fsprogs/old_e2fsprogs/e2p/iod.c
deleted file mode 100644
index 23ab8d5..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/iod.c
+++ b/dev/null
@@ -1,52 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * iod.c - Iterate a function on each entry of a directory
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- */
-
-#include "e2p.h"
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-int iterate_on_dir (const char * dir_name,
- int (*func) (const char *, struct dirent *, void *),
- void * private)
-{
- DIR * dir;
- struct dirent *de, *dep;
- int max_len, len;
-
- max_len = PATH_MAX + sizeof(struct dirent);
- de = xmalloc(max_len+1);
- memset(de, 0, max_len+1);
-
- dir = opendir (dir_name);
- if (dir == NULL) {
- free(de);
- return -1;
- }
- while ((dep = readdir (dir))) {
- len = sizeof(struct dirent);
- if (len < dep->d_reclen)
- len = dep->d_reclen;
- if (len > max_len)
- len = max_len;
- memcpy(de, dep, len);
- (*func) (dir_name, de, private);
- }
- free(de);
- closedir(dir);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/ls.c b/e2fsprogs/old_e2fsprogs/e2p/ls.c
deleted file mode 100644
index 9d29db6..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/ls.c
+++ b/dev/null
@@ -1,273 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ls.c - List the contents of an ext2fs superblock
- *
- * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * Copyright (C) 1995, 1996, 1997 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <string.h>
-#include <grp.h>
-#include <pwd.h>
-#include <time.h>
-
-#include "e2p.h"
-
-static void print_user(unsigned short uid, FILE *f)
-{
- struct passwd *pw = getpwuid(uid);
- fprintf(f, "%u (user %s)\n", uid,
- (pw == NULL ? "unknown" : pw->pw_name));
-}
-
-static void print_group(unsigned short gid, FILE *f)
-{
- struct group *gr = getgrgid(gid);
- fprintf(f, "%u (group %s)\n", gid,
- (gr == NULL ? "unknown" : gr->gr_name));
-}
-
-#define MONTH_INT (86400 * 30)
-#define WEEK_INT (86400 * 7)
-#define DAY_INT (86400)
-#define HOUR_INT (60 * 60)
-#define MINUTE_INT (60)
-
-static const char *interval_string(unsigned int secs)
-{
- static char buf[256], tmp[80];
- int hr, min, num;
-
- buf[0] = 0;
-
- if (secs == 0)
- return "<none>";
-
- if (secs >= MONTH_INT) {
- num = secs / MONTH_INT;
- secs -= num*MONTH_INT;
- sprintf(buf, "%d month%s", num, (num>1) ? "s" : "");
- }
- if (secs >= WEEK_INT) {
- num = secs / WEEK_INT;
- secs -= num*WEEK_INT;
- sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "",
- num, (num>1) ? "s" : "");
- strcat(buf, tmp);
- }
- if (secs >= DAY_INT) {
- num = secs / DAY_INT;
- secs -= num*DAY_INT;
- sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "",
- num, (num>1) ? "s" : "");
- strcat(buf, tmp);
- }
- if (secs > 0) {
- hr = secs / HOUR_INT;
- secs -= hr*HOUR_INT;
- min = secs / MINUTE_INT;
- secs -= min*MINUTE_INT;
- sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "",
- hr, min, secs);
- strcat(buf, tmp);
- }
- return buf;
-}
-
-static void print_features(struct ext2_super_block * s, FILE *f)
-{
-#ifdef EXT2_DYNAMIC_REV
- int i, j, printed=0;
- __u32 *mask = &s->s_feature_compat, m;
-
- fprintf(f, "Filesystem features: ");
- for (i=0; i <3; i++,mask++) {
- for (j=0,m=1; j < 32; j++, m<<=1) {
- if (*mask & m) {
- fprintf(f, " %s", e2p_feature2string(i, m));
- printed++;
- }
- }
- }
- if (printed == 0)
- fprintf(f, " (none)");
- fprintf(f, "\n");
-#endif
-}
-
-static void print_mntopts(struct ext2_super_block * s, FILE *f)
-{
-#ifdef EXT2_DYNAMIC_REV
- int i, printed=0;
- __u32 mask = s->s_default_mount_opts, m;
-
- fprintf(f, "Default mount options: ");
- if (mask & EXT3_DEFM_JMODE) {
- fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE));
- printed++;
- }
- for (i=0,m=1; i < 32; i++, m<<=1) {
- if (m & EXT3_DEFM_JMODE)
- continue;
- if (mask & m) {
- fprintf(f, " %s", e2p_mntopt2string(m));
- printed++;
- }
- }
- if (printed == 0)
- fprintf(f, " (none)");
- fprintf(f, "\n");
-#endif
-}
-
-
-#ifndef EXT2_INODE_SIZE
-#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode)
-#endif
-
-#ifndef EXT2_GOOD_OLD_REV
-#define EXT2_GOOD_OLD_REV 0
-#endif
-
-void list_super2(struct ext2_super_block * sb, FILE *f)
-{
- int inode_blocks_per_group;
- char buf[80], *str;
- time_t tm;
-
- inode_blocks_per_group = (((sb->s_inodes_per_group *
- EXT2_INODE_SIZE(sb)) +
- EXT2_BLOCK_SIZE(sb) - 1) /
- EXT2_BLOCK_SIZE(sb));
- if (sb->s_volume_name[0]) {
- memset(buf, 0, sizeof(buf));
- strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name));
- } else
- strcpy(buf, "<none>");
- fprintf(f, "Filesystem volume name: %s\n", buf);
- if (sb->s_last_mounted[0]) {
- memset(buf, 0, sizeof(buf));
- strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted));
- } else
- strcpy(buf, "<not available>");
- fprintf(f,
- "Last mounted on: %s\n"
- "Filesystem UUID: %s\n"
- "Filesystem magic number: 0x%04X\n"
- "Filesystem revision #: %d",
- buf, e2p_uuid2str(sb->s_uuid), sb->s_magic, sb->s_rev_level);
- if (sb->s_rev_level == EXT2_GOOD_OLD_REV) {
- fprintf(f, " (original)\n");
-#ifdef EXT2_DYNAMIC_REV
- } else if (sb->s_rev_level == EXT2_DYNAMIC_REV) {
- fprintf(f, " (dynamic)\n");
-#endif
- } else
- fprintf(f, " (unknown)\n");
- print_features(sb, f);
- print_mntopts(sb, f);
- fprintf(f, "Filesystem state: ");
- print_fs_state (f, sb->s_state);
- fprintf(f, "\nErrors behavior: ");
- print_fs_errors(f, sb->s_errors);
- str = e2p_os2string(sb->s_creator_os);
- fprintf(f,
- "\n"
- "Filesystem OS type: %s\n"
- "Inode count: %u\n"
- "Block count: %u\n"
- "Reserved block count: %u\n"
- "Free blocks: %u\n"
- "Free inodes: %u\n"
- "First block: %u\n"
- "Block size: %u\n"
- "Fragment size: %u\n",
- str, sb->s_inodes_count, sb->s_blocks_count, sb->s_r_blocks_count,
- sb->s_free_blocks_count, sb->s_free_inodes_count,
- sb->s_first_data_block, EXT2_BLOCK_SIZE(sb), EXT2_FRAG_SIZE(sb));
- free(str);
- if (sb->s_reserved_gdt_blocks)
- fprintf(f, "Reserved GDT blocks: %u\n",
- sb->s_reserved_gdt_blocks);
- fprintf(f,
- "Blocks per group: %u\n"
- "Fragments per group: %u\n"
- "Inodes per group: %u\n"
- "Inode blocks per group: %u\n",
- sb->s_blocks_per_group, sb->s_frags_per_group,
- sb->s_inodes_per_group, inode_blocks_per_group);
- if (sb->s_first_meta_bg)
- fprintf(f, "First meta block group: %u\n",
- sb->s_first_meta_bg);
- if (sb->s_mkfs_time) {
- tm = sb->s_mkfs_time;
- fprintf(f, "Filesystem created: %s", ctime(&tm));
- }
- tm = sb->s_mtime;
- fprintf(f, "Last mount time: %s",
- sb->s_mtime ? ctime(&tm) : "n/a\n");
- tm = sb->s_wtime;
- fprintf(f,
- "Last write time: %s"
- "Mount count: %u\n"
- "Maximum mount count: %d\n",
- ctime(&tm), sb->s_mnt_count, sb->s_max_mnt_count);
- tm = sb->s_lastcheck;
- fprintf(f,
- "Last checked: %s"
- "Check interval: %u (%s)\n",
- ctime(&tm),
- sb->s_checkinterval, interval_string(sb->s_checkinterval));
- if (sb->s_checkinterval)
- {
- time_t next;
-
- next = sb->s_lastcheck + sb->s_checkinterval;
- fprintf(f, "Next check after: %s", ctime(&next));
- }
- fprintf(f, "Reserved blocks uid: ");
- print_user(sb->s_def_resuid, f);
- fprintf(f, "Reserved blocks gid: ");
- print_group(sb->s_def_resgid, f);
- if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
- fprintf(f,
- "First inode: %d\n"
- "Inode size: %d\n",
- sb->s_first_ino, sb->s_inode_size);
- }
- if (!e2p_is_null_uuid(sb->s_journal_uuid))
- fprintf(f, "Journal UUID: %s\n",
- e2p_uuid2str(sb->s_journal_uuid));
- if (sb->s_journal_inum)
- fprintf(f, "Journal inode: %u\n",
- sb->s_journal_inum);
- if (sb->s_journal_dev)
- fprintf(f, "Journal device: 0x%04x\n",
- sb->s_journal_dev);
- if (sb->s_last_orphan)
- fprintf(f, "First orphan inode: %u\n",
- sb->s_last_orphan);
- if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
- sb->s_def_hash_version)
- fprintf(f, "Default directory hash: %s\n",
- e2p_hash2string(sb->s_def_hash_version));
- if (!e2p_is_null_uuid(sb->s_hash_seed))
- fprintf(f, "Directory Hash Seed: %s\n",
- e2p_uuid2str(sb->s_hash_seed));
- if (sb->s_jnl_backup_type) {
- fprintf(f, "Journal backup: ");
- if (sb->s_jnl_backup_type == 1)
- fprintf(f, "inode blocks\n");
- else
- fprintf(f, "type %u\n", sb->s_jnl_backup_type);
- }
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/mntopts.c b/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
deleted file mode 100644
index 17c26c4..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
+++ b/dev/null
@@ -1,134 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * mountopts.c --- convert between default mount options and strings
- *
- * Copyright (C) 2002 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-
-#include "e2p.h"
-
-struct mntopt {
- unsigned int mask;
- const char *string;
-};
-
-static const struct mntopt mntopt_list[] = {
- { EXT2_DEFM_DEBUG, "debug" },
- { EXT2_DEFM_BSDGROUPS, "bsdgroups" },
- { EXT2_DEFM_XATTR_USER, "user_xattr" },
- { EXT2_DEFM_ACL, "acl" },
- { EXT2_DEFM_UID16, "uid16" },
- { EXT3_DEFM_JMODE_DATA, "journal_data" },
- { EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" },
- { EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" },
- { 0, 0 },
-};
-
-const char *e2p_mntopt2string(unsigned int mask)
-{
- const struct mntopt *f;
- static char buf[20];
- int fnum;
-
- for (f = mntopt_list; f->string; f++) {
- if (mask == f->mask)
- return f->string;
- }
- for (fnum = 0; mask >>= 1; fnum++);
- sprintf(buf, "MNTOPT_%d", fnum);
- return buf;
-}
-
-int e2p_string2mntopt(char *string, unsigned int *mask)
-{
- const struct mntopt *f;
- char *eptr;
- int num;
-
- for (f = mntopt_list; f->string; f++) {
- if (!strcasecmp(string, f->string)) {
- *mask = f->mask;
- return 0;
- }
- }
- if (strncasecmp(string, "MNTOPT_", 8))
- return 1;
-
- if (string[8] == 0)
- return 1;
- num = strtol(string+8, &eptr, 10);
- if (num > 32 || num < 0)
- return 1;
- if (*eptr)
- return 1;
- *mask = 1 << num;
- return 0;
-}
-
-static char *skip_over_blanks(char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
- return cp;
-}
-
-static char *skip_over_word(char *cp)
-{
- while (*cp && !isspace(*cp) && *cp != ',')
- cp++;
- return cp;
-}
-
-/*
- * Edit a mntopt set array as requested by the user. The ok
- * parameter, if non-zero, allows the application to limit what
- * mntopts the user is allowed to set or clear using this function.
- */
-int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok)
-{
- char *cp, *buf, *next;
- int neg;
- unsigned int mask;
-
- buf = xstrdup(str);
- cp = buf;
- while (cp && *cp) {
- neg = 0;
- cp = skip_over_blanks(cp);
- next = skip_over_word(cp);
- if (*next == 0)
- next = 0;
- else
- *next = 0;
- switch (*cp) {
- case '-':
- case '^':
- neg++;
- case '+':
- cp++;
- break;
- }
- if (e2p_string2mntopt(cp, &mask))
- return 1;
- if (ok && !(ok & mask))
- return 1;
- if (mask & EXT3_DEFM_JMODE)
- *mntopts &= ~EXT3_DEFM_JMODE;
- if (neg)
- *mntopts &= ~mask;
- else
- *mntopts |= mask;
- cp = next ? next+1 : 0;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/ostype.c b/e2fsprogs/old_e2fsprogs/e2p/ostype.c
deleted file mode 100644
index 6a2f178..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/ostype.c
+++ b/dev/null
@@ -1,72 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * getostype.c - Get the Filesystem OS type
- *
- * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-#include "e2p.h"
-#include <string.h>
-#include <stdlib.h>
-
-static const char *const os_tab[] =
- { "Linux",
- "Hurd",
- "Masix",
- "FreeBSD",
- "Lites",
- 0 };
-
-/*
- * Convert an os_type to a string
- */
-char *e2p_os2string(int os_type)
-{
- const char *os;
- char *ret;
-
- if (os_type <= EXT2_OS_LITES)
- os = os_tab[os_type];
- else
- os = "(unknown os)";
-
- ret = xstrdup(os);
- return ret;
-}
-
-/*
- * Convert an os_type to a string
- */
-int e2p_string2os(char *str)
-{
- const char *const *cpp;
- int i = 0;
-
- for (cpp = os_tab; *cpp; cpp++, i++) {
- if (!strcasecmp(str, *cpp))
- return i;
- }
- return -1;
-}
-
-#ifdef TEST_PROGRAM
-int main(int argc, char **argv)
-{
- char *s;
- int i, os;
-
- for (i=0; i <= EXT2_OS_LITES; i++) {
- s = e2p_os2string(i);
- os = e2p_string2os(s);
- printf("%d: %s (%d)\n", i, s, os);
- if (i != os) {
- fprintf(stderr, "Failure!\n");
- exit(1);
- }
- }
- exit(0);
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/e2p/parse_num.c b/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
deleted file mode 100644
index 6db076f..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
+++ b/dev/null
@@ -1,65 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * parse_num.c - Parse the number of blocks
- *
- * Copyright (C) 2004,2005 Theodore Ts'o <tytso@mit.edu>
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-#include "e2p.h"
-
-#include <stdlib.h>
-
-unsigned long parse_num_blocks(const char *arg, int log_block_size)
-{
- char *p;
- unsigned long long num;
-
- num = strtoull(arg, &p, 0);
-
- if (p[0] && p[1])
- return 0;
-
- switch (*p) { /* Using fall-through logic */
- case 'T': case 't':
- num <<= 10;
- case 'G': case 'g':
- num <<= 10;
- case 'M': case 'm':
- num <<= 10;
- case 'K': case 'k':
- num >>= log_block_size;
- break;
- case 's':
- num >>= 1;
- break;
- case '\0':
- break;
- default:
- return 0;
- }
- return num;
-}
-
-#ifdef DEBUG
-#include <unistd.h>
-#include <stdio.h>
-
-main(int argc, char **argv)
-{
- unsigned long num;
- int log_block_size = 0;
-
- if (argc != 2) {
- fprintf(stderr, "Usage: %s arg\n", argv[0]);
- exit(1);
- }
-
- num = parse_num_blocks(argv[1], log_block_size);
-
- printf("Parsed number: %lu\n", num);
- exit(0);
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/e2p/pe.c b/e2fsprogs/old_e2fsprogs/e2p/pe.c
deleted file mode 100644
index 835274b..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/pe.c
+++ b/dev/null
@@ -1,32 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * pe.c - Print a second extended filesystem errors behavior
- *
- * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 94/01/09 - Creation
- */
-
-#include <stdio.h>
-
-#include "e2p.h"
-
-void print_fs_errors(FILE *f, unsigned short errors)
-{
- char *disp = NULL;
- switch (errors) {
- case EXT2_ERRORS_CONTINUE: disp = "Continue"; break;
- case EXT2_ERRORS_RO: disp = "Remount read-only"; break;
- case EXT2_ERRORS_PANIC: disp = "Panic"; break;
- default: disp = "Unknown (continue)";
- }
- fprintf(f, disp);
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/pf.c b/e2fsprogs/old_e2fsprogs/e2p/pf.c
deleted file mode 100644
index 02cbec7..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/pf.c
+++ b/dev/null
@@ -1,74 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * pf.c - Print file attributes on an ext2 file system
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- */
-
-#include <stdio.h>
-
-#include "e2p.h"
-
-struct flags_name {
- unsigned long flag;
- const char *short_name;
- const char *long_name;
-};
-
-static const struct flags_name flags_array[] = {
- { EXT2_SECRM_FL, "s", "Secure_Deletion" },
- { EXT2_UNRM_FL, "u" , "Undelete" },
- { EXT2_SYNC_FL, "S", "Synchronous_Updates" },
- { EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" },
- { EXT2_IMMUTABLE_FL, "i", "Immutable" },
- { EXT2_APPEND_FL, "a", "Append_Only" },
- { EXT2_NODUMP_FL, "d", "No_Dump" },
- { EXT2_NOATIME_FL, "A", "No_Atime" },
- { EXT2_COMPR_FL, "c", "Compression_Requested" },
-#ifdef ENABLE_COMPRESSION
- { EXT2_COMPRBLK_FL, "B", "Compressed_File" },
- { EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" },
- { EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" },
- { EXT2_ECOMPR_FL, "E", "Compression_Error" },
-#endif
- { EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" },
- { EXT2_INDEX_FL, "I", "Indexed_direcctory" },
- { EXT2_NOTAIL_FL, "t", "No_Tailmerging" },
- { EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" },
- { 0, NULL, NULL }
-};
-
-void print_flags (FILE *f, unsigned long flags, unsigned options)
-{
- int long_opt = (options & PFOPT_LONG);
- const struct flags_name *fp;
- int first = 1;
-
- for (fp = flags_array; fp->flag != 0; fp++) {
- if (flags & fp->flag) {
- if (long_opt) {
- if (first)
- first = 0;
- else
- fputs(", ", f);
- fputs(fp->long_name, f);
- } else
- fputs(fp->short_name, f);
- } else {
- if (!long_opt)
- fputs("-", f);
- }
- }
- if (long_opt && first)
- fputs("---", f);
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/ps.c b/e2fsprogs/old_e2fsprogs/e2p/ps.c
deleted file mode 100644
index a6b4099..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/ps.c
+++ b/dev/null
@@ -1,27 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ps.c - Print filesystem state
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU Library General
- * Public License
- */
-
-/*
- * History:
- * 93/12/22 - Creation
- */
-
-#include <stdio.h>
-
-#include "e2p.h"
-
-void print_fs_state(FILE *f, unsigned short state)
-{
- fprintf(f, (state & EXT2_VALID_FS ? " clean" : " not clean"));
- if (state & EXT2_ERROR_FS)
- fprintf(f, " with errors");
-}
diff --git a/e2fsprogs/old_e2fsprogs/e2p/uuid.c b/e2fsprogs/old_e2fsprogs/e2p/uuid.c
deleted file mode 100644
index 474d64a..0000000
--- a/e2fsprogs/old_e2fsprogs/e2p/uuid.c
+++ b/dev/null
@@ -1,78 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * uuid.c -- utility routines for manipulating UUID's.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "../ext2fs/ext2_types.h"
-
-#include "e2p.h"
-
-struct uuid {
- __u32 time_low;
- __u16 time_mid;
- __u16 time_hi_and_version;
- __u16 clock_seq;
- __u8 node[6];
-};
-
-/* Returns 1 if the uuid is the NULL uuid */
-int e2p_is_null_uuid(void *uu)
-{
- __u8 *cp;
- int i;
-
- for (i=0, cp = uu; i < 16; i++)
- if (*cp)
- return 0;
- return 1;
-}
-
-static void e2p_unpack_uuid(void *in, struct uuid *uu)
-{
- __u8 *ptr = in;
- __u32 tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_low = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_mid = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_hi_and_version = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->clock_seq = tmp;
-
- memcpy(uu->node, ptr, 6);
-}
-
-void e2p_uuid_to_str(void *uu, char *out)
-{
- struct uuid uuid;
-
- e2p_unpack_uuid(uu, &uuid);
- sprintf(out,
- "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
- uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
- uuid.node[0], uuid.node[1], uuid.node[2],
- uuid.node[3], uuid.node[4], uuid.node[5]);
-}
-
-const char *e2p_uuid2str(void *uu)
-{
- static char buf[80];
- if (e2p_is_null_uuid(uu))
- return "<none>";
- e2p_uuid_to_str(uu, buf);
- return buf;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src b/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src
deleted file mode 100644
index 12adc6e..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src
+++ b/dev/null
@@ -1,26 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under GPLv2, see file LICENSE in this source tree.
-
-NEEDED-$(CONFIG_E2FSCK) = y
-NEEDED-$(CONFIG_FSCK) = y
-NEEDED-$(CONFIG_MKE2FS) = y
-NEEDED-$(CONFIG_TUNE2FS) = y
-
-lib-y:=
-
-INSERT
-
-lib-$(NEEDED-y) += gen_bitmap.o bitops.o ismounted.o mkjournal.o unix_io.o \
- rw_bitmaps.o initialize.o bitmaps.o block.o \
- ind_block.o inode.o freefs.o alloc_stats.o closefs.o \
- openfs.o io_manager.o finddev.o read_bb.o alloc.o badblocks.o \
- getsize.o getsectsize.o alloc_tables.o read_bb_file.o mkdir.o \
- bb_inode.o newdir.o alloc_sb.o lookup.o dirblock.o expanddir.o \
- dir_iterate.o link.o res_gdt.o icount.o get_pathname.o dblist.o \
- dirhash.o version.o flushb.o unlink.o check_desc.o valid_blk.o \
- ext_attr.o bmap.o dblist_dir.o ext2fs_inline.o swapfs.o
-
-CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
deleted file mode 100644
index cbb63e1..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
+++ b/dev/null
@@ -1,173 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * alloc.c --- allocate new inodes, blocks for ext2fs
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <time.h>
-#include <string.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * Right now, just search forward from the parent directory's block
- * group to find the next free inode.
- *
- * Should have a special policy for directories.
- */
-errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
- int mode EXT2FS_ATTR((unused)),
- ext2fs_inode_bitmap map, ext2_ino_t *ret)
-{
- ext2_ino_t dir_group = 0;
- ext2_ino_t i;
- ext2_ino_t start_inode;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!map)
- map = fs->inode_map;
- if (!map)
- return EXT2_ET_NO_INODE_BITMAP;
-
- if (dir > 0)
- dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
-
- start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
- if (start_inode < EXT2_FIRST_INODE(fs->super))
- start_inode = EXT2_FIRST_INODE(fs->super);
- i = start_inode;
-
- do {
- if (!ext2fs_fast_test_inode_bitmap(map, i))
- break;
- i++;
- if (i > fs->super->s_inodes_count)
- i = EXT2_FIRST_INODE(fs->super);
- } while (i != start_inode);
-
- if (ext2fs_test_inode_bitmap(map, i))
- return EXT2_ET_INODE_ALLOC_FAIL;
- *ret = i;
- return 0;
-}
-
-/*
- * Stupid algorithm --- we now just search forward starting from the
- * goal. Should put in a smarter one someday....
- */
-errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
- ext2fs_block_bitmap map, blk_t *ret)
-{
- blk_t i;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!map)
- map = fs->block_map;
- if (!map)
- return EXT2_ET_NO_BLOCK_BITMAP;
- if (!goal || (goal >= fs->super->s_blocks_count))
- goal = fs->super->s_first_data_block;
- i = goal;
- do {
- if (!ext2fs_fast_test_block_bitmap(map, i)) {
- *ret = i;
- return 0;
- }
- i++;
- if (i >= fs->super->s_blocks_count)
- i = fs->super->s_first_data_block;
- } while (i != goal);
- return EXT2_ET_BLOCK_ALLOC_FAIL;
-}
-
-/*
- * This function zeros out the allocated block, and updates all of the
- * appropriate filesystem records.
- */
-errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
- char *block_buf, blk_t *ret)
-{
- errcode_t retval;
- blk_t block;
- char *buf = NULL;
-
- if (!block_buf) {
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- block_buf = buf;
- }
- memset(block_buf, 0, fs->blocksize);
-
- if (!fs->block_map) {
- retval = ext2fs_read_block_bitmap(fs);
- if (retval)
- goto fail;
- }
-
- retval = ext2fs_new_block(fs, goal, 0, &block);
- if (retval)
- goto fail;
-
- retval = io_channel_write_blk(fs->io, block, 1, block_buf);
- if (retval)
- goto fail;
-
- ext2fs_block_alloc_stats(fs, block, +1);
- *ret = block;
- return 0;
-
-fail:
- if (buf)
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
- int num, ext2fs_block_bitmap map, blk_t *ret)
-{
- blk_t b = start;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!map)
- map = fs->block_map;
- if (!map)
- return EXT2_ET_NO_BLOCK_BITMAP;
- if (!b)
- b = fs->super->s_first_data_block;
- if (!finish)
- finish = start;
- if (!num)
- num = 1;
- do {
- if (b+num-1 > fs->super->s_blocks_count)
- b = fs->super->s_first_data_block;
- if (ext2fs_fast_test_block_bitmap_range(map, b, num)) {
- *ret = b;
- return 0;
- }
- b++;
- } while (b != finish);
- return EXT2_ET_BLOCK_ALLOC_FAIL;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
deleted file mode 100644
index a7437c9..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
+++ b/dev/null
@@ -1,58 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * alloc_sb.c --- Allocate the superblock and block group descriptors for a
- * newly initialized filesystem. Used by mke2fs when initializing a filesystem
- *
- * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
- dgrp_t group,
- ext2fs_block_bitmap bmap)
-{
- blk_t super_blk, old_desc_blk, new_desc_blk;
- int j, old_desc_blocks, num_blocks;
-
- num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk,
- &old_desc_blk, &new_desc_blk, 0);
-
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- old_desc_blocks = fs->super->s_first_meta_bg;
- else
- old_desc_blocks =
- fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
-
- if (super_blk || (group == 0))
- ext2fs_mark_block_bitmap(bmap, super_blk);
-
- if (old_desc_blk) {
- for (j=0; j < old_desc_blocks; j++)
- ext2fs_mark_block_bitmap(bmap, old_desc_blk + j);
- }
- if (new_desc_blk)
- ext2fs_mark_block_bitmap(bmap, new_desc_blk);
-
- return num_blocks;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
deleted file mode 100644
index f3ab06a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
+++ b/dev/null
@@ -1,53 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * alloc_stats.c --- Update allocation statistics for ext2fs
- *
- * Copyright (C) 2001 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- */
-
-#include <stdio.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
- int inuse, int isdir)
-{
- int group = ext2fs_group_of_ino(fs, ino);
-
- if (inuse > 0)
- ext2fs_mark_inode_bitmap(fs->inode_map, ino);
- else
- ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
- fs->group_desc[group].bg_free_inodes_count -= inuse;
- if (isdir)
- fs->group_desc[group].bg_used_dirs_count += inuse;
- fs->super->s_free_inodes_count -= inuse;
- ext2fs_mark_super_dirty(fs);
- ext2fs_mark_ib_dirty(fs);
-}
-
-void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse)
-{
- ext2fs_inode_alloc_stats2(fs, ino, inuse, 0);
-}
-
-void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse)
-{
- int group = ext2fs_group_of_blk(fs, blk);
-
- if (inuse > 0)
- ext2fs_mark_block_bitmap(fs->block_map, blk);
- else
- ext2fs_unmark_block_bitmap(fs->block_map, blk);
- fs->group_desc[group].bg_free_blocks_count -= inuse;
- fs->super->s_free_blocks_count -= inuse;
- ext2fs_mark_super_dirty(fs);
- ext2fs_mark_bb_dirty(fs);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c b/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
deleted file mode 100644
index 7c60e2b..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
+++ b/dev/null
@@ -1,114 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * alloc_tables.c --- Allocate tables for a newly initialized
- * filesystem. Used by mke2fs when initializing a filesystem
- *
- * Copyright (C) 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
- ext2fs_block_bitmap bmap)
-{
- errcode_t retval;
- blk_t group_blk, start_blk, last_blk, new_blk, blk;
- int j;
-
- group_blk = fs->super->s_first_data_block +
- (group * fs->super->s_blocks_per_group);
-
- last_blk = group_blk + fs->super->s_blocks_per_group;
- if (last_blk >= fs->super->s_blocks_count)
- last_blk = fs->super->s_blocks_count - 1;
-
- if (!bmap)
- bmap = fs->block_map;
-
- /*
- * Allocate the block and inode bitmaps, if necessary
- */
- if (fs->stride) {
- start_blk = group_blk + fs->inode_blocks_per_group;
- start_blk += ((fs->stride * group) %
- (last_blk - start_blk));
- if (start_blk > last_blk)
- start_blk = group_blk;
- } else
- start_blk = group_blk;
-
- if (!fs->group_desc[group].bg_block_bitmap) {
- retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
- 1, bmap, &new_blk);
- if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
- retval = ext2fs_get_free_blocks(fs, group_blk,
- last_blk, 1, bmap, &new_blk);
- if (retval)
- return retval;
- ext2fs_mark_block_bitmap(bmap, new_blk);
- fs->group_desc[group].bg_block_bitmap = new_blk;
- }
-
- if (!fs->group_desc[group].bg_inode_bitmap) {
- retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
- 1, bmap, &new_blk);
- if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
- retval = ext2fs_get_free_blocks(fs, group_blk,
- last_blk, 1, bmap, &new_blk);
- if (retval)
- return retval;
- ext2fs_mark_block_bitmap(bmap, new_blk);
- fs->group_desc[group].bg_inode_bitmap = new_blk;
- }
-
- /*
- * Allocate the inode table
- */
- if (!fs->group_desc[group].bg_inode_table) {
- retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
- fs->inode_blocks_per_group,
- bmap, &new_blk);
- if (retval)
- return retval;
- for (j=0, blk = new_blk;
- j < fs->inode_blocks_per_group;
- j++, blk++)
- ext2fs_mark_block_bitmap(bmap, blk);
- fs->group_desc[group].bg_inode_table = new_blk;
- }
-
- return 0;
-}
-
-errcode_t ext2fs_allocate_tables(ext2_filsys fs)
-{
- errcode_t retval;
- dgrp_t i;
-
- for (i = 0; i < fs->group_desc_count; i++) {
- retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
- if (retval)
- return retval;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c b/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
deleted file mode 100644
index 6e5cc10..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
+++ b/dev/null
@@ -1,328 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * badblocks.c --- routines to manipulate the bad block structure
- *
- * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-/*
- * Helper function for making a badblocks list
- */
-static errcode_t make_u32_list(int size, int num, __u32 *list,
- ext2_u32_list *ret)
-{
- ext2_u32_list bb;
- errcode_t retval;
-
- retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb);
- if (retval)
- return retval;
- memset(bb, 0, sizeof(struct ext2_struct_u32_list));
- bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST;
- bb->size = size ? size : 10;
- bb->num = num;
- retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list);
- if (!bb->list) {
- ext2fs_free_mem(&bb);
- return retval;
- }
- if (list)
- memcpy(bb->list, list, bb->size * sizeof(blk_t));
- else
- memset(bb->list, 0, bb->size * sizeof(blk_t));
- *ret = bb;
- return 0;
-}
-
-
-/*
- * This procedure creates an empty u32 list.
- */
-errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size)
-{
- return make_u32_list(size, 0, 0, ret);
-}
-
-/*
- * This procedure creates an empty badblocks list.
- */
-errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size)
-{
- return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret);
-}
-
-
-/*
- * This procedure copies a badblocks list
- */
-errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
-{
- errcode_t retval;
-
- retval = make_u32_list(src->size, src->num, src->list, dest);
- if (retval)
- return retval;
- (*dest)->badblocks_flags = src->badblocks_flags;
- return 0;
-}
-
-errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
- ext2_badblocks_list *dest)
-{
- return ext2fs_u32_copy((ext2_u32_list) src,
- (ext2_u32_list *) dest);
-}
-
-/*
- * This procedure frees a badblocks list.
- *
- * (note: moved to closefs.c)
- */
-
-
-/*
- * This procedure adds a block to a badblocks list.
- */
-errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk)
-{
- errcode_t retval;
- int i, j;
- unsigned long old_size;
-
- EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
-
- if (bb->num >= bb->size) {
- old_size = bb->size * sizeof(__u32);
- bb->size += 100;
- retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32),
- &bb->list);
- if (retval) {
- bb->size -= 100;
- return retval;
- }
- }
-
- /*
- * Add special case code for appending to the end of the list
- */
- i = bb->num-1;
- if ((bb->num != 0) && (bb->list[i] == blk))
- return 0;
- if ((bb->num == 0) || (bb->list[i] < blk)) {
- bb->list[bb->num++] = blk;
- return 0;
- }
-
- j = bb->num;
- for (i=0; i < bb->num; i++) {
- if (bb->list[i] == blk)
- return 0;
- if (bb->list[i] > blk) {
- j = i;
- break;
- }
- }
- for (i=bb->num; i > j; i--)
- bb->list[i] = bb->list[i-1];
- bb->list[j] = blk;
- bb->num++;
- return 0;
-}
-
-errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk)
-{
- return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk);
-}
-
-/*
- * This procedure finds a particular block is on a badblocks
- * list.
- */
-int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk)
-{
- int low, high, mid;
-
- if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
- return -1;
-
- if (bb->num == 0)
- return -1;
-
- low = 0;
- high = bb->num-1;
- if (blk == bb->list[low])
- return low;
- if (blk == bb->list[high])
- return high;
-
- while (low < high) {
- mid = (low+high)/2;
- if (mid == low || mid == high)
- break;
- if (blk == bb->list[mid])
- return mid;
- if (blk < bb->list[mid])
- high = mid;
- else
- low = mid;
- }
- return -1;
-}
-
-/*
- * This procedure tests to see if a particular block is on a badblocks
- * list.
- */
-int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk)
-{
- if (ext2fs_u32_list_find(bb, blk) < 0)
- return 0;
- else
- return 1;
-}
-
-int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk)
-{
- return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk);
-}
-
-
-/*
- * Remove a block from the badblock list
- */
-int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk)
-{
- int remloc, i;
-
- if (bb->num == 0)
- return -1;
-
- remloc = ext2fs_u32_list_find(bb, blk);
- if (remloc < 0)
- return -1;
-
- for (i = remloc; i < bb->num - 1; i++)
- bb->list[i] = bb->list[i+1];
- bb->num--;
- return 0;
-}
-
-void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk)
-{
- ext2fs_u32_list_del(bb, blk);
-}
-
-errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
- ext2_u32_iterate *ret)
-{
- ext2_u32_iterate iter;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
-
- retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter);
- if (retval)
- return retval;
-
- iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE;
- iter->bb = bb;
- iter->ptr = 0;
- *ret = iter;
- return 0;
-}
-
-errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
- ext2_badblocks_iterate *ret)
-{
- return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb,
- (ext2_u32_iterate *) ret);
-}
-
-
-int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk)
-{
- ext2_u32_list bb;
-
- if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)
- return 0;
-
- bb = iter->bb;
-
- if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
- return 0;
-
- if (iter->ptr < bb->num) {
- *blk = bb->list[iter->ptr++];
- return 1;
- }
- *blk = 0;
- return 0;
-}
-
-int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk)
-{
- return ext2fs_u32_list_iterate((ext2_u32_iterate) iter,
- (__u32 *) blk);
-}
-
-
-void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter)
-{
- if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE))
- return;
-
- iter->bb = 0;
- ext2fs_free_mem(&iter);
-}
-
-void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter)
-{
- ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter);
-}
-
-
-int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2)
-{
- EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST);
- EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST);
-
- if (bb1->num != bb2->num)
- return 0;
-
- if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0)
- return 0;
- return 1;
-}
-
-int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2)
-{
- return ext2fs_u32_list_equal((ext2_u32_list) bb1,
- (ext2_u32_list) bb2);
-}
-
-int ext2fs_u32_list_count(ext2_u32_list bb)
-{
- return bb->num;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c b/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
deleted file mode 100644
index 419ac77..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
+++ b/dev/null
@@ -1,64 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bb_compat.c --- compatibility badblocks routines
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-errcode_t badblocks_list_create(badblocks_list *ret, int size)
-{
- return ext2fs_badblocks_list_create(ret, size);
-}
-
-void badblocks_list_free(badblocks_list bb)
-{
- ext2fs_badblocks_list_free(bb);
-}
-
-errcode_t badblocks_list_add(badblocks_list bb, blk_t blk)
-{
- return ext2fs_badblocks_list_add(bb, blk);
-}
-
-int badblocks_list_test(badblocks_list bb, blk_t blk)
-{
- return ext2fs_badblocks_list_test(bb, blk);
-}
-
-errcode_t badblocks_list_iterate_begin(badblocks_list bb,
- badblocks_iterate *ret)
-{
- return ext2fs_badblocks_list_iterate_begin(bb, ret);
-}
-
-int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk)
-{
- return ext2fs_badblocks_list_iterate(iter, blk);
-}
-
-void badblocks_list_iterate_end(badblocks_iterate iter)
-{
- ext2fs_badblocks_list_iterate_end(iter);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c b/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
deleted file mode 100644
index a967896..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
+++ b/dev/null
@@ -1,262 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bb_inode.c --- routines to update the bad block inode.
- *
- * WARNING: This routine modifies a lot of state in the filesystem; if
- * this routine returns an error, the bad block inode may be in an
- * inconsistent state.
- *
- * Copyright (C) 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct set_badblock_record {
- ext2_badblocks_iterate bb_iter;
- int bad_block_count;
- blk_t *ind_blocks;
- int max_ind_blocks;
- int ind_blocks_size;
- int ind_blocks_ptr;
- char *block_buf;
- errcode_t err;
-};
-
-static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block, int ref_offset,
- void *priv_data);
-static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block, int ref_offset,
- void *priv_data);
-
-/*
- * Given a bad blocks bitmap, update the bad blocks inode to reflect
- * the map.
- */
-errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
-{
- errcode_t retval;
- struct set_badblock_record rec;
- struct ext2_inode inode;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!fs->block_map)
- return EXT2_ET_NO_BLOCK_BITMAP;
-
- rec.bad_block_count = 0;
- rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
- rec.max_ind_blocks = 10;
- retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
- &rec.ind_blocks);
- if (retval)
- return retval;
- memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
- retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf);
- if (retval)
- goto cleanup;
- memset(rec.block_buf, 0, fs->blocksize);
- rec.err = 0;
-
- /*
- * First clear the old bad blocks (while saving the indirect blocks)
- */
- retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
- BLOCK_FLAG_DEPTH_TRAVERSE, 0,
- clear_bad_block_proc, &rec);
- if (retval)
- goto cleanup;
- if (rec.err) {
- retval = rec.err;
- goto cleanup;
- }
-
- /*
- * Now set the bad blocks!
- *
- * First, mark the bad blocks as used. This prevents a bad
- * block from being used as an indirecto block for the bad
- * block inode (!).
- */
- if (bb_list) {
- retval = ext2fs_badblocks_list_iterate_begin(bb_list,
- &rec.bb_iter);
- if (retval)
- goto cleanup;
- retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
- BLOCK_FLAG_APPEND, 0,
- set_bad_block_proc, &rec);
- ext2fs_badblocks_list_iterate_end(rec.bb_iter);
- if (retval)
- goto cleanup;
- if (rec.err) {
- retval = rec.err;
- goto cleanup;
- }
- }
-
- /*
- * Update the bad block inode's mod time and block count
- * field.
- */
- retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
- if (retval)
- goto cleanup;
-
- inode.i_atime = inode.i_mtime = time(NULL);
- if (!inode.i_ctime)
- inode.i_ctime = time(NULL);
- inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
- inode.i_size = rec.bad_block_count * fs->blocksize;
-
- retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
- if (retval)
- goto cleanup;
-
-cleanup:
- ext2fs_free_mem(&rec.ind_blocks);
- ext2fs_free_mem(&rec.block_buf);
- return retval;
-}
-
-/*
- * Helper function for update_bb_inode()
- *
- * Clear the bad blocks in the bad block inode, while saving the
- * indirect blocks.
- */
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct set_badblock_record *rec = (struct set_badblock_record *)
- priv_data;
- errcode_t retval;
- unsigned long old_size;
-
- if (!*block_nr)
- return 0;
-
- /*
- * If the block number is outrageous, clear it and ignore it.
- */
- if (*block_nr >= fs->super->s_blocks_count ||
- *block_nr < fs->super->s_first_data_block) {
- *block_nr = 0;
- return BLOCK_CHANGED;
- }
-
- if (blockcnt < 0) {
- if (rec->ind_blocks_size >= rec->max_ind_blocks) {
- old_size = rec->max_ind_blocks * sizeof(blk_t);
- rec->max_ind_blocks += 10;
- retval = ext2fs_resize_mem(old_size,
- rec->max_ind_blocks * sizeof(blk_t),
- &rec->ind_blocks);
- if (retval) {
- rec->max_ind_blocks -= 10;
- rec->err = retval;
- return BLOCK_ABORT;
- }
- }
- rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
- }
-
- /*
- * Mark the block as unused, and update accounting information
- */
- ext2fs_block_alloc_stats(fs, *block_nr, -1);
-
- *block_nr = 0;
- return BLOCK_CHANGED;
-}
-
-
-/*
- * Helper function for update_bb_inode()
- *
- * Set the block list in the bad block inode, using the supplied bitmap.
- */
-#ifdef __TURBOC__
- #pragma argsused
-#endif
-static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct set_badblock_record *rec = (struct set_badblock_record *)
- priv_data;
- errcode_t retval;
- blk_t blk;
-
- if (blockcnt >= 0) {
- /*
- * Get the next bad block.
- */
- if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
- return BLOCK_ABORT;
- rec->bad_block_count++;
- } else {
- /*
- * An indirect block; fetch a block from the
- * previously used indirect block list. The block
- * most be not marked as used; if so, get another one.
- * If we run out of reserved indirect blocks, allocate
- * a new one.
- */
- retry:
- if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
- blk = rec->ind_blocks[rec->ind_blocks_ptr++];
- if (ext2fs_test_block_bitmap(fs->block_map, blk))
- goto retry;
- } else {
- retval = ext2fs_new_block(fs, 0, 0, &blk);
- if (retval) {
- rec->err = retval;
- return BLOCK_ABORT;
- }
- }
- retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
- if (retval) {
- rec->err = retval;
- return BLOCK_ABORT;
- }
- }
-
- /*
- * Update block counts
- */
- ext2fs_block_alloc_stats(fs, blk, +1);
-
- *block_nr = blk;
- return BLOCK_CHANGED;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
deleted file mode 100644
index 637ed27..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
+++ b/dev/null
@@ -1,211 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bitmaps.c --- routines to read, write, and manipulate the inode and
- * block bitmaps.
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end,
- const char *descr, char *init_map,
- ext2fs_generic_bitmap *ret)
-{
- ext2fs_generic_bitmap bitmap;
- errcode_t retval;
- size_t size;
-
- retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap),
- &bitmap);
- if (retval)
- return retval;
-
- bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
- bitmap->fs = NULL;
- bitmap->start = start;
- bitmap->end = end;
- bitmap->real_end = real_end;
- bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
- if (descr) {
- retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
- if (retval) {
- ext2fs_free_mem(&bitmap);
- return retval;
- }
- strcpy(bitmap->description, descr);
- } else
- bitmap->description = 0;
-
- size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
- retval = ext2fs_get_mem(size, &bitmap->bitmap);
- if (retval) {
- ext2fs_free_mem(&bitmap->description);
- ext2fs_free_mem(&bitmap);
- return retval;
- }
-
- if (init_map)
- memcpy(bitmap->bitmap, init_map, size);
- else
- memset(bitmap->bitmap, 0, size);
- *ret = bitmap;
- return 0;
-}
-
-errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
- __u32 end,
- __u32 real_end,
- const char *descr,
- ext2fs_generic_bitmap *ret)
-{
- return make_bitmap(start, end, real_end, descr, 0, ret);
-}
-
-errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
- ext2fs_generic_bitmap *dest)
-{
- errcode_t retval;
- ext2fs_generic_bitmap new_map;
-
- retval = make_bitmap(src->start, src->end, src->real_end,
- src->description, src->bitmap, &new_map);
- if (retval)
- return retval;
- new_map->magic = src->magic;
- new_map->fs = src->fs;
- new_map->base_error_code = src->base_error_code;
- *dest = new_map;
- return 0;
-}
-
-void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
-{
- __u32 i, j;
-
- for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++)
- ext2fs_set_bit(j, map->bitmap);
-}
-
-errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
- const char *descr,
- ext2fs_inode_bitmap *ret)
-{
- ext2fs_inode_bitmap bitmap;
- errcode_t retval;
- __u32 start, end, real_end;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- fs->write_bitmaps = ext2fs_write_bitmaps;
-
- start = 1;
- end = fs->super->s_inodes_count;
- real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count);
-
- retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
- descr, &bitmap);
- if (retval)
- return retval;
-
- bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
- bitmap->fs = fs;
- bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
-
- *ret = bitmap;
- return 0;
-}
-
-errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
- const char *descr,
- ext2fs_block_bitmap *ret)
-{
- ext2fs_block_bitmap bitmap;
- errcode_t retval;
- __u32 start, end, real_end;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- fs->write_bitmaps = ext2fs_write_bitmaps;
-
- start = fs->super->s_first_data_block;
- end = fs->super->s_blocks_count-1;
- real_end = (EXT2_BLOCKS_PER_GROUP(fs->super)
- * fs->group_desc_count)-1 + start;
-
- retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
- descr, &bitmap);
- if (retval)
- return retval;
-
- bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
- bitmap->fs = fs;
- bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
-
- *ret = bitmap;
- return 0;
-}
-
-errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
- ext2_ino_t end, ext2_ino_t *oend)
-{
- EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP);
-
- if (end > bitmap->real_end)
- return EXT2_ET_FUDGE_INODE_BITMAP_END;
- if (oend)
- *oend = bitmap->end;
- bitmap->end = end;
- return 0;
-}
-
-errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
- blk_t end, blk_t *oend)
-{
- EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
-
- if (end > bitmap->real_end)
- return EXT2_ET_FUDGE_BLOCK_BITMAP_END;
- if (oend)
- *oend = bitmap->end;
- bitmap->end = end;
- return 0;
-}
-
-void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap)
-{
- if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
- return;
-
- memset(bitmap->bitmap, 0,
- (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
-}
-
-void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap)
-{
- if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
- return;
-
- memset(bitmap->bitmap, 0,
- (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
deleted file mode 100644
index 3d08394..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
+++ b/dev/null
@@ -1,90 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined
- * routines.
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#ifndef _EXT2_HAVE_ASM_BITOPS_
-
-/*
- * For the benefit of those who are trying to port Linux to another
- * architecture, here are some C-language equivalents. You should
- * recode these in the native assmebly language, if at all possible.
- *
- * C language equivalents written by Theodore Ts'o, 9/26/92.
- * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian
- * systems, as well as non-32 bit systems.
- */
-
-int ext2fs_set_bit(unsigned int nr,void * addr)
-{
- int mask, retval;
- unsigned char *ADDR = (unsigned char *) addr;
-
- ADDR += nr >> 3;
- mask = 1 << (nr & 0x07);
- retval = mask & *ADDR;
- *ADDR |= mask;
- return retval;
-}
-
-int ext2fs_clear_bit(unsigned int nr, void * addr)
-{
- int mask, retval;
- unsigned char *ADDR = (unsigned char *) addr;
-
- ADDR += nr >> 3;
- mask = 1 << (nr & 0x07);
- retval = mask & *ADDR;
- *ADDR &= ~mask;
- return retval;
-}
-
-int ext2fs_test_bit(unsigned int nr, const void * addr)
-{
- int mask;
- const unsigned char *ADDR = (const unsigned char *) addr;
-
- ADDR += nr >> 3;
- mask = 1 << (nr & 0x07);
- return (mask & *ADDR);
-}
-
-#endif /* !_EXT2_HAVE_ASM_BITOPS_ */
-
-void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
- const char *description)
-{
-#ifndef OMIT_COM_ERR
- if (description)
- bb_error_msg("#%lu for %s", arg, description);
- else
- bb_error_msg("#%lu", arg);
-#endif
-}
-
-void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
- int code, unsigned long arg)
-{
-#ifndef OMIT_COM_ERR
- if (bitmap->description)
- bb_error_msg("#%lu for %s", arg, bitmap->description);
- else
- bb_error_msg("#%lu", arg);
-#endif
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h b/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
deleted file mode 100644
index 7271a49..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
+++ b/dev/null
@@ -1,105 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bitops.h --- Bitmap frobbing code. The byte swapping routines are
- * also included here.
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- * i386 bitops operations taken from <asm/bitops.h>, Copyright 1992,
- * Linus Torvalds.
- */
-#include <string.h>
-
-extern int ext2fs_set_bit(unsigned int nr,void * addr);
-extern int ext2fs_clear_bit(unsigned int nr, void * addr);
-extern int ext2fs_test_bit(unsigned int nr, const void * addr);
-extern __u16 ext2fs_swab16(__u16 val);
-extern __u32 ext2fs_swab32(__u32 val);
-
-#ifdef WORDS_BIGENDIAN
-#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x))
-#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x))
-#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x))
-#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x))
-#define ext2fs_cpu_to_be32(x) ((__u32)(x))
-#define ext2fs_be32_to_cpu(x) ((__u32)(x))
-#define ext2fs_cpu_to_be16(x) ((__u16)(x))
-#define ext2fs_be16_to_cpu(x) ((__u16)(x))
-#else
-#define ext2fs_cpu_to_le32(x) ((__u32)(x))
-#define ext2fs_le32_to_cpu(x) ((__u32)(x))
-#define ext2fs_cpu_to_le16(x) ((__u16)(x))
-#define ext2fs_le16_to_cpu(x) ((__u16)(x))
-#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x))
-#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x))
-#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x))
-#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x))
-#endif
-
-/*
- * EXT2FS bitmap manipulation routines.
- */
-
-/* Support for sending warning messages from the inline subroutines */
-extern const char *ext2fs_block_string;
-extern const char *ext2fs_inode_string;
-extern const char *ext2fs_mark_string;
-extern const char *ext2fs_unmark_string;
-extern const char *ext2fs_test_string;
-extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
- const char *description);
-extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
- int code, unsigned long arg);
-
-extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
-extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block);
-extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
-
-extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
-extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode);
-extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
-
-extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block);
-extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block);
-extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block);
-
-extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode);
-extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode);
-extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode);
-extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap);
-extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap);
-extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap);
-extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap);
-
-extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num);
-extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
-
-/* These two routines moved to gen_bitmap.c */
-extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
- __u32 bitno);
-extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
- blk_t bitno);
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/block.c b/e2fsprogs/old_e2fsprogs/ext2fs/block.c
deleted file mode 100644
index dbd04f8..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/block.c
+++ b/dev/null
@@ -1,437 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * block.c --- iterate over all blocks in an inode
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct block_context {
- ext2_filsys fs;
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t bcount,
- blk_t ref_blk,
- int ref_offset,
- void *priv_data);
- e2_blkcnt_t bcount;
- int bsize;
- int flags;
- errcode_t errcode;
- char *ind_buf;
- char *dind_buf;
- char *tind_buf;
- void *priv_data;
-};
-
-static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
- int ref_offset, struct block_context *ctx)
-{
- int ret = 0, changed = 0;
- int i, flags, limit, offset;
- blk_t *block_nr;
-
- limit = ctx->fs->blocksize >> 2;
- if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
- !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
- ret = (*ctx->func)(ctx->fs, ind_block,
- BLOCK_COUNT_IND, ref_block,
- ref_offset, ctx->priv_data);
- if (!*ind_block || (ret & BLOCK_ABORT)) {
- ctx->bcount += limit;
- return ret;
- }
- if (*ind_block >= ctx->fs->super->s_blocks_count ||
- *ind_block < ctx->fs->super->s_first_data_block) {
- ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
- ret |= BLOCK_ERROR;
- return ret;
- }
- ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
- ctx->ind_buf);
- if (ctx->errcode) {
- ret |= BLOCK_ERROR;
- return ret;
- }
-
- block_nr = (blk_t *) ctx->ind_buf;
- offset = 0;
- if (ctx->flags & BLOCK_FLAG_APPEND) {
- for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
- flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
- *ind_block, offset,
- ctx->priv_data);
- changed |= flags;
- if (flags & BLOCK_ABORT) {
- ret |= BLOCK_ABORT;
- break;
- }
- offset += sizeof(blk_t);
- }
- } else {
- for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
- if (*block_nr == 0)
- continue;
- flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
- *ind_block, offset,
- ctx->priv_data);
- changed |= flags;
- if (flags & BLOCK_ABORT) {
- ret |= BLOCK_ABORT;
- break;
- }
- offset += sizeof(blk_t);
- }
- }
- if (changed & BLOCK_CHANGED) {
- ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
- ctx->ind_buf);
- if (ctx->errcode)
- ret |= BLOCK_ERROR | BLOCK_ABORT;
- }
- if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
- !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
- !(ret & BLOCK_ABORT))
- ret |= (*ctx->func)(ctx->fs, ind_block,
- BLOCK_COUNT_IND, ref_block,
- ref_offset, ctx->priv_data);
- return ret;
-}
-
-static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
- int ref_offset, struct block_context *ctx)
-{
- int ret = 0, changed = 0;
- int i, flags, limit, offset;
- blk_t *block_nr;
-
- limit = ctx->fs->blocksize >> 2;
- if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
- BLOCK_FLAG_DATA_ONLY)))
- ret = (*ctx->func)(ctx->fs, dind_block,
- BLOCK_COUNT_DIND, ref_block,
- ref_offset, ctx->priv_data);
- if (!*dind_block || (ret & BLOCK_ABORT)) {
- ctx->bcount += limit*limit;
- return ret;
- }
- if (*dind_block >= ctx->fs->super->s_blocks_count ||
- *dind_block < ctx->fs->super->s_first_data_block) {
- ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
- ret |= BLOCK_ERROR;
- return ret;
- }
- ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
- ctx->dind_buf);
- if (ctx->errcode) {
- ret |= BLOCK_ERROR;
- return ret;
- }
-
- block_nr = (blk_t *) ctx->dind_buf;
- offset = 0;
- if (ctx->flags & BLOCK_FLAG_APPEND) {
- for (i = 0; i < limit; i++, block_nr++) {
- flags = block_iterate_ind(block_nr,
- *dind_block, offset,
- ctx);
- changed |= flags;
- if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
- ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
- break;
- }
- offset += sizeof(blk_t);
- }
- } else {
- for (i = 0; i < limit; i++, block_nr++) {
- if (*block_nr == 0) {
- ctx->bcount += limit;
- continue;
- }
- flags = block_iterate_ind(block_nr,
- *dind_block, offset,
- ctx);
- changed |= flags;
- if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
- ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
- break;
- }
- offset += sizeof(blk_t);
- }
- }
- if (changed & BLOCK_CHANGED) {
- ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
- ctx->dind_buf);
- if (ctx->errcode)
- ret |= BLOCK_ERROR | BLOCK_ABORT;
- }
- if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
- !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
- !(ret & BLOCK_ABORT))
- ret |= (*ctx->func)(ctx->fs, dind_block,
- BLOCK_COUNT_DIND, ref_block,
- ref_offset, ctx->priv_data);
- return ret;
-}
-
-static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
- int ref_offset, struct block_context *ctx)
-{
- int ret = 0, changed = 0;
- int i, flags, limit, offset;
- blk_t *block_nr;
-
- limit = ctx->fs->blocksize >> 2;
- if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
- BLOCK_FLAG_DATA_ONLY)))
- ret = (*ctx->func)(ctx->fs, tind_block,
- BLOCK_COUNT_TIND, ref_block,
- ref_offset, ctx->priv_data);
- if (!*tind_block || (ret & BLOCK_ABORT)) {
- ctx->bcount += limit*limit*limit;
- return ret;
- }
- if (*tind_block >= ctx->fs->super->s_blocks_count ||
- *tind_block < ctx->fs->super->s_first_data_block) {
- ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
- ret |= BLOCK_ERROR;
- return ret;
- }
- ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
- ctx->tind_buf);
- if (ctx->errcode) {
- ret |= BLOCK_ERROR;
- return ret;
- }
-
- block_nr = (blk_t *) ctx->tind_buf;
- offset = 0;
- if (ctx->flags & BLOCK_FLAG_APPEND) {
- for (i = 0; i < limit; i++, block_nr++) {
- flags = block_iterate_dind(block_nr,
- *tind_block,
- offset, ctx);
- changed |= flags;
- if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
- ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
- break;
- }
- offset += sizeof(blk_t);
- }
- } else {
- for (i = 0; i < limit; i++, block_nr++) {
- if (*block_nr == 0) {
- ctx->bcount += limit*limit;
- continue;
- }
- flags = block_iterate_dind(block_nr,
- *tind_block,
- offset, ctx);
- changed |= flags;
- if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
- ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
- break;
- }
- offset += sizeof(blk_t);
- }
- }
- if (changed & BLOCK_CHANGED) {
- ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
- ctx->tind_buf);
- if (ctx->errcode)
- ret |= BLOCK_ERROR | BLOCK_ABORT;
- }
- if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
- !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
- !(ret & BLOCK_ABORT))
- ret |= (*ctx->func)(ctx->fs, tind_block,
- BLOCK_COUNT_TIND, ref_block,
- ref_offset, ctx->priv_data);
-
- return ret;
-}
-
-errcode_t ext2fs_block_iterate2(ext2_filsys fs,
- ext2_ino_t ino,
- int flags,
- char *block_buf,
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_blk,
- int ref_offset,
- void *priv_data),
- void *priv_data)
-{
- int i;
- int got_inode = 0;
- int ret = 0;
- blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
- struct ext2_inode inode;
- errcode_t retval;
- struct block_context ctx;
- int limit;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- /*
- * Check to see if we need to limit large files
- */
- if (flags & BLOCK_FLAG_NO_LARGE) {
- ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
- if (ctx.errcode)
- return ctx.errcode;
- got_inode = 1;
- if (!LINUX_S_ISDIR(inode.i_mode) &&
- (inode.i_size_high != 0))
- return EXT2_ET_FILE_TOO_BIG;
- }
-
- retval = ext2fs_get_blocks(fs, ino, blocks);
- if (retval)
- return retval;
-
- limit = fs->blocksize >> 2;
-
- ctx.fs = fs;
- ctx.func = func;
- ctx.priv_data = priv_data;
- ctx.flags = flags;
- ctx.bcount = 0;
- if (block_buf) {
- ctx.ind_buf = block_buf;
- } else {
- retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
- if (retval)
- return retval;
- }
- ctx.dind_buf = ctx.ind_buf + fs->blocksize;
- ctx.tind_buf = ctx.dind_buf + fs->blocksize;
-
- /*
- * Iterate over the HURD translator block (if present)
- */
- if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
- !(flags & BLOCK_FLAG_DATA_ONLY)) {
- ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
- if (ctx.errcode)
- goto abort_exit;
- got_inode = 1;
- if (inode.osd1.hurd1.h_i_translator) {
- ret |= (*ctx.func)(fs,
- &inode.osd1.hurd1.h_i_translator,
- BLOCK_COUNT_TRANSLATOR,
- 0, 0, priv_data);
- if (ret & BLOCK_ABORT)
- goto abort_exit;
- }
- }
-
- /*
- * Iterate over normal data blocks
- */
- for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) {
- if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
- ret |= (*ctx.func)(fs, &blocks[i],
- ctx.bcount, 0, i, priv_data);
- if (ret & BLOCK_ABORT)
- goto abort_exit;
- }
- }
- if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
- ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
- 0, EXT2_IND_BLOCK, &ctx);
- if (ret & BLOCK_ABORT)
- goto abort_exit;
- } else
- ctx.bcount += limit;
- if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
- ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
- 0, EXT2_DIND_BLOCK, &ctx);
- if (ret & BLOCK_ABORT)
- goto abort_exit;
- } else
- ctx.bcount += limit * limit;
- if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
- ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
- 0, EXT2_TIND_BLOCK, &ctx);
- if (ret & BLOCK_ABORT)
- goto abort_exit;
- }
-
-abort_exit:
- if (ret & BLOCK_CHANGED) {
- if (!got_inode) {
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
- return retval;
- }
- for (i=0; i < EXT2_N_BLOCKS; i++)
- inode.i_block[i] = blocks[i];
- retval = ext2fs_write_inode(fs, ino, &inode);
- if (retval)
- return retval;
- }
-
- if (!block_buf)
- ext2fs_free_mem(&ctx.ind_buf);
-
- return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
-}
-
-/*
- * Emulate the old ext2fs_block_iterate function!
- */
-
-struct xlate {
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- int bcount,
- void *priv_data);
- void *real_private;
-};
-
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct xlate *xl = (struct xlate *) priv_data;
-
- return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
-}
-
-errcode_t ext2fs_block_iterate(ext2_filsys fs,
- ext2_ino_t ino,
- int flags,
- char *block_buf,
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- int blockcnt,
- void *priv_data),
- void *priv_data)
-{
- struct xlate xl;
-
- xl.real_private = priv_data;
- xl.func = func;
-
- return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
- block_buf, xlate_func, &xl);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
deleted file mode 100644
index 796b0e4..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
+++ b/dev/null
@@ -1,261 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bmap.c --- logical to physical block mapping
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- char *block_buf, int bmap_flags,
- blk_t block, blk_t *phys_blk);
-
-#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
-
-static errcode_t block_ind_bmap(ext2_filsys fs, int flags,
- blk_t ind, char *block_buf,
- int *blocks_alloc,
- blk_t nr, blk_t *ret_blk)
-{
- errcode_t retval;
- blk_t b;
-
- if (!ind) {
- if (flags & BMAP_SET)
- return EXT2_ET_SET_BMAP_NO_IND;
- *ret_blk = 0;
- return 0;
- }
- retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
- if (retval)
- return retval;
-
- if (flags & BMAP_SET) {
- b = *ret_blk;
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
- b = ext2fs_swab32(b);
-#endif
- ((blk_t *) block_buf)[nr] = b;
- return io_channel_write_blk(fs->io, ind, 1, block_buf);
- }
-
- b = ((blk_t *) block_buf)[nr];
-
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
- b = ext2fs_swab32(b);
-#endif
-
- if (!b && (flags & BMAP_ALLOC)) {
- b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
- retval = ext2fs_alloc_block(fs, b,
- block_buf + fs->blocksize, &b);
- if (retval)
- return retval;
-
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
- ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
- else
-#endif
- ((blk_t *) block_buf)[nr] = b;
-
- retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
- if (retval)
- return retval;
-
- (*blocks_alloc)++;
- }
-
- *ret_blk = b;
- return 0;
-}
-
-static errcode_t block_dind_bmap(ext2_filsys fs, int flags,
- blk_t dind, char *block_buf,
- int *blocks_alloc,
- blk_t nr, blk_t *ret_blk)
-{
- blk_t b;
- errcode_t retval;
- blk_t addr_per_block;
-
- addr_per_block = (blk_t) fs->blocksize >> 2;
-
- retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
- blocks_alloc, nr / addr_per_block, &b);
- if (retval)
- return retval;
- retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
- nr % addr_per_block, ret_blk);
- return retval;
-}
-
-static errcode_t block_tind_bmap(ext2_filsys fs, int flags,
- blk_t tind, char *block_buf,
- int *blocks_alloc,
- blk_t nr, blk_t *ret_blk)
-{
- blk_t b;
- errcode_t retval;
- blk_t addr_per_block;
-
- addr_per_block = (blk_t) fs->blocksize >> 2;
-
- retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
- blocks_alloc, nr / addr_per_block, &b);
- if (retval)
- return retval;
- retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
- nr % addr_per_block, ret_blk);
- return retval;
-}
-
-errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
- char *block_buf, int bmap_flags, blk_t block,
- blk_t *phys_blk)
-{
- struct ext2_inode inode_buf;
- blk_t addr_per_block;
- blk_t b;
- char *buf = NULL;
- errcode_t retval = 0;
- int blocks_alloc = 0, inode_dirty = 0;
-
- if (!(bmap_flags & BMAP_SET))
- *phys_blk = 0;
-
- /* Read inode structure if necessary */
- if (!inode) {
- retval = ext2fs_read_inode(fs, ino, &inode_buf);
- if (retval)
- return retval;
- inode = &inode_buf;
- }
- addr_per_block = (blk_t) fs->blocksize >> 2;
-
- if (!block_buf) {
- retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
- if (retval)
- return retval;
- block_buf = buf;
- }
-
- if (block < EXT2_NDIR_BLOCKS) {
- if (bmap_flags & BMAP_SET) {
- b = *phys_blk;
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
- b = ext2fs_swab32(b);
-#endif
- inode_bmap(inode, block) = b;
- inode_dirty++;
- goto done;
- }
-
- *phys_blk = inode_bmap(inode, block);
- b = block ? inode_bmap(inode, block-1) : 0;
-
- if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
- retval = ext2fs_alloc_block(fs, b, block_buf, &b);
- if (retval)
- goto done;
- inode_bmap(inode, block) = b;
- blocks_alloc++;
- *phys_blk = b;
- }
- goto done;
- }
-
- /* Indirect block */
- block -= EXT2_NDIR_BLOCKS;
- if (block < addr_per_block) {
- b = inode_bmap(inode, EXT2_IND_BLOCK);
- if (!b) {
- if (!(bmap_flags & BMAP_ALLOC)) {
- if (bmap_flags & BMAP_SET)
- retval = EXT2_ET_SET_BMAP_NO_IND;
- goto done;
- }
-
- b = inode_bmap(inode, EXT2_IND_BLOCK-1);
- retval = ext2fs_alloc_block(fs, b, block_buf, &b);
- if (retval)
- goto done;
- inode_bmap(inode, EXT2_IND_BLOCK) = b;
- blocks_alloc++;
- }
- retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
- &blocks_alloc, block, phys_blk);
- goto done;
- }
-
- /* Doubly indirect block */
- block -= addr_per_block;
- if (block < addr_per_block * addr_per_block) {
- b = inode_bmap(inode, EXT2_DIND_BLOCK);
- if (!b) {
- if (!(bmap_flags & BMAP_ALLOC)) {
- if (bmap_flags & BMAP_SET)
- retval = EXT2_ET_SET_BMAP_NO_IND;
- goto done;
- }
-
- b = inode_bmap(inode, EXT2_IND_BLOCK);
- retval = ext2fs_alloc_block(fs, b, block_buf, &b);
- if (retval)
- goto done;
- inode_bmap(inode, EXT2_DIND_BLOCK) = b;
- blocks_alloc++;
- }
- retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
- &blocks_alloc, block, phys_blk);
- goto done;
- }
-
- /* Triply indirect block */
- block -= addr_per_block * addr_per_block;
- b = inode_bmap(inode, EXT2_TIND_BLOCK);
- if (!b) {
- if (!(bmap_flags & BMAP_ALLOC)) {
- if (bmap_flags & BMAP_SET)
- retval = EXT2_ET_SET_BMAP_NO_IND;
- goto done;
- }
-
- b = inode_bmap(inode, EXT2_DIND_BLOCK);
- retval = ext2fs_alloc_block(fs, b, block_buf, &b);
- if (retval)
- goto done;
- inode_bmap(inode, EXT2_TIND_BLOCK) = b;
- blocks_alloc++;
- }
- retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
- &blocks_alloc, block, phys_blk);
-done:
- ext2fs_free_mem(&buf);
- if ((retval == 0) && (blocks_alloc || inode_dirty)) {
- inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
- retval = ext2fs_write_inode(fs, ino, inode);
- }
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c b/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
deleted file mode 100644
index ec9244d..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
+++ b/dev/null
@@ -1,155 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * bmove.c --- Move blocks around to make way for a particular
- * filesystem structure.
- *
- * Copyright (C) 1997 Theodore Ts'o. This file may be redistributed
- * under the terms of the GNU Public License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-struct process_block_struct {
- ext2_ino_t ino;
- struct ext2_inode * inode;
- ext2fs_block_bitmap reserve;
- ext2fs_block_bitmap alloc_map;
- errcode_t error;
- char *buf;
- int add_dir;
- int flags;
-};
-
-static int process_block(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt, blk_t ref_block,
- int ref_offset, void *priv_data)
-{
- struct process_block_struct *pb;
- errcode_t retval;
- int ret;
- blk_t block, orig;
-
- pb = (struct process_block_struct *) priv_data;
- block = orig = *block_nr;
- ret = 0;
-
- /*
- * Let's see if this is one which we need to relocate
- */
- if (ext2fs_test_block_bitmap(pb->reserve, block)) {
- do {
- if (++block >= fs->super->s_blocks_count)
- block = fs->super->s_first_data_block;
- if (block == orig) {
- pb->error = EXT2_ET_BLOCK_ALLOC_FAIL;
- return BLOCK_ABORT;
- }
- } while (ext2fs_test_block_bitmap(pb->reserve, block) ||
- ext2fs_test_block_bitmap(pb->alloc_map, block));
-
- retval = io_channel_read_blk(fs->io, orig, 1, pb->buf);
- if (retval) {
- pb->error = retval;
- return BLOCK_ABORT;
- }
- retval = io_channel_write_blk(fs->io, block, 1, pb->buf);
- if (retval) {
- pb->error = retval;
- return BLOCK_ABORT;
- }
- *block_nr = block;
- ext2fs_mark_block_bitmap(pb->alloc_map, block);
- ret = BLOCK_CHANGED;
- if (pb->flags & EXT2_BMOVE_DEBUG)
- printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino,
- blockcnt, orig, block);
- }
- if (pb->add_dir) {
- retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
- block, (int) blockcnt);
- if (retval) {
- pb->error = retval;
- ret |= BLOCK_ABORT;
- }
- }
- return ret;
-}
-
-errcode_t ext2fs_move_blocks(ext2_filsys fs,
- ext2fs_block_bitmap reserve,
- ext2fs_block_bitmap alloc_map,
- int flags)
-{
- ext2_ino_t ino;
- struct ext2_inode inode;
- errcode_t retval;
- struct process_block_struct pb;
- ext2_inode_scan scan;
- char *block_buf;
-
- retval = ext2fs_open_inode_scan(fs, 0, &scan);
- if (retval)
- return retval;
-
- pb.reserve = reserve;
- pb.error = 0;
- pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
- pb.flags = flags;
-
- retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf);
- if (retval)
- return retval;
- pb.buf = block_buf + fs->blocksize * 3;
-
- /*
- * If GET_DBLIST is set in the flags field, then we should
- * gather directory block information while we're doing the
- * block move.
- */
- if (flags & EXT2_BMOVE_GET_DBLIST) {
- ext2fs_free_dblist(fs->dblist);
- fs->dblist = NULL;
- retval = ext2fs_init_dblist(fs, 0);
- if (retval)
- return retval;
- }
-
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
- if (retval)
- return retval;
-
- while (ino) {
- if ((inode.i_links_count == 0) ||
- !ext2fs_inode_has_valid_blocks(&inode))
- goto next;
-
- pb.ino = ino;
- pb.inode = &inode;
-
- pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
- flags & EXT2_BMOVE_GET_DBLIST);
-
- retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
- process_block, &pb);
- if (retval)
- return retval;
- if (pb.error)
- return pb.error;
-
- next:
- retval = ext2fs_get_next_inode(scan, &ino, &inode);
- if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
- goto next;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/brel.h b/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
deleted file mode 100644
index 87bf72b..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
+++ b/dev/null
@@ -1,86 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * brel.h
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-struct ext2_block_relocate_entry {
- blk_t new;
- __s16 offset;
- __u16 flags;
- union {
- blk_t block_ref;
- ext2_ino_t inode_ref;
- } owner;
-};
-
-#define RELOCATE_TYPE_REF 0x0007
-#define RELOCATE_BLOCK_REF 0x0001
-#define RELOCATE_INODE_REF 0x0002
-
-typedef struct ext2_block_relocation_table *ext2_brel;
-
-struct ext2_block_relocation_table {
- __u32 magic;
- char *name;
- blk_t current;
- void *priv_data;
-
- /*
- * Add a block relocation entry.
- */
- errcode_t (*put)(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent);
-
- /*
- * Get a block relocation entry.
- */
- errcode_t (*get)(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent);
-
- /*
- * Initialize for iterating over the block relocation entries.
- */
- errcode_t (*start_iter)(ext2_brel brel);
-
- /*
- * The iterator function for the inode relocation entries.
- * Returns an inode number of 0 when out of entries.
- */
- errcode_t (*next)(ext2_brel brel, blk_t *old,
- struct ext2_block_relocate_entry *ent);
-
- /*
- * Move the inode relocation table from one block number to
- * another.
- */
- errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new);
-
- /*
- * Remove a block relocation entry.
- */
- errcode_t (*delete)(ext2_brel brel, blk_t old);
-
-
- /*
- * Free the block relocation table.
- */
- errcode_t (*free)(ext2_brel brel);
-};
-
-errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
- ext2_brel *brel);
-
-#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent))
-#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent))
-#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel)))
-#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent))
-#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new))
-#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old))
-#define ext2fs_brel_free(brel) ((brel)->free((brel)))
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c b/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
deleted file mode 100644
index 652a350..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
+++ b/dev/null
@@ -1,196 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * brel_ma.c
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * TODO: rewrite to not use a direct array!!! (Fortunately this
- * module isn't really used yet.)
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-#include "brel.h"
-
-static errcode_t bma_put(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent);
-static errcode_t bma_get(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent);
-static errcode_t bma_start_iter(ext2_brel brel);
-static errcode_t bma_next(ext2_brel brel, blk_t *old,
- struct ext2_block_relocate_entry *ent);
-static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new);
-static errcode_t bma_delete(ext2_brel brel, blk_t old);
-static errcode_t bma_free(ext2_brel brel);
-
-struct brel_ma {
- __u32 magic;
- blk_t max_block;
- struct ext2_block_relocate_entry *entries;
-};
-
-errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
- ext2_brel *new_brel)
-{
- ext2_brel brel = 0;
- errcode_t retval;
- struct brel_ma *ma = 0;
- size_t size;
-
- *new_brel = 0;
-
- /*
- * Allocate memory structures
- */
- retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table),
- &brel);
- if (retval)
- goto errout;
- memset(brel, 0, sizeof(struct ext2_block_relocation_table));
-
- retval = ext2fs_get_mem(strlen(name)+1, &brel->name);
- if (retval)
- goto errout;
- strcpy(brel->name, name);
-
- retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma);
- if (retval)
- goto errout;
- memset(ma, 0, sizeof(struct brel_ma));
- brel->priv_data = ma;
-
- size = (size_t) (sizeof(struct ext2_block_relocate_entry) *
- (max_block+1));
- retval = ext2fs_get_mem(size, &ma->entries);
- if (retval)
- goto errout;
- memset(ma->entries, 0, size);
- ma->max_block = max_block;
-
- /*
- * Fill in the brel data structure
- */
- brel->put = bma_put;
- brel->get = bma_get;
- brel->start_iter = bma_start_iter;
- brel->next = bma_next;
- brel->move = bma_move;
- brel->delete = bma_delete;
- brel->free = bma_free;
-
- *new_brel = brel;
- return 0;
-
-errout:
- bma_free(brel);
- return retval;
-}
-
-static errcode_t bma_put(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent)
-{
- struct brel_ma *ma;
-
- ma = brel->priv_data;
- if (old > ma->max_block)
- return EXT2_ET_INVALID_ARGUMENT;
- ma->entries[(unsigned)old] = *ent;
- return 0;
-}
-
-static errcode_t bma_get(ext2_brel brel, blk_t old,
- struct ext2_block_relocate_entry *ent)
-{
- struct brel_ma *ma;
-
- ma = brel->priv_data;
- if (old > ma->max_block)
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned)old].new == 0)
- return ENOENT;
- *ent = ma->entries[old];
- return 0;
-}
-
-static errcode_t bma_start_iter(ext2_brel brel)
-{
- brel->current = 0;
- return 0;
-}
-
-static errcode_t bma_next(ext2_brel brel, blk_t *old,
- struct ext2_block_relocate_entry *ent)
-{
- struct brel_ma *ma;
-
- ma = brel->priv_data;
- while (++brel->current < ma->max_block) {
- if (ma->entries[(unsigned)brel->current].new == 0)
- continue;
- *old = brel->current;
- *ent = ma->entries[(unsigned)brel->current];
- return 0;
- }
- *old = 0;
- return 0;
-}
-
-static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
-{
- struct brel_ma *ma;
-
- ma = brel->priv_data;
- if ((old > ma->max_block) || (new > ma->max_block))
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned)old].new == 0)
- return ENOENT;
- ma->entries[(unsigned)new] = ma->entries[old];
- ma->entries[(unsigned)old].new = 0;
- return 0;
-}
-
-static errcode_t bma_delete(ext2_brel brel, blk_t old)
-{
- struct brel_ma *ma;
-
- ma = brel->priv_data;
- if (old > ma->max_block)
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned)old].new == 0)
- return ENOENT;
- ma->entries[(unsigned)old].new = 0;
- return 0;
-}
-
-static errcode_t bma_free(ext2_brel brel)
-{
- struct brel_ma *ma;
-
- if (!brel)
- return 0;
-
- ma = brel->priv_data;
-
- if (ma) {
- ext2fs_free_mem(&ma->entries);
- ext2fs_free_mem(&ma);
- }
- ext2fs_free_mem(&brel->name);
- ext2fs_free_mem(&brel);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c b/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
deleted file mode 100644
index dd4b0e9..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
+++ b/dev/null
@@ -1,69 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * check_desc.c --- Check the group descriptors of an ext2 filesystem
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * This routine sanity checks the group descriptors
- */
-errcode_t ext2fs_check_desc(ext2_filsys fs)
-{
- dgrp_t i;
- blk_t block = fs->super->s_first_data_block;
- blk_t next;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- for (i = 0; i < fs->group_desc_count; i++) {
- next = block + fs->super->s_blocks_per_group;
- /*
- * Check to make sure block bitmap for group is
- * located within the group.
- */
- if (fs->group_desc[i].bg_block_bitmap < block ||
- fs->group_desc[i].bg_block_bitmap >= next)
- return EXT2_ET_GDESC_BAD_BLOCK_MAP;
- /*
- * Check to make sure inode bitmap for group is
- * located within the group
- */
- if (fs->group_desc[i].bg_inode_bitmap < block ||
- fs->group_desc[i].bg_inode_bitmap >= next)
- return EXT2_ET_GDESC_BAD_INODE_MAP;
- /*
- * Check to make sure inode table for group is located
- * within the group
- */
- if (fs->group_desc[i].bg_inode_table < block ||
- ((fs->group_desc[i].bg_inode_table +
- fs->inode_blocks_per_group) >= next))
- return EXT2_ET_GDESC_BAD_INODE_TABLE;
-
- block = next;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c b/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
deleted file mode 100644
index bfa15e2..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
+++ b/dev/null
@@ -1,380 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * closefs.c --- close an ext2 filesystem
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <time.h>
-#include <string.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static int test_root(int a, int b)
-{
- if (a == 0)
- return 1;
- while (1) {
- if (a == 1)
- return 1;
- if (a % b)
- return 0;
- a = a / b;
- }
-}
-
-int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
-{
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
- return 1;
-
- if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
- test_root(group_block, 7))
- return 1;
-
- return 0;
-}
-
-int ext2fs_super_and_bgd_loc(ext2_filsys fs,
- dgrp_t group,
- blk_t *ret_super_blk,
- blk_t *ret_old_desc_blk,
- blk_t *ret_new_desc_blk,
- int *ret_meta_bg)
-{
- blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0;
- unsigned int meta_bg, meta_bg_size;
- int numblocks, has_super;
- int old_desc_blocks;
-
- group_block = fs->super->s_first_data_block +
- (group * fs->super->s_blocks_per_group);
-
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- old_desc_blocks = fs->super->s_first_meta_bg;
- else
- old_desc_blocks =
- fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
-
- if (group == fs->group_desc_count-1) {
- numblocks = (fs->super->s_blocks_count -
- fs->super->s_first_data_block) %
- fs->super->s_blocks_per_group;
- if (!numblocks)
- numblocks = fs->super->s_blocks_per_group;
- } else
- numblocks = fs->super->s_blocks_per_group;
-
- has_super = ext2fs_bg_has_super(fs, group);
-
- if (has_super) {
- super_blk = group_block;
- numblocks--;
- }
- meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
- meta_bg = group / meta_bg_size;
-
- if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
- (meta_bg < fs->super->s_first_meta_bg)) {
- if (has_super) {
- old_desc_blk = group_block + 1;
- numblocks -= old_desc_blocks;
- }
- } else {
- if (((group % meta_bg_size) == 0) ||
- ((group % meta_bg_size) == 1) ||
- ((group % meta_bg_size) == (meta_bg_size-1))) {
- if (has_super)
- has_super = 1;
- new_desc_blk = group_block + has_super;
- numblocks--;
- }
- }
-
- numblocks -= 2 + fs->inode_blocks_per_group;
-
- if (ret_super_blk)
- *ret_super_blk = super_blk;
- if (ret_old_desc_blk)
- *ret_old_desc_blk = old_desc_blk;
- if (ret_new_desc_blk)
- *ret_new_desc_blk = new_desc_blk;
- if (ret_meta_bg)
- *ret_meta_bg = meta_bg;
- return numblocks;
-}
-
-
-/*
- * This function forces out the primary superblock. We need to only
- * write out those fields which we have changed, since if the
- * filesystem is mounted, it may have changed some of the other
- * fields.
- *
- * It takes as input a superblock which has already been byte swapped
- * (if necessary).
- *
- */
-static errcode_t write_primary_superblock(ext2_filsys fs,
- struct ext2_super_block *super)
-{
- __u16 *old_super, *new_super;
- int check_idx, write_idx, size;
- errcode_t retval;
-
- if (!fs->io->manager->write_byte || !fs->orig_super) {
- io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
- retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
- super);
- io_channel_set_blksize(fs->io, fs->blocksize);
- return retval;
- }
-
- old_super = (__u16 *) fs->orig_super;
- new_super = (__u16 *) super;
-
- for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
- if (old_super[check_idx] == new_super[check_idx])
- continue;
- write_idx = check_idx;
- for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
- if (old_super[check_idx] == new_super[check_idx])
- break;
- size = 2 * (check_idx - write_idx);
- retval = io_channel_write_byte(fs->io,
- SUPERBLOCK_OFFSET + (2 * write_idx), size,
- new_super + write_idx);
- if (retval)
- return retval;
- }
- memcpy(fs->orig_super, super, SUPERBLOCK_SIZE);
- return 0;
-}
-
-
-/*
- * Updates the revision to EXT2_DYNAMIC_REV
- */
-void ext2fs_update_dynamic_rev(ext2_filsys fs)
-{
- struct ext2_super_block *sb = fs->super;
-
- if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
- return;
-
- sb->s_rev_level = EXT2_DYNAMIC_REV;
- sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
- sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
- /* s_uuid is handled by e2fsck already */
- /* other fields should be left alone */
-}
-
-static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
- blk_t group_block,
- struct ext2_super_block *super_shadow)
-{
- dgrp_t sgrp = group;
-
- if (sgrp > ((1 << 16) - 1))
- sgrp = (1 << 16) - 1;
-#if BB_BIG_ENDIAN
- if (fs->flags & EXT2_FLAG_SWAP_BYTES)
- super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
- else
-#endif
- fs->super->s_block_group_nr = sgrp;
-
- return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE,
- super_shadow);
-}
-
-
-errcode_t ext2fs_flush(ext2_filsys fs)
-{
- dgrp_t i;
- blk_t group_block;
- errcode_t retval;
- unsigned long fs_state;
- struct ext2_super_block *super_shadow = NULL;
- struct ext2_group_desc *group_shadow = NULL;
- char *group_ptr;
- int old_desc_blocks;
-#if BB_BIG_ENDIAN
- dgrp_t j;
- struct ext2_group_desc *s, *t;
-#endif
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- fs_state = fs->super->s_state;
-
- fs->super->s_wtime = time(NULL);
- fs->super->s_block_group_nr = 0;
-#if BB_BIG_ENDIAN
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- retval = EXT2_ET_NO_MEMORY;
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
- if (retval)
- goto errout;
- retval = ext2fs_get_mem((size_t)(fs->blocksize *
- fs->desc_blocks),
- &group_shadow);
- if (retval)
- goto errout;
- memset(group_shadow, 0, (size_t) fs->blocksize *
- fs->desc_blocks);
-
- /* swap the group descriptors */
- for (j=0, s=fs->group_desc, t=group_shadow;
- j < fs->group_desc_count; j++, t++, s++) {
- *t = *s;
- ext2fs_swap_group_desc(t);
- }
- } else {
- super_shadow = fs->super;
- group_shadow = fs->group_desc;
- }
-#else
- super_shadow = fs->super;
- group_shadow = fs->group_desc;
-#endif
-
- /*
- * If this is an external journal device, don't write out the
- * block group descriptors or any of the backup superblocks
- */
- if (fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
- goto write_primary_superblock_only;
-
- /*
- * Set the state of the FS to be non-valid. (The state has
- * already been backed up earlier, and will be restored after
- * we write out the backup superblocks.)
- */
- fs->super->s_state &= ~EXT2_VALID_FS;
-#if BB_BIG_ENDIAN
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- *super_shadow = *fs->super;
- ext2fs_swap_super(super_shadow);
- }
-#endif
-
- /*
- * Write out the master group descriptors, and the backup
- * superblocks and group descriptors.
- */
- group_block = fs->super->s_first_data_block;
- group_ptr = (char *) group_shadow;
- if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
- old_desc_blocks = fs->super->s_first_meta_bg;
- else
- old_desc_blocks = fs->desc_blocks;
-
- for (i = 0; i < fs->group_desc_count; i++) {
- blk_t super_blk, old_desc_blk, new_desc_blk;
- int meta_bg;
-
- ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk,
- &new_desc_blk, &meta_bg);
-
- if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) {
- retval = write_backup_super(fs, i, super_blk,
- super_shadow);
- if (retval)
- goto errout;
- }
- if (fs->flags & EXT2_FLAG_SUPER_ONLY)
- continue;
- if ((old_desc_blk) &&
- (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) {
- retval = io_channel_write_blk(fs->io,
- old_desc_blk, old_desc_blocks, group_ptr);
- if (retval)
- goto errout;
- }
- if (new_desc_blk) {
- retval = io_channel_write_blk(fs->io, new_desc_blk,
- 1, group_ptr + (meta_bg*fs->blocksize));
- if (retval)
- goto errout;
- }
- }
- fs->super->s_block_group_nr = 0;
- fs->super->s_state = fs_state;
-#if BB_BIG_ENDIAN
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- *super_shadow = *fs->super;
- ext2fs_swap_super(super_shadow);
- }
-#endif
-
- /*
- * If the write_bitmaps() function is present, call it to
- * flush the bitmaps. This is done this way so that a simple
- * program that doesn't mess with the bitmaps doesn't need to
- * drag in the bitmaps.c code.
- */
- if (fs->write_bitmaps) {
- retval = fs->write_bitmaps(fs);
- if (retval)
- goto errout;
- }
-
-write_primary_superblock_only:
- /*
- * Write out master superblock. This has to be done
- * separately, since it is located at a fixed location
- * (SUPERBLOCK_OFFSET). We flush all other pending changes
- * out to disk first, just to avoid a race condition with an
- * insy-tinsy window....
- */
- retval = io_channel_flush(fs->io);
- retval = write_primary_superblock(fs, super_shadow);
- if (retval)
- goto errout;
-
- fs->flags &= ~EXT2_FLAG_DIRTY;
-
- retval = io_channel_flush(fs->io);
-errout:
- fs->super->s_state = fs_state;
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- if (super_shadow)
- ext2fs_free_mem(&super_shadow);
- if (group_shadow)
- ext2fs_free_mem(&group_shadow);
- }
- return retval;
-}
-
-errcode_t ext2fs_close(ext2_filsys fs)
-{
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (fs->flags & EXT2_FLAG_DIRTY) {
- retval = ext2fs_flush(fs);
- if (retval)
- return retval;
- }
- if (fs->write_bitmaps) {
- retval = fs->write_bitmaps(fs);
- if (retval)
- return retval;
- }
- ext2fs_free(fs);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
deleted file mode 100644
index 7f78ff8..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
+++ b/dev/null
@@ -1,72 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * cmp_bitmaps.c --- routines to compare inode and block bitmaps.
- *
- * Copyright (C) 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
- ext2fs_block_bitmap bm2)
-{
- blk_t i;
-
- EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP);
- EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP);
-
- if ((bm1->start != bm2->start) ||
- (bm1->end != bm2->end) ||
- (memcmp(bm1->bitmap, bm2->bitmap,
- (size_t) (bm1->end - bm1->start)/8)))
- return EXT2_ET_NEQ_BLOCK_BITMAP;
-
- for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
- if (ext2fs_fast_test_block_bitmap(bm1, i) !=
- ext2fs_fast_test_block_bitmap(bm2, i))
- return EXT2_ET_NEQ_BLOCK_BITMAP;
-
- return 0;
-}
-
-errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
- ext2fs_inode_bitmap bm2)
-{
- ext2_ino_t i;
-
- EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP);
- EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP);
-
- if ((bm1->start != bm2->start) ||
- (bm1->end != bm2->end) ||
- (memcmp(bm1->bitmap, bm2->bitmap,
- (size_t) (bm1->end - bm1->start)/8)))
- return EXT2_ET_NEQ_INODE_BITMAP;
-
- for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
- if (ext2fs_fast_test_inode_bitmap(bm1, i) !=
- ext2fs_fast_test_inode_bitmap(bm2, i))
- return EXT2_ET_NEQ_INODE_BITMAP;
-
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c b/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
deleted file mode 100644
index 06ff6d8..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
+++ b/dev/null
@@ -1,260 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dblist.c -- directory block list functions
- *
- * Copyright 1997 by Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static int dir_block_cmp(const void *a, const void *b);
-
-/*
- * Returns the number of directories in the filesystem as reported by
- * the group descriptors. Of course, the group descriptors could be
- * wrong!
- */
-errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
-{
- dgrp_t i;
- ext2_ino_t num_dirs, max_dirs;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- num_dirs = 0;
- max_dirs = fs->super->s_inodes_per_group;
- for (i = 0; i < fs->group_desc_count; i++) {
- if (fs->group_desc[i].bg_used_dirs_count > max_dirs)
- num_dirs += max_dirs / 8;
- else
- num_dirs += fs->group_desc[i].bg_used_dirs_count;
- }
- if (num_dirs > fs->super->s_inodes_count)
- num_dirs = fs->super->s_inodes_count;
-
- *ret_num_dirs = num_dirs;
-
- return 0;
-}
-
-/*
- * helper function for making a new directory block list (for
- * initialize and copy).
- */
-static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count,
- struct ext2_db_entry *list,
- ext2_dblist *ret_dblist)
-{
- ext2_dblist dblist;
- errcode_t retval;
- size_t len;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if ((ret_dblist == 0) && fs->dblist &&
- (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST))
- return 0;
-
- retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist);
- if (retval)
- return retval;
- memset(dblist, 0, sizeof(struct ext2_struct_dblist));
-
- dblist->magic = EXT2_ET_MAGIC_DBLIST;
- dblist->fs = fs;
- if (size)
- dblist->size = size;
- else {
- retval = ext2fs_get_num_dirs(fs, &dblist->size);
- if (retval)
- goto cleanup;
- dblist->size = (dblist->size * 2) + 12;
- }
- len = (size_t) sizeof(struct ext2_db_entry) * dblist->size;
- dblist->count = count;
- retval = ext2fs_get_mem(len, &dblist->list);
- if (retval)
- goto cleanup;
-
- if (list)
- memcpy(dblist->list, list, len);
- else
- memset(dblist->list, 0, len);
- if (ret_dblist)
- *ret_dblist = dblist;
- else
- fs->dblist = dblist;
- return 0;
-cleanup:
- ext2fs_free_mem(&dblist);
- return retval;
-}
-
-/*
- * Initialize a directory block list
- */
-errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist)
-{
- ext2_dblist dblist;
- errcode_t retval;
-
- retval = make_dblist(fs, 0, 0, 0, &dblist);
- if (retval)
- return retval;
-
- dblist->sorted = 1;
- if (ret_dblist)
- *ret_dblist = dblist;
- else
- fs->dblist = dblist;
-
- return 0;
-}
-
-/*
- * Copy a directory block list
- */
-errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
-{
- ext2_dblist dblist;
- errcode_t retval;
-
- retval = make_dblist(src->fs, src->size, src->count, src->list,
- &dblist);
- if (retval)
- return retval;
- dblist->sorted = src->sorted;
- *dest = dblist;
- return 0;
-}
-
-/*
- * Close a directory block list
- *
- * (moved to closefs.c)
- */
-
-
-/*
- * Add a directory block to the directory block list
- */
-errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
- int blockcnt)
-{
- struct ext2_db_entry *new_entry;
- errcode_t retval;
- unsigned long old_size;
-
- EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
-
- if (dblist->count >= dblist->size) {
- old_size = dblist->size * sizeof(struct ext2_db_entry);
- dblist->size += 100;
- retval = ext2fs_resize_mem(old_size, (size_t) dblist->size *
- sizeof(struct ext2_db_entry),
- &dblist->list);
- if (retval) {
- dblist->size -= 100;
- return retval;
- }
- }
- new_entry = dblist->list + ( (int) dblist->count++);
- new_entry->blk = blk;
- new_entry->ino = ino;
- new_entry->blockcnt = blockcnt;
-
- dblist->sorted = 0;
-
- return 0;
-}
-
-/*
- * Change the directory block to the directory block list
- */
-errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
- int blockcnt)
-{
- dgrp_t i;
-
- EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
-
- for (i=0; i < dblist->count; i++) {
- if ((dblist->list[i].ino != ino) ||
- (dblist->list[i].blockcnt != blockcnt))
- continue;
- dblist->list[i].blk = blk;
- dblist->sorted = 0;
- return 0;
- }
- return EXT2_ET_DB_NOT_FOUND;
-}
-
-void ext2fs_dblist_sort(ext2_dblist dblist,
- int (*sortfunc)(const void *,
- const void *))
-{
- if (!sortfunc)
- sortfunc = dir_block_cmp;
- qsort(dblist->list, (size_t) dblist->count,
- sizeof(struct ext2_db_entry), sortfunc);
- dblist->sorted = 1;
-}
-
-/*
- * This function iterates over the directory block list
- */
-errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
- int (*func)(ext2_filsys fs,
- struct ext2_db_entry *db_info,
- void *priv_data),
- void *priv_data)
-{
- ext2_ino_t i;
- int ret;
-
- EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
-
- if (!dblist->sorted)
- ext2fs_dblist_sort(dblist, 0);
- for (i=0; i < dblist->count; i++) {
- ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data);
- if (ret & DBLIST_ABORT)
- return 0;
- }
- return 0;
-}
-
-static int dir_block_cmp(const void *a, const void *b)
-{
- const struct ext2_db_entry *db_a =
- (const struct ext2_db_entry *) a;
- const struct ext2_db_entry *db_b =
- (const struct ext2_db_entry *) b;
-
- if (db_a->blk != db_b->blk)
- return (int) (db_a->blk - db_b->blk);
-
- if (db_a->ino != db_b->ino)
- return (int) (db_a->ino - db_b->ino);
-
- return (int) (db_a->blockcnt - db_b->blockcnt);
-}
-
-int ext2fs_dblist_count(ext2_dblist dblist)
-{
- return (int) dblist->count;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c b/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
deleted file mode 100644
index b239204..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
+++ b/dev/null
@@ -1,76 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dblist_dir.c --- iterate by directory entry
- *
- * Copyright 1997 by Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
- void *priv_data);
-
-errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
- int flags,
- char *block_buf,
- int (*func)(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data)
-{
- errcode_t retval;
- struct dir_context ctx;
-
- EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
-
- ctx.dir = 0;
- ctx.flags = flags;
- if (block_buf)
- ctx.buf = block_buf;
- else {
- retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf);
- if (retval)
- return retval;
- }
- ctx.func = func;
- ctx.priv_data = priv_data;
- ctx.errcode = 0;
-
- retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx);
-
- if (!block_buf)
- ext2fs_free_mem(&ctx.buf);
- if (retval)
- return retval;
- return ctx.errcode;
-}
-
-static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
- void *priv_data)
-{
- struct dir_context *ctx;
-
- ctx = (struct dir_context *) priv_data;
- ctx->dir = db_info->ino;
-
- return ext2fs_process_dir_block(fs, &db_info->blk,
- db_info->blockcnt, 0, 0, priv_data);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c b/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
deleted file mode 100644
index eb5dae0..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
+++ b/dev/null
@@ -1,219 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dir_iterate.c --- ext2fs directory iteration operations
- *
- * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-/*
- * This function checks to see whether or not a potential deleted
- * directory entry looks valid. What we do is check the deleted entry
- * and each successive entry to make sure that they all look valid and
- * that the last deleted entry ends at the beginning of the next
- * undeleted entry. Returns 1 if the deleted entry looks valid, zero
- * if not valid.
- */
-static int ext2fs_validate_entry(char *buf, int offset, int final_offset)
-{
- struct ext2_dir_entry *dirent;
-
- while (offset < final_offset) {
- dirent = (struct ext2_dir_entry *)(buf + offset);
- offset += dirent->rec_len;
- if ((dirent->rec_len < 8) ||
- ((dirent->rec_len % 4) != 0) ||
- (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
- return 0;
- }
- return (offset == final_offset);
-}
-
-errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
- ext2_ino_t dir,
- int flags,
- char *block_buf,
- int (*func)(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data)
-{
- struct dir_context ctx;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_check_directory(fs, dir);
- if (retval)
- return retval;
-
- ctx.dir = dir;
- ctx.flags = flags;
- if (block_buf)
- ctx.buf = block_buf;
- else {
- retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
- if (retval)
- return retval;
- }
- ctx.func = func;
- ctx.priv_data = priv_data;
- ctx.errcode = 0;
- retval = ext2fs_block_iterate2(fs, dir, 0, 0,
- ext2fs_process_dir_block, &ctx);
- if (!block_buf)
- ext2fs_free_mem(&ctx.buf);
- if (retval)
- return retval;
- return ctx.errcode;
-}
-
-struct xlate {
- int (*func)(struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data);
- void *real_private;
-};
-
-static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)),
- int entry EXT2FS_ATTR((unused)),
- struct ext2_dir_entry *dirent, int offset,
- int blocksize, char *buf, void *priv_data)
-{
- struct xlate *xl = (struct xlate *) priv_data;
-
- return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
-}
-
-extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
- ext2_ino_t dir,
- int flags,
- char *block_buf,
- int (*func)(struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data)
-{
- struct xlate xl;
-
- xl.real_private = priv_data;
- xl.func = func;
-
- return ext2fs_dir_iterate2(fs, dir, flags, block_buf,
- xlate_func, &xl);
-}
-
-
-/*
- * Helper function which is private to this module. Used by
- * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
- */
-int ext2fs_process_dir_block(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct dir_context *ctx = (struct dir_context *) priv_data;
- unsigned int offset = 0;
- unsigned int next_real_entry = 0;
- int ret = 0;
- int changed = 0;
- int do_abort = 0;
- int entry, size;
- struct ext2_dir_entry *dirent;
-
- if (blockcnt < 0)
- return 0;
-
- entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
-
- ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
- if (ctx->errcode)
- return BLOCK_ABORT;
-
- while (offset < fs->blocksize) {
- dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
- if (((offset + dirent->rec_len) > fs->blocksize) ||
- (dirent->rec_len < 8) ||
- ((dirent->rec_len % 4) != 0) ||
- (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
- ctx->errcode = EXT2_ET_DIR_CORRUPTED;
- return BLOCK_ABORT;
- }
- if (!dirent->inode &&
- !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
- goto next;
-
- ret = (ctx->func)(ctx->dir,
- (next_real_entry > offset) ?
- DIRENT_DELETED_FILE : entry,
- dirent, offset,
- fs->blocksize, ctx->buf,
- ctx->priv_data);
- if (entry < DIRENT_OTHER_FILE)
- entry++;
-
- if (ret & DIRENT_CHANGED)
- changed++;
- if (ret & DIRENT_ABORT) {
- do_abort++;
- break;
- }
-next:
- if (next_real_entry == offset)
- next_real_entry += dirent->rec_len;
-
- if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
- size = ((dirent->name_len & 0xFF) + 11) & ~3;
-
- if (dirent->rec_len != size) {
- unsigned int final_offset;
-
- final_offset = offset + dirent->rec_len;
- offset += size;
- while (offset < final_offset &&
- !ext2fs_validate_entry(ctx->buf,
- offset,
- final_offset))
- offset += 4;
- continue;
- }
- }
- offset += dirent->rec_len;
- }
-
- if (changed) {
- ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
- if (ctx->errcode)
- return BLOCK_ABORT;
- }
- if (do_abort)
- return BLOCK_ABORT;
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c b/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
deleted file mode 100644
index f9c5a10..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
+++ b/dev/null
@@ -1,132 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dirblock.c --- directory block routines.
- *
- * Copyright (C) 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
- void *buf, int flags EXT2FS_ATTR((unused)))
-{
- errcode_t retval;
- char *p, *end;
- struct ext2_dir_entry *dirent;
- unsigned int name_len, rec_len;
-#if BB_BIG_ENDIAN
- unsigned int do_swap;
-#endif
-
- retval = io_channel_read_blk(fs->io, block, 1, buf);
- if (retval)
- return retval;
-#if BB_BIG_ENDIAN
- do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES|
- EXT2_FLAG_SWAP_BYTES_READ)) != 0;
-#endif
- p = (char *) buf;
- end = (char *) buf + fs->blocksize;
- while (p < end-8) {
- dirent = (struct ext2_dir_entry *) p;
-#if BB_BIG_ENDIAN
- if (do_swap) {
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
- }
-#endif
- name_len = dirent->name_len;
-#ifdef WORDS_BIGENDIAN
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
- rec_len = dirent->rec_len;
- if ((rec_len < 8) || (rec_len % 4)) {
- rec_len = 8;
- retval = EXT2_ET_DIR_CORRUPTED;
- }
- if (((name_len & 0xFF) + 8) > dirent->rec_len)
- retval = EXT2_ET_DIR_CORRUPTED;
- p += rec_len;
- }
- return retval;
-}
-
-errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
- void *buf)
-{
- return ext2fs_read_dir_block2(fs, block, buf, 0);
-}
-
-
-errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
- void *inbuf, int flags EXT2FS_ATTR((unused)))
-{
-#if BB_BIG_ENDIAN
- int do_swap = 0;
- errcode_t retval;
- char *p, *end;
- char *buf = NULL;
- struct ext2_dir_entry *dirent;
-
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
- do_swap = 1;
-
-#ifndef WORDS_BIGENDIAN
- if (!do_swap)
- return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
-#endif
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- memcpy(buf, inbuf, fs->blocksize);
- p = buf;
- end = buf + fs->blocksize;
- while (p < end) {
- dirent = (struct ext2_dir_entry *) p;
- if ((dirent->rec_len < 8) ||
- (dirent->rec_len % 4)) {
- ext2fs_free_mem(&buf);
- return EXT2_ET_DIR_CORRUPTED;
- }
- p += dirent->rec_len;
- if (do_swap) {
- dirent->inode = ext2fs_swab32(dirent->inode);
- dirent->rec_len = ext2fs_swab16(dirent->rec_len);
- dirent->name_len = ext2fs_swab16(dirent->name_len);
- }
-#ifdef WORDS_BIGENDIAN
- if (flags & EXT2_DIRBLOCK_V2_STRUCT)
- dirent->name_len = ext2fs_swab16(dirent->name_len);
-#endif
- }
- retval = io_channel_write_blk(fs->io, block, 1, buf);
- ext2fs_free_mem(&buf);
- return retval;
-#else
- return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
-#endif
-}
-
-
-errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
- void *inbuf)
-{
- return ext2fs_write_dir_block2(fs, block, inbuf, 0);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c b/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
deleted file mode 100644
index 09e34be..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
+++ b/dev/null
@@ -1,234 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dirhash.c -- Calculate the hash of a directory entry
- *
- * Copyright (c) 2001 Daniel Phillips
- *
- * Copyright (c) 2002 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * Keyed 32-bit hash function using TEA in a Davis-Meyer function
- * H0 = Key
- * Hi = E Mi(Hi-1) + Hi-1
- *
- * (see Applied Cryptography, 2nd edition, p448).
- *
- * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
- *
- * This code is made available under the terms of the GPL
- */
-#define DELTA 0x9E3779B9
-
-static void TEA_transform(__u32 buf[4], __u32 const in[])
-{
- __u32 sum = 0;
- __u32 b0 = buf[0], b1 = buf[1];
- __u32 a = in[0], b = in[1], c = in[2], d = in[3];
- int n = 16;
-
- do {
- sum += DELTA;
- b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b);
- b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d);
- } while (--n);
-
- buf[0] += b0;
- buf[1] += b1;
-}
-
-/* F, G and H are basic MD4 functions: selection, majority, parity */
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-
-/*
- * The generic round function. The application is so specific that
- * we don't bother protecting all the arguments with parens, as is generally
- * good macro practice, in favor of extra legibility.
- * Rotation is separate from addition to prevent recomputation
- */
-#define ROUND(f, a, b, c, d, x, s) \
- (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s)))
-#define K1 0
-#define K2 013240474631UL
-#define K3 015666365641UL
-
-/*
- * Basic cut-down MD4 transform. Returns only 32 bits of result.
- */
-static void halfMD4Transform (__u32 buf[4], __u32 const in[])
-{
- __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
-
- /* Round 1 */
- ROUND(F, a, b, c, d, in[0] + K1, 3);
- ROUND(F, d, a, b, c, in[1] + K1, 7);
- ROUND(F, c, d, a, b, in[2] + K1, 11);
- ROUND(F, b, c, d, a, in[3] + K1, 19);
- ROUND(F, a, b, c, d, in[4] + K1, 3);
- ROUND(F, d, a, b, c, in[5] + K1, 7);
- ROUND(F, c, d, a, b, in[6] + K1, 11);
- ROUND(F, b, c, d, a, in[7] + K1, 19);
-
- /* Round 2 */
- ROUND(G, a, b, c, d, in[1] + K2, 3);
- ROUND(G, d, a, b, c, in[3] + K2, 5);
- ROUND(G, c, d, a, b, in[5] + K2, 9);
- ROUND(G, b, c, d, a, in[7] + K2, 13);
- ROUND(G, a, b, c, d, in[0] + K2, 3);
- ROUND(G, d, a, b, c, in[2] + K2, 5);
- ROUND(G, c, d, a, b, in[4] + K2, 9);
- ROUND(G, b, c, d, a, in[6] + K2, 13);
-
- /* Round 3 */
- ROUND(H, a, b, c, d, in[3] + K3, 3);
- ROUND(H, d, a, b, c, in[7] + K3, 9);
- ROUND(H, c, d, a, b, in[2] + K3, 11);
- ROUND(H, b, c, d, a, in[6] + K3, 15);
- ROUND(H, a, b, c, d, in[1] + K3, 3);
- ROUND(H, d, a, b, c, in[5] + K3, 9);
- ROUND(H, c, d, a, b, in[0] + K3, 11);
- ROUND(H, b, c, d, a, in[4] + K3, 15);
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-#undef ROUND
-#undef F
-#undef G
-#undef H
-#undef K1
-#undef K2
-#undef K3
-
-/* The old legacy hash */
-static ext2_dirhash_t dx_hack_hash (const char *name, int len)
-{
- __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
- while (len--) {
- __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
-
- if (hash & 0x80000000) hash -= 0x7fffffff;
- hash1 = hash0;
- hash0 = hash;
- }
- return (hash0 << 1);
-}
-
-static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
-{
- __u32 pad, val;
- int i;
-
- pad = (__u32)len | ((__u32)len << 8);
- pad |= pad << 16;
-
- val = pad;
- if (len > num*4)
- len = num * 4;
- for (i=0; i < len; i++) {
- if ((i % 4) == 0)
- val = pad;
- val = msg[i] + (val << 8);
- if ((i % 4) == 3) {
- *buf++ = val;
- val = pad;
- num--;
- }
- }
- if (--num >= 0)
- *buf++ = val;
- while (--num >= 0)
- *buf++ = pad;
-}
-
-/*
- * Returns the hash of a filename. If len is 0 and name is NULL, then
- * this function can be used to test whether or not a hash version is
- * supported.
- *
- * The seed is an 4 longword (32 bits) "secret" which can be used to
- * uniquify a hash. If the seed is all zero's, then some default seed
- * may be used.
- *
- * A particular hash version specifies whether or not the seed is
- * represented, and whether or not the returned hash is 32 bits or 64
- * bits. 32 bit hashes will return 0 for the minor hash.
- */
-errcode_t ext2fs_dirhash(int version, const char *name, int len,
- const __u32 *seed,
- ext2_dirhash_t *ret_hash,
- ext2_dirhash_t *ret_minor_hash)
-{
- __u32 hash;
- __u32 minor_hash = 0;
- const char *p;
- int i;
- __u32 in[8], buf[4];
-
- /* Initialize the default seed for the hash checksum functions */
- buf[0] = 0x67452301;
- buf[1] = 0xefcdab89;
- buf[2] = 0x98badcfe;
- buf[3] = 0x10325476;
-
- /* Check to see if the seed is all zero's */
- if (seed) {
- for (i=0; i < 4; i++) {
- if (seed[i])
- break;
- }
- if (i < 4)
- memcpy(buf, seed, sizeof(buf));
- }
-
- switch (version) {
- case EXT2_HASH_LEGACY:
- hash = dx_hack_hash(name, len);
- break;
- case EXT2_HASH_HALF_MD4:
- p = name;
- while (len > 0) {
- str2hashbuf(p, len, in, 8);
- halfMD4Transform(buf, in);
- len -= 32;
- p += 32;
- }
- minor_hash = buf[2];
- hash = buf[1];
- break;
- case EXT2_HASH_TEA:
- p = name;
- while (len > 0) {
- str2hashbuf(p, len, in, 4);
- TEA_transform(buf, in);
- len -= 16;
- p += 16;
- }
- hash = buf[0];
- minor_hash = buf[1];
- break;
- default:
- *ret_hash = 0;
- return EXT2_ET_DIRHASH_UNSUPP;
- }
- *ret_hash = hash & ~1;
- if (ret_minor_hash)
- *ret_minor_hash = minor_hash;
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
deleted file mode 100644
index d187937..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
+++ b/dev/null
@@ -1,95 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * dupfs.c --- duplicate a ext2 filesystem handle
- *
- * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <time.h>
-#include <string.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
-{
- ext2_filsys fs;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
- if (retval)
- return retval;
-
- *fs = *src;
- fs->device_name = 0;
- fs->super = 0;
- fs->orig_super = 0;
- fs->group_desc = 0;
- fs->inode_map = 0;
- fs->block_map = 0;
- fs->badblocks = 0;
- fs->dblist = 0;
-
- io_channel_bumpcount(fs->io);
- if (fs->icache)
- fs->icache->refcount++;
-
- retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name);
- if (retval)
- goto errout;
- strcpy(fs->device_name, src->device_name);
-
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
- if (retval)
- goto errout;
- memcpy(fs->super, src->super, SUPERBLOCK_SIZE);
-
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
- if (retval)
- goto errout;
- memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE);
-
- retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
- &fs->group_desc);
- if (retval)
- goto errout;
- memcpy(fs->group_desc, src->group_desc,
- (size_t) fs->desc_blocks * fs->blocksize);
-
- if (src->inode_map) {
- retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map);
- if (retval)
- goto errout;
- }
- if (src->block_map) {
- retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map);
- if (retval)
- goto errout;
- }
- if (src->badblocks) {
- retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks);
- if (retval)
- goto errout;
- }
- if (src->dblist) {
- retval = ext2fs_copy_dblist(src->dblist, &fs->dblist);
- if (retval)
- goto errout;
- }
- *dest = fs;
- return 0;
-errout:
- ext2fs_free(fs);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h b/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
deleted file mode 100644
index a598d01..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
+++ b/dev/null
@@ -1,39 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * e2image.h --- header file describing the ext2 image format
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * Note: this uses the POSIX IO interfaces, unlike most of the other
- * functions in this library. So sue me.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-
-struct ext2_image_hdr {
- __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */
- char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
- char fs_hostname[64];/* Hostname of machine of image */
- char fs_netaddr[32]; /* Network address */
- __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */
- __u32 fs_device; /* Device number of image */
- char fs_device_name[64]; /* Device name */
- char fs_uuid[16]; /* UUID of filesystem */
- __u32 fs_blocksize; /* Block size of the filesystem */
- __u32 fs_reserved[8];
-
- __u32 image_device; /* Device number of image file */
- __u32 image_inode; /* Inode number of image file */
- __u32 image_time; /* Time of image creation */
- __u32 image_reserved[8];
-
- __u32 offset_super; /* Byte offset of the sb and descriptors */
- __u32 offset_inode; /* Byte offset of the inode table */
- __u32 offset_inodemap; /* Byte offset of the inode bitmaps */
- __u32 offset_blockmap; /* Byte offset of the inode bitmaps */
- __u32 offset_reserved[8];
-};
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c b/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
deleted file mode 100644
index 8a29ae5..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
+++ b/dev/null
@@ -1,127 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * expand.c --- expand an ext2fs directory
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct expand_dir_struct {
- int done;
- int newblocks;
- errcode_t err;
-};
-
-static int expand_dir_proc(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
- blk_t new_blk;
- static blk_t last_blk = 0;
- char *block;
- errcode_t retval;
-
- if (*blocknr) {
- last_blk = *blocknr;
- return 0;
- }
- retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- if (blockcnt > 0) {
- retval = ext2fs_new_dir_block(fs, 0, 0, &block);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- es->done = 1;
- retval = ext2fs_write_dir_block(fs, new_blk, block);
- } else {
- retval = ext2fs_get_mem(fs->blocksize, &block);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- memset(block, 0, fs->blocksize);
- retval = io_channel_write_blk(fs->io, new_blk, 1, block);
- }
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- ext2fs_free_mem(&block);
- *blocknr = new_blk;
- ext2fs_block_alloc_stats(fs, new_blk, +1);
- es->newblocks++;
-
- if (es->done)
- return (BLOCK_CHANGED | BLOCK_ABORT);
- else
- return BLOCK_CHANGED;
-}
-
-errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
-{
- errcode_t retval;
- struct expand_dir_struct es;
- struct ext2_inode inode;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- if (!fs->block_map)
- return EXT2_ET_NO_BLOCK_BITMAP;
-
- retval = ext2fs_check_directory(fs, dir);
- if (retval)
- return retval;
-
- es.done = 0;
- es.err = 0;
- es.newblocks = 0;
-
- retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
- 0, expand_dir_proc, &es);
-
- if (es.err)
- return es.err;
- if (!es.done)
- return EXT2_ET_EXPAND_DIR_ERR;
-
- /*
- * Update the size and block count fields in the inode.
- */
- retval = ext2fs_read_inode(fs, dir, &inode);
- if (retval)
- return retval;
-
- inode.i_size += fs->blocksize;
- inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
-
- retval = ext2fs_write_inode(fs, dir, &inode);
- if (retval)
- return retval;
-
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
deleted file mode 100644
index ead3528..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
+++ b/dev/null
@@ -1,116 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ext2_err.h:
- * This file is automatically generated; please do not edit it.
- */
-
-#define EXT2_ET_BASE (2133571328L)
-#define EXT2_ET_MAGIC_EXT2FS_FILSYS (2133571329L)
-#define EXT2_ET_MAGIC_BADBLOCKS_LIST (2133571330L)
-#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE (2133571331L)
-#define EXT2_ET_MAGIC_INODE_SCAN (2133571332L)
-#define EXT2_ET_MAGIC_IO_CHANNEL (2133571333L)
-#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL (2133571334L)
-#define EXT2_ET_MAGIC_IO_MANAGER (2133571335L)
-#define EXT2_ET_MAGIC_BLOCK_BITMAP (2133571336L)
-#define EXT2_ET_MAGIC_INODE_BITMAP (2133571337L)
-#define EXT2_ET_MAGIC_GENERIC_BITMAP (2133571338L)
-#define EXT2_ET_MAGIC_TEST_IO_CHANNEL (2133571339L)
-#define EXT2_ET_MAGIC_DBLIST (2133571340L)
-#define EXT2_ET_MAGIC_ICOUNT (2133571341L)
-#define EXT2_ET_MAGIC_PQ_IO_CHANNEL (2133571342L)
-#define EXT2_ET_MAGIC_EXT2_FILE (2133571343L)
-#define EXT2_ET_MAGIC_E2IMAGE (2133571344L)
-#define EXT2_ET_MAGIC_INODE_IO_CHANNEL (2133571345L)
-#define EXT2_ET_MAGIC_RESERVED_9 (2133571346L)
-#define EXT2_ET_BAD_MAGIC (2133571347L)
-#define EXT2_ET_REV_TOO_HIGH (2133571348L)
-#define EXT2_ET_RO_FILSYS (2133571349L)
-#define EXT2_ET_GDESC_READ (2133571350L)
-#define EXT2_ET_GDESC_WRITE (2133571351L)
-#define EXT2_ET_GDESC_BAD_BLOCK_MAP (2133571352L)
-#define EXT2_ET_GDESC_BAD_INODE_MAP (2133571353L)
-#define EXT2_ET_GDESC_BAD_INODE_TABLE (2133571354L)
-#define EXT2_ET_INODE_BITMAP_WRITE (2133571355L)
-#define EXT2_ET_INODE_BITMAP_READ (2133571356L)
-#define EXT2_ET_BLOCK_BITMAP_WRITE (2133571357L)
-#define EXT2_ET_BLOCK_BITMAP_READ (2133571358L)
-#define EXT2_ET_INODE_TABLE_WRITE (2133571359L)
-#define EXT2_ET_INODE_TABLE_READ (2133571360L)
-#define EXT2_ET_NEXT_INODE_READ (2133571361L)
-#define EXT2_ET_UNEXPECTED_BLOCK_SIZE (2133571362L)
-#define EXT2_ET_DIR_CORRUPTED (2133571363L)
-#define EXT2_ET_SHORT_READ (2133571364L)
-#define EXT2_ET_SHORT_WRITE (2133571365L)
-#define EXT2_ET_DIR_NO_SPACE (2133571366L)
-#define EXT2_ET_NO_INODE_BITMAP (2133571367L)
-#define EXT2_ET_NO_BLOCK_BITMAP (2133571368L)
-#define EXT2_ET_BAD_INODE_NUM (2133571369L)
-#define EXT2_ET_BAD_BLOCK_NUM (2133571370L)
-#define EXT2_ET_EXPAND_DIR_ERR (2133571371L)
-#define EXT2_ET_TOOSMALL (2133571372L)
-#define EXT2_ET_BAD_BLOCK_MARK (2133571373L)
-#define EXT2_ET_BAD_BLOCK_UNMARK (2133571374L)
-#define EXT2_ET_BAD_BLOCK_TEST (2133571375L)
-#define EXT2_ET_BAD_INODE_MARK (2133571376L)
-#define EXT2_ET_BAD_INODE_UNMARK (2133571377L)
-#define EXT2_ET_BAD_INODE_TEST (2133571378L)
-#define EXT2_ET_FUDGE_BLOCK_BITMAP_END (2133571379L)
-#define EXT2_ET_FUDGE_INODE_BITMAP_END (2133571380L)
-#define EXT2_ET_BAD_IND_BLOCK (2133571381L)
-#define EXT2_ET_BAD_DIND_BLOCK (2133571382L)
-#define EXT2_ET_BAD_TIND_BLOCK (2133571383L)
-#define EXT2_ET_NEQ_BLOCK_BITMAP (2133571384L)
-#define EXT2_ET_NEQ_INODE_BITMAP (2133571385L)
-#define EXT2_ET_BAD_DEVICE_NAME (2133571386L)
-#define EXT2_ET_MISSING_INODE_TABLE (2133571387L)
-#define EXT2_ET_CORRUPT_SUPERBLOCK (2133571388L)
-#define EXT2_ET_BAD_GENERIC_MARK (2133571389L)
-#define EXT2_ET_BAD_GENERIC_UNMARK (2133571390L)
-#define EXT2_ET_BAD_GENERIC_TEST (2133571391L)
-#define EXT2_ET_SYMLINK_LOOP (2133571392L)
-#define EXT2_ET_CALLBACK_NOTHANDLED (2133571393L)
-#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE (2133571394L)
-#define EXT2_ET_UNSUPP_FEATURE (2133571395L)
-#define EXT2_ET_RO_UNSUPP_FEATURE (2133571396L)
-#define EXT2_ET_LLSEEK_FAILED (2133571397L)
-#define EXT2_ET_NO_MEMORY (2133571398L)
-#define EXT2_ET_INVALID_ARGUMENT (2133571399L)
-#define EXT2_ET_BLOCK_ALLOC_FAIL (2133571400L)
-#define EXT2_ET_INODE_ALLOC_FAIL (2133571401L)
-#define EXT2_ET_NO_DIRECTORY (2133571402L)
-#define EXT2_ET_TOO_MANY_REFS (2133571403L)
-#define EXT2_ET_FILE_NOT_FOUND (2133571404L)
-#define EXT2_ET_FILE_RO (2133571405L)
-#define EXT2_ET_DB_NOT_FOUND (2133571406L)
-#define EXT2_ET_DIR_EXISTS (2133571407L)
-#define EXT2_ET_UNIMPLEMENTED (2133571408L)
-#define EXT2_ET_CANCEL_REQUESTED (2133571409L)
-#define EXT2_ET_FILE_TOO_BIG (2133571410L)
-#define EXT2_ET_JOURNAL_NOT_BLOCK (2133571411L)
-#define EXT2_ET_NO_JOURNAL_SB (2133571412L)
-#define EXT2_ET_JOURNAL_TOO_SMALL (2133571413L)
-#define EXT2_ET_JOURNAL_UNSUPP_VERSION (2133571414L)
-#define EXT2_ET_LOAD_EXT_JOURNAL (2133571415L)
-#define EXT2_ET_NO_JOURNAL (2133571416L)
-#define EXT2_ET_DIRHASH_UNSUPP (2133571417L)
-#define EXT2_ET_BAD_EA_BLOCK_NUM (2133571418L)
-#define EXT2_ET_TOO_MANY_INODES (2133571419L)
-#define EXT2_ET_NOT_IMAGE_FILE (2133571420L)
-#define EXT2_ET_RES_GDT_BLOCKS (2133571421L)
-#define EXT2_ET_RESIZE_INODE_CORRUPT (2133571422L)
-#define EXT2_ET_SET_BMAP_NO_IND (2133571423L)
-
-#if 0
-extern const struct error_table et_ext2_error_table;
-extern void initialize_ext2_error_table(void);
-
-/* For compatibility with Heimdal */
-extern void initialize_ext2_error_table_r(struct et_list **list);
-
-#define ERROR_TABLE_BASE_ext2 (2133571328L)
-
-/* for compatibility with older versions... */
-#define init_ext2_err_tbl initialize_ext2_error_table
-#define ext2_err_base ERROR_TABLE_BASE_ext2
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
deleted file mode 100644
index ca309c0..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
+++ b/dev/null
@@ -1,52 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- File: linux/ext2_ext_attr.h
-
- On-disk format of extended attributes for the ext2 filesystem.
-
- (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
-*/
-
-/* Magic value in attribute blocks */
-#define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000
-#define EXT2_EXT_ATTR_MAGIC 0xEA020000
-
-/* Maximum number of references to one attribute block */
-#define EXT2_EXT_ATTR_REFCOUNT_MAX 1024
-
-struct ext2_ext_attr_header {
- __u32 h_magic; /* magic number for identification */
- __u32 h_refcount; /* reference count */
- __u32 h_blocks; /* number of disk blocks used */
- __u32 h_hash; /* hash value of all attributes */
- __u32 h_reserved[4]; /* zero right now */
-};
-
-struct ext2_ext_attr_entry {
- __u8 e_name_len; /* length of name */
- __u8 e_name_index; /* attribute name index */
- __u16 e_value_offs; /* offset in disk block of value */
- __u32 e_value_block; /* disk block attribute is stored on (n/i) */
- __u32 e_value_size; /* size of attribute value */
- __u32 e_hash; /* hash value of name and value */
-};
-
-#define EXT2_EXT_ATTR_PAD_BITS 2
-#define EXT2_EXT_ATTR_PAD (1<<EXT2_EXT_ATTR_PAD_BITS)
-#define EXT2_EXT_ATTR_ROUND (EXT2_EXT_ATTR_PAD-1)
-#define EXT2_EXT_ATTR_LEN(name_len) \
- (((name_len) + EXT2_EXT_ATTR_ROUND + \
- sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
-#define EXT2_EXT_ATTR_NEXT(entry) \
- ( (struct ext2_ext_attr_entry *)( \
- (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->e_name_len)) )
-#define EXT2_EXT_ATTR_SIZE(size) \
- (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
-#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL)
-#define EXT2_EXT_ATTR_NAME(entry) \
- (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry))
-#define EXT2_XATTR_LEN(name_len) \
- (((name_len) + EXT2_EXT_ATTR_ROUND + \
- sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND)
-#define EXT2_XATTR_SIZE(size) \
- (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
deleted file mode 100644
index 80ea2cb..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
+++ b/dev/null
@@ -1,569 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * 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
- */
-#ifndef LINUX_EXT2_FS_H
-#define LINUX_EXT2_FS_H 1
-
-#include "ext2_types.h" /* Changed from linux/types.h */
-
-/*
- * Special inode 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 */
-#define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */
-#define EXT2_JOURNAL_INO 8 /* Journal inode */
-
-/* First non-reserved inode for old ext2 filesystems */
-#define EXT2_GOOD_OLD_FIRST_INO 11
-
-/*
- * The second extended file system magic number
- */
-#define EXT2_SUPER_MAGIC 0xEF53
-
-/* Assume that user mode programs are passing in an ext2fs superblock, not
- * a kernel struct super_block. This will allow us to call the feature-test
- * macros from user land. */
-#define EXT2_SB(sb) (sb)
-
-/*
- * Maximal count of links to a file
- */
-#define EXT2_LINK_MAX 32000
-
-/*
- * Macro-instructions used to manage several block sizes
- */
-#define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */
-#define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */
-#define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE)
-#define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE)
-#define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
-#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10)
-#define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
- EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
-#define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
- EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
-#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
-
-/*
- * Macro-instructions used to manage fragments
- */
-#define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE
-#define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE
-#define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE
-# define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
-# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
-
-/*
- * ACL structures
- */
-struct ext2_acl_header /* Header of Access Control Lists */
-{
- __u32 aclh_size;
- __u32 aclh_file_count;
- __u32 aclh_acle_count;
- __u32 aclh_first_acle;
-};
-
-struct ext2_acl_entry /* Access Control List Entry */
-{
- __u32 acle_size;
- __u16 acle_perms; /* Access permissions */
- __u16 acle_type; /* Type of entry */
- __u16 acle_tag; /* User or group identity */
- __u16 acle_pad1;
- __u32 acle_next; /* Pointer on next entry for the */
- /* same inode or on next free entry */
-};
-
-/*
- * Structure of a blocks group descriptor
- */
-struct ext2_group_desc
-{
- __u32 bg_block_bitmap; /* Blocks bitmap block */
- __u32 bg_inode_bitmap; /* Inodes bitmap block */
- __u32 bg_inode_table; /* Inodes table block */
- __u16 bg_free_blocks_count; /* Free blocks count */
- __u16 bg_free_inodes_count; /* Free inodes count */
- __u16 bg_used_dirs_count; /* Directories count */
- __u16 bg_pad;
- __u32 bg_reserved[3];
-};
-
-/*
- * Data structures used by the directory indexing feature
- *
- * Note: all of the multibyte integer fields are little endian.
- */
-
-/*
- * Note: dx_root_info is laid out so that if it should somehow get
- * overlaid by a dirent the two low bits of the hash version will be
- * zero. Therefore, the hash version mod 4 should never be 0.
- * Sincerely, the paranoia department.
- */
-struct ext2_dx_root_info {
- __u32 reserved_zero;
- __u8 hash_version; /* 0 now, 1 at release */
- __u8 info_length; /* 8 */
- __u8 indirect_levels;
- __u8 unused_flags;
-};
-
-#define EXT2_HASH_LEGACY 0
-#define EXT2_HASH_HALF_MD4 1
-#define EXT2_HASH_TEA 2
-
-#define EXT2_HASH_FLAG_INCOMPAT 0x1
-
-struct ext2_dx_entry {
- __u32 hash;
- __u32 block;
-};
-
-struct ext2_dx_countlimit {
- __u16 limit;
- __u16 count;
-};
-
-
-/*
- * Macro-instructions used to manage group descriptors
- */
-#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
-#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
-#define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
-/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
-#define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8)
-#define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
-#define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
-
-/*
- * Constants relative to the data blocks
- */
-#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)
-
-/*
- * Inode flags
- */
-#define EXT2_SECRM_FL 0x00000001 /* Secure deletion */
-#define EXT2_UNRM_FL 0x00000002 /* Undelete */
-#define EXT2_COMPR_FL 0x00000004 /* Compress file */
-#define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */
-#define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */
-#define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */
-#define EXT2_NODUMP_FL 0x00000040 /* do not dump file */
-#define EXT2_NOATIME_FL 0x00000080 /* do not update atime */
-/* Reserved for compression usage... */
-#define EXT2_DIRTY_FL 0x00000100
-#define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */
-#define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */
-#define EXT2_ECOMPR_FL 0x00000800 /* Compression error */
-/* End compression flags --- maybe not all used */
-#define EXT2_BTREE_FL 0x00001000 /* btree format dir */
-#define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */
-#define EXT2_IMAGIC_FL 0x00002000
-#define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */
-#define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */
-#define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */
-#define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/
-#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */
-#define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */
-
-#define EXT2_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */
-#define EXT2_FL_USER_MODIFIABLE 0x000080FF /* User modifiable flags */
-
-/*
- * ioctl commands
- */
-#define EXT2_IOC_GETFLAGS _IOR('f', 1, long)
-#define EXT2_IOC_SETFLAGS _IOW('f', 2, long)
-#define EXT2_IOC_GETVERSION _IOR('v', 1, long)
-#define EXT2_IOC_SETVERSION _IOW('v', 2, long)
-
-/*
- * Structure of an inode on the disk
- */
-struct ext2_inode {
- __u16 i_mode; /* File mode */
- __u16 i_uid; /* Low 16 bits of Owner Uid */
- __u32 i_size; /* Size in bytes */
- __u32 i_atime; /* Access time */
- __u32 i_ctime; /* Creation time */
- __u32 i_mtime; /* Modification time */
- __u32 i_dtime; /* Deletion Time */
- __u16 i_gid; /* Low 16 bits of Group Id */
- __u16 i_links_count; /* Links count */
- __u32 i_blocks; /* Blocks count */
- __u32 i_flags; /* File flags */
- union {
- struct {
- __u32 l_i_reserved1;
- } linux1;
- struct {
- __u32 h_i_translator;
- } hurd1;
- struct {
- __u32 m_i_reserved1;
- } masix1;
- } osd1; /* OS dependent 1 */
- __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
- __u32 i_generation; /* File version (for NFS) */
- __u32 i_file_acl; /* File ACL */
- __u32 i_dir_acl; /* Directory ACL */
- __u32 i_faddr; /* Fragment address */
- union {
- struct {
- __u8 l_i_frag; /* Fragment number */
- __u8 l_i_fsize; /* Fragment size */
- __u16 i_pad1;
- __u16 l_i_uid_high; /* these 2 fields */
- __u16 l_i_gid_high; /* were reserved2[0] */
- __u32 l_i_reserved2;
- } linux2;
- struct {
- __u8 h_i_frag; /* Fragment number */
- __u8 h_i_fsize; /* Fragment size */
- __u16 h_i_mode_high;
- __u16 h_i_uid_high;
- __u16 h_i_gid_high;
- __u32 h_i_author;
- } hurd2;
- struct {
- __u8 m_i_frag; /* Fragment number */
- __u8 m_i_fsize; /* Fragment size */
- __u16 m_pad1;
- __u32 m_i_reserved2[2];
- } masix2;
- } osd2; /* OS dependent 2 */
-};
-
-/*
- * Permanent part of an large inode on the disk
- */
-struct ext2_inode_large {
- __u16 i_mode; /* File mode */
- __u16 i_uid; /* Low 16 bits of Owner Uid */
- __u32 i_size; /* Size in bytes */
- __u32 i_atime; /* Access time */
- __u32 i_ctime; /* Creation time */
- __u32 i_mtime; /* Modification time */
- __u32 i_dtime; /* Deletion Time */
- __u16 i_gid; /* Low 16 bits of Group Id */
- __u16 i_links_count; /* Links count */
- __u32 i_blocks; /* Blocks count */
- __u32 i_flags; /* File flags */
- union {
- struct {
- __u32 l_i_reserved1;
- } linux1;
- struct {
- __u32 h_i_translator;
- } hurd1;
- struct {
- __u32 m_i_reserved1;
- } masix1;
- } osd1; /* OS dependent 1 */
- __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
- __u32 i_generation; /* File version (for NFS) */
- __u32 i_file_acl; /* File ACL */
- __u32 i_dir_acl; /* Directory ACL */
- __u32 i_faddr; /* Fragment address */
- union {
- struct {
- __u8 l_i_frag; /* Fragment number */
- __u8 l_i_fsize; /* Fragment size */
- __u16 i_pad1;
- __u16 l_i_uid_high; /* these 2 fields */
- __u16 l_i_gid_high; /* were reserved2[0] */
- __u32 l_i_reserved2;
- } linux2;
- struct {
- __u8 h_i_frag; /* Fragment number */
- __u8 h_i_fsize; /* Fragment size */
- __u16 h_i_mode_high;
- __u16 h_i_uid_high;
- __u16 h_i_gid_high;
- __u32 h_i_author;
- } hurd2;
- struct {
- __u8 m_i_frag; /* Fragment number */
- __u8 m_i_fsize; /* Fragment size */
- __u16 m_pad1;
- __u32 m_i_reserved2[2];
- } masix2;
- } osd2; /* OS dependent 2 */
- __u16 i_extra_isize;
- __u16 i_pad1;
-};
-
-#define i_size_high i_dir_acl
-
-/*
- * File system states
- */
-#define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */
-#define EXT2_ERROR_FS 0x0002 /* Errors detected */
-
-/*
- * Mount flags
- */
-#define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */
-#define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */
-#define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */
-#define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */
-#define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */
-#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
-#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
-#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
-
-#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
-#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
-#define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \
- EXT2_MOUNT_##opt)
-/*
- * Maximal mount counts between two filesystem checks
- */
-#define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */
-#define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */
-
-/*
- * 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
-
-/*
- * Structure of the super block
- */
-struct ext2_super_block {
- __u32 s_inodes_count; /* Inodes count */
- __u32 s_blocks_count; /* Blocks count */
- __u32 s_r_blocks_count; /* Reserved blocks count */
- __u32 s_free_blocks_count; /* Free blocks count */
- __u32 s_free_inodes_count; /* Free inodes count */
- __u32 s_first_data_block; /* First Data Block */
- __u32 s_log_block_size; /* Block size */
- __s32 s_log_frag_size; /* Fragment size */
- __u32 s_blocks_per_group; /* # Blocks per group */
- __u32 s_frags_per_group; /* # Fragments per group */
- __u32 s_inodes_per_group; /* # Inodes per group */
- __u32 s_mtime; /* Mount time */
- __u32 s_wtime; /* Write time */
- __u16 s_mnt_count; /* Mount count */
- __s16 s_max_mnt_count; /* Maximal mount count */
- __u16 s_magic; /* Magic signature */
- __u16 s_state; /* File system state */
- __u16 s_errors; /* Behaviour when detecting errors */
- __u16 s_minor_rev_level; /* minor revision level */
- __u32 s_lastcheck; /* time of last check */
- __u32 s_checkinterval; /* max. time between checks */
- __u32 s_creator_os; /* OS */
- __u32 s_rev_level; /* Revision level */
- __u16 s_def_resuid; /* Default uid for reserved blocks */
- __u16 s_def_resgid; /* Default gid for reserved blocks */
- /*
- * These fields are for EXT2_DYNAMIC_REV superblocks only.
- *
- * Note: the difference between the compatible feature set and
- * the incompatible feature set is that if there is a bit set
- * in the incompatible feature set that the kernel doesn't
- * know about, it should refuse to mount the filesystem.
- *
- * e2fsck's requirements are more strict; if it doesn't know
- * about a feature in either the compatible or incompatible
- * feature set, it must abort and not try to meddle with
- * things it doesn't understand...
- */
- __u32 s_first_ino; /* First non-reserved inode */
- __u16 s_inode_size; /* size of inode structure */
- __u16 s_block_group_nr; /* block group # of this superblock */
- __u32 s_feature_compat; /* compatible feature set */
- __u32 s_feature_incompat; /* incompatible feature set */
- __u32 s_feature_ro_compat; /* readonly-compatible feature set */
- __u8 s_uuid[16]; /* 128-bit uuid for volume */
- char s_volume_name[16]; /* volume name */
- char s_last_mounted[64]; /* directory where last mounted */
- __u32 s_algorithm_usage_bitmap; /* For compression */
- /*
- * Performance hints. Directory preallocation should only
- * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
- */
- __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
- __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
- __u16 s_reserved_gdt_blocks; /* Per group table for online growth */
- /*
- * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
- */
- __u8 s_journal_uuid[16]; /* uuid of journal superblock */
- __u32 s_journal_inum; /* inode number of journal file */
- __u32 s_journal_dev; /* device number of journal file */
- __u32 s_last_orphan; /* start of list of inodes to delete */
- __u32 s_hash_seed[4]; /* HTREE hash seed */
- __u8 s_def_hash_version; /* Default hash version to use */
- __u8 s_jnl_backup_type; /* Default type of journal backup */
- __u16 s_reserved_word_pad;
- __u32 s_default_mount_opts;
- __u32 s_first_meta_bg; /* First metablock group */
- __u32 s_mkfs_time; /* When the filesystem was created */
- __u32 s_jnl_blocks[17]; /* Backup of the journal inode */
- __u32 s_reserved[172]; /* Padding to the end of the block */
-};
-
-/*
- * 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
-
-/*
- * Revision levels
- */
-#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
-#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
-
-#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
-#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
-
-#define EXT2_GOOD_OLD_INODE_SIZE 128
-
-/*
- * Journal inode backup types
- */
-#define EXT3_JNL_BACKUP_BLOCKS 1
-
-/*
- * Feature set definitions
- */
-
-#define EXT2_HAS_COMPAT_FEATURE(sb,mask) \
- ( EXT2_SB(sb)->s_feature_compat & (mask) )
-#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \
- ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
-#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \
- ( EXT2_SB(sb)->s_feature_incompat & (mask) )
-
-#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
-#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
-#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
-#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
-#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010
-#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020
-
-#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
-#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
-/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */
-
-#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
-#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
-#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */
-#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */
-#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
-#define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040
-
-
-#define EXT2_FEATURE_COMPAT_SUPP 0
-#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE)
-#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
- EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
-
-/*
- * Default values for user and/or group using reserved blocks
- */
-#define EXT2_DEF_RESUID 0
-#define EXT2_DEF_RESGID 0
-
-/*
- * Default mount options
- */
-#define EXT2_DEFM_DEBUG 0x0001
-#define EXT2_DEFM_BSDGROUPS 0x0002
-#define EXT2_DEFM_XATTR_USER 0x0004
-#define EXT2_DEFM_ACL 0x0008
-#define EXT2_DEFM_UID16 0x0010
-#define EXT3_DEFM_JMODE 0x0060
-#define EXT3_DEFM_JMODE_DATA 0x0020
-#define EXT3_DEFM_JMODE_ORDERED 0x0040
-#define EXT3_DEFM_JMODE_WBACK 0x0060
-
-/*
- * Structure of a directory entry
- */
-#define EXT2_NAME_LEN 255
-
-struct ext2_dir_entry {
- __u32 inode; /* Inode number */
- __u16 rec_len; /* Directory entry length */
- __u16 name_len; /* Name length */
- char name[EXT2_NAME_LEN]; /* File name */
-};
-
-/*
- * The new version of the directory entry. Since EXT2 structures are
- * stored in intel byte order, and the name_len field could never be
- * bigger than 255 chars, it's safe to reclaim the extra byte for the
- * file_type field.
- */
-struct ext2_dir_entry_2 {
- __u32 inode; /* Inode number */
- __u16 rec_len; /* Directory entry length */
- __u8 name_len; /* Name length */
- __u8 file_type;
- char name[EXT2_NAME_LEN]; /* File name */
-};
-
-/*
- * Ext2 directory file types. Only the low 3 bits are used. The
- * other bits are reserved for now.
- */
-#define EXT2_FT_UNKNOWN 0
-#define EXT2_FT_REG_FILE 1
-#define EXT2_FT_DIR 2
-#define EXT2_FT_CHRDEV 3
-#define EXT2_FT_BLKDEV 4
-#define EXT2_FT_FIFO 5
-#define EXT2_FT_SOCK 6
-#define EXT2_FT_SYMLINK 7
-
-#define EXT2_FT_MAX 8
-
-/*
- * EXT2_DIR_PAD defines the directory entries boundaries
- *
- * NOTE: It must be a multiple of 4
- */
-#define EXT2_DIR_PAD 4
-#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
-#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
- ~EXT2_DIR_ROUND)
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
deleted file mode 100644
index 1900a76..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
+++ b/dev/null
@@ -1,112 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * io.h --- the I/O manager abstraction
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-#ifndef EXT2FS_EXT2_IO_H
-#define EXT2FS_EXT2_IO_H 1
-
-/*
- * ext2_loff_t is defined here since unix_io.c needs it.
- */
-#if defined(__GNUC__) || defined(HAS_LONG_LONG)
-typedef long long ext2_loff_t;
-#else
-typedef long ext2_loff_t;
-#endif
-
-/* llseek.c */
-/* ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); */
-#ifdef CONFIG_LFS
-# define ext2fs_llseek lseek64
-#else
-# define ext2fs_llseek lseek
-#endif
-
-typedef struct struct_io_manager *io_manager;
-typedef struct struct_io_channel *io_channel;
-
-#define CHANNEL_FLAGS_WRITETHROUGH 0x01
-
-struct struct_io_channel {
- errcode_t magic;
- io_manager manager;
- char *name;
- int block_size;
- errcode_t (*read_error)(io_channel channel,
- unsigned long block,
- int count,
- void *data,
- size_t size,
- int actual_bytes_read,
- errcode_t error);
- errcode_t (*write_error)(io_channel channel,
- unsigned long block,
- int count,
- const void *data,
- size_t size,
- int actual_bytes_written,
- errcode_t error);
- int refcount;
- int flags;
- int reserved[14];
- void *private_data;
- void *app_data;
-};
-
-struct struct_io_manager {
- errcode_t magic;
- const char *name;
- errcode_t (*open)(const char *name, int flags, io_channel *channel);
- errcode_t (*close)(io_channel channel);
- errcode_t (*set_blksize)(io_channel channel, int blksize);
- errcode_t (*read_blk)(io_channel channel, unsigned long block,
- int count, void *data);
- errcode_t (*write_blk)(io_channel channel, unsigned long block,
- int count, const void *data);
- errcode_t (*flush)(io_channel channel);
- errcode_t (*write_byte)(io_channel channel, unsigned long offset,
- int count, const void *data);
- errcode_t (*set_option)(io_channel channel, const char *option,
- const char *arg);
- int reserved[14];
-};
-
-#define IO_FLAG_RW 1
-
-/*
- * Convenience functions....
- */
-#define io_channel_close(c) ((c)->manager->close((c)))
-#define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s))
-#define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d))
-#define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d))
-#define io_channel_flush(c) ((c)->manager->flush((c)))
-#define io_channel_bumpcount(c) ((c)->refcount++)
-
-/* io_manager.c */
-extern errcode_t io_channel_set_options(io_channel channel,
- const char *options);
-extern errcode_t io_channel_write_byte(io_channel channel,
- unsigned long offset,
- int count, const void *data);
-
-/* unix_io.c */
-extern io_manager unix_io_manager;
-
-/* test_io.c */
-extern io_manager test_io_manager, test_io_backing_manager;
-extern void (*test_io_cb_read_blk)
- (unsigned long block, int count, errcode_t err);
-extern void (*test_io_cb_write_blk)
- (unsigned long block, int count, errcode_t err);
-extern void (*test_io_cb_set_blksize)
- (int blksize, errcode_t err);
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
deleted file mode 100644
index 2c1196b..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
+++ b/dev/null
@@ -1,2 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#include <linux/types.h>
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
deleted file mode 100644
index 39fb116..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
+++ b/dev/null
@@ -1,922 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ext2fs.h --- ext2fs
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-#ifndef EXT2FS_EXT2FS_H
-#define EXT2FS_EXT2FS_H 1
-
-
-#define EXT2FS_ATTR(x)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Where the master copy of the superblock is located, and how big
- * superblocks are supposed to be. We define SUPERBLOCK_SIZE because
- * the size of the superblock structure is not necessarily trustworthy
- * (some versions have the padding set up so that the superblock is
- * 1032 bytes long).
- */
-#define SUPERBLOCK_OFFSET 1024
-#define SUPERBLOCK_SIZE 1024
-
-/*
- * The last ext2fs revision level that this version of the library is
- * able to support.
- */
-#define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "ext2_types.h"
-#include "ext2_fs.h"
-
-typedef __u32 ext2_ino_t;
-typedef __u32 blk_t;
-typedef __u32 dgrp_t;
-typedef __u32 ext2_off_t;
-typedef __s64 e2_blkcnt_t;
-typedef __u32 ext2_dirhash_t;
-
-#include "ext2_io.h"
-#include "ext2_err.h"
-
-typedef struct struct_ext2_filsys *ext2_filsys;
-
-struct ext2fs_struct_generic_bitmap {
- errcode_t magic;
- ext2_filsys fs;
- __u32 start, end;
- __u32 real_end;
- char * description;
- char * bitmap;
- errcode_t base_error_code;
- __u32 reserved[7];
-};
-
-#define EXT2FS_MARK_ERROR 0
-#define EXT2FS_UNMARK_ERROR 1
-#define EXT2FS_TEST_ERROR 2
-
-typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap;
-typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap;
-typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap;
-
-#define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s)
-
-/*
- * badblocks list definitions
- */
-
-typedef struct ext2_struct_u32_list *ext2_badblocks_list;
-typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate;
-
-typedef struct ext2_struct_u32_list *ext2_u32_list;
-typedef struct ext2_struct_u32_iterate *ext2_u32_iterate;
-
-/* old */
-typedef struct ext2_struct_u32_list *badblocks_list;
-typedef struct ext2_struct_u32_iterate *badblocks_iterate;
-
-#define BADBLOCKS_FLAG_DIRTY 1
-
-/*
- * ext2_dblist structure and abstractions (see dblist.c)
- */
-struct ext2_db_entry {
- ext2_ino_t ino;
- blk_t blk;
- int blockcnt;
-};
-
-typedef struct ext2_struct_dblist *ext2_dblist;
-
-#define DBLIST_ABORT 1
-
-/*
- * ext2_fileio definitions
- */
-
-#define EXT2_FILE_WRITE 0x0001
-#define EXT2_FILE_CREATE 0x0002
-
-#define EXT2_FILE_MASK 0x00FF
-
-#define EXT2_FILE_BUF_DIRTY 0x4000
-#define EXT2_FILE_BUF_VALID 0x2000
-
-typedef struct ext2_file *ext2_file_t;
-
-#define EXT2_SEEK_SET 0
-#define EXT2_SEEK_CUR 1
-#define EXT2_SEEK_END 2
-
-/*
- * Flags for the ext2_filsys structure and for ext2fs_open()
- */
-#define EXT2_FLAG_RW 0x01
-#define EXT2_FLAG_CHANGED 0x02
-#define EXT2_FLAG_DIRTY 0x04
-#define EXT2_FLAG_VALID 0x08
-#define EXT2_FLAG_IB_DIRTY 0x10
-#define EXT2_FLAG_BB_DIRTY 0x20
-#define EXT2_FLAG_SWAP_BYTES 0x40
-#define EXT2_FLAG_SWAP_BYTES_READ 0x80
-#define EXT2_FLAG_SWAP_BYTES_WRITE 0x100
-#define EXT2_FLAG_MASTER_SB_ONLY 0x200
-#define EXT2_FLAG_FORCE 0x400
-#define EXT2_FLAG_SUPER_ONLY 0x800
-#define EXT2_FLAG_JOURNAL_DEV_OK 0x1000
-#define EXT2_FLAG_IMAGE_FILE 0x2000
-
-/*
- * Special flag in the ext2 inode i_flag field that means that this is
- * a new inode. (So that ext2_write_inode() can clear extra fields.)
- */
-#define EXT2_NEW_INODE_FL 0x80000000
-
-/*
- * Flags for mkjournal
- *
- * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock
- */
-#define EXT2_MKJOURNAL_V1_SUPER 0x0000001
-
-struct struct_ext2_filsys {
- errcode_t magic;
- io_channel io;
- int flags;
- char * device_name;
- struct ext2_super_block * super;
- unsigned int blocksize;
- int fragsize;
- dgrp_t group_desc_count;
- unsigned long desc_blocks;
- struct ext2_group_desc * group_desc;
- int inode_blocks_per_group;
- ext2fs_inode_bitmap inode_map;
- ext2fs_block_bitmap block_map;
- errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
- errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino);
- errcode_t (*write_bitmaps)(ext2_filsys fs);
- errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode);
- errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode);
- ext2_badblocks_list badblocks;
- ext2_dblist dblist;
- __u32 stride; /* for mke2fs */
- struct ext2_super_block * orig_super;
- struct ext2_image_hdr * image_header;
- __u32 umask;
- /*
- * Reserved for future expansion
- */
- __u32 reserved[8];
-
- /*
- * Reserved for the use of the calling application.
- */
- void * priv_data;
-
- /*
- * Inode cache
- */
- struct ext2_inode_cache *icache;
- io_channel image_io;
-};
-
-#include "bitops.h"
-
-/*
- * Return flags for the block iterator functions
- */
-#define BLOCK_CHANGED 1
-#define BLOCK_ABORT 2
-#define BLOCK_ERROR 4
-
-/*
- * Block interate flags
- *
- * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator
- * function should be called on blocks where the block number is zero.
- * This is used by ext2fs_expand_dir() to be able to add a new block
- * to an inode. It can also be used for programs that want to be able
- * to deal with files that contain "holes".
- *
- * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the
- * indirect, doubly indirect, etc. blocks should be called after all
- * of the blocks containined in the indirect blocks are processed.
- * This is useful if you are going to be deallocating blocks from an
- * inode.
- *
- * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be
- * called for data blocks only.
- *
- * BLOCK_FLAG_NO_LARGE is for internal use only. It informs
- * ext2fs_block_iterate2 that large files won't be accepted.
- */
-#define BLOCK_FLAG_APPEND 1
-#define BLOCK_FLAG_HOLE 1
-#define BLOCK_FLAG_DEPTH_TRAVERSE 2
-#define BLOCK_FLAG_DATA_ONLY 4
-
-#define BLOCK_FLAG_NO_LARGE 0x1000
-
-/*
- * Magic "block count" return values for the block iterator function.
- */
-#define BLOCK_COUNT_IND (-1)
-#define BLOCK_COUNT_DIND (-2)
-#define BLOCK_COUNT_TIND (-3)
-#define BLOCK_COUNT_TRANSLATOR (-4)
-
-#if 0
-/*
- * Flags for ext2fs_move_blocks
- */
-#define EXT2_BMOVE_GET_DBLIST 0x0001
-#define EXT2_BMOVE_DEBUG 0x0002
-#endif
-
-/*
- * Flags for directory block reading and writing functions
- */
-#define EXT2_DIRBLOCK_V2_STRUCT 0x0001
-
-/*
- * Return flags for the directory iterator functions
- */
-#define DIRENT_CHANGED 1
-#define DIRENT_ABORT 2
-#define DIRENT_ERROR 3
-
-/*
- * Directory iterator flags
- */
-
-#define DIRENT_FLAG_INCLUDE_EMPTY 1
-#define DIRENT_FLAG_INCLUDE_REMOVED 2
-
-#define DIRENT_DOT_FILE 1
-#define DIRENT_DOT_DOT_FILE 2
-#define DIRENT_OTHER_FILE 3
-#define DIRENT_DELETED_FILE 4
-
-/*
- * Inode scan definitions
- */
-typedef struct ext2_struct_inode_scan *ext2_inode_scan;
-
-/*
- * ext2fs_scan flags
- */
-#define EXT2_SF_CHK_BADBLOCKS 0x0001
-#define EXT2_SF_BAD_INODE_BLK 0x0002
-#define EXT2_SF_BAD_EXTRA_BYTES 0x0004
-#define EXT2_SF_SKIP_MISSING_ITABLE 0x0008
-
-/*
- * ext2fs_check_if_mounted flags
- */
-#define EXT2_MF_MOUNTED 1
-#define EXT2_MF_ISROOT 2
-#define EXT2_MF_READONLY 4
-#define EXT2_MF_SWAP 8
-#define EXT2_MF_BUSY 16
-
-/*
- * Ext2/linux mode flags. We define them here so that we don't need
- * to depend on the OS's sys/stat.h, since we may be compiling on a
- * non-Linux system.
- */
-#define LINUX_S_IFMT 00170000
-#define LINUX_S_IFSOCK 0140000
-#define LINUX_S_IFLNK 0120000
-#define LINUX_S_IFREG 0100000
-#define LINUX_S_IFBLK 0060000
-#define LINUX_S_IFDIR 0040000
-#define LINUX_S_IFCHR 0020000
-#define LINUX_S_IFIFO 0010000
-#define LINUX_S_ISUID 0004000
-#define LINUX_S_ISGID 0002000
-#define LINUX_S_ISVTX 0001000
-
-#define LINUX_S_IRWXU 00700
-#define LINUX_S_IRUSR 00400
-#define LINUX_S_IWUSR 00200
-#define LINUX_S_IXUSR 00100
-
-#define LINUX_S_IRWXG 00070
-#define LINUX_S_IRGRP 00040
-#define LINUX_S_IWGRP 00020
-#define LINUX_S_IXGRP 00010
-
-#define LINUX_S_IRWXO 00007
-#define LINUX_S_IROTH 00004
-#define LINUX_S_IWOTH 00002
-#define LINUX_S_IXOTH 00001
-
-#define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK)
-#define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG)
-#define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR)
-#define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR)
-#define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK)
-#define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO)
-#define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK)
-
-/*
- * ext2 size of an inode
- */
-#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32))
-
-/*
- * ext2_icount_t abstraction
- */
-#define EXT2_ICOUNT_OPT_INCREMENT 0x01
-
-typedef struct ext2_icount *ext2_icount_t;
-
-/*
- * Flags for ext2fs_bmap
- */
-#define BMAP_ALLOC 0x0001
-#define BMAP_SET 0x0002
-
-/*
- * Flags for imager.c functions
- */
-#define IMAGER_FLAG_INODEMAP 1
-#define IMAGER_FLAG_SPARSEWRITE 2
-
-/*
- * For checking structure magic numbers...
- */
-
-#define EXT2_CHECK_MAGIC(struct, code) \
- if ((struct)->magic != (code)) return (code)
-
-
-/*
- * For ext2 compression support
- */
-#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff)
-#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR)
-
-/*
- * Features supported by this version of the library
- */
-#define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
- EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
- EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
- EXT2_FEATURE_COMPAT_RESIZE_INO|\
- EXT2_FEATURE_COMPAT_DIR_INDEX|\
- EXT2_FEATURE_COMPAT_EXT_ATTR)
-
-/* This #ifdef is temporary until compression is fully supported */
-#ifdef ENABLE_COMPRESSION
-#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL
-/* If the below warning bugs you, then have
- `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your
- environment at configure time. */
- #warning "Compression support is experimental"
-#endif
-#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
- EXT2_FEATURE_INCOMPAT_COMPRESSION|\
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
- EXT2_FEATURE_INCOMPAT_META_BG|\
- EXT3_FEATURE_INCOMPAT_RECOVER)
-#else
-#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
- EXT2_FEATURE_INCOMPAT_META_BG|\
- EXT3_FEATURE_INCOMPAT_RECOVER)
-#endif
-#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
-/*
- * function prototypes
- */
-
-/* alloc.c */
-extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
- ext2fs_inode_bitmap map, ext2_ino_t *ret);
-extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
- ext2fs_block_bitmap map, blk_t *ret);
-extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
- blk_t finish, int num,
- ext2fs_block_bitmap map,
- blk_t *ret);
-extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
- char *block_buf, blk_t *ret);
-
-/* alloc_sb.c */
-extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
- dgrp_t group,
- ext2fs_block_bitmap bmap);
-
-/* alloc_stats.c */
-void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse);
-void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
- int inuse, int isdir);
-void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
-
-/* alloc_tables.c */
-extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
-extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
- ext2fs_block_bitmap bmap);
-
-/* badblocks.c */
-extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size);
-extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk);
-extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk);
-extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk);
-extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
- ext2_u32_iterate *ret);
-extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk);
-extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter);
-extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest);
-extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2);
-
-extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret,
- int size);
-extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb,
- blk_t blk);
-extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb,
- blk_t blk);
-extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk);
-extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk);
-extern errcode_t
- ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
- ext2_badblocks_iterate *ret);
-extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
- blk_t *blk);
-extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
-extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
- ext2_badblocks_list *dest);
-extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
- ext2_badblocks_list bb2);
-extern int ext2fs_u32_list_count(ext2_u32_list bb);
-
-/* bb_compat */
-extern errcode_t badblocks_list_create(badblocks_list *ret, int size);
-extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk);
-extern int badblocks_list_test(badblocks_list bb, blk_t blk);
-extern errcode_t badblocks_list_iterate_begin(badblocks_list bb,
- badblocks_iterate *ret);
-extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk);
-extern void badblocks_list_iterate_end(badblocks_iterate iter);
-extern void badblocks_list_free(badblocks_list bb);
-
-/* bb_inode.c */
-extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs,
- ext2_badblocks_list bb_list);
-
-/* bitmaps.c */
-extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
-extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
-extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
-extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
-extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
- __u32 end,
- __u32 real_end,
- const char *descr,
- ext2fs_generic_bitmap *ret);
-extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
- const char *descr,
- ext2fs_block_bitmap *ret);
-extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
- const char *descr,
- ext2fs_inode_bitmap *ret);
-extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
- ext2_ino_t end, ext2_ino_t *oend);
-extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
- blk_t end, blk_t *oend);
-extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap);
-extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap);
-extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs);
-extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs);
-
-/* block.c */
-extern errcode_t ext2fs_block_iterate(ext2_filsys fs,
- ext2_ino_t ino,
- int flags,
- char *block_buf,
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- int blockcnt,
- void *priv_data),
- void *priv_data);
-errcode_t ext2fs_block_iterate2(ext2_filsys fs,
- ext2_ino_t ino,
- int flags,
- char *block_buf,
- int (*func)(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_blk,
- int ref_offset,
- void *priv_data),
- void *priv_data);
-
-/* bmap.c */
-extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- char *block_buf, int bmap_flags,
- blk_t block, blk_t *phys_blk);
-
-
-#if 0
-/* bmove.c */
-extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
- ext2fs_block_bitmap reserve,
- ext2fs_block_bitmap alloc_map,
- int flags);
-#endif
-
-/* check_desc.c */
-extern errcode_t ext2fs_check_desc(ext2_filsys fs);
-
-/* closefs.c */
-extern errcode_t ext2fs_close(ext2_filsys fs);
-extern errcode_t ext2fs_flush(ext2_filsys fs);
-extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
-extern int ext2fs_super_and_bgd_loc(ext2_filsys fs,
- dgrp_t group,
- blk_t *ret_super_blk,
- blk_t *ret_old_desc_blk,
- blk_t *ret_new_desc_blk,
- int *ret_meta_bg);
-extern void ext2fs_update_dynamic_rev(ext2_filsys fs);
-
-/* cmp_bitmaps.c */
-extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
- ext2fs_block_bitmap bm2);
-extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
- ext2fs_inode_bitmap bm2);
-
-/* dblist.c */
-
-extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
-extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
-extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
- blk_t blk, int blockcnt);
-extern void ext2fs_dblist_sort(ext2_dblist dblist,
- int (*sortfunc)(const void *,
- const void *));
-extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
- int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info,
- void *priv_data),
- void *priv_data);
-extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino,
- blk_t blk, int blockcnt);
-extern errcode_t ext2fs_copy_dblist(ext2_dblist src,
- ext2_dblist *dest);
-extern int ext2fs_dblist_count(ext2_dblist dblist);
-
-/* dblist_dir.c */
-extern errcode_t
- ext2fs_dblist_dir_iterate(ext2_dblist dblist,
- int flags,
- char *block_buf,
- int (*func)(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data);
-
-/* dirblock.c */
-extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
- void *buf);
-extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
- void *buf, int flags);
-extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
- void *buf);
-extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
- void *buf, int flags);
-
-/* dirhash.c */
-extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
- const __u32 *seed,
- ext2_dirhash_t *ret_hash,
- ext2_dirhash_t *ret_minor_hash);
-
-
-/* dir_iterate.c */
-extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
- ext2_ino_t dir,
- int flags,
- char *block_buf,
- int (*func)(struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data);
-extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
- ext2_ino_t dir,
- int flags,
- char *block_buf,
- int (*func)(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data),
- void *priv_data);
-
-/* dupfs.c */
-extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
-
-/* expanddir.c */
-extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
-
-/* ext_attr.c */
-extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
-extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
- void *buf);
-extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
- char *block_buf,
- int adjust, __u32 *newcount);
-
-/* fileio.c */
-extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- int flags, ext2_file_t *ret);
-extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
- int flags, ext2_file_t *ret);
-extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file);
-extern errcode_t ext2fs_file_close(ext2_file_t file);
-extern errcode_t ext2fs_file_flush(ext2_file_t file);
-extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
- unsigned int wanted, unsigned int *got);
-extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
- unsigned int nbytes, unsigned int *written);
-extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
- int whence, __u64 *ret_pos);
-extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
- int whence, ext2_off_t *ret_pos);
-errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size);
-extern ext2_off_t ext2fs_file_get_size(ext2_file_t file);
-extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size);
-
-/* finddev.c */
-extern char *ext2fs_find_block_device(dev_t device);
-
-/* flushb.c */
-extern errcode_t ext2fs_sync_device(int fd, int flushb);
-
-/* freefs.c */
-extern void ext2fs_free(ext2_filsys fs);
-extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap);
-extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
-extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
-extern void ext2fs_free_dblist(ext2_dblist dblist);
-extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb);
-extern void ext2fs_u32_list_free(ext2_u32_list bb);
-
-/* getsize.c */
-extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
- blk_t *retblocks);
-
-/* getsectsize.c */
-errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize);
-
-/* imager.c */
-extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
-extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags);
-extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags);
-extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags);
-extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags);
-extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
-
-/* ind_block.c */
-errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
-errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
-
-/* initialize.c */
-extern errcode_t ext2fs_initialize(const char *name, int flags,
- struct ext2_super_block *param,
- io_manager manager, ext2_filsys *ret_fs);
-
-/* icount.c */
-extern void ext2fs_free_icount(ext2_icount_t icount);
-extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags,
- unsigned int size,
- ext2_icount_t hint, ext2_icount_t *ret);
-extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
- unsigned int size,
- ext2_icount_t *ret);
-extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *ret);
-extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *ret);
-extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *ret);
-extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
- __u16 count);
-extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
-errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
-
-/* inode.c */
-extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
-extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
- ext2_ino_t *ino,
- struct ext2_inode *inode,
- int bufsize);
-extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
- ext2_inode_scan *ret_scan);
-extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
-extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
- struct ext2_inode *inode);
-extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
- int group);
-extern void ext2fs_set_inode_callback
- (ext2_inode_scan scan,
- errcode_t (*done_group)(ext2_filsys fs,
- dgrp_t group,
- void * priv_data),
- void *done_group_data);
-extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
- int clear_flags);
-extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode,
- int bufsize);
-extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode);
-extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode,
- int bufsize);
-extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode);
-extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode);
-extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
-extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino);
-
-/* inode_io.c */
-extern io_manager inode_io_manager;
-extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
- char **name);
-extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- char **name);
-
-/* ismounted.c */
-extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
-extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
- char *mtpt, int mtlen);
-
-/* namei.c */
-extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
- int namelen, char *buf, ext2_ino_t *inode);
-extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- const char *name, ext2_ino_t *inode);
-errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- const char *name, ext2_ino_t *inode);
-extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- ext2_ino_t inode, ext2_ino_t *res_inode);
-
-/* native.c */
-int ext2fs_native_flag(void);
-
-/* newdir.c */
-extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
- ext2_ino_t parent_ino, char **block);
-
-/* mkdir.c */
-extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
- const char *name);
-
-/* mkjournal.c */
-extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
- __u32 size, int flags,
- char **ret_jsb);
-extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
- ext2_filsys journal_dev);
-extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
- int flags);
-
-/* openfs.c */
-extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
- unsigned int block_size, io_manager manager,
- ext2_filsys *ret_fs);
-extern errcode_t ext2fs_open2(const char *name, const char *io_options,
- int flags, int superblock,
- unsigned int block_size, io_manager manager,
- ext2_filsys *ret_fs);
-extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block,
- dgrp_t i);
-errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io);
-errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io);
-errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io);
-
-/* get_pathname.c */
-extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
- char **name);
-
-/* link.c */
-errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
- ext2_ino_t ino, int flags);
-errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
- ext2_ino_t ino, int flags);
-
-/* read_bb.c */
-extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
- ext2_badblocks_list *bb_list);
-
-/* read_bb_file.c */
-extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
- ext2_badblocks_list *bb_list,
- void *priv_data,
- void (*invalid)(ext2_filsys fs,
- blk_t blk,
- char *badstr,
- void *priv_data));
-extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
- ext2_badblocks_list *bb_list,
- void (*invalid)(ext2_filsys fs,
- blk_t blk));
-
-/* res_gdt.c */
-extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
-
-/* rs_bitmap.c */
-extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end,
- __u32 new_real_end,
- ext2fs_generic_bitmap bmap);
-extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
- ext2fs_inode_bitmap bmap);
-extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
- ext2fs_block_bitmap bmap);
-extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
- ext2fs_generic_bitmap *dest);
-
-/* swapfs.c */
-extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
- int has_header);
-extern void ext2fs_swap_super(struct ext2_super_block * super);
-extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
-extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
- struct ext2_inode_large *f, int hostorder,
- int bufsize);
-extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
- struct ext2_inode *f, int hostorder);
-
-/* valid_blk.c */
-extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
-
-/* version.c */
-extern int ext2fs_parse_version_string(const char *ver_string);
-extern int ext2fs_get_library_version(const char **ver_string,
- const char **date_string);
-
-/* write_bb_file.c */
-extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
- unsigned int flags,
- FILE *f);
-
-
-/* inline functions */
-extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
-extern errcode_t ext2fs_free_mem(void *ptr);
-extern errcode_t ext2fs_resize_mem(unsigned long old_size,
- unsigned long size, void *ptr);
-extern void ext2fs_mark_super_dirty(ext2_filsys fs);
-extern void ext2fs_mark_changed(ext2_filsys fs);
-extern int ext2fs_test_changed(ext2_filsys fs);
-extern void ext2fs_mark_valid(ext2_filsys fs);
-extern void ext2fs_unmark_valid(ext2_filsys fs);
-extern int ext2fs_test_valid(ext2_filsys fs);
-extern void ext2fs_mark_ib_dirty(ext2_filsys fs);
-extern void ext2fs_mark_bb_dirty(ext2_filsys fs);
-extern int ext2fs_test_ib_dirty(ext2_filsys fs);
-extern int ext2fs_test_bb_dirty(ext2_filsys fs);
-extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
-extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
-extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
- struct ext2_inode *inode);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
deleted file mode 100644
index 7a02e9a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
+++ b/dev/null
@@ -1,87 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ext2fsP.h --- private header file for ext2 library
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include "ext2fs.h"
-
-/*
- * Badblocks list
- */
-struct ext2_struct_u32_list {
- int magic;
- int num;
- int size;
- __u32 *list;
- int badblocks_flags;
-};
-
-struct ext2_struct_u32_iterate {
- int magic;
- ext2_u32_list bb;
- int ptr;
-};
-
-
-/*
- * Directory block iterator definition
- */
-struct ext2_struct_dblist {
- int magic;
- ext2_filsys fs;
- ext2_ino_t size;
- ext2_ino_t count;
- int sorted;
- struct ext2_db_entry * list;
-};
-
-/*
- * For directory iterators
- */
-struct dir_context {
- ext2_ino_t dir;
- int flags;
- char *buf;
- int (*func)(ext2_ino_t dir,
- int entry,
- struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data);
- void *priv_data;
- errcode_t errcode;
-};
-
-/*
- * Inode cache structure
- */
-struct ext2_inode_cache {
- void * buffer;
- blk_t buffer_blk;
- int cache_last;
- int cache_size;
- int refcount;
- struct ext2_inode_cache_ent *cache;
-};
-
-struct ext2_inode_cache_ent {
- ext2_ino_t ino;
- struct ext2_inode inode;
-};
-
-/* Function prototypes */
-
-extern int ext2fs_process_dir_block(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block,
- int ref_offset,
- void *priv_data);
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c b/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
deleted file mode 100644
index 7d37d23..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
+++ b/dev/null
@@ -1,365 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ext2fs.h --- ext2fs
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include "ext2fs.h"
-#include "bitops.h"
-#include <string.h>
-
-/*
- * Allocate memory
- */
-errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
-{
- void **pp = (void **)ptr;
-
- *pp = malloc(size);
- if (!*pp)
- return EXT2_ET_NO_MEMORY;
- return 0;
-}
-
-/*
- * Free memory
- */
-errcode_t ext2fs_free_mem(void *ptr)
-{
- void **pp = (void **)ptr;
-
- free(*pp);
- *pp = 0;
- return 0;
-}
-
-/*
- * Resize memory
- */
-errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
- unsigned long size, void *ptr)
-{
- void *p;
-
- /* Use "memcpy" for pointer assignments here to avoid problems
- * with C99 strict type aliasing rules. */
- memcpy(&p, ptr, sizeof (p));
- p = xrealloc(p, size);
- memcpy(ptr, &p, sizeof (p));
- return 0;
-}
-
-/*
- * Mark a filesystem superblock as dirty
- */
-void ext2fs_mark_super_dirty(ext2_filsys fs)
-{
- fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED;
-}
-
-/*
- * Mark a filesystem as changed
- */
-void ext2fs_mark_changed(ext2_filsys fs)
-{
- fs->flags |= EXT2_FLAG_CHANGED;
-}
-
-/*
- * Check to see if a filesystem has changed
- */
-int ext2fs_test_changed(ext2_filsys fs)
-{
- return (fs->flags & EXT2_FLAG_CHANGED);
-}
-
-/*
- * Mark a filesystem as valid
- */
-void ext2fs_mark_valid(ext2_filsys fs)
-{
- fs->flags |= EXT2_FLAG_VALID;
-}
-
-/*
- * Mark a filesystem as NOT valid
- */
-void ext2fs_unmark_valid(ext2_filsys fs)
-{
- fs->flags &= ~EXT2_FLAG_VALID;
-}
-
-/*
- * Check to see if a filesystem is valid
- */
-int ext2fs_test_valid(ext2_filsys fs)
-{
- return (fs->flags & EXT2_FLAG_VALID);
-}
-
-/*
- * Mark the inode bitmap as dirty
- */
-void ext2fs_mark_ib_dirty(ext2_filsys fs)
-{
- fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED;
-}
-
-/*
- * Mark the block bitmap as dirty
- */
-void ext2fs_mark_bb_dirty(ext2_filsys fs)
-{
- fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED;
-}
-
-/*
- * Check to see if a filesystem's inode bitmap is dirty
- */
-int ext2fs_test_ib_dirty(ext2_filsys fs)
-{
- return (fs->flags & EXT2_FLAG_IB_DIRTY);
-}
-
-/*
- * Check to see if a filesystem's block bitmap is dirty
- */
-int ext2fs_test_bb_dirty(ext2_filsys fs)
-{
- return (fs->flags & EXT2_FLAG_BB_DIRTY);
-}
-
-/*
- * Return the group # of a block
- */
-int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
-{
- return (blk - fs->super->s_first_data_block) /
- fs->super->s_blocks_per_group;
-}
-
-/*
- * Return the group # of an inode number
- */
-int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
-{
- return (ino - 1) / fs->super->s_inodes_per_group;
-}
-
-blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
- struct ext2_inode *inode)
-{
- return inode->i_blocks -
- (inode->i_file_acl ? fs->blocksize >> 9 : 0);
-}
-
-
-
-
-
-
-
-
-
-__u16 ext2fs_swab16(__u16 val)
-{
- return (val >> 8) | (val << 8);
-}
-
-__u32 ext2fs_swab32(__u32 val)
-{
- return ((val>>24) | ((val>>8)&0xFF00) |
- ((val<<8)&0xFF0000) | (val<<24));
-}
-
-int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
- blk_t bitno);
-
-int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
- blk_t bitno)
-{
- if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
- ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
- return 0;
- }
- return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap);
-}
-
-int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
- bitmap,
- block);
-}
-
-int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
- block);
-}
-
-int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
- block);
-}
-
-int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
- inode);
-}
-
-int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
- inode);
-}
-
-int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
- inode);
-}
-
-void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- ext2fs_set_bit(block - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap);
-}
-
-int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
- blk_t block)
-{
- return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap);
-}
-
-int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
- ext2_ino_t inode)
-{
- return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap);
-}
-
-blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap)
-{
- return bitmap->start;
-}
-
-ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap)
-{
- return bitmap->start;
-}
-
-blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap)
-{
- return bitmap->end;
-}
-
-ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap)
-{
- return bitmap->end;
-}
-
-int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
-
- if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
- ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
- block, bitmap->description);
- return 0;
- }
- for (i=0; i < num; i++) {
- if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
- return 0;
- }
- return 1;
-}
-
-int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
-
- for (i=0; i < num; i++) {
- if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
- return 0;
- }
- return 1;
-}
-
-void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
-
- if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
- ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
- bitmap->description);
- return;
- }
- for (i=0; i < num; i++)
- ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
-
- for (i=0; i < num; i++)
- ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
-
- if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
- ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
- bitmap->description);
- return;
- }
- for (i=0; i < num; i++)
- ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
-}
-
-void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
- blk_t block, int num)
-{
- int i;
- for (i=0; i < num; i++)
- ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c b/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
deleted file mode 100644
index a2ba12d..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
+++ b/dev/null
@@ -1,101 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ext_attr.c --- extended attribute blocks
- *
- * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
- *
- * Copyright (C) 2002 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2_ext_attr.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
-{
- errcode_t retval;
-
- retval = io_channel_read_blk(fs->io, block, 1, buf);
- if (retval)
- return retval;
-#if BB_BIG_ENDIAN
- if ((fs->flags & (EXT2_FLAG_SWAP_BYTES|
- EXT2_FLAG_SWAP_BYTES_READ)) != 0)
- ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
-#endif
- return 0;
-}
-
-errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
-{
- errcode_t retval;
- char *write_buf;
- char *buf = NULL;
-
- if (BB_BIG_ENDIAN && ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) {
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- write_buf = buf;
- ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
- } else
- write_buf = (char *) inbuf;
- retval = io_channel_write_blk(fs->io, block, 1, write_buf);
- if (buf)
- ext2fs_free_mem(&buf);
- if (!retval)
- ext2fs_mark_changed(fs);
- return retval;
-}
-
-/*
- * This function adjusts the reference count of the EA block.
- */
-errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
- char *block_buf, int adjust,
- __u32 *newcount)
-{
- errcode_t retval;
- struct ext2_ext_attr_header *header;
- char *buf = NULL;
-
- if ((blk >= fs->super->s_blocks_count) ||
- (blk < fs->super->s_first_data_block))
- return EXT2_ET_BAD_EA_BLOCK_NUM;
-
- if (!block_buf) {
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- block_buf = buf;
- }
-
- retval = ext2fs_read_ext_attr(fs, blk, block_buf);
- if (retval)
- goto errout;
-
- header = (struct ext2_ext_attr_header *) block_buf;
- header->h_refcount += adjust;
- if (newcount)
- *newcount = header->h_refcount;
-
- retval = ext2fs_write_ext_attr(fs, blk, block_buf);
- if (retval)
- goto errout;
-
-errout:
- if (buf)
- ext2fs_free_mem(&buf);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c b/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
deleted file mode 100644
index 5a5ad3e..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
+++ b/dev/null
@@ -1,377 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * fileio.c --- Simple file I/O routines
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct ext2_file {
- errcode_t magic;
- ext2_filsys fs;
- ext2_ino_t ino;
- struct ext2_inode inode;
- int flags;
- __u64 pos;
- blk_t blockno;
- blk_t physblock;
- char *buf;
-};
-
-#define BMAP_BUFFER (file->buf + fs->blocksize)
-
-errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- int flags, ext2_file_t *ret)
-{
- ext2_file_t file;
- errcode_t retval;
-
- /*
- * Don't let caller create or open a file for writing if the
- * filesystem is read-only.
- */
- if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
- !(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
- if (retval)
- return retval;
-
- memset(file, 0, sizeof(struct ext2_file));
- file->magic = EXT2_ET_MAGIC_EXT2_FILE;
- file->fs = fs;
- file->ino = ino;
- file->flags = flags & EXT2_FILE_MASK;
-
- if (inode) {
- memcpy(&file->inode, inode, sizeof(struct ext2_inode));
- } else {
- retval = ext2fs_read_inode(fs, ino, &file->inode);
- if (retval)
- goto fail;
- }
-
- retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
- if (retval)
- goto fail;
-
- *ret = file;
- return 0;
-
-fail:
- ext2fs_free_mem(&file->buf);
- ext2fs_free_mem(&file);
- return retval;
-}
-
-errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
- int flags, ext2_file_t *ret)
-{
- return ext2fs_file_open2(fs, ino, NULL, flags, ret);
-}
-
-/*
- * This function returns the filesystem handle of a file from the structure
- */
-ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
-{
- if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
- return 0;
- return file->fs;
-}
-
-/*
- * This function flushes the dirty block buffer out to disk if
- * necessary.
- */
-errcode_t ext2fs_file_flush(ext2_file_t file)
-{
- errcode_t retval;
- ext2_filsys fs;
-
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
- fs = file->fs;
-
- if (!(file->flags & EXT2_FILE_BUF_VALID) ||
- !(file->flags & EXT2_FILE_BUF_DIRTY))
- return 0;
-
- /*
- * OK, the physical block hasn't been allocated yet.
- * Allocate it.
- */
- if (!file->physblock) {
- retval = ext2fs_bmap(fs, file->ino, &file->inode,
- BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
- file->blockno, &file->physblock);
- if (retval)
- return retval;
- }
-
- retval = io_channel_write_blk(fs->io, file->physblock,
- 1, file->buf);
- if (retval)
- return retval;
-
- file->flags &= ~EXT2_FILE_BUF_DIRTY;
-
- return retval;
-}
-
-/*
- * This function synchronizes the file's block buffer and the current
- * file position, possibly invalidating block buffer if necessary
- */
-static errcode_t sync_buffer_position(ext2_file_t file)
-{
- blk_t b;
- errcode_t retval;
-
- b = file->pos / file->fs->blocksize;
- if (b != file->blockno) {
- retval = ext2fs_file_flush(file);
- if (retval)
- return retval;
- file->flags &= ~EXT2_FILE_BUF_VALID;
- }
- file->blockno = b;
- return 0;
-}
-
-/*
- * This function loads the file's block buffer with valid data from
- * the disk as necessary.
- *
- * If dontfill is true, then skip initializing the buffer since we're
- * going to be replacing its entire contents anyway. If set, then the
- * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
- */
-#define DONTFILL 1
-static errcode_t load_buffer(ext2_file_t file, int dontfill)
-{
- ext2_filsys fs = file->fs;
- errcode_t retval;
-
- if (!(file->flags & EXT2_FILE_BUF_VALID)) {
- retval = ext2fs_bmap(fs, file->ino, &file->inode,
- BMAP_BUFFER, 0, file->blockno,
- &file->physblock);
- if (retval)
- return retval;
- if (!dontfill) {
- if (file->physblock) {
- retval = io_channel_read_blk(fs->io,
- file->physblock,
- 1, file->buf);
- if (retval)
- return retval;
- } else
- memset(file->buf, 0, fs->blocksize);
- }
- file->flags |= EXT2_FILE_BUF_VALID;
- }
- return 0;
-}
-
-
-errcode_t ext2fs_file_close(ext2_file_t file)
-{
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
-
- retval = ext2fs_file_flush(file);
-
- ext2fs_free_mem(&file->buf);
- ext2fs_free_mem(&file);
-
- return retval;
-}
-
-
-errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
- unsigned int wanted, unsigned int *got)
-{
- ext2_filsys fs;
- errcode_t retval = 0;
- unsigned int start, c, count = 0;
- __u64 left;
- char *ptr = (char *) buf;
-
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
- fs = file->fs;
-
- while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
- retval = sync_buffer_position(file);
- if (retval)
- goto fail;
- retval = load_buffer(file, 0);
- if (retval)
- goto fail;
-
- start = file->pos % fs->blocksize;
- c = fs->blocksize - start;
- if (c > wanted)
- c = wanted;
- left = EXT2_I_SIZE(&file->inode) - file->pos;
- if (c > left)
- c = left;
-
- memcpy(ptr, file->buf+start, c);
- file->pos += c;
- ptr += c;
- count += c;
- wanted -= c;
- }
-
-fail:
- if (got)
- *got = count;
- return retval;
-}
-
-
-errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
- unsigned int nbytes, unsigned int *written)
-{
- ext2_filsys fs;
- errcode_t retval = 0;
- unsigned int start, c, count = 0;
- const char *ptr = (const char *) buf;
-
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
- fs = file->fs;
-
- if (!(file->flags & EXT2_FILE_WRITE))
- return EXT2_ET_FILE_RO;
-
- while (nbytes > 0) {
- retval = sync_buffer_position(file);
- if (retval)
- goto fail;
-
- start = file->pos % fs->blocksize;
- c = fs->blocksize - start;
- if (c > nbytes)
- c = nbytes;
-
- /*
- * We only need to do a read-modify-update cycle if
- * we're doing a partial write.
- */
- retval = load_buffer(file, (c == fs->blocksize));
- if (retval)
- goto fail;
-
- file->flags |= EXT2_FILE_BUF_DIRTY;
- memcpy(file->buf+start, ptr, c);
- file->pos += c;
- ptr += c;
- count += c;
- nbytes -= c;
- }
-
-fail:
- if (written)
- *written = count;
- return retval;
-}
-
-errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
- int whence, __u64 *ret_pos)
-{
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
-
- if (whence == EXT2_SEEK_SET)
- file->pos = offset;
- else if (whence == EXT2_SEEK_CUR)
- file->pos += offset;
- else if (whence == EXT2_SEEK_END)
- file->pos = EXT2_I_SIZE(&file->inode) + offset;
- else
- return EXT2_ET_INVALID_ARGUMENT;
-
- if (ret_pos)
- *ret_pos = file->pos;
-
- return 0;
-}
-
-errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
- int whence, ext2_off_t *ret_pos)
-{
- __u64 loffset, ret_loffset;
- errcode_t retval;
-
- loffset = offset;
- retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
- if (ret_pos)
- *ret_pos = (ext2_off_t) ret_loffset;
- return retval;
-}
-
-
-/*
- * This function returns the size of the file, according to the inode
- */
-errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
-{
- if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
- return EXT2_ET_MAGIC_EXT2_FILE;
- *ret_size = EXT2_I_SIZE(&file->inode);
- return 0;
-}
-
-/*
- * This function returns the size of the file, according to the inode
- */
-ext2_off_t ext2fs_file_get_size(ext2_file_t file)
-{
- __u64 size;
-
- if (ext2fs_file_get_lsize(file, &size))
- return 0;
- if ((size >> 32) != 0)
- return 0;
- return size;
-}
-
-/*
- * This function sets the size of the file, truncating it if necessary
- *
- * XXX still need to call truncate
- */
-errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
-{
- errcode_t retval;
- EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
-
- file->inode.i_size = size;
- file->inode.i_size_high = 0;
- if (file->ino) {
- retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
- if (retval)
- return retval;
- }
-
- /*
- * XXX truncate inode if necessary
- */
-
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c b/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
deleted file mode 100644
index e9e83fd..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
+++ b/dev/null
@@ -1,199 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * finddev.c -- this routine attempts to find a particular device in
- * /dev
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#include <dirent.h>
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct dir_list {
- char *name;
- struct dir_list *next;
-};
-
-/*
- * This function adds an entry to the directory list
- */
-static void add_to_dirlist(const char *name, struct dir_list **list)
-{
- struct dir_list *dp;
-
- dp = xmalloc(sizeof(struct dir_list));
- dp->name = xmalloc(strlen(name)+1);
- strcpy(dp->name, name);
- dp->next = *list;
- *list = dp;
-}
-
-/*
- * This function frees a directory list
- */
-static void free_dirlist(struct dir_list **list)
-{
- struct dir_list *dp, *next;
-
- for (dp = *list; dp; dp = next) {
- next = dp->next;
- free(dp->name);
- free(dp);
- }
- *list = 0;
-}
-
-static int scan_dir(char *dir_name, dev_t device, struct dir_list **list,
- char **ret_path)
-{
- DIR *dir;
- struct dirent *dp;
- char path[1024], *cp;
- int dirlen;
- struct stat st;
-
- dirlen = strlen(dir_name);
- if ((dir = opendir(dir_name)) == NULL)
- return errno;
- dp = readdir(dir);
- while (dp) {
- if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
- goto skip_to_next;
- if (dp->d_name[0] == '.' &&
- ((dp->d_name[1] == 0) ||
- ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
- goto skip_to_next;
- sprintf(path, "%s/%s", dir_name, dp->d_name);
- if (stat(path, &st) < 0)
- goto skip_to_next;
- if (S_ISDIR(st.st_mode))
- add_to_dirlist(path, list);
- if (S_ISBLK(st.st_mode) && st.st_rdev == device) {
- cp = xmalloc(strlen(path)+1);
- strcpy(cp, path);
- *ret_path = cp;
- goto success;
- }
- skip_to_next:
- dp = readdir(dir);
- }
-success:
- closedir(dir);
- return 0;
-}
-
-/*
- * This function finds the pathname to a block device with a given
- * device number. It returns a pointer to allocated memory to the
- * pathname on success, and NULL on failure.
- */
-char *ext2fs_find_block_device(dev_t device)
-{
- struct dir_list *list = NULL, *new_list = NULL;
- struct dir_list *current;
- char *ret_path = NULL;
-
- /*
- * Add the starting directories to search...
- */
- add_to_dirlist("/devices", &list);
- add_to_dirlist("/devfs", &list);
- add_to_dirlist("/dev", &list);
-
- while (list) {
- current = list;
- list = list->next;
-#ifdef DEBUG
- printf("Scanning directory %s\n", current->name);
-#endif
- scan_dir(current->name, device, &new_list, &ret_path);
- free(current->name);
- free(current);
- if (ret_path)
- break;
- /*
- * If we're done checking at this level, descend to
- * the next level of subdirectories. (breadth-first)
- */
- if (list == 0) {
- list = new_list;
- new_list = 0;
- }
- }
- free_dirlist(&list);
- free_dirlist(&new_list);
- return ret_path;
-}
-
-
-#ifdef DEBUG
-int main(int argc, char** argv)
-{
- char *devname, *tmp;
- int major, minor;
- dev_t device;
- const char *errmsg = "Cannot parse %s: %s\n";
-
- if ((argc != 2) && (argc != 3)) {
- fprintf(stderr, "Usage: %s device_number\n", argv[0]);
- fprintf(stderr, "\t: %s major minor\n", argv[0]);
- exit(1);
- }
- if (argc == 2) {
- device = strtoul(argv[1], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "device number", argv[1]);
- exit(1);
- }
- } else {
- major = strtoul(argv[1], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "major number", argv[1]);
- exit(1);
- }
- minor = strtoul(argv[2], &tmp, 0);
- if (*tmp) {
- fprintf(stderr, errmsg, "minor number", argv[2]);
- exit(1);
- }
- device = makedev(major, minor);
- printf("Looking for device 0x%04x (%d:%d)\n", device,
- major, minor);
- }
- devname = ext2fs_find_block_device(device);
- if (devname) {
- printf("Found device! %s\n", devname);
- free(devname);
- } else {
- printf("Cannot find device.\n");
- }
- return 0;
-}
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c b/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
deleted file mode 100644
index 45ed765..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
+++ b/dev/null
@@ -1,83 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * flushb.c --- Hides system-dependent information for both syncing a
- * device to disk and to flush any buffers from disk cache.
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#if HAVE_SYS_MOUNT_H
-#include <sys/param.h>
-#include <sys/mount.h> /* This may define BLKFLSBUF */
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since
- * not all portable header file does so for us. This really should be
- * fixed in the glibc header files. (Recent glibcs appear to define
- * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be
- * defined anywhere portable.) Until then....
- */
-#ifdef __linux__
-#ifndef BLKFLSBUF
-#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
-#endif
-#ifndef FDFLUSH
-#define FDFLUSH _IO(2,0x4b) /* flush floppy disk */
-#endif
-#endif
-
-/*
- * This function will sync a device/file, and optionally attempt to
- * flush the buffer cache. The latter is basically only useful for
- * system benchmarks and for torturing systems in burn-in tests. :)
- */
-errcode_t ext2fs_sync_device(int fd, int flushb)
-{
- /*
- * We always sync the device in case we're running on old
- * kernels for which we can lose data if we don't. (There
- * still is a race condition for those kernels, but this
- * reduces it greatly.)
- */
- if (fsync (fd) == -1)
- return errno;
-
- if (flushb) {
-
-#ifdef BLKFLSBUF
- if (ioctl (fd, BLKFLSBUF, 0) == 0)
- return 0;
-#else
-#ifdef __GNUC__
-# warning BLKFLSBUF not defined
-#endif /* __GNUC__ */
-#endif
-#ifdef FDFLUSH
- ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */
-#else
-#ifdef __GNUC__
-# warning FDFLUSH not defined
-#endif /* __GNUC__ */
-#endif
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c b/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
deleted file mode 100644
index 0c5d48b..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
+++ b/dev/null
@@ -1,127 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * freefs.c --- free an ext2 filesystem
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
-
-void ext2fs_free(ext2_filsys fs)
-{
- if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS))
- return;
- if (fs->image_io != fs->io) {
- if (fs->image_io)
- io_channel_close(fs->image_io);
- }
- if (fs->io) {
- io_channel_close(fs->io);
- }
- ext2fs_free_mem(&fs->device_name);
- ext2fs_free_mem(&fs->super);
- ext2fs_free_mem(&fs->orig_super);
- ext2fs_free_mem(&fs->group_desc);
- ext2fs_free_block_bitmap(fs->block_map);
- ext2fs_free_inode_bitmap(fs->inode_map);
-
- ext2fs_badblocks_list_free(fs->badblocks);
- fs->badblocks = 0;
-
- ext2fs_free_dblist(fs->dblist);
-
- if (fs->icache)
- ext2fs_free_inode_cache(fs->icache);
-
- fs->magic = 0;
-
- ext2fs_free_mem(&fs);
-}
-
-void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap)
-{
- if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP))
- return;
-
- bitmap->magic = 0;
- ext2fs_free_mem(&bitmap->description);
- ext2fs_free_mem(&bitmap->bitmap);
- ext2fs_free_mem(&bitmap);
-}
-
-void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap)
-{
- if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
- return;
-
- bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
- ext2fs_free_generic_bitmap(bitmap);
-}
-
-void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap)
-{
- if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
- return;
-
- bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
- ext2fs_free_generic_bitmap(bitmap);
-}
-
-/*
- * Free the inode cache structure
- */
-static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
-{
- if (--icache->refcount)
- return;
- ext2fs_free_mem(&icache->buffer);
- ext2fs_free_mem(&icache->cache);
- icache->buffer_blk = 0;
- ext2fs_free_mem(&icache);
-}
-
-/*
- * This procedure frees a badblocks list.
- */
-void ext2fs_u32_list_free(ext2_u32_list bb)
-{
- if (!bb || bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
- return;
-
- ext2fs_free_mem(&bb->list);
- ext2fs_free_mem(&bb);
-}
-
-void ext2fs_badblocks_list_free(ext2_badblocks_list bb)
-{
- ext2fs_u32_list_free((ext2_u32_list) bb);
-}
-
-
-/*
- * Free a directory block list
- */
-void ext2fs_free_dblist(ext2_dblist dblist)
-{
- if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST))
- return;
-
- ext2fs_free_mem(&dblist->list);
- if (dblist->fs && dblist->fs->dblist == dblist)
- dblist->fs->dblist = 0;
- dblist->magic = 0;
- ext2fs_free_mem(&dblist);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
deleted file mode 100644
index d0869c9..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
+++ b/dev/null
@@ -1,49 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * gen_bitmap.c --- Generic bitmap routines that used to be inlined.
- *
- * Copyright (C) 2001 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
- __u32 bitno)
-{
- if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
- ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno);
- return 0;
- }
- return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap);
-}
-
-int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
- blk_t bitno)
-{
- if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
- ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
- return 0;
- }
- return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c b/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
deleted file mode 100644
index 2bb1cc2..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
+++ b/dev/null
@@ -1,156 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * get_pathname.c --- do directry/inode -> name translation
- *
- * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- * ext2fs_get_pathname(fs, dir, ino, name)
- *
- * This function translates takes two inode numbers into a
- * string, placing the result in <name>. <dir> is the containing
- * directory inode, and <ino> is the inode number itself. If
- * <ino> is zero, then ext2fs_get_pathname will return pathname
- * of the directory <dir>.
- *
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct get_pathname_struct {
- ext2_ino_t search_ino;
- ext2_ino_t parent;
- char *name;
- errcode_t errcode;
-};
-
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int get_pathname_proc(struct ext2_dir_entry *dirent,
- int offset EXT2FS_ATTR((unused)),
- int blocksize EXT2FS_ATTR((unused)),
- char *buf EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct get_pathname_struct *gp;
- errcode_t retval;
-
- gp = (struct get_pathname_struct *) priv_data;
-
- if (((dirent->name_len & 0xFF) == 2) &&
- !strncmp(dirent->name, "..", 2))
- gp->parent = dirent->inode;
- if (dirent->inode == gp->search_ino) {
- retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1,
- &gp->name);
- if (retval) {
- gp->errcode = retval;
- return DIRENT_ABORT;
- }
- strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF));
- gp->name[dirent->name_len & 0xFF] = '\0';
- return DIRENT_ABORT;
- }
- return 0;
-}
-
-static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir,
- ext2_ino_t ino, int maxdepth,
- char *buf, char **name)
-{
- struct get_pathname_struct gp;
- char *parent_name, *ret;
- errcode_t retval;
-
- if (dir == ino) {
- retval = ext2fs_get_mem(2, name);
- if (retval)
- return retval;
- strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : ".");
- return 0;
- }
-
- if (!dir || (maxdepth < 0)) {
- retval = ext2fs_get_mem(4, name);
- if (retval)
- return retval;
- strcpy(*name, "...");
- return 0;
- }
-
- gp.search_ino = ino;
- gp.parent = 0;
- gp.name = 0;
- gp.errcode = 0;
-
- retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp);
- if (retval)
- goto cleanup;
- if (gp.errcode) {
- retval = gp.errcode;
- goto cleanup;
- }
-
- retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1,
- buf, &parent_name);
- if (retval)
- goto cleanup;
- if (!ino) {
- *name = parent_name;
- return 0;
- }
-
- if (gp.name)
- retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2,
- &ret);
- else
- retval = ext2fs_get_mem(strlen(parent_name)+5, &ret);
- if (retval)
- goto cleanup;
-
- ret[0] = 0;
- if (parent_name[1])
- strcat(ret, parent_name);
- strcat(ret, "/");
- if (gp.name)
- strcat(ret, gp.name);
- else
- strcat(ret, "???");
- *name = ret;
- ext2fs_free_mem(&parent_name);
- retval = 0;
-
-cleanup:
- ext2fs_free_mem(&gp.name);
- return retval;
-}
-
-errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
- char **name)
-{
- char *buf;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- if (dir == ino)
- ino = 0;
- retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name);
- ext2fs_free_mem(&buf);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
deleted file mode 100644
index 163ec65..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
+++ b/dev/null
@@ -1,58 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * getsectsize.c --- get the sector size of a device.
- *
- * Copyright (C) 1995, 1995 Theodore Ts'o.
- * Copyright (C) 2003 VMware, Inc.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#ifdef HAVE_LINUX_FD_H
-#include <sys/ioctl.h>
-#include <linux/fd.h>
-#endif
-
-#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
-#define BLKSSZGET _IO(0x12,104)/* get block device sector size */
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * Returns the number of blocks in a partition
- */
-errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize)
-{
- int fd;
-
-#ifdef CONFIG_LFS
- fd = open64(file, O_RDONLY);
-#else
- fd = open(file, O_RDONLY);
-#endif
- if (fd < 0)
- return errno;
-
-#ifdef BLKSSZGET
- if (ioctl(fd, BLKSSZGET, sectsize) >= 0) {
- close(fd);
- return 0;
- }
-#endif
- *sectsize = 0;
- close(fd);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
deleted file mode 100644
index ee4bbb7..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
+++ b/dev/null
@@ -1,291 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * getsize.c --- get the size of a partition.
- *
- * Copyright (C) 1995, 1995 Theodore Ts'o.
- * Copyright (C) 2003 VMware, Inc.
- *
- * Windows version of ext2fs_get_device_size by Chris Li, VMware.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#ifdef HAVE_LINUX_FD_H
-#include <linux/fd.h>
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
-#include <sys/disklabel.h>
-#endif
-#ifdef HAVE_SYS_DISK_H
-#ifdef HAVE_SYS_QUEUE_H
-#include <sys/queue.h> /* for LIST_HEAD */
-#endif
-#include <sys/disk.h>
-#endif
-#ifdef __linux__
-#include <sys/utsname.h>
-#endif
-
-#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
-#define BLKGETSIZE _IO(0x12,96) /* return device size */
-#endif
-
-#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
-#define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
-#endif
-
-#ifdef APPLE_DARWIN
-#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
-#endif /* APPLE_DARWIN */
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#if defined(__CYGWIN__) || defined(WIN32)
-#include <windows.h>
-#include <winioctl.h>
-
-#if (_WIN32_WINNT >= 0x0500)
-#define HAVE_GET_FILE_SIZE_EX 1
-#endif
-
-errcode_t ext2fs_get_device_size(const char *file, int blocksize,
- blk_t *retblocks)
-{
- HANDLE dev;
- PARTITION_INFORMATION pi;
- DISK_GEOMETRY gi;
- DWORD retbytes;
-#ifdef HAVE_GET_FILE_SIZE_EX
- LARGE_INTEGER filesize;
-#else
- DWORD filesize;
-#endif /* HAVE_GET_FILE_SIZE_EX */
-
- dev = CreateFile(file, GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
- if (dev == INVALID_HANDLE_VALUE)
- return EBADF;
- if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
- &pi, sizeof(PARTITION_INFORMATION),
- &pi, sizeof(PARTITION_INFORMATION),
- &retbytes, NULL)) {
-
- *retblocks = pi.PartitionLength.QuadPart / blocksize;
-
- } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
- &gi, sizeof(DISK_GEOMETRY),
- &gi, sizeof(DISK_GEOMETRY),
- &retbytes, NULL)) {
-
- *retblocks = gi.BytesPerSector *
- gi.SectorsPerTrack *
- gi.TracksPerCylinder *
- gi.Cylinders.QuadPart / blocksize;
-
-#ifdef HAVE_GET_FILE_SIZE_EX
- } else if (GetFileSizeEx(dev, &filesize)) {
- *retblocks = filesize.QuadPart / blocksize;
- }
-#else
- } else {
- filesize = GetFileSize(dev, NULL);
- if (INVALID_FILE_SIZE != filesize) {
- *retblocks = filesize / blocksize;
- }
- }
-#endif /* HAVE_GET_FILE_SIZE_EX */
-
- CloseHandle(dev);
- return 0;
-}
-
-#else
-
-static int valid_offset (int fd, ext2_loff_t offset)
-{
- char ch;
-
- if (ext2fs_llseek (fd, offset, 0) < 0)
- return 0;
- if (read (fd, &ch, 1) < 1)
- return 0;
- return 1;
-}
-
-/*
- * Returns the number of blocks in a partition
- */
-errcode_t ext2fs_get_device_size(const char *file, int blocksize,
- blk_t *retblocks)
-{
- int fd;
- int valid_blkgetsize64 = 1;
-#ifdef __linux__
- struct utsname ut;
-#endif
- unsigned long long size64;
- unsigned long size;
- ext2_loff_t high, low;
-#ifdef FDGETPRM
- struct floppy_struct this_floppy;
-#endif
-#ifdef HAVE_SYS_DISKLABEL_H
- int part;
- struct disklabel lab;
- struct partition *pp;
- char ch;
-#endif /* HAVE_SYS_DISKLABEL_H */
-
-#ifdef CONFIG_LFS
- fd = open64(file, O_RDONLY);
-#else
- fd = open(file, O_RDONLY);
-#endif
- if (fd < 0)
- return errno;
-
-#ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
- if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
- if ((sizeof(*retblocks) < sizeof(unsigned long long))
- && ((size64 / (blocksize / 512)) > 0xFFFFFFFF))
- return EFBIG;
- close(fd);
- *retblocks = size64 / (blocksize / 512);
- return 0;
- }
-#endif
-
-#ifdef BLKGETSIZE64
-#ifdef __linux__
- uname(&ut);
- if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
- (ut.release[2] < '6') && (ut.release[3] == '.'))
- valid_blkgetsize64 = 0;
-#endif
- if (valid_blkgetsize64 &&
- ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
- if ((sizeof(*retblocks) < sizeof(unsigned long long))
- && ((size64 / blocksize) > 0xFFFFFFFF))
- return EFBIG;
- close(fd);
- *retblocks = size64 / blocksize;
- return 0;
- }
-#endif
-
-#ifdef BLKGETSIZE
- if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
- close(fd);
- *retblocks = size / (blocksize / 512);
- return 0;
- }
-#endif
-
-#ifdef FDGETPRM
- if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
- close(fd);
- *retblocks = this_floppy.size / (blocksize / 512);
- return 0;
- }
-#endif
-
-#ifdef HAVE_SYS_DISKLABEL_H
-#if defined(DIOCGMEDIASIZE)
- {
- off_t ms;
- u_int bs;
- if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
- *retblocks = ms / blocksize;
- return 0;
- }
- }
-#elif defined(DIOCGDINFO)
- /* old disklabel interface */
- part = strlen(file) - 1;
- if (part >= 0) {
- ch = file[part];
- if (isdigit(ch))
- part = 0;
- else if (ch >= 'a' && ch <= 'h')
- part = ch - 'a';
- else
- part = -1;
- }
- if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
- pp = &lab.d_partitions[part];
- if (pp->p_size) {
- close(fd);
- *retblocks = pp->p_size / (blocksize / 512);
- return 0;
- }
- }
-#endif /* defined(DIOCG*) */
-#endif /* HAVE_SYS_DISKLABEL_H */
-
- /*
- * OK, we couldn't figure it out by using a specialized ioctl,
- * which is generally the best way. So do binary search to
- * find the size of the partition.
- */
- low = 0;
- for (high = 1024; valid_offset (fd, high); high *= 2)
- low = high;
- while (low < high - 1)
- {
- const ext2_loff_t mid = (low + high) / 2;
-
- if (valid_offset (fd, mid))
- low = mid;
- else
- high = mid;
- }
- valid_offset (fd, 0);
- close(fd);
- size64 = low + 1;
- if ((sizeof(*retblocks) < sizeof(unsigned long long))
- && ((size64 / blocksize) > 0xFFFFFFFF))
- return EFBIG;
- *retblocks = size64 / blocksize;
- return 0;
-}
-
-#endif /* WIN32 */
-
-#ifdef DEBUG
-int main(int argc, char **argv)
-{
- blk_t blocks;
- int retval;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s device\n", argv[0]);
- exit(1);
- }
-
- retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
- if (retval) {
- com_err(argv[0], retval,
- "while calling ext2fs_get_device_size");
- exit(1);
- }
- printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
- exit(0);
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/icount.c b/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
deleted file mode 100644
index 7ab5f51..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
+++ b/dev/null
@@ -1,467 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * icount.c --- an efficient inode count abstraction
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <stdio.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * The data storage strategy used by icount relies on the observation
- * that most inode counts are either zero (for non-allocated inodes),
- * one (for most files), and only a few that are two or more
- * (directories and files that are linked to more than one directory).
- *
- * Also, e2fsck tends to load the icount data sequentially.
- *
- * So, we use an inode bitmap to indicate which inodes have a count of
- * one, and then use a sorted list to store the counts for inodes
- * which are greater than one.
- *
- * We also use an optional bitmap to indicate which inodes are already
- * in the sorted list, to speed up the use of this abstraction by
- * e2fsck's pass 2. Pass 2 increments inode counts as it finds them,
- * so this extra bitmap avoids searching the sorted list to see if a
- * particular inode is on the sorted list already.
- */
-
-struct ext2_icount_el {
- ext2_ino_t ino;
- __u16 count;
-};
-
-struct ext2_icount {
- errcode_t magic;
- ext2fs_inode_bitmap single;
- ext2fs_inode_bitmap multiple;
- ext2_ino_t count;
- ext2_ino_t size;
- ext2_ino_t num_inodes;
- ext2_ino_t cursor;
- struct ext2_icount_el *list;
-};
-
-void ext2fs_free_icount(ext2_icount_t icount)
-{
- if (!icount)
- return;
-
- icount->magic = 0;
- ext2fs_free_mem(&icount->list);
- ext2fs_free_inode_bitmap(icount->single);
- ext2fs_free_inode_bitmap(icount->multiple);
- ext2fs_free_mem(&icount);
-}
-
-errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
- ext2_icount_t hint, ext2_icount_t *ret)
-{
- ext2_icount_t icount;
- errcode_t retval;
- size_t bytes;
- ext2_ino_t i;
-
- if (hint) {
- EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
- if (hint->size > size)
- size = (size_t) hint->size;
- }
-
- retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
- if (retval)
- return retval;
- memset(icount, 0, sizeof(struct ext2_icount));
-
- retval = ext2fs_allocate_inode_bitmap(fs, 0,
- &icount->single);
- if (retval)
- goto errout;
-
- if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
- retval = ext2fs_allocate_inode_bitmap(fs, 0,
- &icount->multiple);
- if (retval)
- goto errout;
- } else
- icount->multiple = 0;
-
- if (size) {
- icount->size = size;
- } else {
- /*
- * Figure out how many special case inode counts we will
- * have. We know we will need one for each directory;
- * we also need to reserve some extra room for file links
- */
- retval = ext2fs_get_num_dirs(fs, &icount->size);
- if (retval)
- goto errout;
- icount->size += fs->super->s_inodes_count / 50;
- }
-
- bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
- retval = ext2fs_get_mem(bytes, &icount->list);
- if (retval)
- goto errout;
- memset(icount->list, 0, bytes);
-
- icount->magic = EXT2_ET_MAGIC_ICOUNT;
- icount->count = 0;
- icount->cursor = 0;
- icount->num_inodes = fs->super->s_inodes_count;
-
- /*
- * Populate the sorted list with those entries which were
- * found in the hint icount (since those are ones which will
- * likely need to be in the sorted list this time around).
- */
- if (hint) {
- for (i=0; i < hint->count; i++)
- icount->list[i].ino = hint->list[i].ino;
- icount->count = hint->count;
- }
-
- *ret = icount;
- return 0;
-
-errout:
- ext2fs_free_icount(icount);
- return retval;
-}
-
-errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
- unsigned int size,
- ext2_icount_t *ret)
-{
- return ext2fs_create_icount2(fs, flags, size, 0, ret);
-}
-
-/*
- * insert_icount_el() --- Insert a new entry into the sorted list at a
- * specified position.
- */
-static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
- ext2_ino_t ino, int pos)
-{
- struct ext2_icount_el *el;
- errcode_t retval;
- ext2_ino_t new_size = 0;
- int num;
-
- if (icount->count >= icount->size) {
- if (icount->count) {
- new_size = icount->list[(unsigned)icount->count-1].ino;
- new_size = (ext2_ino_t) (icount->count *
- ((float) icount->num_inodes / new_size));
- }
- if (new_size < (icount->size + 100))
- new_size = icount->size + 100;
- retval = ext2fs_resize_mem((size_t) icount->size *
- sizeof(struct ext2_icount_el),
- (size_t) new_size *
- sizeof(struct ext2_icount_el),
- &icount->list);
- if (retval)
- return 0;
- icount->size = new_size;
- }
- num = (int) icount->count - pos;
- if (num < 0)
- return 0; /* should never happen */
- if (num) {
- memmove(&icount->list[pos+1], &icount->list[pos],
- sizeof(struct ext2_icount_el) * num);
- }
- icount->count++;
- el = &icount->list[pos];
- el->count = 0;
- el->ino = ino;
- return el;
-}
-
-/*
- * get_icount_el() --- given an inode number, try to find icount
- * information in the sorted list. If the create flag is set,
- * and we can't find an entry, create one in the sorted list.
- */
-static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
- ext2_ino_t ino, int create)
-{
- float range;
- int low, high, mid;
- ext2_ino_t lowval, highval;
-
- if (!icount || !icount->list)
- return 0;
-
- if (create && ((icount->count == 0) ||
- (ino > icount->list[(unsigned)icount->count-1].ino))) {
- return insert_icount_el(icount, ino, (unsigned) icount->count);
- }
- if (icount->count == 0)
- return 0;
-
- if (icount->cursor >= icount->count)
- icount->cursor = 0;
- if (ino == icount->list[icount->cursor].ino)
- return &icount->list[icount->cursor++];
- low = 0;
- high = (int) icount->count-1;
- while (low <= high) {
- if (low == high)
- mid = low;
- else {
- /* Interpolate for efficiency */
- lowval = icount->list[low].ino;
- highval = icount->list[high].ino;
-
- if (ino < lowval)
- range = 0;
- else if (ino > highval)
- range = 1;
- else
- range = ((float) (ino - lowval)) /
- (highval - lowval);
- mid = low + ((int) (range * (high-low)));
- }
- if (ino == icount->list[mid].ino) {
- icount->cursor = mid+1;
- return &icount->list[mid];
- }
- if (ino < icount->list[mid].ino)
- high = mid-1;
- else
- low = mid+1;
- }
- /*
- * If we need to create a new entry, it should be right at
- * low (where high will be left at low-1).
- */
- if (create)
- return insert_icount_el(icount, ino, low);
- return 0;
-}
-
-errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
-{
- errcode_t ret = 0;
- unsigned int i;
- const char *bad = "bad icount";
-
- EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
-
- if (icount->count > icount->size) {
- fprintf(out, "%s: count > size\n", bad);
- return EXT2_ET_INVALID_ARGUMENT;
- }
- for (i=1; i < icount->count; i++) {
- if (icount->list[i-1].ino >= icount->list[i].ino) {
- fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
- bad, i-1, icount->list[i-1].ino,
- i, icount->list[i].ino);
- ret = EXT2_ET_INVALID_ARGUMENT;
- }
- }
- return ret;
-}
-
-errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
-{
- struct ext2_icount_el *el;
-
- EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
-
- if (!ino || (ino > icount->num_inodes))
- return EXT2_ET_INVALID_ARGUMENT;
-
- if (ext2fs_test_inode_bitmap(icount->single, ino)) {
- *ret = 1;
- return 0;
- }
- if (icount->multiple &&
- !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
- *ret = 0;
- return 0;
- }
- el = get_icount_el(icount, ino, 0);
- if (!el) {
- *ret = 0;
- return 0;
- }
- *ret = el->count;
- return 0;
-}
-
-errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *ret)
-{
- struct ext2_icount_el *el;
-
- EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
-
- if (!ino || (ino > icount->num_inodes))
- return EXT2_ET_INVALID_ARGUMENT;
-
- if (ext2fs_test_inode_bitmap(icount->single, ino)) {
- /*
- * If the existing count is 1, then we know there is
- * no entry in the list.
- */
- el = get_icount_el(icount, ino, 1);
- if (!el)
- return EXT2_ET_NO_MEMORY;
- ext2fs_unmark_inode_bitmap(icount->single, ino);
- el->count = 2;
- } else if (icount->multiple) {
- /*
- * The count is either zero or greater than 1; if the
- * inode is set in icount->multiple, then there should
- * be an entry in the list, so find it using
- * get_icount_el().
- */
- if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
- el = get_icount_el(icount, ino, 1);
- if (!el)
- return EXT2_ET_NO_MEMORY;
- el->count++;
- } else {
- /*
- * The count was zero; mark the single bitmap
- * and return.
- */
- zero_count:
- ext2fs_mark_inode_bitmap(icount->single, ino);
- if (ret)
- *ret = 1;
- return 0;
- }
- } else {
- /*
- * The count is either zero or greater than 1; try to
- * find an entry in the list to determine which.
- */
- el = get_icount_el(icount, ino, 0);
- if (!el) {
- /* No entry means the count was zero */
- goto zero_count;
- }
- el = get_icount_el(icount, ino, 1);
- if (!el)
- return EXT2_ET_NO_MEMORY;
- el->count++;
- }
- if (icount->multiple)
- ext2fs_mark_inode_bitmap(icount->multiple, ino);
- if (ret)
- *ret = el->count;
- return 0;
-}
-
-errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *ret)
-{
- struct ext2_icount_el *el;
-
- if (!ino || (ino > icount->num_inodes))
- return EXT2_ET_INVALID_ARGUMENT;
-
- EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
-
- if (ext2fs_test_inode_bitmap(icount->single, ino)) {
- ext2fs_unmark_inode_bitmap(icount->single, ino);
- if (icount->multiple)
- ext2fs_unmark_inode_bitmap(icount->multiple, ino);
- else {
- el = get_icount_el(icount, ino, 0);
- if (el)
- el->count = 0;
- }
- if (ret)
- *ret = 0;
- return 0;
- }
-
- if (icount->multiple &&
- !ext2fs_test_inode_bitmap(icount->multiple, ino))
- return EXT2_ET_INVALID_ARGUMENT;
-
- el = get_icount_el(icount, ino, 0);
- if (!el || el->count == 0)
- return EXT2_ET_INVALID_ARGUMENT;
-
- el->count--;
- if (el->count == 1)
- ext2fs_mark_inode_bitmap(icount->single, ino);
- if ((el->count == 0) && icount->multiple)
- ext2fs_unmark_inode_bitmap(icount->multiple, ino);
-
- if (ret)
- *ret = el->count;
- return 0;
-}
-
-errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
- __u16 count)
-{
- struct ext2_icount_el *el;
-
- if (!ino || (ino > icount->num_inodes))
- return EXT2_ET_INVALID_ARGUMENT;
-
- EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
-
- if (count == 1) {
- ext2fs_mark_inode_bitmap(icount->single, ino);
- if (icount->multiple)
- ext2fs_unmark_inode_bitmap(icount->multiple, ino);
- return 0;
- }
- if (count == 0) {
- ext2fs_unmark_inode_bitmap(icount->single, ino);
- if (icount->multiple) {
- /*
- * If the icount->multiple bitmap is enabled,
- * we can just clear both bitmaps and we're done
- */
- ext2fs_unmark_inode_bitmap(icount->multiple, ino);
- } else {
- el = get_icount_el(icount, ino, 0);
- if (el)
- el->count = 0;
- }
- return 0;
- }
-
- /*
- * Get the icount element
- */
- el = get_icount_el(icount, ino, 1);
- if (!el)
- return EXT2_ET_NO_MEMORY;
- el->count = count;
- ext2fs_unmark_inode_bitmap(icount->single, ino);
- if (icount->multiple)
- ext2fs_mark_inode_bitmap(icount->multiple, ino);
- return 0;
-}
-
-ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
-{
- if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
- return 0;
-
- return icount->size;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/imager.c b/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
deleted file mode 100644
index 0f9cfcf..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
+++ b/dev/null
@@ -1,377 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * image.c --- writes out the critical parts of the filesystem as a
- * flat file.
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * Note: this uses the POSIX IO interfaces, unlike most of the other
- * functions in this library. So sue me.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#ifndef HAVE_TYPE_SSIZE_T
-typedef int ssize_t;
-#endif
-
-/*
- * This function returns 1 if the specified block is all zeros
- */
-static int check_zero_block(char *buf, int blocksize)
-{
- char *cp = buf;
- int left = blocksize;
-
- while (left > 0) {
- if (*cp++)
- return 0;
- left--;
- }
- return 1;
-}
-
-/*
- * Write the inode table out as a single block.
- */
-#define BUF_BLOCKS 32
-
-errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
-{
- unsigned int group, left, c, d;
- char *buf, *cp;
- blk_t blk;
- ssize_t actual;
- errcode_t retval;
-
- buf = xmalloc(fs->blocksize * BUF_BLOCKS);
-
- for (group = 0; group < fs->group_desc_count; group++) {
- blk = fs->group_desc[(unsigned)group].bg_inode_table;
- if (!blk)
- return EXT2_ET_MISSING_INODE_TABLE;
- left = fs->inode_blocks_per_group;
- while (left) {
- c = BUF_BLOCKS;
- if (c > left)
- c = left;
- retval = io_channel_read_blk(fs->io, blk, c, buf);
- if (retval)
- goto errout;
- cp = buf;
- while (c) {
- if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
- d = c;
- goto skip_sparse;
- }
- /* Skip zero blocks */
- if (check_zero_block(cp, fs->blocksize)) {
- c--;
- blk++;
- left--;
- cp += fs->blocksize;
- lseek(fd, fs->blocksize, SEEK_CUR);
- continue;
- }
- /* Find non-zero blocks */
- for (d=1; d < c; d++) {
- if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
- break;
- }
- skip_sparse:
- actual = write(fd, cp, fs->blocksize * d);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != (ssize_t) (fs->blocksize * d)) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
- blk += d;
- left -= d;
- cp += fs->blocksize * d;
- c -= d;
- }
- }
- }
- retval = 0;
-
-errout:
- free(buf);
- return retval;
-}
-
-/*
- * Read in the inode table and stuff it into place
- */
-errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
- int flags EXT2FS_ATTR((unused)))
-{
- unsigned int group, c, left;
- char *buf;
- blk_t blk;
- ssize_t actual;
- errcode_t retval;
-
- buf = xmalloc(fs->blocksize * BUF_BLOCKS);
-
- for (group = 0; group < fs->group_desc_count; group++) {
- blk = fs->group_desc[(unsigned)group].bg_inode_table;
- if (!blk) {
- retval = EXT2_ET_MISSING_INODE_TABLE;
- goto errout;
- }
- left = fs->inode_blocks_per_group;
- while (left) {
- c = BUF_BLOCKS;
- if (c > left)
- c = left;
- actual = read(fd, buf, fs->blocksize * c);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != (ssize_t) (fs->blocksize * c)) {
- retval = EXT2_ET_SHORT_READ;
- goto errout;
- }
- retval = io_channel_write_blk(fs->io, blk, c, buf);
- if (retval)
- goto errout;
-
- blk += c;
- left -= c;
- }
- }
- retval = ext2fs_flush_icache(fs);
-
-errout:
- free(buf);
- return retval;
-}
-
-/*
- * Write out superblock and group descriptors
- */
-errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
- int flags EXT2FS_ATTR((unused)))
-{
- char *buf, *cp;
- ssize_t actual;
- errcode_t retval;
-
- buf = xmalloc(fs->blocksize);
-
- /*
- * Write out the superblock
- */
- memset(buf, 0, fs->blocksize);
- memcpy(buf, fs->super, SUPERBLOCK_SIZE);
- actual = write(fd, buf, fs->blocksize);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != (ssize_t) fs->blocksize) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
-
- /*
- * Now write out the block group descriptors
- */
- cp = (char *) fs->group_desc;
- actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
-
- retval = 0;
-
-errout:
- free(buf);
- return retval;
-}
-
-/*
- * Read the superblock and group descriptors and overwrite them.
- */
-errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
- int flags EXT2FS_ATTR((unused)))
-{
- char *buf;
- ssize_t actual, size;
- errcode_t retval;
-
- size = fs->blocksize * (fs->group_desc_count + 1);
- buf = xmalloc(size);
-
- /*
- * Read it all in.
- */
- actual = read(fd, buf, size);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != size) {
- retval = EXT2_ET_SHORT_READ;
- goto errout;
- }
-
- /*
- * Now copy in the superblock and group descriptors
- */
- memcpy(fs->super, buf, SUPERBLOCK_SIZE);
-
- memcpy(fs->group_desc, buf + fs->blocksize,
- fs->blocksize * fs->group_desc_count);
-
- retval = 0;
-
-errout:
- free(buf);
- return retval;
-}
-
-/*
- * Write the block/inode bitmaps.
- */
-errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
-{
- char *ptr;
- int c, size;
- char zero_buf[1024];
- ssize_t actual;
- errcode_t retval;
-
- if (flags & IMAGER_FLAG_INODEMAP) {
- if (!fs->inode_map) {
- retval = ext2fs_read_inode_bitmap(fs);
- if (retval)
- return retval;
- }
- ptr = fs->inode_map->bitmap;
- size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
- } else {
- if (!fs->block_map) {
- retval = ext2fs_read_block_bitmap(fs);
- if (retval)
- return retval;
- }
- ptr = fs->block_map->bitmap;
- size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
- }
- size = size * fs->group_desc_count;
-
- actual = write(fd, ptr, size);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != size) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
- size = size % fs->blocksize;
- memset(zero_buf, 0, sizeof(zero_buf));
- if (size) {
- size = fs->blocksize - size;
- while (size) {
- c = size;
- if (c > (int) sizeof(zero_buf))
- c = sizeof(zero_buf);
- actual = write(fd, zero_buf, c);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != c) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
- size -= c;
- }
- }
- retval = 0;
-errout:
- return retval;
-}
-
-
-/*
- * Read the block/inode bitmaps.
- */
-errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
-{
- char *ptr, *buf = NULL;
- int size;
- ssize_t actual;
- errcode_t retval;
-
- if (flags & IMAGER_FLAG_INODEMAP) {
- if (!fs->inode_map) {
- retval = ext2fs_read_inode_bitmap(fs);
- if (retval)
- return retval;
- }
- ptr = fs->inode_map->bitmap;
- size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
- } else {
- if (!fs->block_map) {
- retval = ext2fs_read_block_bitmap(fs);
- if (retval)
- return retval;
- }
- ptr = fs->block_map->bitmap;
- size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
- }
- size = size * fs->group_desc_count;
-
- buf = xmalloc(size);
-
- actual = read(fd, buf, size);
- if (actual == -1) {
- retval = errno;
- goto errout;
- }
- if (actual != size) {
- retval = EXT2_ET_SHORT_WRITE;
- goto errout;
- }
- memcpy(ptr, buf, size);
-
- retval = 0;
-errout:
- free(buf);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c b/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
deleted file mode 100644
index a103830..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
+++ b/dev/null
@@ -1,69 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ind_block.c --- indirect block I/O routines
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
-{
- errcode_t retval;
-#if BB_BIG_ENDIAN
- blk_t *block_nr;
- int i;
- int limit = fs->blocksize >> 2;
-#endif
-
- if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
- (fs->io != fs->image_io))
- memset(buf, 0, fs->blocksize);
- else {
- retval = io_channel_read_blk(fs->io, blk, 1, buf);
- if (retval)
- return retval;
- }
-#if BB_BIG_ENDIAN
- if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
- block_nr = (blk_t *) buf;
- for (i = 0; i < limit; i++, block_nr++)
- *block_nr = ext2fs_swab32(*block_nr);
- }
-#endif
- return 0;
-}
-
-errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
-{
-#if BB_BIG_ENDIAN
- blk_t *block_nr;
- int i;
- int limit = fs->blocksize >> 2;
-#endif
-
- if (fs->flags & EXT2_FLAG_IMAGE_FILE)
- return 0;
-
-#if BB_BIG_ENDIAN
- if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
- block_nr = (blk_t *) buf;
- for (i = 0; i < limit; i++, block_nr++)
- *block_nr = ext2fs_swab32(*block_nr);
- }
-#endif
- return io_channel_write_blk(fs->io, blk, 1, buf);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c b/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
deleted file mode 100644
index 240335b..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
+++ b/dev/null
@@ -1,388 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * initialize.c --- initialize a filesystem handle given superblock
- * parameters. Used by mke2fs when initializing a filesystem.
- *
- * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#if defined(__linux__) && defined(EXT2_OS_LINUX)
-#define CREATOR_OS EXT2_OS_LINUX
-#else
-#if defined(__GNU__) && defined(EXT2_OS_HURD)
-#define CREATOR_OS EXT2_OS_HURD
-#else
-#if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD)
-#define CREATOR_OS EXT2_OS_FREEBSD
-#else
-#if defined(LITES) && defined(EXT2_OS_LITES)
-#define CREATOR_OS EXT2_OS_LITES
-#else
-#define CREATOR_OS EXT2_OS_LINUX /* by default */
-#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
-#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
-#endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */
-#endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */
-
-/*
- * Note we override the kernel include file's idea of what the default
- * check interval (never) should be. It's a good idea to check at
- * least *occasionally*, specially since servers will never rarely get
- * to reboot, since Linux is so robust these days. :-)
- *
- * 180 days (six months) seems like a good value.
- */
-#ifdef EXT2_DFL_CHECKINTERVAL
-#undef EXT2_DFL_CHECKINTERVAL
-#endif
-#define EXT2_DFL_CHECKINTERVAL (86400L * 180L)
-
-/*
- * Calculate the number of GDT blocks to reserve for online filesystem growth.
- * The absolute maximum number of GDT blocks we can reserve is determined by
- * the number of block pointers that can fit into a single block.
- */
-static int calc_reserved_gdt_blocks(ext2_filsys fs)
-{
- struct ext2_super_block *sb = fs->super;
- unsigned long bpg = sb->s_blocks_per_group;
- unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc);
- unsigned long max_blocks = 0xffffffff;
- unsigned long rsv_groups;
- int rsv_gdb;
-
- /* We set it at 1024x the current filesystem size, or
- * the upper block count limit (2^32), whichever is lower.
- */
- if (sb->s_blocks_count < max_blocks / 1024)
- max_blocks = sb->s_blocks_count * 1024;
- rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg;
- rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks;
- if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb))
- rsv_gdb = EXT2_ADDR_PER_BLOCK(sb);
-#ifdef RES_GDT_DEBUG
- printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n",
- max_blocks, rsv_groups, rsv_gdb);
-#endif
-
- return rsv_gdb;
-}
-
-errcode_t ext2fs_initialize(const char *name, int flags,
- struct ext2_super_block *param,
- io_manager manager, ext2_filsys *ret_fs)
-{
- ext2_filsys fs;
- errcode_t retval;
- struct ext2_super_block *super;
- int frags_per_block;
- unsigned int rem;
- unsigned int overhead = 0;
- blk_t group_block;
- unsigned int ipg;
- dgrp_t i;
- blk_t numblocks;
- int rsv_gdt;
- char *buf;
-
- if (!param || !param->s_blocks_count)
- return EXT2_ET_INVALID_ARGUMENT;
-
- retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
- if (retval)
- return retval;
-
- memset(fs, 0, sizeof(struct struct_ext2_filsys));
- fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
- fs->flags = flags | EXT2_FLAG_RW;
- fs->umask = 022;
-#ifdef WORDS_BIGENDIAN
- fs->flags |= EXT2_FLAG_SWAP_BYTES;
-#endif
- retval = manager->open(name, IO_FLAG_RW, &fs->io);
- if (retval)
- goto cleanup;
- fs->image_io = fs->io;
- fs->io->app_data = fs;
- retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
- if (retval)
- goto cleanup;
-
- strcpy(fs->device_name, name);
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super);
- if (retval)
- goto cleanup;
- fs->super = super;
-
- memset(super, 0, SUPERBLOCK_SIZE);
-
-#define set_field(field, default) (super->field = param->field ? \
- param->field : (default))
-
- super->s_magic = EXT2_SUPER_MAGIC;
- super->s_state = EXT2_VALID_FS;
-
- set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */
- set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */
- set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
- set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
- set_field(s_errors, EXT2_ERRORS_DEFAULT);
- set_field(s_feature_compat, 0);
- set_field(s_feature_incompat, 0);
- set_field(s_feature_ro_compat, 0);
- set_field(s_first_meta_bg, 0);
- if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
- retval = EXT2_ET_UNSUPP_FEATURE;
- goto cleanup;
- }
- if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
- retval = EXT2_ET_RO_UNSUPP_FEATURE;
- goto cleanup;
- }
-
- set_field(s_rev_level, EXT2_GOOD_OLD_REV);
- if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
- set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
- set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
- }
-
- set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL);
- super->s_mkfs_time = super->s_lastcheck = time(NULL);
-
- super->s_creator_os = CREATOR_OS;
-
- fs->blocksize = EXT2_BLOCK_SIZE(super);
- fs->fragsize = EXT2_FRAG_SIZE(super);
- frags_per_block = fs->blocksize / fs->fragsize;
-
- /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */
- set_field(s_blocks_per_group, fs->blocksize * 8);
- if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
- super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
- super->s_frags_per_group = super->s_blocks_per_group * frags_per_block;
-
- super->s_blocks_count = param->s_blocks_count;
- super->s_r_blocks_count = param->s_r_blocks_count;
- if (super->s_r_blocks_count >= param->s_blocks_count) {
- retval = EXT2_ET_INVALID_ARGUMENT;
- goto cleanup;
- }
-
- /*
- * If we're creating an external journal device, we don't need
- * to bother with the rest.
- */
- if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
- fs->group_desc_count = 0;
- ext2fs_mark_super_dirty(fs);
- *ret_fs = fs;
- return 0;
- }
-
-retry:
- fs->group_desc_count = (super->s_blocks_count -
- super->s_first_data_block +
- EXT2_BLOCKS_PER_GROUP(super) - 1)
- / EXT2_BLOCKS_PER_GROUP(super);
- if (fs->group_desc_count == 0) {
- retval = EXT2_ET_TOOSMALL;
- goto cleanup;
- }
- fs->desc_blocks = (fs->group_desc_count +
- EXT2_DESC_PER_BLOCK(super) - 1)
- / EXT2_DESC_PER_BLOCK(super);
-
- i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize;
- set_field(s_inodes_count, super->s_blocks_count / i);
-
- /*
- * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so
- * that we have enough inodes for the filesystem(!)
- */
- if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1)
- super->s_inodes_count = EXT2_FIRST_INODE(super)+1;
-
- /*
- * There should be at least as many inodes as the user
- * requested. Figure out how many inodes per group that
- * should be. But make sure that we don't allocate more than
- * one bitmap's worth of inodes each group.
- */
- ipg = (super->s_inodes_count + fs->group_desc_count - 1) /
- fs->group_desc_count;
- if (ipg > fs->blocksize * 8) {
- if (super->s_blocks_per_group >= 256) {
- /* Try again with slightly different parameters */
- super->s_blocks_per_group -= 8;
- super->s_blocks_count = param->s_blocks_count;
- super->s_frags_per_group = super->s_blocks_per_group *
- frags_per_block;
- goto retry;
- } else
- return EXT2_ET_TOO_MANY_INODES;
- }
-
- if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super))
- ipg = EXT2_MAX_INODES_PER_GROUP(super);
-
- super->s_inodes_per_group = ipg;
- if (super->s_inodes_count > ipg * fs->group_desc_count)
- super->s_inodes_count = ipg * fs->group_desc_count;
-
- /*
- * Make sure the number of inodes per group completely fills
- * the inode table blocks in the descriptor. If not, add some
- * additional inodes/group. Waste not, want not...
- */
- fs->inode_blocks_per_group = (((super->s_inodes_per_group *
- EXT2_INODE_SIZE(super)) +
- EXT2_BLOCK_SIZE(super) - 1) /
- EXT2_BLOCK_SIZE(super));
- super->s_inodes_per_group = ((fs->inode_blocks_per_group *
- EXT2_BLOCK_SIZE(super)) /
- EXT2_INODE_SIZE(super));
- /*
- * Finally, make sure the number of inodes per group is a
- * multiple of 8. This is needed to simplify the bitmap
- * splicing code.
- */
- super->s_inodes_per_group &= ~7;
- fs->inode_blocks_per_group = (((super->s_inodes_per_group *
- EXT2_INODE_SIZE(super)) +
- EXT2_BLOCK_SIZE(super) - 1) /
- EXT2_BLOCK_SIZE(super));
-
- /*
- * adjust inode count to reflect the adjusted inodes_per_group
- */
- super->s_inodes_count = super->s_inodes_per_group *
- fs->group_desc_count;
- super->s_free_inodes_count = super->s_inodes_count;
-
- /*
- * check the number of reserved group descriptor table blocks
- */
- if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO)
- rsv_gdt = calc_reserved_gdt_blocks(fs);
- else
- rsv_gdt = 0;
- set_field(s_reserved_gdt_blocks, rsv_gdt);
- if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) {
- retval = EXT2_ET_RES_GDT_BLOCKS;
- goto cleanup;
- }
-
- /*
- * Overhead is the number of bookkeeping blocks per group. It
- * includes the superblock backup, the group descriptor
- * backups, the inode bitmap, the block bitmap, and the inode
- * table.
- */
-
- overhead = (int) (2 + fs->inode_blocks_per_group);
-
- if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
- overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks;
-
- /* This can only happen if the user requested too many inodes */
- if (overhead > super->s_blocks_per_group)
- return EXT2_ET_TOO_MANY_INODES;
-
- /*
- * See if the last group is big enough to support the
- * necessary data structures. If not, we need to get rid of
- * it.
- */
- rem = ((super->s_blocks_count - super->s_first_data_block) %
- super->s_blocks_per_group);
- if ((fs->group_desc_count == 1) && rem && (rem < overhead))
- return EXT2_ET_TOOSMALL;
- if (rem && (rem < overhead+50)) {
- super->s_blocks_count -= rem;
- goto retry;
- }
-
- /*
- * At this point we know how big the filesystem will be. So
- * we can do any and all allocations that depend on the block
- * count.
- */
-
- retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
- if (retval)
- goto cleanup;
-
- sprintf(buf, "block bitmap for %s", fs->device_name);
- retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
- if (retval)
- goto cleanup;
-
- sprintf(buf, "inode bitmap for %s", fs->device_name);
- retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
- if (retval)
- goto cleanup;
-
- ext2fs_free_mem(&buf);
-
- retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
- &fs->group_desc);
- if (retval)
- goto cleanup;
-
- memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize);
-
- /*
- * Reserve the superblock and group descriptors for each
- * group, and fill in the correct group statistics for group.
- * Note that although the block bitmap, inode bitmap, and
- * inode table have not been allocated (and in fact won't be
- * by this routine), they are accounted for nevertheless.
- */
- group_block = super->s_first_data_block;
- super->s_free_blocks_count = 0;
- for (i = 0; i < fs->group_desc_count; i++) {
- numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
-
- super->s_free_blocks_count += numblocks;
- fs->group_desc[i].bg_free_blocks_count = numblocks;
- fs->group_desc[i].bg_free_inodes_count =
- fs->super->s_inodes_per_group;
- fs->group_desc[i].bg_used_dirs_count = 0;
-
- group_block += super->s_blocks_per_group;
- }
-
- ext2fs_mark_super_dirty(fs);
- ext2fs_mark_bb_dirty(fs);
- ext2fs_mark_ib_dirty(fs);
-
- io_channel_set_blksize(fs->io, fs->blocksize);
-
- *ret_fs = fs;
- return 0;
-cleanup:
- ext2fs_free(fs);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inline.c b/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
deleted file mode 100644
index 7457b93..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
+++ b/dev/null
@@ -1,32 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * inline.c --- Includes the inlined functions defined in the header
- * files as standalone functions, in case the application program
- * is compiled with inlining turned off.
- *
- * Copyright (C) 1993, 1994 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#define INCLUDE_INLINE_FUNCS
-#include "ext2fs.h"
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inode.c b/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
deleted file mode 100644
index 7a1d5c9..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
+++ b/dev/null
@@ -1,766 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * inode.c --- utility routines to read and write inodes
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-#include "e2image.h"
-
-struct ext2_struct_inode_scan {
- errcode_t magic;
- ext2_filsys fs;
- ext2_ino_t current_inode;
- blk_t current_block;
- dgrp_t current_group;
- ext2_ino_t inodes_left;
- blk_t blocks_left;
- dgrp_t groups_left;
- blk_t inode_buffer_blocks;
- char * inode_buffer;
- int inode_size;
- char * ptr;
- int bytes_left;
- char *temp_buffer;
- errcode_t (*done_group)(ext2_filsys fs,
- dgrp_t group,
- void * priv_data);
- void * done_group_data;
- int bad_block_ptr;
- int scan_flags;
- int reserved[6];
-};
-
-/*
- * This routine flushes the icache, if it exists.
- */
-errcode_t ext2fs_flush_icache(ext2_filsys fs)
-{
- int i;
-
- if (!fs->icache)
- return 0;
-
- for (i=0; i < fs->icache->cache_size; i++)
- fs->icache->cache[i].ino = 0;
-
- fs->icache->buffer_blk = 0;
- return 0;
-}
-
-static errcode_t create_icache(ext2_filsys fs)
-{
- errcode_t retval;
-
- if (fs->icache)
- return 0;
- retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
- if (retval)
- return retval;
-
- memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
- retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
- if (retval) {
- ext2fs_free_mem(&fs->icache);
- return retval;
- }
- fs->icache->buffer_blk = 0;
- fs->icache->cache_last = -1;
- fs->icache->cache_size = 4;
- fs->icache->refcount = 1;
- retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
- * fs->icache->cache_size,
- &fs->icache->cache);
- if (retval) {
- ext2fs_free_mem(&fs->icache->buffer);
- ext2fs_free_mem(&fs->icache);
- return retval;
- }
- ext2fs_flush_icache(fs);
- return 0;
-}
-
-errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
- ext2_inode_scan *ret_scan)
-{
- ext2_inode_scan scan;
- errcode_t retval;
- errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- /*
- * If fs->badblocks isn't set, then set it --- since the inode
- * scanning functions require it.
- */
- if (fs->badblocks == 0) {
- /*
- * Temporarly save fs->get_blocks and set it to zero,
- * for compatibility with old e2fsck's.
- */
- save_get_blocks = fs->get_blocks;
- fs->get_blocks = 0;
- retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
- if (retval) {
- ext2fs_badblocks_list_free(fs->badblocks);
- fs->badblocks = 0;
- }
- fs->get_blocks = save_get_blocks;
- }
-
- retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
- if (retval)
- return retval;
- memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
-
- scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
- scan->fs = fs;
- scan->inode_size = EXT2_INODE_SIZE(fs->super);
- scan->bytes_left = 0;
- scan->current_group = 0;
- scan->groups_left = fs->group_desc_count - 1;
- scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
- scan->current_block = scan->fs->
- group_desc[scan->current_group].bg_inode_table;
- scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
- scan->blocks_left = scan->fs->inode_blocks_per_group;
- retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
- fs->blocksize),
- &scan->inode_buffer);
- scan->done_group = 0;
- scan->done_group_data = 0;
- scan->bad_block_ptr = 0;
- if (retval) {
- ext2fs_free_mem(&scan);
- return retval;
- }
- retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
- if (retval) {
- ext2fs_free_mem(&scan->inode_buffer);
- ext2fs_free_mem(&scan);
- return retval;
- }
- if (scan->fs->badblocks && scan->fs->badblocks->num)
- scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
- *ret_scan = scan;
- return 0;
-}
-
-void ext2fs_close_inode_scan(ext2_inode_scan scan)
-{
- if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
- return;
-
- ext2fs_free_mem(&scan->inode_buffer);
- scan->inode_buffer = NULL;
- ext2fs_free_mem(&scan->temp_buffer);
- scan->temp_buffer = NULL;
- ext2fs_free_mem(&scan);
-}
-
-void ext2fs_set_inode_callback(ext2_inode_scan scan,
- errcode_t (*done_group)(ext2_filsys fs,
- dgrp_t group,
- void * priv_data),
- void *done_group_data)
-{
- if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
- return;
-
- scan->done_group = done_group;
- scan->done_group_data = done_group_data;
-}
-
-int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
- int clear_flags)
-{
- int old_flags;
-
- if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
- return 0;
-
- old_flags = scan->scan_flags;
- scan->scan_flags &= ~clear_flags;
- scan->scan_flags |= set_flags;
- return old_flags;
-}
-
-/*
- * This function is called by ext2fs_get_next_inode when it needs to
- * get ready to read in a new blockgroup.
- */
-static errcode_t get_next_blockgroup(ext2_inode_scan scan)
-{
- scan->current_group++;
- scan->groups_left--;
-
- scan->current_block = scan->fs->
- group_desc[scan->current_group].bg_inode_table;
-
- scan->current_inode = scan->current_group *
- EXT2_INODES_PER_GROUP(scan->fs->super);
-
- scan->bytes_left = 0;
- scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
- scan->blocks_left = scan->fs->inode_blocks_per_group;
- return 0;
-}
-
-errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
- int group)
-{
- scan->current_group = group - 1;
- scan->groups_left = scan->fs->group_desc_count - group;
- return get_next_blockgroup(scan);
-}
-
-/*
- * This function is called by get_next_blocks() to check for bad
- * blocks in the inode table.
- *
- * This function assumes that badblocks_list->list is sorted in
- * increasing order.
- */
-static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
- blk_t *num_blocks)
-{
- blk_t blk = scan->current_block;
- badblocks_list bb = scan->fs->badblocks;
-
- /*
- * If the inode table is missing, then obviously there are no
- * bad blocks. :-)
- */
- if (blk == 0)
- return 0;
-
- /*
- * If the current block is greater than the bad block listed
- * in the bad block list, then advance the pointer until this
- * is no longer the case. If we run out of bad blocks, then
- * we don't need to do any more checking!
- */
- while (blk > bb->list[scan->bad_block_ptr]) {
- if (++scan->bad_block_ptr >= bb->num) {
- scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
- return 0;
- }
- }
-
- /*
- * If the current block is equal to the bad block listed in
- * the bad block list, then handle that one block specially.
- * (We could try to handle runs of bad blocks, but that
- * only increases CPU efficiency by a small amount, at the
- * expense of a huge expense of code complexity, and for an
- * uncommon case at that.)
- */
- if (blk == bb->list[scan->bad_block_ptr]) {
- scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
- *num_blocks = 1;
- if (++scan->bad_block_ptr >= bb->num)
- scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
- return 0;
- }
-
- /*
- * If there is a bad block in the range that we're about to
- * read in, adjust the number of blocks to read so that we we
- * don't read in the bad block. (Then the next block to read
- * will be the bad block, which is handled in the above case.)
- */
- if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
- *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
-
- return 0;
-}
-
-/*
- * This function is called by ext2fs_get_next_inode when it needs to
- * read in more blocks from the current blockgroup's inode table.
- */
-static errcode_t get_next_blocks(ext2_inode_scan scan)
-{
- blk_t num_blocks;
- errcode_t retval;
-
- /*
- * Figure out how many blocks to read; we read at most
- * inode_buffer_blocks, and perhaps less if there aren't that
- * many blocks left to read.
- */
- num_blocks = scan->inode_buffer_blocks;
- if (num_blocks > scan->blocks_left)
- num_blocks = scan->blocks_left;
-
- /*
- * If the past block "read" was a bad block, then mark the
- * left-over extra bytes as also being bad.
- */
- if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
- if (scan->bytes_left)
- scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
- scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
- }
-
- /*
- * Do inode bad block processing, if necessary.
- */
- if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
- retval = check_for_inode_bad_blocks(scan, &num_blocks);
- if (retval)
- return retval;
- }
-
- if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
- (scan->current_block == 0)) {
- memset(scan->inode_buffer, 0,
- (size_t) num_blocks * scan->fs->blocksize);
- } else {
- retval = io_channel_read_blk(scan->fs->io,
- scan->current_block,
- (int) num_blocks,
- scan->inode_buffer);
- if (retval)
- return EXT2_ET_NEXT_INODE_READ;
- }
- scan->ptr = scan->inode_buffer;
- scan->bytes_left = num_blocks * scan->fs->blocksize;
-
- scan->blocks_left -= num_blocks;
- if (scan->current_block)
- scan->current_block += num_blocks;
- return 0;
-}
-
-errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
- struct ext2_inode *inode, int bufsize)
-{
- errcode_t retval;
- int extra_bytes = 0;
-
- EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
-
- /*
- * Do we need to start reading a new block group?
- */
- if (scan->inodes_left <= 0) {
- force_new_group:
- if (scan->done_group) {
- retval = (scan->done_group)
- (scan->fs, scan->current_group,
- scan->done_group_data);
- if (retval)
- return retval;
- }
- if (scan->groups_left <= 0) {
- *ino = 0;
- return 0;
- }
- retval = get_next_blockgroup(scan);
- if (retval)
- return retval;
- }
- /*
- * This is done outside the above if statement so that the
- * check can be done for block group #0.
- */
- if (scan->current_block == 0) {
- if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
- goto force_new_group;
- } else
- return EXT2_ET_MISSING_INODE_TABLE;
- }
-
-
- /*
- * Have we run out of space in the inode buffer? If so, we
- * need to read in more blocks.
- */
- if (scan->bytes_left < scan->inode_size) {
- memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
- extra_bytes = scan->bytes_left;
-
- retval = get_next_blocks(scan);
- if (retval)
- return retval;
-#if 0
- /*
- * XXX test Need check for used inode somehow.
- * (Note: this is hard.)
- */
- if (is_empty_scan(scan))
- goto force_new_group;
-#endif
- }
-
- retval = 0;
- if (extra_bytes) {
- memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
- scan->inode_size - extra_bytes);
- scan->ptr += scan->inode_size - extra_bytes;
- scan->bytes_left -= scan->inode_size - extra_bytes;
-
-#if BB_BIG_ENDIAN
- if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
- ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) scan->temp_buffer,
- 0, bufsize);
- else
-#endif
- *inode = *((struct ext2_inode *) scan->temp_buffer);
- if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
- retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
- scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
- } else {
-#if BB_BIG_ENDIAN
- if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
- ext2fs_swap_inode_full(scan->fs,
- (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) scan->ptr,
- 0, bufsize);
- else
-#endif
- memcpy(inode, scan->ptr, bufsize);
- scan->ptr += scan->inode_size;
- scan->bytes_left -= scan->inode_size;
- if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
- retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
- }
-
- scan->inodes_left--;
- scan->current_inode++;
- *ino = scan->current_inode;
- return retval;
-}
-
-errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
- struct ext2_inode *inode)
-{
- return ext2fs_get_next_inode_full(scan, ino, inode,
- sizeof(struct ext2_inode));
-}
-
-/*
- * Functions to read and write a single inode.
- */
-errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode, int bufsize)
-{
- unsigned long group, block, block_nr, offset;
- char *ptr;
- errcode_t retval;
- int clen, i, inodes_per_block, length;
- io_channel io;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- /* Check to see if user has an override function */
- if (fs->read_inode) {
- retval = (fs->read_inode)(fs, ino, inode);
- if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
- return retval;
- }
- /* Create inode cache if not present */
- if (!fs->icache) {
- retval = create_icache(fs);
- if (retval)
- return retval;
- }
- /* Check to see if it's in the inode cache */
- if (bufsize == sizeof(struct ext2_inode)) {
- /* only old good inode can be retrieve from the cache */
- for (i=0; i < fs->icache->cache_size; i++) {
- if (fs->icache->cache[i].ino == ino) {
- *inode = fs->icache->cache[i].inode;
- return 0;
- }
- }
- }
- if ((ino == 0) || (ino > fs->super->s_inodes_count))
- return EXT2_ET_BAD_INODE_NUM;
- if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
- inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
- block_nr = fs->image_header->offset_inode / fs->blocksize;
- block_nr += (ino - 1) / inodes_per_block;
- offset = ((ino - 1) % inodes_per_block) *
- EXT2_INODE_SIZE(fs->super);
- io = fs->image_io;
- } else {
- group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
- offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
- EXT2_INODE_SIZE(fs->super);
- block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
- if (!fs->group_desc[(unsigned)group].bg_inode_table)
- return EXT2_ET_MISSING_INODE_TABLE;
- block_nr = fs->group_desc[(unsigned)group].bg_inode_table +
- block;
- io = fs->io;
- }
- offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
-
- length = EXT2_INODE_SIZE(fs->super);
- if (bufsize < length)
- length = bufsize;
-
- ptr = (char *) inode;
- while (length) {
- clen = length;
- if ((offset + length) > fs->blocksize)
- clen = fs->blocksize - offset;
-
- if (block_nr != fs->icache->buffer_blk) {
- retval = io_channel_read_blk(io, block_nr, 1,
- fs->icache->buffer);
- if (retval)
- return retval;
- fs->icache->buffer_blk = block_nr;
- }
-
- memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
- clen);
-
- offset = 0;
- length -= clen;
- ptr += clen;
- block_nr++;
- }
-
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
- ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
- (struct ext2_inode_large *) inode,
- 0, length);
-#endif
-
- /* Update the inode cache */
- fs->icache->cache_last = (fs->icache->cache_last + 1) %
- fs->icache->cache_size;
- fs->icache->cache[fs->icache->cache_last].ino = ino;
- fs->icache->cache[fs->icache->cache_last].inode = *inode;
-
- return 0;
-}
-
-errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode)
-{
- return ext2fs_read_inode_full(fs, ino, inode,
- sizeof(struct ext2_inode));
-}
-
-errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode * inode, int bufsize)
-{
- unsigned long group, block, block_nr, offset;
- errcode_t retval = 0;
- struct ext2_inode_large temp_inode, *w_inode;
- char *ptr;
- int clen, i, length;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- /* Check to see if user provided an override function */
- if (fs->write_inode) {
- retval = (fs->write_inode)(fs, ino, inode);
- if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
- return retval;
- }
-
- /* Check to see if the inode cache needs to be updated */
- if (fs->icache) {
- for (i=0; i < fs->icache->cache_size; i++) {
- if (fs->icache->cache[i].ino == ino) {
- fs->icache->cache[i].inode = *inode;
- break;
- }
- }
- } else {
- retval = create_icache(fs);
- if (retval)
- return retval;
- }
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- if ((ino == 0) || (ino > fs->super->s_inodes_count))
- return EXT2_ET_BAD_INODE_NUM;
-
- length = bufsize;
- if (length < EXT2_INODE_SIZE(fs->super))
- length = EXT2_INODE_SIZE(fs->super);
-
- if (length > (int) sizeof(struct ext2_inode_large)) {
- w_inode = xmalloc(length);
- } else
- w_inode = &temp_inode;
- memset(w_inode, 0, length);
-
-#if BB_BIG_ENDIAN
- if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
- ext2fs_swap_inode_full(fs, w_inode,
- (struct ext2_inode_large *) inode,
- 1, bufsize);
- else
-#endif
- memcpy(w_inode, inode, bufsize);
-
- group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
- offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
- EXT2_INODE_SIZE(fs->super);
- block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
- if (!fs->group_desc[(unsigned) group].bg_inode_table)
- return EXT2_ET_MISSING_INODE_TABLE;
- block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
-
- offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
-
- length = EXT2_INODE_SIZE(fs->super);
- if (length > bufsize)
- length = bufsize;
-
- ptr = (char *) w_inode;
-
- while (length) {
- clen = length;
- if ((offset + length) > fs->blocksize)
- clen = fs->blocksize - offset;
-
- if (fs->icache->buffer_blk != block_nr) {
- retval = io_channel_read_blk(fs->io, block_nr, 1,
- fs->icache->buffer);
- if (retval)
- goto errout;
- fs->icache->buffer_blk = block_nr;
- }
-
-
- memcpy((char *) fs->icache->buffer + (unsigned) offset,
- ptr, clen);
-
- retval = io_channel_write_blk(fs->io, block_nr, 1,
- fs->icache->buffer);
- if (retval)
- goto errout;
-
- offset = 0;
- ptr += clen;
- length -= clen;
- block_nr++;
- }
-
- fs->flags |= EXT2_FLAG_CHANGED;
-errout:
- if (w_inode && w_inode != &temp_inode)
- free(w_inode);
- return retval;
-}
-
-errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode)
-{
- return ext2fs_write_inode_full(fs, ino, inode,
- sizeof(struct ext2_inode));
-}
-
-/*
- * This function should be called when writing a new inode. It makes
- * sure that extra part of large inodes is initialized properly.
- */
-errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode)
-{
- struct ext2_inode *buf;
- int size = EXT2_INODE_SIZE(fs->super);
- struct ext2_inode_large *large_inode;
-
- if (size == sizeof(struct ext2_inode))
- return ext2fs_write_inode_full(fs, ino, inode,
- sizeof(struct ext2_inode));
-
- buf = xmalloc(size);
-
- memset(buf, 0, size);
- *buf = *inode;
-
- large_inode = (struct ext2_inode_large *) buf;
- large_inode->i_extra_isize = sizeof(struct ext2_inode_large) -
- EXT2_GOOD_OLD_INODE_SIZE;
-
- return ext2fs_write_inode_full(fs, ino, buf, size);
-}
-
-
-errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
-{
- struct ext2_inode inode;
- int i;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (ino > fs->super->s_inodes_count)
- return EXT2_ET_BAD_INODE_NUM;
-
- if (fs->get_blocks) {
- if (!(*fs->get_blocks)(fs, ino, blocks))
- return 0;
- }
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
- return retval;
- for (i=0; i < EXT2_N_BLOCKS; i++)
- blocks[i] = inode.i_block[i];
- return 0;
-}
-
-errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
-{
- struct ext2_inode inode;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (ino > fs->super->s_inodes_count)
- return EXT2_ET_BAD_INODE_NUM;
-
- if (fs->check_directory) {
- retval = (fs->check_directory)(fs, ino);
- if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
- return retval;
- }
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
- return retval;
- if (!LINUX_S_ISDIR(inode.i_mode))
- return EXT2_ET_NO_DIRECTORY;
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
deleted file mode 100644
index b861d5f..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
+++ b/dev/null
@@ -1,270 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * inode_io.c --- This is allows an inode in an ext2 filesystem image
- * to be accessed via the I/O manager interface.
- *
- * Copyright (C) 2002 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * For checking structure magic numbers...
- */
-
-#define EXT2_CHECK_MAGIC(struct, code) \
- if ((struct)->magic != (code)) return (code)
-
-struct inode_private_data {
- int magic;
- char name[32];
- ext2_file_t file;
- ext2_filsys fs;
- ext2_ino_t ino;
- struct ext2_inode inode;
- int flags;
- struct inode_private_data *next;
-};
-
-#define CHANNEL_HAS_INODE 0x8000
-
-static struct inode_private_data *top_intern;
-static int ino_unique = 0;
-
-static errcode_t inode_open(const char *name, int flags, io_channel *channel);
-static errcode_t inode_close(io_channel channel);
-static errcode_t inode_set_blksize(io_channel channel, int blksize);
-static errcode_t inode_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t inode_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t inode_flush(io_channel channel);
-static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
- int size, const void *data);
-
-static struct struct_io_manager struct_inode_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Inode I/O Manager",
- inode_open,
- inode_close,
- inode_set_blksize,
- inode_read_blk,
- inode_write_blk,
- inode_flush,
- inode_write_byte
-};
-
-io_manager inode_io_manager = &struct_inode_manager;
-
-errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
- struct ext2_inode *inode,
- char **name)
-{
- struct inode_private_data *data;
- errcode_t retval;
-
- if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
- &data)))
- return retval;
- data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
- sprintf(data->name, "%u:%d", ino, ino_unique++);
- data->file = 0;
- data->fs = fs;
- data->ino = ino;
- data->flags = 0;
- if (inode) {
- memcpy(&data->inode, inode, sizeof(struct ext2_inode));
- data->flags |= CHANNEL_HAS_INODE;
- }
- data->next = top_intern;
- top_intern = data;
- *name = data->name;
- return 0;
-}
-
-errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
- char **name)
-{
- return ext2fs_inode_io_intern2(fs, ino, NULL, name);
-}
-
-
-static errcode_t inode_open(const char *name, int flags, io_channel *channel)
-{
- io_channel io = NULL;
- struct inode_private_data *prev, *data = NULL;
- errcode_t retval;
- int open_flags;
-
- if (name == 0)
- return EXT2_ET_BAD_DEVICE_NAME;
-
- for (data = top_intern, prev = NULL; data;
- prev = data, data = data->next)
- if (strcmp(name, data->name) == 0)
- break;
- if (!data)
- return ENOENT;
- if (prev)
- prev->next = data->next;
- else
- top_intern = data->next;
-
- retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
- if (retval)
- goto cleanup;
- memset(io, 0, sizeof(struct struct_io_channel));
-
- io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
- io->manager = inode_io_manager;
- retval = ext2fs_get_mem(strlen(name)+1, &io->name);
- if (retval)
- goto cleanup;
-
- strcpy(io->name, name);
- io->private_data = data;
- io->block_size = 1024;
- io->read_error = 0;
- io->write_error = 0;
- io->refcount = 1;
-
- open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0;
- retval = ext2fs_file_open2(data->fs, data->ino,
- (data->flags & CHANNEL_HAS_INODE) ?
- &data->inode : 0, open_flags,
- &data->file);
- if (retval)
- goto cleanup;
-
- *channel = io;
- return 0;
-
-cleanup:
- if (data) {
- ext2fs_free_mem(&data);
- }
- if (io)
- ext2fs_free_mem(&io);
- return retval;
-}
-
-static errcode_t inode_close(io_channel channel)
-{
- struct inode_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- if (--channel->refcount > 0)
- return 0;
-
- retval = ext2fs_file_close(data->file);
-
- ext2fs_free_mem(&channel->private_data);
- if (channel->name)
- ext2fs_free_mem(&channel->name);
- ext2fs_free_mem(&channel);
- return retval;
-}
-
-static errcode_t inode_set_blksize(io_channel channel, int blksize)
-{
- struct inode_private_data *data;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- channel->block_size = blksize;
- return 0;
-}
-
-
-static errcode_t inode_read_blk(io_channel channel, unsigned long block,
- int count, void *buf)
-{
- struct inode_private_data *data;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- if ((retval = ext2fs_file_lseek(data->file,
- block * channel->block_size,
- EXT2_SEEK_SET, 0)))
- return retval;
-
- count = (count < 0) ? -count : (count * channel->block_size);
-
- return ext2fs_file_read(data->file, buf, count, 0);
-}
-
-static errcode_t inode_write_blk(io_channel channel, unsigned long block,
- int count, const void *buf)
-{
- struct inode_private_data *data;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- if ((retval = ext2fs_file_lseek(data->file,
- block * channel->block_size,
- EXT2_SEEK_SET, 0)))
- return retval;
-
- count = (count < 0) ? -count : (count * channel->block_size);
-
- return ext2fs_file_write(data->file, buf, count, 0);
-}
-
-static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
- int size, const void *buf)
-{
- struct inode_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- if ((retval = ext2fs_file_lseek(data->file, offset,
- EXT2_SEEK_SET, 0)))
- return retval;
-
- return ext2fs_file_write(data->file, buf, size, 0);
-}
-
-/*
- * Flush data buffers to disk.
- */
-static errcode_t inode_flush(io_channel channel)
-{
- struct inode_private_data *data;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct inode_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
-
- return ext2fs_file_flush(data->file);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c b/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
deleted file mode 100644
index b470386..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
+++ b/dev/null
@@ -1,70 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * io_manager.c --- the I/O manager abstraction
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t io_channel_set_options(io_channel channel, const char *opts)
-{
- errcode_t retval = 0;
- char *next, *ptr, *options, *arg;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
-
- if (!opts)
- return 0;
-
- if (!channel->manager->set_option)
- return EXT2_ET_INVALID_ARGUMENT;
-
- options = malloc(strlen(opts)+1);
- if (!options)
- return EXT2_ET_NO_MEMORY;
- strcpy(options, opts);
- ptr = options;
-
- while (ptr && *ptr) {
- next = strchr(ptr, '&');
- if (next)
- *next++ = 0;
-
- arg = strchr(ptr, '=');
- if (arg)
- *arg++ = 0;
-
- retval = (channel->manager->set_option)(channel, ptr, arg);
- if (retval)
- break;
- ptr = next;
- }
- free(options);
- return retval;
-}
-
-errcode_t io_channel_write_byte(io_channel channel, unsigned long offset,
- int count, const void *data)
-{
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
-
- if (channel->manager->write_byte)
- return channel->manager->write_byte(channel, offset,
- count, data);
-
- return EXT2_ET_UNIMPLEMENTED;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/irel.h b/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
deleted file mode 100644
index 91d1d89..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
+++ b/dev/null
@@ -1,115 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * irel.h
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-struct ext2_inode_reference {
- blk_t block;
- __u16 offset;
-};
-
-struct ext2_inode_relocate_entry {
- ext2_ino_t new;
- ext2_ino_t orig;
- __u16 flags;
- __u16 max_refs;
-};
-
-typedef struct ext2_inode_relocation_table *ext2_irel;
-
-struct ext2_inode_relocation_table {
- __u32 magic;
- char *name;
- ext2_ino_t current;
- void *priv_data;
-
- /*
- * Add an inode relocation entry.
- */
- errcode_t (*put)(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent);
- /*
- * Get an inode relocation entry.
- */
- errcode_t (*get)(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent);
-
- /*
- * Get an inode relocation entry by its original inode number
- */
- errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent);
-
- /*
- * Initialize for iterating over the inode relocation entries.
- */
- errcode_t (*start_iter)(ext2_irel irel);
-
- /*
- * The iterator function for the inode relocation entries.
- * Returns an inode number of 0 when out of entries.
- */
- errcode_t (*next)(ext2_irel irel, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent);
-
- /*
- * Add an inode reference (i.e., note the fact that a
- * particular block/offset contains a reference to an inode)
- */
- errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino,
- struct ext2_inode_reference *ref);
-
- /*
- * Initialize for iterating over the inode references for a
- * particular inode.
- */
- errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino);
-
- /*
- * The iterator function for the inode references for an
- * inode. The references for only one inode can be interator
- * over at a time, as the iterator state is stored in ext2_irel.
- */
- errcode_t (*next_ref)(ext2_irel irel,
- struct ext2_inode_reference *ref);
-
- /*
- * Move the inode relocation table from one inode number to
- * another. Note that the inode references also must move.
- */
- errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
-
- /*
- * Remove an inode relocation entry, along with all of the
- * inode references.
- */
- errcode_t (*delete)(ext2_irel irel, ext2_ino_t old);
-
- /*
- * Free the inode relocation table.
- */
- errcode_t (*free)(ext2_irel irel);
-};
-
-errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
- ext2_irel *irel);
-
-#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent))
-#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent))
-#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \
- ((irel)->get_by_orig((irel), orig, old, ent))
-#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel)))
-#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent))
-#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref))
-#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino))
-#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref))
-#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new))
-#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old))
-#define ext2fs_irel_free(irel) ((irel)->free((irel)))
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c b/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
deleted file mode 100644
index c871b18..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
+++ b/dev/null
@@ -1,367 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * irel_ma.c
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-#include "irel.h"
-
-static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent);
-static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent);
-static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent);
-static errcode_t ima_start_iter(ext2_irel irel);
-static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent);
-static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
- struct ext2_inode_reference *ref);
-static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino);
-static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref);
-static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
-static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old);
-static errcode_t ima_free(ext2_irel irel);
-
-/*
- * This data structure stores the array of inode references; there is
- * a structure for each inode.
- */
-struct inode_reference_entry {
- __u16 num;
- struct ext2_inode_reference *refs;
-};
-
-struct irel_ma {
- __u32 magic;
- ext2_ino_t max_inode;
- ext2_ino_t ref_current;
- int ref_iter;
- ext2_ino_t *orig_map;
- struct ext2_inode_relocate_entry *entries;
- struct inode_reference_entry *ref_entries;
-};
-
-errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
- ext2_irel *new_irel)
-{
- ext2_irel irel = 0;
- errcode_t retval;
- struct irel_ma *ma = 0;
- size_t size;
-
- *new_irel = 0;
-
- /*
- * Allocate memory structures
- */
- retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table),
- &irel);
- if (retval)
- goto errout;
- memset(irel, 0, sizeof(struct ext2_inode_relocation_table));
-
- retval = ext2fs_get_mem(strlen(name)+1, &irel->name);
- if (retval)
- goto errout;
- strcpy(irel->name, name);
-
- retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma);
- if (retval)
- goto errout;
- memset(ma, 0, sizeof(struct irel_ma));
- irel->priv_data = ma;
-
- size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1));
- retval = ext2fs_get_mem(size, &ma->orig_map);
- if (retval)
- goto errout;
- memset(ma->orig_map, 0, size);
-
- size = (size_t) (sizeof(struct ext2_inode_relocate_entry) *
- (max_inode+1));
- retval = ext2fs_get_mem(size, &ma->entries);
- if (retval)
- goto errout;
- memset(ma->entries, 0, size);
-
- size = (size_t) (sizeof(struct inode_reference_entry) *
- (max_inode+1));
- retval = ext2fs_get_mem(size, &ma->ref_entries);
- if (retval)
- goto errout;
- memset(ma->ref_entries, 0, size);
- ma->max_inode = max_inode;
-
- /*
- * Fill in the irel data structure
- */
- irel->put = ima_put;
- irel->get = ima_get;
- irel->get_by_orig = ima_get_by_orig;
- irel->start_iter = ima_start_iter;
- irel->next = ima_next;
- irel->add_ref = ima_add_ref;
- irel->start_iter_ref = ima_start_iter_ref;
- irel->next_ref = ima_next_ref;
- irel->move = ima_move;
- irel->delete = ima_delete;
- irel->free = ima_free;
-
- *new_irel = irel;
- return 0;
-
-errout:
- ima_free(irel);
- return retval;
-}
-
-static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent)
-{
- struct inode_reference_entry *ref_ent;
- struct irel_ma *ma;
- errcode_t retval;
- size_t size, old_size;
-
- ma = irel->priv_data;
- if (old > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
-
- /*
- * Force the orig field to the correct value; the application
- * program shouldn't be messing with this field.
- */
- if (ma->entries[(unsigned) old].new == 0)
- ent->orig = old;
- else
- ent->orig = ma->entries[(unsigned) old].orig;
-
- /*
- * If max_refs has changed, reallocate the refs array
- */
- ref_ent = ma->ref_entries + (unsigned) old;
- if (ref_ent->refs && ent->max_refs !=
- ma->entries[(unsigned) old].max_refs) {
- size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
- old_size = (sizeof(struct ext2_inode_reference) *
- ma->entries[(unsigned) old].max_refs);
- retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs);
- if (retval)
- return retval;
- }
-
- ma->entries[(unsigned) old] = *ent;
- ma->orig_map[(unsigned) ent->orig] = old;
- return 0;
-}
-
-static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
- struct ext2_inode_relocate_entry *ent)
-{
- struct irel_ma *ma;
-
- ma = irel->priv_data;
- if (old > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned) old].new == 0)
- return ENOENT;
- *ent = ma->entries[(unsigned) old];
- return 0;
-}
-
-static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent)
-{
- struct irel_ma *ma;
- ext2_ino_t ino;
-
- ma = irel->priv_data;
- if (orig > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
- ino = ma->orig_map[(unsigned) orig];
- if (ino == 0)
- return ENOENT;
- *old = ino;
- *ent = ma->entries[(unsigned) ino];
- return 0;
-}
-
-static errcode_t ima_start_iter(ext2_irel irel)
-{
- irel->current = 0;
- return 0;
-}
-
-static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
- struct ext2_inode_relocate_entry *ent)
-{
- struct irel_ma *ma;
-
- ma = irel->priv_data;
- while (++irel->current < ma->max_inode) {
- if (ma->entries[(unsigned) irel->current].new == 0)
- continue;
- *old = irel->current;
- *ent = ma->entries[(unsigned) irel->current];
- return 0;
- }
- *old = 0;
- return 0;
-}
-
-static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
- struct ext2_inode_reference *ref)
-{
- struct irel_ma *ma;
- size_t size;
- struct inode_reference_entry *ref_ent;
- struct ext2_inode_relocate_entry *ent;
- errcode_t retval;
-
- ma = irel->priv_data;
- if (ino > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
-
- ref_ent = ma->ref_entries + (unsigned) ino;
- ent = ma->entries + (unsigned) ino;
-
- /*
- * If the inode reference array doesn't exist, create it.
- */
- if (ref_ent->refs == 0) {
- size = (size_t) ((sizeof(struct ext2_inode_reference) *
- ent->max_refs));
- retval = ext2fs_get_mem(size, &ref_ent->refs);
- if (retval)
- return retval;
- memset(ref_ent->refs, 0, size);
- ref_ent->num = 0;
- }
-
- if (ref_ent->num >= ent->max_refs)
- return EXT2_ET_TOO_MANY_REFS;
-
- ref_ent->refs[(unsigned) ref_ent->num++] = *ref;
- return 0;
-}
-
-static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino)
-{
- struct irel_ma *ma;
-
- ma = irel->priv_data;
- if (ino > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned) ino].new == 0)
- return ENOENT;
- ma->ref_current = ino;
- ma->ref_iter = 0;
- return 0;
-}
-
-static errcode_t ima_next_ref(ext2_irel irel,
- struct ext2_inode_reference *ref)
-{
- struct irel_ma *ma;
- struct inode_reference_entry *ref_ent;
-
- ma = irel->priv_data;
-
- ref_ent = ma->ref_entries + ma->ref_current;
-
- if ((ref_ent->refs == NULL) ||
- (ma->ref_iter >= ref_ent->num)) {
- ref->block = 0;
- ref->offset = 0;
- return 0;
- }
- *ref = ref_ent->refs[ma->ref_iter++];
- return 0;
-}
-
-
-static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new)
-{
- struct irel_ma *ma;
-
- ma = irel->priv_data;
- if ((old > ma->max_inode) || (new > ma->max_inode))
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned) old].new == 0)
- return ENOENT;
-
- ma->entries[(unsigned) new] = ma->entries[(unsigned) old];
- ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs);
- ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old];
-
- ma->entries[(unsigned) old].new = 0;
- ma->ref_entries[(unsigned) old].num = 0;
- ma->ref_entries[(unsigned) old].refs = 0;
-
- ma->orig_map[ma->entries[new].orig] = new;
- return 0;
-}
-
-static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old)
-{
- struct irel_ma *ma;
-
- ma = irel->priv_data;
- if (old > ma->max_inode)
- return EXT2_ET_INVALID_ARGUMENT;
- if (ma->entries[(unsigned) old].new == 0)
- return ENOENT;
-
- ma->entries[old].new = 0;
- ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs);
- ma->orig_map[ma->entries[(unsigned) old].orig] = 0;
-
- ma->ref_entries[(unsigned) old].num = 0;
- ma->ref_entries[(unsigned) old].refs = 0;
- return 0;
-}
-
-static errcode_t ima_free(ext2_irel irel)
-{
- struct irel_ma *ma;
- ext2_ino_t ino;
-
- if (!irel)
- return 0;
-
- ma = irel->priv_data;
-
- if (ma) {
- ext2fs_free_mem(&ma->orig_map);
- ext2fs_free_mem(&ma->entries);
- if (ma->ref_entries) {
- for (ino = 0; ino <= ma->max_inode; ino++) {
- ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs);
- }
- ext2fs_free_mem(&ma->ref_entries);
- }
- ext2fs_free_mem(&ma);
- }
- ext2fs_free_mem(&irel->name);
- ext2fs_free_mem(&irel);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c b/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
deleted file mode 100644
index f5f6f31..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
+++ b/dev/null
@@ -1,357 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * ismounted.c --- Check to see if the filesystem was mounted
- *
- * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#ifdef HAVE_LINUX_FD_H
-#include <linux/fd.h>
-#endif
-#ifdef HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#ifdef HAVE_GETMNTINFO
-#include <paths.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-#endif /* HAVE_GETMNTINFO */
-#include <string.h>
-#include <sys/stat.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#ifdef HAVE_MNTENT_H
-/*
- * Helper function which checks a file in /etc/mtab format to see if a
- * filesystem is mounted. Returns an error if the file doesn't exist
- * or can't be opened.
- */
-static errcode_t check_mntent_file(const char *mtab_file, const char *file,
- int *mount_flags, char *mtpt, int mtlen)
-{
- struct mntent *mnt;
- struct stat st_buf;
- errcode_t retval = 0;
- dev_t file_dev=0, file_rdev=0;
- ino_t file_ino=0;
- FILE *f;
- int fd;
-
- *mount_flags = 0;
- if ((f = setmntent (mtab_file, "r")) == NULL)
- return errno;
- if (stat(file, &st_buf) == 0) {
- if (S_ISBLK(st_buf.st_mode)) {
-#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
- file_rdev = st_buf.st_rdev;
-#endif /* __GNU__ */
- } else {
- file_dev = st_buf.st_dev;
- file_ino = st_buf.st_ino;
- }
- }
- while ((mnt = getmntent (f)) != NULL) {
- if (strcmp(file, mnt->mnt_fsname) == 0)
- break;
- if (stat(mnt->mnt_fsname, &st_buf) == 0) {
- if (S_ISBLK(st_buf.st_mode)) {
-#ifndef __GNU__
- if (file_rdev && (file_rdev == st_buf.st_rdev))
- break;
-#endif /* __GNU__ */
- } else {
- if (file_dev && ((file_dev == st_buf.st_dev) &&
- (file_ino == st_buf.st_ino)))
- break;
- }
- }
- }
-
- if (mnt == 0) {
-#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
- /*
- * Do an extra check to see if this is the root device. We
- * can't trust /etc/mtab, and /proc/mounts will only list
- * /dev/root for the root filesystem. Argh. Instead we
- * check if the given device has the same major/minor number
- * as the device that the root directory is on.
- */
- if (file_rdev && stat("/", &st_buf) == 0) {
- if (st_buf.st_dev == file_rdev) {
- *mount_flags = EXT2_MF_MOUNTED;
- if (mtpt)
- strncpy(mtpt, "/", mtlen);
- goto is_root;
- }
- }
-#endif /* __GNU__ */
- goto errout;
- }
-#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
- /* Validate the entry in case /etc/mtab is out of date */
- /*
- * We need to be paranoid, because some broken distributions
- * (read: Slackware) don't initialize /etc/mtab before checking
- * all of the non-root filesystems on the disk.
- */
- if (stat(mnt->mnt_dir, &st_buf) < 0) {
- retval = errno;
- if (retval == ENOENT) {
-#ifdef DEBUG
- printf("Bogus entry in %s! (%s does not exist)\n",
- mtab_file, mnt->mnt_dir);
-#endif /* DEBUG */
- retval = 0;
- }
- goto errout;
- }
- if (file_rdev && (st_buf.st_dev != file_rdev)) {
-#ifdef DEBUG
- printf("Bogus entry in %s! (%s not mounted on %s)\n",
- mtab_file, file, mnt->mnt_dir);
-#endif /* DEBUG */
- goto errout;
- }
-#endif /* __GNU__ */
- *mount_flags = EXT2_MF_MOUNTED;
-
-#ifdef MNTOPT_RO
- /* Check to see if the ro option is set */
- if (hasmntopt(mnt, MNTOPT_RO))
- *mount_flags |= EXT2_MF_READONLY;
-#endif
-
- if (mtpt)
- strncpy(mtpt, mnt->mnt_dir, mtlen);
- /*
- * Check to see if we're referring to the root filesystem.
- * If so, do a manual check to see if we can open /etc/mtab
- * read/write, since if the root is mounted read/only, the
- * contents of /etc/mtab may not be accurate.
- */
- if (LONE_CHAR(mnt->mnt_dir, '/')) {
-is_root:
-#define TEST_FILE "/.ismount-test-file"
- *mount_flags |= EXT2_MF_ISROOT;
- fd = open(TEST_FILE, O_RDWR|O_CREAT);
- if (fd < 0) {
- if (errno == EROFS)
- *mount_flags |= EXT2_MF_READONLY;
- } else
- close(fd);
- (void) unlink(TEST_FILE);
- }
- retval = 0;
-errout:
- endmntent (f);
- return retval;
-}
-
-static errcode_t check_mntent(const char *file, int *mount_flags,
- char *mtpt, int mtlen)
-{
- errcode_t retval;
-
-#ifdef DEBUG
- retval = check_mntent_file("/tmp/mtab", file, mount_flags,
- mtpt, mtlen);
- if (retval == 0)
- return 0;
-#endif /* DEBUG */
-#ifdef __linux__
- retval = check_mntent_file("/proc/mounts", file, mount_flags,
- mtpt, mtlen);
- if (retval == 0 && (*mount_flags != 0))
- return 0;
-#endif /* __linux__ */
-#if defined(MOUNTED) || defined(_PATH_MOUNTED)
-#ifndef MOUNTED
-#define MOUNTED _PATH_MOUNTED
-#endif /* MOUNTED */
- retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
- return retval;
-#else
- *mount_flags = 0;
- return 0;
-#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
-}
-
-#else
-#if defined(HAVE_GETMNTINFO)
-
-static errcode_t check_getmntinfo(const char *file, int *mount_flags,
- char *mtpt, int mtlen)
-{
- struct statfs *mp;
- int len, n;
- const char *s1;
- char *s2;
-
- n = getmntinfo(&mp, MNT_NOWAIT);
- if (n == 0)
- return errno;
-
- len = sizeof(_PATH_DEV) - 1;
- s1 = file;
- if (strncmp(_PATH_DEV, s1, len) == 0)
- s1 += len;
-
- *mount_flags = 0;
- while (--n >= 0) {
- s2 = mp->f_mntfromname;
- if (strncmp(_PATH_DEV, s2, len) == 0) {
- s2 += len - 1;
- *s2 = 'r';
- }
- if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
- *mount_flags = EXT2_MF_MOUNTED;
- break;
- }
- ++mp;
- }
- if (mtpt)
- strncpy(mtpt, mp->f_mntonname, mtlen);
- return 0;
-}
-#endif /* HAVE_GETMNTINFO */
-#endif /* HAVE_MNTENT_H */
-
-/*
- * Check to see if we're dealing with the swap device.
- */
-static int is_swap_device(const char *file)
-{
- FILE *f;
- char buf[1024], *cp;
- dev_t file_dev;
- struct stat st_buf;
- int ret = 0;
-
- file_dev = 0;
-#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
- if ((stat(file, &st_buf) == 0) &&
- S_ISBLK(st_buf.st_mode))
- file_dev = st_buf.st_rdev;
-#endif /* __GNU__ */
-
- if (!(f = fopen_for_read("/proc/swaps")))
- return 0;
- /* Skip the first line */
- fgets(buf, sizeof(buf), f);
- while (!feof(f)) {
- if (!fgets(buf, sizeof(buf), f))
- break;
- if ((cp = strchr(buf, ' ')) != NULL)
- *cp = 0;
- if ((cp = strchr(buf, '\t')) != NULL)
- *cp = 0;
- if (strcmp(buf, file) == 0) {
- ret++;
- break;
- }
-#ifndef __GNU__
- if (file_dev && (stat(buf, &st_buf) == 0) &&
- S_ISBLK(st_buf.st_mode) &&
- file_dev == st_buf.st_rdev) {
- ret++;
- break;
- }
-#endif /* __GNU__ */
- }
- fclose(f);
- return ret;
-}
-
-
-/*
- * ext2fs_check_mount_point() returns 1 if the device is mounted, 0
- * otherwise. If mtpt is non-NULL, the directory where the device is
- * mounted is copied to where mtpt is pointing, up to mtlen
- * characters.
- */
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
- char *mtpt, int mtlen)
-{
- if (is_swap_device(device)) {
- *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
- strncpy(mtpt, "<swap>", mtlen);
- return 0;
- }
-#ifdef HAVE_MNTENT_H
- return check_mntent(device, mount_flags, mtpt, mtlen);
-#else
-#ifdef HAVE_GETMNTINFO
- return check_getmntinfo(device, mount_flags, mtpt, mtlen);
-#else
-#ifdef __GNUC__
- #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
-#endif
- *mount_flags = 0;
- return 0;
-#endif /* HAVE_GETMNTINFO */
-#endif /* HAVE_MNTENT_H */
-}
-
-/*
- * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED,
- * EXT2_MF_READONLY, and EXT2_MF_ROOT
- *
- */
-errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags)
-{
- return ext2fs_check_mount_point(file, mount_flags, NULL, 0);
-}
-
-#ifdef DEBUG
-int main(int argc, char **argv)
-{
- int retval, mount_flags;
- char mntpt[80];
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s device\n", argv[0]);
- exit(1);
- }
-
- mntpt[0] = 0;
- retval = ext2fs_check_mount_point(argv[1], &mount_flags,
- mntpt, sizeof(mntpt));
- if (retval) {
- com_err(argv[0], retval,
- "while calling ext2fs_check_if_mounted");
- exit(1);
- }
- printf("Device %s reports flags %02x\n", argv[1], mount_flags);
- if (mount_flags & EXT2_MF_BUSY)
- printf("\t%s is apparently in use.\n", argv[1]);
- if (mount_flags & EXT2_MF_MOUNTED)
- printf("\t%s is mounted.\n", argv[1]);
- if (mount_flags & EXT2_MF_SWAP)
- printf("\t%s is a swap device.\n", argv[1]);
- if (mount_flags & EXT2_MF_READONLY)
- printf("\t%s is read-only.\n", argv[1]);
- if (mount_flags & EXT2_MF_ISROOT)
- printf("\t%s is the root filesystem.\n", argv[1]);
- if (mntpt[0])
- printf("\t%s is mounted on %s.\n", argv[1], mntpt);
- exit(0);
-}
-#endif /* DEBUG */
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h b/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
deleted file mode 100644
index 17c586a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
+++ b/dev/null
@@ -1,63 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * jfs_dat.h --- stripped down header file which only contains the JFS
- * on-disk data structures
- */
-
-#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
-
-/*
- * On-disk structures
- */
-
-/*
- * Descriptor block types:
- */
-
-#define JFS_DESCRIPTOR_BLOCK 1
-#define JFS_COMMIT_BLOCK 2
-#define JFS_SUPERBLOCK 3
-
-/*
- * Standard header for all descriptor blocks:
- */
-typedef struct journal_header_s
-{
- __u32 h_magic;
- __u32 h_blocktype;
- __u32 h_sequence;
-} journal_header_t;
-
-
-/*
- * The block tag: used to describe a single buffer in the journal
- */
-typedef struct journal_block_tag_s
-{
- __u32 t_blocknr; /* The on-disk block number */
- __u32 t_flags; /* See below */
-} journal_block_tag_t;
-
-/* Definitions for the journal tag flags word: */
-#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */
-#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */
-#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */
-#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
-
-
-/*
- * The journal superblock
- */
-typedef struct journal_superblock_s
-{
- journal_header_t s_header;
-
- /* Static information describing the journal */
- __u32 s_blocksize; /* journal device blocksize */
- __u32 s_maxlen; /* total blocks in journal file */
- __u32 s_first; /* first block of log information */
-
- /* Dynamic information describing the current state of the log */
- __u32 s_sequence; /* first commit ID expected in log */
- __u32 s_start; /* blocknr of start of log */
-} journal_superblock_t;
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
deleted file mode 100644
index 853d97a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
+++ b/dev/null
@@ -1,235 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * linux/include/linux/jbd.h
- *
- * Written by Stephen C. Tweedie <sct@redhat.com>
- *
- * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
- *
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- *
- * Definitions for transaction data structures for the buffer cache
- * filesystem journaling support.
- */
-#ifndef LINUX_JBD_H
-#define LINUX_JBD_H 1
-
-#include <sys/types.h>
-#include <linux/types.h>
-#include "ext2fs.h"
-
-/*
- * Standard header for all descriptor blocks:
- */
-
-typedef struct journal_header_s
-{
- __u32 h_magic;
- __u32 h_blocktype;
- __u32 h_sequence;
-} journal_header_t;
-
-/*
- * This is the global e2fsck structure.
- */
-typedef struct e2fsck_struct *e2fsck_t;
-
-
-struct inode {
- e2fsck_t i_ctx;
- ext2_ino_t i_ino;
- struct ext2_inode i_ext2;
-};
-
-
-/*
- * The journal superblock. All fields are in big-endian byte order.
- */
-typedef struct journal_superblock_s
-{
-/* 0x0000 */
- journal_header_t s_header;
-
-/* 0x000C */
- /* Static information describing the journal */
- __u32 s_blocksize; /* journal device blocksize */
- __u32 s_maxlen; /* total blocks in journal file */
- __u32 s_first; /* first block of log information */
-
-/* 0x0018 */
- /* Dynamic information describing the current state of the log */
- __u32 s_sequence; /* first commit ID expected in log */
- __u32 s_start; /* blocknr of start of log */
-
-/* 0x0020 */
- /* Error value, as set by journal_abort(). */
- __s32 s_errno;
-
-/* 0x0024 */
- /* Remaining fields are only valid in a version-2 superblock */
- __u32 s_feature_compat; /* compatible feature set */
- __u32 s_feature_incompat; /* incompatible feature set */
- __u32 s_feature_ro_compat; /* readonly-compatible feature set */
-/* 0x0030 */
- __u8 s_uuid[16]; /* 128-bit uuid for journal */
-
-/* 0x0040 */
- __u32 s_nr_users; /* Nr of filesystems sharing log */
-
- __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/
-
-/* 0x0048 */
- __u32 s_max_transaction; /* Limit of journal blocks per trans.*/
- __u32 s_max_trans_data; /* Limit of data blocks per trans. */
-
-/* 0x0050 */
- __u32 s_padding[44];
-
-/* 0x0100 */
- __u8 s_users[16*48]; /* ids of all fs'es sharing the log */
-/* 0x0400 */
-} journal_superblock_t;
-
-
-extern int journal_blocks_per_page(struct inode *inode);
-extern int jbd_blocks_per_page(struct inode *inode);
-
-#define JFS_MIN_JOURNAL_BLOCKS 1024
-
-
-/*
- * Internal structures used by the logging mechanism:
- */
-
-#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
-
-/*
- * Descriptor block types:
- */
-
-#define JFS_DESCRIPTOR_BLOCK 1
-#define JFS_COMMIT_BLOCK 2
-#define JFS_SUPERBLOCK_V1 3
-#define JFS_SUPERBLOCK_V2 4
-#define JFS_REVOKE_BLOCK 5
-
-/*
- * The block tag: used to describe a single buffer in the journal
- */
-typedef struct journal_block_tag_s
-{
- __u32 t_blocknr; /* The on-disk block number */
- __u32 t_flags; /* See below */
-} journal_block_tag_t;
-
-/*
- * The revoke descriptor: used on disk to describe a series of blocks to
- * be revoked from the log
- */
-typedef struct journal_revoke_header_s
-{
- journal_header_t r_header;
- int r_count; /* Count of bytes used in the block */
-} journal_revoke_header_t;
-
-
-/* Definitions for the journal tag flags word: */
-#define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */
-#define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */
-#define JFS_FLAG_DELETED 4 /* block deleted by this transaction */
-#define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */
-
-
-
-
-#define JFS_HAS_COMPAT_FEATURE(j,mask) \
- ((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
-#define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \
- ((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
-#define JFS_HAS_INCOMPAT_FEATURE(j,mask) \
- ((j)->j_format_version >= 2 && \
- ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
-
-#define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001
-
-/* Features known to this kernel version: */
-#define JFS_KNOWN_COMPAT_FEATURES 0
-#define JFS_KNOWN_ROCOMPAT_FEATURES 0
-#define JFS_KNOWN_INCOMPAT_FEATURES JFS_FEATURE_INCOMPAT_REVOKE
-
-/* Comparison functions for transaction IDs: perform comparisons using
- * modulo arithmetic so that they work over sequence number wraps. */
-
-
-/*
- * Definitions which augment the buffer_head layer
- */
-
-/* journaling buffer types */
-#define BJ_None 0 /* Not journaled */
-#define BJ_SyncData 1 /* Normal data: flush before commit */
-#define BJ_AsyncData 2 /* writepage data: wait on it before commit */
-#define BJ_Metadata 3 /* Normal journaled metadata */
-#define BJ_Forget 4 /* Buffer superceded by this transaction */
-#define BJ_IO 5 /* Buffer is for temporary IO use */
-#define BJ_Shadow 6 /* Buffer contents being shadowed to the log */
-#define BJ_LogCtl 7 /* Buffer contains log descriptors */
-#define BJ_Reserved 8 /* Buffer is reserved for access by journal */
-#define BJ_Types 9
-
-
-struct kdev_s {
- e2fsck_t k_ctx;
- int k_dev;
-};
-
-typedef struct kdev_s *kdev_t;
-typedef unsigned int tid_t;
-
-struct journal_s
-{
- unsigned long j_flags;
- int j_errno;
- struct buffer_head * j_sb_buffer;
- struct journal_superblock_s *j_superblock;
- int j_format_version;
- unsigned long j_head;
- unsigned long j_tail;
- unsigned long j_free;
- unsigned long j_first, j_last;
- kdev_t j_dev;
- kdev_t j_fs_dev;
- int j_blocksize;
- unsigned int j_blk_offset;
- unsigned int j_maxlen;
- struct inode * j_inode;
- tid_t j_tail_sequence;
- tid_t j_transaction_sequence;
- __u8 j_uuid[16];
- struct jbd_revoke_table_s *j_revoke;
-};
-
-typedef struct journal_s journal_t;
-
-extern int journal_recover (journal_t *journal);
-extern int journal_skip_recovery (journal_t *);
-
-/* Primary revoke support */
-extern int journal_init_revoke(journal_t *, int);
-extern void journal_destroy_revoke_caches(void);
-extern int journal_init_revoke_caches(void);
-
-/* Recovery revoke support */
-extern int journal_set_revoke(journal_t *, unsigned long, tid_t);
-extern int journal_test_revoke(journal_t *, unsigned long, tid_t);
-extern void journal_clear_revoke(journal_t *);
-extern void journal_brelse_array(struct buffer_head *b[], int n);
-
-extern void journal_destroy_revoke(journal_t *);
-
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h b/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
deleted file mode 100644
index d80716a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
+++ b/dev/null
@@ -1,113 +0,0 @@
-/* vi: set sw=4 ts=4: */
-#ifndef LINUX_LIST_H
-#define LINUX_LIST_H 1
-
-/*
- * Simple doubly linked list implementation.
- *
- * 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 = { &name, &name }
-
-#define INIT_LIST_HEAD(ptr) do { \
- (ptr)->next = (ptr); (ptr)->prev = (ptr); \
-} while (0)
-
-#if (!defined(__GNUC__) && !defined(__WATCOMC__))
-#define __inline__
-#endif
-
-/*
- * Insert a new entry between two known consecutive entries.
- *
- * 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;
-}
-
-/*
- * Insert a new entry after the specified head..
- */
-static __inline__ void list_add(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head, head->next);
-}
-
-/*
- * Insert a new entry at the tail
- */
-static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
-{
- __list_add(new, head->prev, head);
-}
-
-/*
- * 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;
-}
-
-static __inline__ void list_del(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
-}
-
-static __inline__ int list_empty(struct list_head *head)
-{
- return head->next == head;
-}
-
-/*
- * Splice in "list" into "head"
- */
-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;
- }
-}
-
-#define list_entry(ptr, type, member) \
- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
-
-#define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/link.c b/e2fsprogs/old_e2fsprogs/ext2fs/link.c
deleted file mode 100644
index 08b2e96..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/link.c
+++ b/dev/null
@@ -1,135 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * link.c --- create links in a ext2fs directory
- *
- * Copyright (C) 1993, 1994 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct link_struct {
- const char *name;
- int namelen;
- ext2_ino_t inode;
- int flags;
- int done;
- struct ext2_super_block *sb;
-};
-
-static int link_proc(struct ext2_dir_entry *dirent,
- int offset,
- int blocksize,
- char *buf,
- void *priv_data)
-{
- struct link_struct *ls = (struct link_struct *) priv_data;
- struct ext2_dir_entry *next;
- int rec_len, min_rec_len;
- int ret = 0;
-
- rec_len = EXT2_DIR_REC_LEN(ls->namelen);
-
- /*
- * See if the following directory entry (if any) is unused;
- * if so, absorb it into this one.
- */
- next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
- if ((offset + dirent->rec_len < blocksize - 8) &&
- (next->inode == 0) &&
- (offset + dirent->rec_len + next->rec_len <= blocksize)) {
- dirent->rec_len += next->rec_len;
- ret = DIRENT_CHANGED;
- }
-
- /*
- * If the directory entry is used, see if we can split the
- * directory entry to make room for the new name. If so,
- * truncate it and return.
- */
- if (dirent->inode) {
- min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
- if (dirent->rec_len < (min_rec_len + rec_len))
- return ret;
- rec_len = dirent->rec_len - min_rec_len;
- dirent->rec_len = min_rec_len;
- next = (struct ext2_dir_entry *) (buf + offset +
- dirent->rec_len);
- next->inode = 0;
- next->name_len = 0;
- next->rec_len = rec_len;
- return DIRENT_CHANGED;
- }
-
- /*
- * If we get this far, then the directory entry is not used.
- * See if we can fit the request entry in. If so, do it.
- */
- if (dirent->rec_len < rec_len)
- return ret;
- dirent->inode = ls->inode;
- dirent->name_len = ls->namelen;
- strncpy(dirent->name, ls->name, ls->namelen);
- if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
- dirent->name_len |= (ls->flags & 0x7) << 8;
-
- ls->done++;
- return DIRENT_ABORT|DIRENT_CHANGED;
-}
-
-/*
- * Note: the low 3 bits of the flags field are used as the directory
- * entry filetype.
- */
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
- ext2_ino_t ino, int flags)
-{
- errcode_t retval;
- struct link_struct ls;
- struct ext2_inode inode;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- ls.name = name;
- ls.namelen = name ? strlen(name) : 0;
- ls.inode = ino;
- ls.flags = flags;
- ls.done = 0;
- ls.sb = fs->super;
-
- retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
- 0, link_proc, &ls);
- if (retval)
- return retval;
-
- if (!ls.done)
- return EXT2_ET_DIR_NO_SPACE;
-
- if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
- return retval;
-
- if (inode.i_flags & EXT2_INDEX_FL) {
- inode.i_flags &= ~EXT2_INDEX_FL;
- if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
- return retval;
- }
-
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c b/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
deleted file mode 100644
index b2e8de8..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
+++ b/dev/null
@@ -1,68 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * lookup.c --- ext2fs directory lookup operations
- *
- * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct lookup_struct {
- const char *name;
- int len;
- ext2_ino_t *inode;
- int found;
-};
-
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int lookup_proc(struct ext2_dir_entry *dirent,
- int offset EXT2FS_ATTR((unused)),
- int blocksize EXT2FS_ATTR((unused)),
- char *buf EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct lookup_struct *ls = (struct lookup_struct *) priv_data;
-
- if (ls->len != (dirent->name_len & 0xFF))
- return 0;
- if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF)))
- return 0;
- *ls->inode = dirent->inode;
- ls->found++;
- return DIRENT_ABORT;
-}
-
-
-errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
- int namelen, char *buf, ext2_ino_t *inode)
-{
- errcode_t retval;
- struct lookup_struct ls;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- ls.name = name;
- ls.len = namelen;
- ls.inode = inode;
- ls.found = 0;
-
- retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
- if (retval)
- return retval;
-
- return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c b/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
deleted file mode 100644
index a86ac8e..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
+++ b/dev/null
@@ -1,139 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * mkdir.c --- make a directory in the filesystem
- *
- * Copyright (C) 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#ifndef EXT2_FT_DIR
-#define EXT2_FT_DIR 2
-#endif
-
-errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
- const char *name)
-{
- errcode_t retval;
- struct ext2_inode parent_inode, inode;
- ext2_ino_t ino = inum;
- ext2_ino_t scratch_ino;
- blk_t blk;
- char *block = NULL;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- /*
- * Allocate an inode, if necessary
- */
- if (!ino) {
- retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
- 0, &ino);
- if (retval)
- goto cleanup;
- }
-
- /*
- * Allocate a data block for the directory
- */
- retval = ext2fs_new_block(fs, 0, 0, &blk);
- if (retval)
- goto cleanup;
-
- /*
- * Create a scratch template for the directory
- */
- retval = ext2fs_new_dir_block(fs, ino, parent, &block);
- if (retval)
- goto cleanup;
-
- /*
- * Get the parent's inode, if necessary
- */
- if (parent != ino) {
- retval = ext2fs_read_inode(fs, parent, &parent_inode);
- if (retval)
- goto cleanup;
- } else
- memset(&parent_inode, 0, sizeof(parent_inode));
-
- /*
- * Create the inode structure....
- */
- memset(&inode, 0, sizeof(struct ext2_inode));
- inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
- inode.i_uid = inode.i_gid = 0;
- inode.i_blocks = fs->blocksize / 512;
- inode.i_block[0] = blk;
- inode.i_links_count = 2;
- inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL);
- inode.i_size = fs->blocksize;
-
- /*
- * Write out the inode and inode data block
- */
- retval = ext2fs_write_dir_block(fs, blk, block);
- if (retval)
- goto cleanup;
- retval = ext2fs_write_new_inode(fs, ino, &inode);
- if (retval)
- goto cleanup;
-
- /*
- * Link the directory into the filesystem hierarchy
- */
- if (name) {
- retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
- &scratch_ino);
- if (!retval) {
- retval = EXT2_ET_DIR_EXISTS;
- name = 0;
- goto cleanup;
- }
- if (retval != EXT2_ET_FILE_NOT_FOUND)
- goto cleanup;
- retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
- if (retval)
- goto cleanup;
- }
-
- /*
- * Update parent inode's counts
- */
- if (parent != ino) {
- parent_inode.i_links_count++;
- retval = ext2fs_write_inode(fs, parent, &parent_inode);
- if (retval)
- goto cleanup;
- }
-
- /*
- * Update accounting....
- */
- ext2fs_block_alloc_stats(fs, blk, +1);
- ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
-
-cleanup:
- ext2fs_free_mem(&block);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c b/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
deleted file mode 100644
index 748d9ab..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
+++ b/dev/null
@@ -1,426 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * mkjournal.c --- make a journal for a filesystem
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#if HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#include "ext2_fs.h"
-#include "../e2p/e2p.h"
-#include "../e2fsck.h"
-#include "ext2fs.h"
-#include "kernel-jbd.h"
-
-/*
- * This function automatically sets up the journal superblock and
- * returns it as an allocated block.
- */
-errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
- __u32 size, int flags,
- char **ret_jsb)
-{
- errcode_t retval;
- journal_superblock_t *jsb;
-
- if (size < 1024)
- return EXT2_ET_JOURNAL_TOO_SMALL;
-
- if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
- return retval;
-
- memset (jsb, 0, fs->blocksize);
-
- jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
- if (flags & EXT2_MKJOURNAL_V1_SUPER)
- jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
- else
- jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
- jsb->s_blocksize = htonl(fs->blocksize);
- jsb->s_maxlen = htonl(size);
- jsb->s_nr_users = htonl(1);
- jsb->s_first = htonl(1);
- jsb->s_sequence = htonl(1);
- memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
- /*
- * If we're creating an external journal device, we need to
- * adjust these fields.
- */
- if (fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
- jsb->s_nr_users = 0;
- if (fs->blocksize == 1024)
- jsb->s_first = htonl(3);
- else
- jsb->s_first = htonl(2);
- }
-
- *ret_jsb = (char *) jsb;
- return 0;
-}
-
-/*
- * This function writes a journal using POSIX routines. It is used
- * for creating external journals and creating journals on live
- * filesystems.
- */
-static errcode_t write_journal_file(ext2_filsys fs, char *filename,
- blk_t size, int flags)
-{
- errcode_t retval;
- char *buf = NULL;
- int fd, ret_size;
- blk_t i;
-
- if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
- return retval;
-
- /* Open the device or journal file */
- if ((fd = open(filename, O_WRONLY)) < 0) {
- retval = errno;
- goto errout;
- }
-
- /* Write the superblock out */
- retval = EXT2_ET_SHORT_WRITE;
- ret_size = write(fd, buf, fs->blocksize);
- if (ret_size < 0) {
- retval = errno;
- goto errout;
- }
- if (ret_size != (int) fs->blocksize)
- goto errout;
- memset(buf, 0, fs->blocksize);
-
- for (i = 1; i < size; i++) {
- ret_size = write(fd, buf, fs->blocksize);
- if (ret_size < 0) {
- retval = errno;
- goto errout;
- }
- if (ret_size != (int) fs->blocksize)
- goto errout;
- }
- close(fd);
-
- retval = 0;
-errout:
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-/*
- * Helper function for creating the journal using direct I/O routines
- */
-struct mkjournal_struct {
- int num_blocks;
- int newblocks;
- char *buf;
- errcode_t err;
-};
-
-static int mkjournal_proc(ext2_filsys fs,
- blk_t *blocknr,
- e2_blkcnt_t blockcnt,
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data;
- blk_t new_blk;
- static blk_t last_blk = 0;
- errcode_t retval;
-
- if (*blocknr) {
- last_blk = *blocknr;
- return 0;
- }
- retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- if (blockcnt > 0)
- es->num_blocks--;
-
- es->newblocks++;
- retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf);
-
- if (blockcnt == 0)
- memset(es->buf, 0, fs->blocksize);
-
- if (retval) {
- es->err = retval;
- return BLOCK_ABORT;
- }
- *blocknr = new_blk;
- last_blk = new_blk;
- ext2fs_block_alloc_stats(fs, new_blk, +1);
-
- if (es->num_blocks == 0)
- return (BLOCK_CHANGED | BLOCK_ABORT);
- else
- return BLOCK_CHANGED;
-}
-
-/*
- * This function creates a journal using direct I/O routines.
- */
-static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
- blk_t size, int flags)
-{
- char *buf;
- errcode_t retval;
- struct ext2_inode inode;
- struct mkjournal_struct es;
-
- if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
- return retval;
-
- if ((retval = ext2fs_read_bitmaps(fs)))
- return retval;
-
- if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
- return retval;
-
- if (inode.i_blocks > 0)
- return EEXIST;
-
- es.num_blocks = size;
- es.newblocks = 0;
- es.buf = buf;
- es.err = 0;
-
- retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND,
- 0, mkjournal_proc, &es);
- if (es.err) {
- retval = es.err;
- goto errout;
- }
-
- if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
- goto errout;
-
- inode.i_size += fs->blocksize * size;
- inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
- inode.i_mtime = inode.i_ctime = time(NULL);
- inode.i_links_count = 1;
- inode.i_mode = LINUX_S_IFREG | 0600;
-
- if ((retval = ext2fs_write_inode(fs, journal_ino, &inode)))
- goto errout;
- retval = 0;
-
- memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
- fs->super->s_jnl_blocks[16] = inode.i_size;
- fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
- ext2fs_mark_super_dirty(fs);
-
-errout:
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-/*
- * This function adds a journal device to a filesystem
- */
-errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
-{
- struct stat st;
- errcode_t retval;
- char buf[1024];
- journal_superblock_t *jsb;
- int start;
- __u32 i, nr_users;
-
- /* Make sure the device exists and is a block device */
- if (stat(journal_dev->device_name, &st) < 0)
- return errno;
-
- if (!S_ISBLK(st.st_mode))
- return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
-
- /* Get the journal superblock */
- start = 1;
- if (journal_dev->blocksize == 1024)
- start++;
- if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf)))
- return retval;
-
- jsb = (journal_superblock_t *) buf;
- if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
- (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2)))
- return EXT2_ET_NO_JOURNAL_SB;
-
- if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
- return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
-
- /* Check and see if this filesystem has already been added */
- nr_users = ntohl(jsb->s_nr_users);
- for (i=0; i < nr_users; i++) {
- if (memcmp(fs->super->s_uuid,
- &jsb->s_users[i*16], 16) == 0)
- break;
- }
- if (i >= nr_users) {
- memcpy(&jsb->s_users[nr_users*16],
- fs->super->s_uuid, 16);
- jsb->s_nr_users = htonl(nr_users+1);
- }
-
- /* Writeback the journal superblock */
- if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf)))
- return retval;
-
- fs->super->s_journal_inum = 0;
- fs->super->s_journal_dev = st.st_rdev;
- memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
- sizeof(fs->super->s_journal_uuid));
- fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- ext2fs_mark_super_dirty(fs);
- return 0;
-}
-
-/*
- * This function adds a journal inode to a filesystem, using either
- * POSIX routines if the filesystem is mounted, or using direct I/O
- * functions if it is not.
- */
-errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags)
-{
- errcode_t retval;
- ext2_ino_t journal_ino;
- struct stat st;
- char jfile[1024];
- int fd, mount_flags, f;
-
- retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
- jfile, sizeof(jfile)-10);
- if (retval)
- return retval;
-
- if (mount_flags & EXT2_MF_MOUNTED) {
- strcat(jfile, "/.journal");
-
- /*
- * If .../.journal already exists, make sure any
- * immutable or append-only flags are cleared.
- */
-#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
- (void) chflags (jfile, 0);
-#else
-#if HAVE_EXT2_IOCTLS
- fd = open(jfile, O_RDONLY);
- if (fd >= 0) {
- f = 0;
- ioctl(fd, EXT2_IOC_SETFLAGS, &f);
- close(fd);
- }
-#endif
-#endif
-
- /* Create the journal file */
- if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
- return errno;
-
- if ((retval = write_journal_file(fs, jfile, size, flags)))
- goto errout;
-
- /* Get inode number of the journal file */
- if (fstat(fd, &st) < 0)
- goto errout;
-
-#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
- retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
-#else
-#if HAVE_EXT2_IOCTLS
- f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
- retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
-#endif
-#endif
- if (retval)
- goto errout;
-
- close(fd);
- journal_ino = st.st_ino;
- } else {
- journal_ino = EXT2_JOURNAL_INO;
- if ((retval = write_journal_inode(fs, journal_ino,
- size, flags)))
- return retval;
- }
-
- fs->super->s_journal_inum = journal_ino;
- fs->super->s_journal_dev = 0;
- memset(fs->super->s_journal_uuid, 0,
- sizeof(fs->super->s_journal_uuid));
- fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
-
- ext2fs_mark_super_dirty(fs);
- return 0;
-errout:
- close(fd);
- return retval;
-}
-
-#ifdef DEBUG
-main(int argc, char **argv)
-{
- errcode_t retval;
- char *device_name;
- ext2_filsys fs;
-
- if (argc < 2) {
- fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
- exit(1);
- }
- device_name = argv[1];
-
- retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
- unix_io_manager, &fs);
- if (retval) {
- com_err(argv[0], retval, "while opening %s", device_name);
- exit(1);
- }
-
- retval = ext2fs_add_journal_inode(fs, 1024);
- if (retval) {
- com_err(argv[0], retval, "while adding journal to %s",
- device_name);
- exit(1);
- }
- retval = ext2fs_flush(fs);
- if (retval) {
- printf("Warning, had trouble writing out superblocks.\n");
- }
- ext2fs_close(fs);
- exit(0);
-}
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/namei.c b/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
deleted file mode 100644
index 1824461..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
+++ b/dev/null
@@ -1,204 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * namei.c --- ext2fs directory lookup operations
- *
- * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-/* #define NAMEI_DEBUG */
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
- const char *pathname, size_t pathlen, int follow,
- int link_count, char *buf, ext2_ino_t *res_inode);
-
-static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
- ext2_ino_t inode, int link_count,
- char *buf, ext2_ino_t *res_inode)
-{
- char *pathname;
- char *buffer = NULL;
- errcode_t retval;
- struct ext2_inode ei;
-
-#ifdef NAMEI_DEBUG
- printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n",
- root, dir, inode, link_count);
-
-#endif
- retval = ext2fs_read_inode (fs, inode, &ei);
- if (retval) return retval;
- if (!LINUX_S_ISLNK (ei.i_mode)) {
- *res_inode = inode;
- return 0;
- }
- if (link_count++ > 5) {
- return EXT2_ET_SYMLINK_LOOP;
- }
- if (ext2fs_inode_data_blocks(fs, &ei)) {
- retval = ext2fs_get_mem(fs->blocksize, &buffer);
- if (retval)
- return retval;
- retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer);
- if (retval) {
- ext2fs_free_mem(&buffer);
- return retval;
- }
- pathname = buffer;
- } else
- pathname = (char *)&(ei.i_block[0]);
- retval = open_namei(fs, root, dir, pathname, ei.i_size, 1,
- link_count, buf, res_inode);
- ext2fs_free_mem(&buffer);
- return retval;
-}
-
-/*
- * This routine interprets a pathname in the context of the current
- * directory and the root directory, and returns the inode of the
- * containing directory, and a pointer to the filename of the file
- * (pointing into the pathname) and the length of the filename.
- */
-static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
- const char *pathname, int pathlen,
- int link_count, char *buf,
- const char **name, int *namelen,
- ext2_ino_t *res_inode)
-{
- char c;
- const char *thisname;
- int len;
- ext2_ino_t inode;
- errcode_t retval;
-
- if ((c = *pathname) == '/') {
- dir = root;
- pathname++;
- pathlen--;
- }
- while (1) {
- thisname = pathname;
- for (len=0; --pathlen >= 0;len++) {
- c = *(pathname++);
- if (c == '/')
- break;
- }
- if (pathlen < 0)
- break;
- retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode);
- if (retval) return retval;
- retval = follow_link (fs, root, dir, inode,
- link_count, buf, &dir);
- if (retval) return retval;
- }
- *name = thisname;
- *namelen = len;
- *res_inode = dir;
- return 0;
-}
-
-static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
- const char *pathname, size_t pathlen, int follow,
- int link_count, char *buf, ext2_ino_t *res_inode)
-{
- const char *basename;
- int namelen;
- ext2_ino_t dir, inode;
- errcode_t retval;
-
-#ifdef NAMEI_DEBUG
- printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n",
- root, base, pathlen, pathname, link_count);
-#endif
- retval = dir_namei(fs, root, base, pathname, pathlen,
- link_count, buf, &basename, &namelen, &dir);
- if (retval) return retval;
- if (!namelen) { /* special case: '/usr/' etc */
- *res_inode=dir;
- return 0;
- }
- retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode);
- if (retval)
- return retval;
- if (follow) {
- retval = follow_link(fs, root, dir, inode, link_count,
- buf, &inode);
- if (retval)
- return retval;
- }
-#ifdef NAMEI_DEBUG
- printf("open_namei: (link_count=%d) returns %lu\n",
- link_count, inode);
-#endif
- *res_inode = inode;
- return 0;
-}
-
-errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- const char *name, ext2_ino_t *inode)
-{
- char *buf;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
-
- retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0,
- buf, inode);
-
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- const char *name, ext2_ino_t *inode)
-{
- char *buf;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
-
- retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0,
- buf, inode);
-
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
- ext2_ino_t inode, ext2_ino_t *res_inode)
-{
- char *buf;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
-
- retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode);
-
- ext2fs_free_mem(&buf);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c b/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
deleted file mode 100644
index 9f15662..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
+++ b/dev/null
@@ -1,72 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * newdir.c --- create a new directory block
- *
- * Copyright (C) 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-#ifndef EXT2_FT_DIR
-#define EXT2_FT_DIR 2
-#endif
-
-/*
- * Create new directory block
- */
-errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
- ext2_ino_t parent_ino, char **block)
-{
- struct ext2_dir_entry *dir = NULL;
- errcode_t retval;
- char *buf;
- int rec_len;
- int filetype = 0;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- retval = ext2fs_get_mem(fs->blocksize, &buf);
- if (retval)
- return retval;
- memset(buf, 0, fs->blocksize);
- dir = (struct ext2_dir_entry *) buf;
- dir->rec_len = fs->blocksize;
-
- if (dir_ino) {
- if (fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)
- filetype = EXT2_FT_DIR << 8;
- /*
- * Set up entry for '.'
- */
- dir->inode = dir_ino;
- dir->name_len = 1 | filetype;
- dir->name[0] = '.';
- rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
- dir->rec_len = EXT2_DIR_REC_LEN(1);
-
- /*
- * Set up entry for '..'
- */
- dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
- dir->rec_len = rec_len;
- dir->inode = parent_ino;
- dir->name_len = 2 | filetype;
- dir->name[0] = '.';
- dir->name[1] = '.';
- }
- *block = buf;
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
deleted file mode 100644
index 1b27119..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
+++ b/dev/null
@@ -1,330 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * openfs.c --- open an ext2 filesystem
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-
-
-#include "ext2fs.h"
-#include "e2image.h"
-
-blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
-{
- int bg;
- int has_super = 0;
- int ret_blk;
-
- if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
- (i < fs->super->s_first_meta_bg))
- return (group_block + i + 1);
-
- bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i;
- if (ext2fs_bg_has_super(fs, bg))
- has_super = 1;
- ret_blk = (fs->super->s_first_data_block + has_super +
- (bg * fs->super->s_blocks_per_group));
- /*
- * If group_block is not the normal value, we're trying to use
- * the backup group descriptors and superblock --- so use the
- * alternate location of the second block group in the
- * metablock group. Ideally we should be testing each bg
- * descriptor block individually for correctness, but we don't
- * have the infrastructure in place to do that.
- */
- if (group_block != fs->super->s_first_data_block &&
- ((ret_blk + fs->super->s_blocks_per_group) <
- fs->super->s_blocks_count))
- ret_blk += fs->super->s_blocks_per_group;
- return ret_blk;
-}
-
-errcode_t ext2fs_open(const char *name, int flags, int superblock,
- unsigned int block_size, io_manager manager,
- ext2_filsys *ret_fs)
-{
- return ext2fs_open2(name, 0, flags, superblock, block_size,
- manager, ret_fs);
-}
-
-/*
- * Note: if superblock is non-zero, block-size must also be non-zero.
- * Superblock and block_size can be zero to use the default size.
- *
- * Valid flags for ext2fs_open()
- *
- * EXT2_FLAG_RW - Open the filesystem for read/write.
- * EXT2_FLAG_FORCE - Open the filesystem even if some of the
- * features aren't supported.
- * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
- */
-errcode_t ext2fs_open2(const char *name, const char *io_options,
- int flags, int superblock,
- unsigned int block_size, io_manager manager,
- ext2_filsys *ret_fs)
-{
- ext2_filsys fs;
- errcode_t retval;
- unsigned long i;
- int groups_per_block, blocks_per_group;
- blk_t group_block, blk;
- char *dest, *cp;
-#if BB_BIG_ENDIAN
- int j;
- struct ext2_group_desc *gdp;
-#endif
-
- EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
-
- retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
- if (retval)
- return retval;
-
- memset(fs, 0, sizeof(struct struct_ext2_filsys));
- fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
- fs->flags = flags;
- fs->umask = 022;
- retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
- if (retval)
- goto cleanup;
- strcpy(fs->device_name, name);
- cp = strchr(fs->device_name, '?');
- if (!io_options && cp) {
- *cp++ = 0;
- io_options = cp;
- }
-
- retval = manager->open(fs->device_name,
- (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0,
- &fs->io);
- if (retval)
- goto cleanup;
- if (io_options &&
- (retval = io_channel_set_options(fs->io, io_options)))
- goto cleanup;
- fs->image_io = fs->io;
- fs->io->app_data = fs;
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
- if (retval)
- goto cleanup;
- if (flags & EXT2_FLAG_IMAGE_FILE) {
- retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr),
- &fs->image_header);
- if (retval)
- goto cleanup;
- retval = io_channel_read_blk(fs->io, 0,
- -(int)sizeof(struct ext2_image_hdr),
- fs->image_header);
- if (retval)
- goto cleanup;
- if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE)
- return EXT2_ET_MAGIC_E2IMAGE;
- superblock = 1;
- block_size = fs->image_header->fs_blocksize;
- }
-
- /*
- * If the user specifies a specific block # for the
- * superblock, then he/she must also specify the block size!
- * Otherwise, read the master superblock located at offset
- * SUPERBLOCK_OFFSET from the start of the partition.
- *
- * Note: we only save a backup copy of the superblock if we
- * are reading the superblock from the primary superblock location.
- */
- if (superblock) {
- if (!block_size) {
- retval = EXT2_ET_INVALID_ARGUMENT;
- goto cleanup;
- }
- io_channel_set_blksize(fs->io, block_size);
- group_block = superblock;
- fs->orig_super = 0;
- } else {
- io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
- superblock = 1;
- group_block = 0;
- retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
- if (retval)
- goto cleanup;
- }
- retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
- fs->super);
- if (retval)
- goto cleanup;
- if (fs->orig_super)
- memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
-
-#if BB_BIG_ENDIAN
- if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES)) {
- fs->flags |= EXT2_FLAG_SWAP_BYTES;
-
- ext2fs_swap_super(fs->super);
- }
-#endif
-
- if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
- retval = EXT2_ET_BAD_MAGIC;
- goto cleanup;
- }
- if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) {
- retval = EXT2_ET_REV_TOO_HIGH;
- goto cleanup;
- }
-
- /*
- * Check for feature set incompatibility
- */
- if (!(flags & EXT2_FLAG_FORCE)) {
- if (fs->super->s_feature_incompat &
- ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
- retval = EXT2_ET_UNSUPP_FEATURE;
- goto cleanup;
- }
- if ((flags & EXT2_FLAG_RW) &&
- (fs->super->s_feature_ro_compat &
- ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
- retval = EXT2_ET_RO_UNSUPP_FEATURE;
- goto cleanup;
- }
- if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) &&
- (fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
- retval = EXT2_ET_UNSUPP_FEATURE;
- goto cleanup;
- }
- }
-
- fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
- if (fs->blocksize == 0) {
- retval = EXT2_ET_CORRUPT_SUPERBLOCK;
- goto cleanup;
- }
- fs->fragsize = EXT2_FRAG_SIZE(fs->super);
- fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group *
- EXT2_INODE_SIZE(fs->super) +
- EXT2_BLOCK_SIZE(fs->super) - 1) /
- EXT2_BLOCK_SIZE(fs->super));
- if (block_size) {
- if (block_size != fs->blocksize) {
- retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE;
- goto cleanup;
- }
- }
- /*
- * Set the blocksize to the filesystem's blocksize.
- */
- io_channel_set_blksize(fs->io, fs->blocksize);
-
- /*
- * If this is an external journal device, don't try to read
- * the group descriptors, because they're not there.
- */
- if (fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
- fs->group_desc_count = 0;
- *ret_fs = fs;
- return 0;
- }
-
- /*
- * Read group descriptors
- */
- blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
- if (blocks_per_group == 0 ||
- blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) ||
- fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) {
- retval = EXT2_ET_CORRUPT_SUPERBLOCK;
- goto cleanup;
- }
- fs->group_desc_count = (fs->super->s_blocks_count -
- fs->super->s_first_data_block +
- blocks_per_group - 1) / blocks_per_group;
- fs->desc_blocks = (fs->group_desc_count +
- EXT2_DESC_PER_BLOCK(fs->super) - 1)
- / EXT2_DESC_PER_BLOCK(fs->super);
- retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize,
- &fs->group_desc);
- if (retval)
- goto cleanup;
- if (!group_block)
- group_block = fs->super->s_first_data_block;
- dest = (char *) fs->group_desc;
- groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc);
- for (i = 0; i < fs->desc_blocks; i++) {
- blk = ext2fs_descriptor_block_loc(fs, group_block, i);
- retval = io_channel_read_blk(fs->io, blk, 1, dest);
- if (retval)
- goto cleanup;
-#if BB_BIG_ENDIAN
- if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
- gdp = (struct ext2_group_desc *) dest;
- for (j=0; j < groups_per_block; j++)
- ext2fs_swap_group_desc(gdp++);
- }
-#endif
- dest += fs->blocksize;
- }
-
- *ret_fs = fs;
- return 0;
-cleanup:
- ext2fs_free(fs);
- return retval;
-}
-
-/*
- * Set/get the filesystem data I/O channel.
- *
- * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true.
- */
-errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io)
-{
- if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
- return EXT2_ET_NOT_IMAGE_FILE;
- if (old_io) {
- *old_io = (fs->image_io == fs->io) ? 0 : fs->io;
- }
- return 0;
-}
-
-errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
-{
- if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
- return EXT2_ET_NOT_IMAGE_FILE;
- fs->io = new_io ? new_io : fs->image_io;
- return 0;
-}
-
-errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
-{
- if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
- return EXT2_ET_NOT_IMAGE_FILE;
- fs->io = fs->image_io = new_io;
- fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
- EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
- fs->flags &= ~EXT2_FLAG_IMAGE_FILE;
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
deleted file mode 100644
index ce77bc9..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
+++ b/dev/null
@@ -1,96 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * read_bb --- read the bad blocks inode
- *
- * Copyright (C) 1994 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct read_bb_record {
- ext2_badblocks_list bb_list;
- errcode_t err;
-};
-
-/*
- * Helper function for ext2fs_read_bb_inode()
- */
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int mark_bad_block(ext2_filsys fs, blk_t *block_nr,
- e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
- blk_t ref_block EXT2FS_ATTR((unused)),
- int ref_offset EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct read_bb_record *rb = (struct read_bb_record *) priv_data;
-
- if (blockcnt < 0)
- return 0;
-
- if ((*block_nr < fs->super->s_first_data_block) ||
- (*block_nr >= fs->super->s_blocks_count))
- return 0; /* Ignore illegal blocks */
-
- rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr);
- if (rb->err)
- return BLOCK_ABORT;
- return 0;
-}
-
-/*
- * Reads the current bad blocks from the bad blocks inode.
- */
-errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
-{
- errcode_t retval;
- struct read_bb_record rb;
- struct ext2_inode inode;
- blk_t numblocks;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!*bb_list) {
- retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
- if (retval)
- return retval;
- if (inode.i_blocks < 500)
- numblocks = (inode.i_blocks /
- (fs->blocksize / 512)) + 20;
- else
- numblocks = 500;
- retval = ext2fs_badblocks_list_create(bb_list, numblocks);
- if (retval)
- return retval;
- }
-
- rb.bb_list = *bb_list;
- rb.err = 0;
- retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0,
- mark_bad_block, &rb);
- if (retval)
- return retval;
-
- return rb.err;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c b/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
deleted file mode 100644
index bf1fc32..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
+++ b/dev/null
@@ -1,96 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * read_bb_file.c --- read a list of bad blocks from a FILE *
- *
- * Copyright (C) 1994, 1995, 2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * Reads a list of bad blocks from a FILE *
- */
-errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
- ext2_badblocks_list *bb_list,
- void *priv_data,
- void (*invalid)(ext2_filsys fs,
- blk_t blk,
- char *badstr,
- void *priv_data))
-{
- errcode_t retval;
- blk_t blockno;
- int count;
- char buf[128];
-
- if (fs)
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!*bb_list) {
- retval = ext2fs_badblocks_list_create(bb_list, 10);
- if (retval)
- return retval;
- }
-
- while (!feof (f)) {
- if (fgets(buf, sizeof(buf), f) == NULL)
- break;
- count = sscanf(buf, "%u", &blockno);
- if (count <= 0)
- continue;
- if (fs &&
- ((blockno < fs->super->s_first_data_block) ||
- (blockno >= fs->super->s_blocks_count))) {
- if (invalid)
- (invalid)(fs, blockno, buf, priv_data);
- continue;
- }
- retval = ext2fs_badblocks_list_add(*bb_list, blockno);
- if (retval)
- return retval;
- }
- return 0;
-}
-
-static void call_compat_invalid(ext2_filsys fs, blk_t blk,
- char *badstr EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- void (*invalid)(ext2_filsys, blk_t);
-
- invalid = (void (*)(ext2_filsys, blk_t)) priv_data;
- if (invalid)
- invalid(fs, blk);
-}
-
-
-/*
- * Reads a list of bad blocks from a FILE *
- */
-errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
- ext2_badblocks_list *bb_list,
- void (*invalid)(ext2_filsys fs, blk_t blk))
-{
- return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid,
- call_compat_invalid);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c b/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
deleted file mode 100644
index 403463a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
+++ b/dev/null
@@ -1,220 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * res_gdt.c --- reserve blocks for growing the group descriptor table
- * during online resizing.
- *
- * Copyright (C) 2002 Andreas Dilger
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * Iterate through the groups which hold BACKUP superblock/GDT copies in an
- * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
- * calling this for the first time. In a sparse filesystem it will be the
- * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
- * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
- */
-static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
- unsigned int *five, unsigned int *seven)
-{
- unsigned int *min = three;
- int mult = 3;
- unsigned int ret;
-
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
- ret = *min;
- *min += 1;
- return ret;
- }
-
- if (*five < *min) {
- min = five;
- mult = 5;
- }
- if (*seven < *min) {
- min = seven;
- mult = 7;
- }
-
- ret = *min;
- *min *= mult;
-
- return ret;
-}
-
-/*
- * This code assumes that the reserved blocks have already been marked in-use
- * during ext2fs_initialize(), so that they are not allocated for other
- * uses before we can add them to the resize inode (which has to come
- * after the creation of the inode table).
- */
-errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
-{
- errcode_t retval, retval2;
- struct ext2_super_block *sb;
- struct ext2_inode inode;
- __u32 *dindir_buf, *gdt_buf;
- int rsv_add;
- unsigned long long apb, inode_size;
- blk_t dindir_blk, rsv_off, gdt_off, gdt_blk;
- int dindir_dirty = 0, inode_dirty = 0;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- sb = fs->super;
-
- retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf);
- if (retval)
- goto out_free;
- gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
-
- retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
- if (retval)
- goto out_free;
-
- /* Maximum possible file size (we donly use the dindirect blocks) */
- apb = EXT2_ADDR_PER_BLOCK(sb);
- rsv_add = fs->blocksize / 512;
- if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
-#ifdef RES_GDT_DEBUG
- printf("reading GDT dindir %u\n", dindir_blk);
-#endif
- retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
- if (retval)
- goto out_inode;
- } else {
- blk_t goal = 3 + sb->s_reserved_gdt_blocks +
- fs->desc_blocks + fs->inode_blocks_per_group;
-
- retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
- if (retval)
- goto out_free;
- inode.i_mode = LINUX_S_IFREG | 0600;
- inode.i_links_count = 1;
- inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
- inode.i_blocks = rsv_add;
- memset(dindir_buf, 0, fs->blocksize);
-#ifdef RES_GDT_DEBUG
- printf("allocated GDT dindir %u\n", dindir_blk);
-#endif
- dindir_dirty = inode_dirty = 1;
- inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
- inode_size *= fs->blocksize;
- inode.i_size = inode_size & 0xFFFFFFFF;
- inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
- if (inode.i_size_high) {
- sb->s_feature_ro_compat |=
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
- }
- inode.i_ctime = time(NULL);
- }
-
- for (rsv_off = 0, gdt_off = fs->desc_blocks,
- gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks;
- rsv_off < sb->s_reserved_gdt_blocks;
- rsv_off++, gdt_off++, gdt_blk++) {
- unsigned int three = 1, five = 5, seven = 7;
- unsigned int grp, last = 0;
- int gdt_dirty = 0;
-
- gdt_off %= apb;
- if (!dindir_buf[gdt_off]) {
- /* FIXME XXX XXX
- blk_t new_blk;
-
- retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
- if (retval)
- goto out_free;
- if (new_blk != gdt_blk) {
- // XXX free block
- retval = -1; // XXX
- }
- */
- gdt_dirty = dindir_dirty = inode_dirty = 1;
- memset(gdt_buf, 0, fs->blocksize);
- dindir_buf[gdt_off] = gdt_blk;
- inode.i_blocks += rsv_add;
-#ifdef RES_GDT_DEBUG
- printf("added primary GDT block %u at %u[%u]\n",
- gdt_blk, dindir_blk, gdt_off);
-#endif
- } else if (dindir_buf[gdt_off] == gdt_blk) {
-#ifdef RES_GDT_DEBUG
- printf("reading primary GDT block %u\n", gdt_blk);
-#endif
- retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
- if (retval)
- goto out_dindir;
- } else {
-#ifdef RES_GDT_DEBUG
- printf("bad primary GDT %u != %u at %u[%u]\n",
- dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
-#endif
- retval = EXT2_ET_RESIZE_INODE_CORRUPT;
- goto out_dindir;
- }
-
- while ((grp = list_backups(fs, &three, &five, &seven)) <
- fs->group_desc_count) {
- blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
-
- if (!gdt_buf[last]) {
-#ifdef RES_GDT_DEBUG
- printf("added backup GDT %u grp %u@%u[%u]\n",
- expect, grp, gdt_blk, last);
-#endif
- gdt_buf[last] = expect;
- inode.i_blocks += rsv_add;
- gdt_dirty = inode_dirty = 1;
- } else if (gdt_buf[last] != expect) {
-#ifdef RES_GDT_DEBUG
- printf("bad backup GDT %u != %u at %u[%u]\n",
- gdt_buf[last], expect, gdt_blk, last);
-#endif
- retval = EXT2_ET_RESIZE_INODE_CORRUPT;
- goto out_dindir;
- }
- last++;
- }
- if (gdt_dirty) {
-#ifdef RES_GDT_DEBUG
- printf("writing primary GDT block %u\n", gdt_blk);
-#endif
- retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
- if (retval)
- goto out_dindir;
- }
- }
-
-out_dindir:
- if (dindir_dirty) {
- retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
- if (!retval)
- retval = retval2;
- }
-out_inode:
-#ifdef RES_GDT_DEBUG
- printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks,
- inode.i_size);
-#endif
- if (inode_dirty) {
- inode.i_atime = inode.i_mtime = time(NULL);
- retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
- if (!retval)
- retval = retval2;
- }
-out_free:
- ext2fs_free_mem((void *)&dindir_buf);
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c b/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
deleted file mode 100644
index 32e87b7..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
+++ b/dev/null
@@ -1,106 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * rs_bitmap.c --- routine for changing the size of a bitmap
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end,
- ext2fs_generic_bitmap bmap)
-{
- errcode_t retval;
- size_t size, new_size;
- __u32 bitno;
-
- if (!bmap)
- return EXT2_ET_INVALID_ARGUMENT;
-
- EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP);
-
- /*
- * If we're expanding the bitmap, make sure all of the new
- * parts of the bitmap are zero.
- */
- if (new_end > bmap->end) {
- bitno = bmap->real_end;
- if (bitno > new_end)
- bitno = new_end;
- for (; bitno > bmap->end; bitno--)
- ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap);
- }
- if (new_real_end == bmap->real_end) {
- bmap->end = new_end;
- return 0;
- }
-
- size = ((bmap->real_end - bmap->start) / 8) + 1;
- new_size = ((new_real_end - bmap->start) / 8) + 1;
-
- if (size != new_size) {
- retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap);
- if (retval)
- return retval;
- }
- if (new_size > size)
- memset(bmap->bitmap + size, 0, new_size - size);
-
- bmap->end = new_end;
- bmap->real_end = new_real_end;
- return 0;
-}
-
-errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
- ext2fs_inode_bitmap bmap)
-{
- errcode_t retval;
-
- if (!bmap)
- return EXT2_ET_INVALID_ARGUMENT;
-
- EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP);
-
- bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
- retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
- bmap);
- bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
- return retval;
-}
-
-errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
- ext2fs_block_bitmap bmap)
-{
- errcode_t retval;
-
- if (!bmap)
- return EXT2_ET_INVALID_ARGUMENT;
-
- EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
-
- bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
- retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
- bmap);
- bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c b/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
deleted file mode 100644
index bba4326..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
+++ b/dev/null
@@ -1,294 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * rw_bitmaps.c --- routines to read and write the inode and block bitmaps.
- *
- * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-#include "e2image.h"
-
-#if defined(__powerpc__) && BB_BIG_ENDIAN
-/*
- * On the PowerPC, the big-endian variant of the ext2 filesystem
- * has its bitmaps stored as 32-bit words with bit 0 as the LSB
- * of each word. Thus a bitmap with only bit 0 set would be, as
- * a string of bytes, 00 00 00 01 00 ...
- * To cope with this, we byte-reverse each word of a bitmap if
- * we have a big-endian filesystem, that is, if we are *not*
- * byte-swapping other word-sized numbers.
- */
-#define EXT2_BIG_ENDIAN_BITMAPS
-#endif
-
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
-static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes)
-{
- __u32 *p = (__u32 *) bitmap;
- int n;
-
- for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
- *p = ext2fs_swab32(*p);
-}
-#endif
-
-errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
-{
- dgrp_t i;
- size_t nbytes;
- errcode_t retval;
- char * inode_bitmap = fs->inode_map->bitmap;
- char * bitmap_block = NULL;
- blk_t blk;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
- if (!inode_bitmap)
- return 0;
- nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
-
- retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
- if (retval)
- return retval;
- memset(bitmap_block, 0xff, fs->blocksize);
- for (i = 0; i < fs->group_desc_count; i++) {
- memcpy(bitmap_block, inode_bitmap, nbytes);
- blk = fs->group_desc[i].bg_inode_bitmap;
- if (blk) {
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
- if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
- ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
-#endif
- retval = io_channel_write_blk(fs->io, blk, 1,
- bitmap_block);
- if (retval)
- return EXT2_ET_INODE_BITMAP_WRITE;
- }
- inode_bitmap += nbytes;
- }
- fs->flags &= ~EXT2_FLAG_IB_DIRTY;
- ext2fs_free_mem(&bitmap_block);
- return 0;
-}
-
-errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
-{
- dgrp_t i;
- unsigned int j;
- int nbytes;
- unsigned int nbits;
- errcode_t retval;
- char * block_bitmap = fs->block_map->bitmap;
- char * bitmap_block = NULL;
- blk_t blk;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
- if (!block_bitmap)
- return 0;
- nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
- retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
- if (retval)
- return retval;
- memset(bitmap_block, 0xff, fs->blocksize);
- for (i = 0; i < fs->group_desc_count; i++) {
- memcpy(bitmap_block, block_bitmap, nbytes);
- if (i == fs->group_desc_count - 1) {
- /* Force bitmap padding for the last group */
- nbits = ((fs->super->s_blocks_count
- - fs->super->s_first_data_block)
- % EXT2_BLOCKS_PER_GROUP(fs->super));
- if (nbits)
- for (j = nbits; j < fs->blocksize * 8; j++)
- ext2fs_set_bit(j, bitmap_block);
- }
- blk = fs->group_desc[i].bg_block_bitmap;
- if (blk) {
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
- if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
- ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
-#endif
- retval = io_channel_write_blk(fs->io, blk, 1,
- bitmap_block);
- if (retval)
- return EXT2_ET_BLOCK_BITMAP_WRITE;
- }
- block_bitmap += nbytes;
- }
- fs->flags &= ~EXT2_FLAG_BB_DIRTY;
- ext2fs_free_mem(&bitmap_block);
- return 0;
-}
-
-static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
-{
- dgrp_t i;
- char *block_bitmap = NULL, *inode_bitmap = NULL;
- char *buf;
- errcode_t retval;
- int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
- int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8;
- blk_t blk;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- fs->write_bitmaps = ext2fs_write_bitmaps;
-
- retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
- if (retval)
- return retval;
- if (do_block) {
- ext2fs_free_block_bitmap(fs->block_map);
- sprintf(buf, "block bitmap for %s", fs->device_name);
- retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
- if (retval)
- goto cleanup;
- block_bitmap = fs->block_map->bitmap;
- }
- if (do_inode) {
- ext2fs_free_inode_bitmap(fs->inode_map);
- sprintf(buf, "inode bitmap for %s", fs->device_name);
- retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
- if (retval)
- goto cleanup;
- inode_bitmap = fs->inode_map->bitmap;
- }
- ext2fs_free_mem(&buf);
-
- if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
- if (inode_bitmap) {
- blk = (fs->image_header->offset_inodemap /
- fs->blocksize);
- retval = io_channel_read_blk(fs->image_io, blk,
- -(inode_nbytes * fs->group_desc_count),
- inode_bitmap);
- if (retval)
- goto cleanup;
- }
- if (block_bitmap) {
- blk = (fs->image_header->offset_blockmap /
- fs->blocksize);
- retval = io_channel_read_blk(fs->image_io, blk,
- -(block_nbytes * fs->group_desc_count),
- block_bitmap);
- if (retval)
- goto cleanup;
- }
- return 0;
- }
-
- for (i = 0; i < fs->group_desc_count; i++) {
- if (block_bitmap) {
- blk = fs->group_desc[i].bg_block_bitmap;
- if (blk) {
- retval = io_channel_read_blk(fs->io, blk,
- -block_nbytes, block_bitmap);
- if (retval) {
- retval = EXT2_ET_BLOCK_BITMAP_READ;
- goto cleanup;
- }
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
- if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
- ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes);
-#endif
- } else
- memset(block_bitmap, 0, block_nbytes);
- block_bitmap += block_nbytes;
- }
- if (inode_bitmap) {
- blk = fs->group_desc[i].bg_inode_bitmap;
- if (blk) {
- retval = io_channel_read_blk(fs->io, blk,
- -inode_nbytes, inode_bitmap);
- if (retval) {
- retval = EXT2_ET_INODE_BITMAP_READ;
- goto cleanup;
- }
-#ifdef EXT2_BIG_ENDIAN_BITMAPS
- if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
- (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
- ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes);
-#endif
- } else
- memset(inode_bitmap, 0, inode_nbytes);
- inode_bitmap += inode_nbytes;
- }
- }
- return 0;
-
-cleanup:
- if (do_block) {
- ext2fs_free_mem(&fs->block_map);
- }
- if (do_inode) {
- ext2fs_free_mem(&fs->inode_map);
- }
- ext2fs_free_mem(&buf);
- return retval;
-}
-
-errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs)
-{
- return read_bitmaps(fs, 1, 0);
-}
-
-errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
-{
- return read_bitmaps(fs, 0, 1);
-}
-
-errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
-{
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (fs->inode_map && fs->block_map)
- return 0;
-
- return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
-}
-
-errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
-{
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (fs->block_map && ext2fs_test_bb_dirty(fs)) {
- retval = ext2fs_write_block_bitmap(fs);
- if (retval)
- return retval;
- }
- if (fs->inode_map && ext2fs_test_ib_dirty(fs)) {
- retval = ext2fs_write_inode_bitmap(fs);
- if (retval)
- return retval;
- }
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c b/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
deleted file mode 100644
index b3d3071..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
+++ b/dev/null
@@ -1,79 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * sparse.c --- find the groups in an ext2 filesystem with metadata backups
- *
- * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
- * Copyright (C) 2002 Andreas Dilger.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-
-#include "ext2_fs.h"
-#include "ext2fsP.h"
-
-static int test_root(int a, int b)
-{
- if (a == 0)
- return 1;
- while (1) {
- if (a == 1)
- return 1;
- if (a % b)
- return 0;
- a = a / b;
- }
-}
-
-int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
-{
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
- return 1;
-
- if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
- test_root(group_block, 7))
- return 1;
-
- return 0;
-}
-
-/*
- * Iterate through the groups which hold BACKUP superblock/GDT copies in an
- * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before
- * calling this for the first time. In a sparse filesystem it will be the
- * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
- * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
- */
-unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three,
- unsigned int *five, unsigned int *seven)
-{
- unsigned int *min = three;
- int mult = 3;
- unsigned int ret;
-
- if (!(fs->super->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
- ret = *min;
- *min += 1;
- return ret;
- }
-
- if (*five < *min) {
- min = five;
- mult = 5;
- }
- if (*seven < *min) {
- min = seven;
- mult = 7;
- }
-
- ret = *min;
- *min *= mult;
-
- return ret;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c b/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
deleted file mode 100644
index 07b757a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
+++ b/dev/null
@@ -1,234 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * swapfs.c --- swap ext2 filesystem data structures
- *
- * Copyright (C) 1995, 1996, 2002 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-#include "ext2_ext_attr.h"
-
-#if BB_BIG_ENDIAN
-void ext2fs_swap_super(struct ext2_super_block * sb)
-{
- int i;
- sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count);
- sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count);
- sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count);
- sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count);
- sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count);
- sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block);
- sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size);
- sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size);
- sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group);
- sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group);
- sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group);
- sb->s_mtime = ext2fs_swab32(sb->s_mtime);
- sb->s_wtime = ext2fs_swab32(sb->s_wtime);
- sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count);
- sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count);
- sb->s_magic = ext2fs_swab16(sb->s_magic);
- sb->s_state = ext2fs_swab16(sb->s_state);
- sb->s_errors = ext2fs_swab16(sb->s_errors);
- sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level);
- sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck);
- sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval);
- sb->s_creator_os = ext2fs_swab32(sb->s_creator_os);
- sb->s_rev_level = ext2fs_swab32(sb->s_rev_level);
- sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid);
- sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid);
- sb->s_first_ino = ext2fs_swab32(sb->s_first_ino);
- sb->s_inode_size = ext2fs_swab16(sb->s_inode_size);
- sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr);
- sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat);
- sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat);
- sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat);
- sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap);
- sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks);
- sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum);
- sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev);
- sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan);
- sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts);
- sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg);
- sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time);
- for (i=0; i < 4; i++)
- sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
- for (i=0; i < 17; i++)
- sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
-}
-
-void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
-{
- gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap);
- gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap);
- gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table);
- gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count);
- gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count);
- gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count);
-}
-
-void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
-{
- struct ext2_ext_attr_header *from_header =
- (struct ext2_ext_attr_header *)from;
- struct ext2_ext_attr_header *to_header =
- (struct ext2_ext_attr_header *)to;
- struct ext2_ext_attr_entry *from_entry, *to_entry;
- char *from_end = (char *)from_header + bufsize;
- int n;
-
- if (to_header != from_header)
- memcpy(to_header, from_header, bufsize);
-
- from_entry = (struct ext2_ext_attr_entry *)from_header;
- to_entry = (struct ext2_ext_attr_entry *)to_header;
-
- if (has_header) {
- to_header->h_magic = ext2fs_swab32(from_header->h_magic);
- to_header->h_blocks = ext2fs_swab32(from_header->h_blocks);
- to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
- for (n=0; n<4; n++)
- to_header->h_reserved[n] =
- ext2fs_swab32(from_header->h_reserved[n]);
- from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
- to_entry = (struct ext2_ext_attr_entry *)(to_header+1);
- }
-
- while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
- to_entry->e_value_offs =
- ext2fs_swab16(from_entry->e_value_offs);
- to_entry->e_value_block =
- ext2fs_swab32(from_entry->e_value_block);
- to_entry->e_value_size =
- ext2fs_swab32(from_entry->e_value_size);
- from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
- to_entry = EXT2_EXT_ATTR_NEXT(to_entry);
- }
-}
-
-void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
- struct ext2_inode_large *f, int hostorder,
- int bufsize)
-{
- unsigned i;
- int islnk = 0;
- __u32 *eaf, *eat;
-
- if (hostorder && LINUX_S_ISLNK(f->i_mode))
- islnk = 1;
- t->i_mode = ext2fs_swab16(f->i_mode);
- if (!hostorder && LINUX_S_ISLNK(t->i_mode))
- islnk = 1;
- t->i_uid = ext2fs_swab16(f->i_uid);
- t->i_size = ext2fs_swab32(f->i_size);
- t->i_atime = ext2fs_swab32(f->i_atime);
- t->i_ctime = ext2fs_swab32(f->i_ctime);
- t->i_mtime = ext2fs_swab32(f->i_mtime);
- t->i_dtime = ext2fs_swab32(f->i_dtime);
- t->i_gid = ext2fs_swab16(f->i_gid);
- t->i_links_count = ext2fs_swab16(f->i_links_count);
- t->i_blocks = ext2fs_swab32(f->i_blocks);
- t->i_flags = ext2fs_swab32(f->i_flags);
- t->i_file_acl = ext2fs_swab32(f->i_file_acl);
- t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
- if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) {
- for (i = 0; i < EXT2_N_BLOCKS; i++)
- t->i_block[i] = ext2fs_swab32(f->i_block[i]);
- } else if (t != f) {
- for (i = 0; i < EXT2_N_BLOCKS; i++)
- t->i_block[i] = f->i_block[i];
- }
- t->i_generation = ext2fs_swab32(f->i_generation);
- t->i_faddr = ext2fs_swab32(f->i_faddr);
-
- switch (fs->super->s_creator_os) {
- case EXT2_OS_LINUX:
- t->osd1.linux1.l_i_reserved1 =
- ext2fs_swab32(f->osd1.linux1.l_i_reserved1);
- t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
- t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
- t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
- t->osd2.linux2.l_i_uid_high =
- ext2fs_swab16 (f->osd2.linux2.l_i_uid_high);
- t->osd2.linux2.l_i_gid_high =
- ext2fs_swab16 (f->osd2.linux2.l_i_gid_high);
- t->osd2.linux2.l_i_reserved2 =
- ext2fs_swab32(f->osd2.linux2.l_i_reserved2);
- break;
- case EXT2_OS_HURD:
- t->osd1.hurd1.h_i_translator =
- ext2fs_swab32 (f->osd1.hurd1.h_i_translator);
- t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag;
- t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize;
- t->osd2.hurd2.h_i_mode_high =
- ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high);
- t->osd2.hurd2.h_i_uid_high =
- ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high);
- t->osd2.hurd2.h_i_gid_high =
- ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high);
- t->osd2.hurd2.h_i_author =
- ext2fs_swab32 (f->osd2.hurd2.h_i_author);
- break;
- case EXT2_OS_MASIX:
- t->osd1.masix1.m_i_reserved1 =
- ext2fs_swab32(f->osd1.masix1.m_i_reserved1);
- t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag;
- t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize;
- t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1);
- t->osd2.masix2.m_i_reserved2[0] =
- ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]);
- t->osd2.masix2.m_i_reserved2[1] =
- ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]);
- break;
- }
-
- if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
- return; /* no i_extra_isize field */
-
- t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
- if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) -
- sizeof(struct ext2_inode)) {
- /* this is error case: i_extra_size is too large */
- return;
- }
-
- i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32);
- if (bufsize < (int) i)
- return; /* no space for EA magic */
-
- eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) +
- f->i_extra_isize);
-
- if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC)
- return; /* it seems no magic here */
-
- eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) +
- f->i_extra_isize);
- *eat = ext2fs_swab32(*eaf);
-
- /* convert EA(s) */
- ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1),
- bufsize - sizeof(struct ext2_inode) -
- t->i_extra_isize - sizeof(__u32), 0);
-}
-
-void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t,
- struct ext2_inode *f, int hostorder)
-{
- ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t,
- (struct ext2_inode_large *) f, hostorder,
- sizeof(struct ext2_inode));
-}
-
-#endif
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
deleted file mode 100644
index 3d40d9a..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
+++ b/dev/null
@@ -1,380 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * test_io.c --- This is the Test I/O interface.
- *
- * Copyright (C) 1996 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * For checking structure magic numbers...
- */
-
-#define EXT2_CHECK_MAGIC(struct, code) \
- if ((struct)->magic != (code)) return (code)
-
-struct test_private_data {
- int magic;
- io_channel real;
- int flags;
- FILE *outfile;
- unsigned long block;
- int read_abort_count, write_abort_count;
- void (*read_blk)(unsigned long block, int count, errcode_t err);
- void (*write_blk)(unsigned long block, int count, errcode_t err);
- void (*set_blksize)(int blksize, errcode_t err);
- void (*write_byte)(unsigned long block, int count, errcode_t err);
-};
-
-static errcode_t test_open(const char *name, int flags, io_channel *channel);
-static errcode_t test_close(io_channel channel);
-static errcode_t test_set_blksize(io_channel channel, int blksize);
-static errcode_t test_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t test_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t test_flush(io_channel channel);
-static errcode_t test_write_byte(io_channel channel, unsigned long offset,
- int count, const void *buf);
-static errcode_t test_set_option(io_channel channel, const char *option,
- const char *arg);
-
-static struct struct_io_manager struct_test_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Test I/O Manager",
- test_open,
- test_close,
- test_set_blksize,
- test_read_blk,
- test_write_blk,
- test_flush,
- test_write_byte,
- test_set_option
-};
-
-io_manager test_io_manager = &struct_test_manager;
-
-/*
- * These global variable can be set by the test program as
- * necessary *before* calling test_open
- */
-io_manager test_io_backing_manager = 0;
-void (*test_io_cb_read_blk)
- (unsigned long block, int count, errcode_t err) = 0;
-void (*test_io_cb_write_blk)
- (unsigned long block, int count, errcode_t err) = 0;
-void (*test_io_cb_set_blksize)
- (int blksize, errcode_t err) = 0;
-void (*test_io_cb_write_byte)
- (unsigned long block, int count, errcode_t err) = 0;
-
-/*
- * Test flags
- */
-#define TEST_FLAG_READ 0x01
-#define TEST_FLAG_WRITE 0x02
-#define TEST_FLAG_SET_BLKSIZE 0x04
-#define TEST_FLAG_FLUSH 0x08
-#define TEST_FLAG_DUMP 0x10
-#define TEST_FLAG_SET_OPTION 0x20
-
-static void test_dump_block(io_channel channel,
- struct test_private_data *data,
- unsigned long block, const void *buf)
-{
- const unsigned char *cp;
- FILE *f = data->outfile;
- int i;
- unsigned long cksum = 0;
-
- for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
- cksum += *cp;
- }
- fprintf(f, "Contents of block %lu, checksum %08lu:\n", block, cksum);
- for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
- if ((i % 16) == 0)
- fprintf(f, "%04x: ", i);
- fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
- }
-}
-
-static void test_abort(io_channel channel, unsigned long block)
-{
- struct test_private_data *data;
- FILE *f;
-
- data = (struct test_private_data *) channel->private_data;
- f = data->outfile;
- test_flush(channel);
-
- fprintf(f, "Aborting due to I/O to block %lu\n", block);
- fflush(f);
- abort();
-}
-
-static errcode_t test_open(const char *name, int flags, io_channel *channel)
-{
- io_channel io = NULL;
- struct test_private_data *data = NULL;
- errcode_t retval;
- char *value;
-
- if (name == 0)
- return EXT2_ET_BAD_DEVICE_NAME;
- retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
- if (retval)
- return retval;
- memset(io, 0, sizeof(struct struct_io_channel));
- io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
- retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
- if (retval) {
- retval = EXT2_ET_NO_MEMORY;
- goto cleanup;
- }
- io->manager = test_io_manager;
- retval = ext2fs_get_mem(strlen(name)+1, &io->name);
- if (retval)
- goto cleanup;
-
- strcpy(io->name, name);
- io->private_data = data;
- io->block_size = 1024;
- io->read_error = 0;
- io->write_error = 0;
- io->refcount = 1;
-
- memset(data, 0, sizeof(struct test_private_data));
- data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
- if (test_io_backing_manager) {
- retval = test_io_backing_manager->open(name, flags,
- &data->real);
- if (retval)
- goto cleanup;
- } else
- data->real = 0;
- data->read_blk = test_io_cb_read_blk;
- data->write_blk = test_io_cb_write_blk;
- data->set_blksize = test_io_cb_set_blksize;
- data->write_byte = test_io_cb_write_byte;
-
- data->outfile = NULL;
- if ((value = getenv("TEST_IO_LOGFILE")) != NULL)
- data->outfile = fopen_for_write(value);
- if (!data->outfile)
- data->outfile = stderr;
-
- data->flags = 0;
- if ((value = getenv("TEST_IO_FLAGS")) != NULL)
- data->flags = strtoul(value, NULL, 0);
-
- data->block = 0;
- if ((value = getenv("TEST_IO_BLOCK")) != NULL)
- data->block = strtoul(value, NULL, 0);
-
- data->read_abort_count = 0;
- if ((value = getenv("TEST_IO_READ_ABORT")) != NULL)
- data->read_abort_count = strtoul(value, NULL, 0);
-
- data->write_abort_count = 0;
- if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL)
- data->write_abort_count = strtoul(value, NULL, 0);
-
- *channel = io;
- return 0;
-
-cleanup:
- ext2fs_free_mem(&io);
- ext2fs_free_mem(&data);
- return retval;
-}
-
-static errcode_t test_close(io_channel channel)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (--channel->refcount > 0)
- return 0;
-
- if (data->real)
- retval = io_channel_close(data->real);
-
- if (data->outfile && data->outfile != stderr)
- fclose(data->outfile);
-
- ext2fs_free_mem(&channel->private_data);
- ext2fs_free_mem(&channel->name);
- ext2fs_free_mem(&channel);
- return retval;
-}
-
-static errcode_t test_set_blksize(io_channel channel, int blksize)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real)
- retval = io_channel_set_blksize(data->real, blksize);
- if (data->set_blksize)
- data->set_blksize(blksize, retval);
- if (data->flags & TEST_FLAG_SET_BLKSIZE)
- fprintf(data->outfile,
- "Test_io: set_blksize(%d) returned %s\n",
- blksize, retval ? error_message(retval) : "OK");
- channel->block_size = blksize;
- return retval;
-}
-
-
-static errcode_t test_read_blk(io_channel channel, unsigned long block,
- int count, void *buf)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real)
- retval = io_channel_read_blk(data->real, block, count, buf);
- if (data->read_blk)
- data->read_blk(block, count, retval);
- if (data->flags & TEST_FLAG_READ)
- fprintf(data->outfile,
- "Test_io: read_blk(%lu, %d) returned %s\n",
- block, count, retval ? error_message(retval) : "OK");
- if (data->block && data->block == block) {
- if (data->flags & TEST_FLAG_DUMP)
- test_dump_block(channel, data, block, buf);
- if (--data->read_abort_count == 0)
- test_abort(channel, block);
- }
- return retval;
-}
-
-static errcode_t test_write_blk(io_channel channel, unsigned long block,
- int count, const void *buf)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real)
- retval = io_channel_write_blk(data->real, block, count, buf);
- if (data->write_blk)
- data->write_blk(block, count, retval);
- if (data->flags & TEST_FLAG_WRITE)
- fprintf(data->outfile,
- "Test_io: write_blk(%lu, %d) returned %s\n",
- block, count, retval ? error_message(retval) : "OK");
- if (data->block && data->block == block) {
- if (data->flags & TEST_FLAG_DUMP)
- test_dump_block(channel, data, block, buf);
- if (--data->write_abort_count == 0)
- test_abort(channel, block);
- }
- return retval;
-}
-
-static errcode_t test_write_byte(io_channel channel, unsigned long offset,
- int count, const void *buf)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real && data->real->manager->write_byte)
- retval = io_channel_write_byte(data->real, offset, count, buf);
- if (data->write_byte)
- data->write_byte(offset, count, retval);
- if (data->flags & TEST_FLAG_WRITE)
- fprintf(data->outfile,
- "Test_io: write_byte(%lu, %d) returned %s\n",
- offset, count, retval ? error_message(retval) : "OK");
- return retval;
-}
-
-/*
- * Flush data buffers to disk.
- */
-static errcode_t test_flush(io_channel channel)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
- if (data->real)
- retval = io_channel_flush(data->real);
-
- if (data->flags & TEST_FLAG_FLUSH)
- fprintf(data->outfile, "Test_io: flush() returned %s\n",
- retval ? error_message(retval) : "OK");
-
- return retval;
-}
-
-static errcode_t test_set_option(io_channel channel, const char *option,
- const char *arg)
-{
- struct test_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct test_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
-
-
- if (data->flags & TEST_FLAG_SET_OPTION)
- fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
- option, arg);
- if (data->real && data->real->manager->set_option) {
- retval = (data->real->manager->set_option)(data->real,
- option, arg);
- if (data->flags & TEST_FLAG_SET_OPTION)
- fprintf(data->outfile, "returned %s\n",
- retval ? error_message(retval) : "OK");
- } else {
- if (data->flags & TEST_FLAG_SET_OPTION)
- fprintf(data->outfile, "not implemented\n");
- }
- return retval;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c b/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
deleted file mode 100644
index 3c95829..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
+++ b/dev/null
@@ -1,703 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * unix_io.c --- This is the Unix (well, really POSIX) implementation
- * of the I/O manager.
- *
- * Implements a one-block write-through cache.
- *
- * Includes support for Windows NT support under Cygwin.
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- * 2002 by Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#include <fcntl.h>
-#include <time.h>
-#ifdef __linux__
-#include <sys/utsname.h>
-#endif
-#if HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#include <sys/resource.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * For checking structure magic numbers...
- */
-
-#define EXT2_CHECK_MAGIC(struct, code) \
- if ((struct)->magic != (code)) return (code)
-
-struct unix_cache {
- char *buf;
- unsigned long block;
- int access_time;
- unsigned dirty:1;
- unsigned in_use:1;
-};
-
-#define CACHE_SIZE 8
-#define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */
-#define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */
-
-struct unix_private_data {
- int magic;
- int dev;
- int flags;
- int access_time;
- ext2_loff_t offset;
- struct unix_cache cache[CACHE_SIZE];
-};
-
-static errcode_t unix_open(const char *name, int flags, io_channel *channel);
-static errcode_t unix_close(io_channel channel);
-static errcode_t unix_set_blksize(io_channel channel, int blksize);
-static errcode_t unix_read_blk(io_channel channel, unsigned long block,
- int count, void *data);
-static errcode_t unix_write_blk(io_channel channel, unsigned long block,
- int count, const void *data);
-static errcode_t unix_flush(io_channel channel);
-static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
- int size, const void *data);
-static errcode_t unix_set_option(io_channel channel, const char *option,
- const char *arg);
-
-static void reuse_cache(io_channel channel, struct unix_private_data *data,
- struct unix_cache *cache, unsigned long block);
-
-/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel
- * does not know buffered block devices - everything is raw. */
-#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#define NEED_BOUNCE_BUFFER
-#else
-#undef NEED_BOUNCE_BUFFER
-#endif
-
-static struct struct_io_manager struct_unix_manager = {
- EXT2_ET_MAGIC_IO_MANAGER,
- "Unix I/O Manager",
- unix_open,
- unix_close,
- unix_set_blksize,
- unix_read_blk,
- unix_write_blk,
- unix_flush,
-#ifdef NEED_BOUNCE_BUFFER
- 0,
-#else
- unix_write_byte,
-#endif
- unix_set_option
-};
-
-io_manager unix_io_manager = &struct_unix_manager;
-
-/*
- * Here are the raw I/O functions
- */
-#ifndef NEED_BOUNCE_BUFFER
-static errcode_t raw_read_blk(io_channel channel,
- struct unix_private_data *data,
- unsigned long block,
- int count, void *buf)
-{
- errcode_t retval;
- ssize_t size;
- ext2_loff_t location;
- int actual = 0;
-
- size = (count < 0) ? -count : count * channel->block_size;
- location = ((ext2_loff_t) block * channel->block_size) + data->offset;
- if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
- retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
- goto error_out;
- }
- actual = read(data->dev, buf, size);
- if (actual != size) {
- if (actual < 0)
- actual = 0;
- retval = EXT2_ET_SHORT_READ;
- goto error_out;
- }
- return 0;
-
-error_out:
- memset((char *) buf+actual, 0, size-actual);
- if (channel->read_error)
- retval = (channel->read_error)(channel, block, count, buf,
- size, actual, retval);
- return retval;
-}
-#else /* NEED_BOUNCE_BUFFER */
-/*
- * Windows and FreeBSD block devices only allow sector alignment IO in offset and size
- */
-static errcode_t raw_read_blk(io_channel channel,
- struct unix_private_data *data,
- unsigned long block,
- int count, void *buf)
-{
- errcode_t retval;
- size_t size, alignsize, fragment;
- ext2_loff_t location;
- int total = 0, actual;
-#define BLOCKALIGN 512
- char sector[BLOCKALIGN];
-
- size = (count < 0) ? -count : count * channel->block_size;
- location = ((ext2_loff_t) block * channel->block_size) + data->offset;
-#ifdef DEBUG
- printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n",
- count, size, block, channel->block_size, location);
-#endif
- if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
- retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
- goto error_out;
- }
- fragment = size % BLOCKALIGN;
- alignsize = size - fragment;
- if (alignsize) {
- actual = read(data->dev, buf, alignsize);
- if (actual != alignsize)
- goto short_read;
- }
- if (fragment) {
- actual = read(data->dev, sector, BLOCKALIGN);
- if (actual != BLOCKALIGN)
- goto short_read;
- memcpy(buf+alignsize, sector, fragment);
- }
- return 0;
-
-short_read:
- if (actual>0)
- total += actual;
- retval = EXT2_ET_SHORT_READ;
-
-error_out:
- memset((char *) buf+total, 0, size-actual);
- if (channel->read_error)
- retval = (channel->read_error)(channel, block, count, buf,
- size, actual, retval);
- return retval;
-}
-#endif
-
-static errcode_t raw_write_blk(io_channel channel,
- struct unix_private_data *data,
- unsigned long block,
- int count, const void *buf)
-{
- ssize_t size;
- ext2_loff_t location;
- int actual = 0;
- errcode_t retval;
-
- if (count == 1)
- size = channel->block_size;
- else {
- if (count < 0)
- size = -count;
- else
- size = count * channel->block_size;
- }
-
- location = ((ext2_loff_t) block * channel->block_size) + data->offset;
- if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
- retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
- goto error_out;
- }
-
- actual = write(data->dev, buf, size);
- if (actual != size) {
- retval = EXT2_ET_SHORT_WRITE;
- goto error_out;
- }
- return 0;
-
-error_out:
- if (channel->write_error)
- retval = (channel->write_error)(channel, block, count, buf,
- size, actual, retval);
- return retval;
-}
-
-
-/*
- * Here we implement the cache functions
- */
-
-/* Allocate the cache buffers */
-static errcode_t alloc_cache(io_channel channel,
- struct unix_private_data *data)
-{
- errcode_t retval;
- struct unix_cache *cache;
- int i;
-
- data->access_time = 0;
- for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
- cache->block = 0;
- cache->access_time = 0;
- cache->dirty = 0;
- cache->in_use = 0;
- if ((retval = ext2fs_get_mem(channel->block_size,
- &cache->buf)))
- return retval;
- }
- return 0;
-}
-
-/* Free the cache buffers */
-static void free_cache(struct unix_private_data *data)
-{
- struct unix_cache *cache;
- int i;
-
- data->access_time = 0;
- for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
- cache->block = 0;
- cache->access_time = 0;
- cache->dirty = 0;
- cache->in_use = 0;
- ext2fs_free_mem(&cache->buf);
- cache->buf = 0;
- }
-}
-
-#ifndef NO_IO_CACHE
-/*
- * Try to find a block in the cache. If the block is not found, and
- * eldest is a non-zero pointer, then fill in eldest with the cache
- * entry to that should be reused.
- */
-static struct unix_cache *find_cached_block(struct unix_private_data *data,
- unsigned long block,
- struct unix_cache **eldest)
-{
- struct unix_cache *cache, *unused_cache, *oldest_cache;
- int i;
-
- unused_cache = oldest_cache = 0;
- for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
- if (!cache->in_use) {
- if (!unused_cache)
- unused_cache = cache;
- continue;
- }
- if (cache->block == block) {
- cache->access_time = ++data->access_time;
- return cache;
- }
- if (!oldest_cache ||
- (cache->access_time < oldest_cache->access_time))
- oldest_cache = cache;
- }
- if (eldest)
- *eldest = (unused_cache) ? unused_cache : oldest_cache;
- return 0;
-}
-
-/*
- * Reuse a particular cache entry for another block.
- */
-static void reuse_cache(io_channel channel, struct unix_private_data *data,
- struct unix_cache *cache, unsigned long block)
-{
- if (cache->dirty && cache->in_use)
- raw_write_blk(channel, data, cache->block, 1, cache->buf);
-
- cache->in_use = 1;
- cache->dirty = 0;
- cache->block = block;
- cache->access_time = ++data->access_time;
-}
-
-/*
- * Flush all of the blocks in the cache
- */
-static errcode_t flush_cached_blocks(io_channel channel,
- struct unix_private_data *data,
- int invalidate)
-
-{
- struct unix_cache *cache;
- errcode_t retval, retval2;
- int i;
-
- retval2 = 0;
- for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
- if (!cache->in_use)
- continue;
-
- if (invalidate)
- cache->in_use = 0;
-
- if (!cache->dirty)
- continue;
-
- retval = raw_write_blk(channel, data,
- cache->block, 1, cache->buf);
- if (retval)
- retval2 = retval;
- else
- cache->dirty = 0;
- }
- return retval2;
-}
-#endif /* NO_IO_CACHE */
-
-static errcode_t unix_open(const char *name, int flags, io_channel *channel)
-{
- io_channel io = NULL;
- struct unix_private_data *data = NULL;
- errcode_t retval;
- int open_flags;
- struct stat st;
-#ifdef __linux__
- struct utsname ut;
-#endif
-
- if (name == 0)
- return EXT2_ET_BAD_DEVICE_NAME;
- retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
- if (retval)
- return retval;
- memset(io, 0, sizeof(struct struct_io_channel));
- io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
- retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
- if (retval)
- goto cleanup;
-
- io->manager = unix_io_manager;
- retval = ext2fs_get_mem(strlen(name)+1, &io->name);
- if (retval)
- goto cleanup;
-
- strcpy(io->name, name);
- io->private_data = data;
- io->block_size = 1024;
- io->read_error = 0;
- io->write_error = 0;
- io->refcount = 1;
-
- memset(data, 0, sizeof(struct unix_private_data));
- data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
-
- if ((retval = alloc_cache(io, data)))
- goto cleanup;
-
- open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
-#ifdef CONFIG_LFS
- data->dev = open64(io->name, open_flags);
-#else
- data->dev = open(io->name, open_flags);
-#endif
- if (data->dev < 0) {
- retval = errno;
- goto cleanup;
- }
-
-#ifdef __linux__
-#undef RLIM_INFINITY
-#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
-#define RLIM_INFINITY ((unsigned long)(~0UL>>1))
-#else
-#define RLIM_INFINITY (~0UL)
-#endif
- /*
- * Work around a bug in 2.4.10-2.4.18 kernels where writes to
- * block devices are wrongly getting hit by the filesize
- * limit. This workaround isn't perfect, since it won't work
- * if glibc wasn't built against 2.2 header files. (Sigh.)
- *
- */
- if ((flags & IO_FLAG_RW) &&
- (uname(&ut) == 0) &&
- ((ut.release[0] == '2') && (ut.release[1] == '.') &&
- (ut.release[2] == '4') && (ut.release[3] == '.') &&
- (ut.release[4] == '1') && (ut.release[5] >= '0') &&
- (ut.release[5] < '8')) &&
- (fstat(data->dev, &st) == 0) &&
- (S_ISBLK(st.st_mode))) {
- struct rlimit rlim;
-
- rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
- setrlimit(RLIMIT_FSIZE, &rlim);
- getrlimit(RLIMIT_FSIZE, &rlim);
- if (((unsigned long) rlim.rlim_cur) <
- ((unsigned long) rlim.rlim_max)) {
- rlim.rlim_cur = rlim.rlim_max;
- setrlimit(RLIMIT_FSIZE, &rlim);
- }
- }
-#endif
- *channel = io;
- return 0;
-
-cleanup:
- if (data) {
- free_cache(data);
- ext2fs_free_mem(&data);
- }
- ext2fs_free_mem(&io);
- return retval;
-}
-
-static errcode_t unix_close(io_channel channel)
-{
- struct unix_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
- if (--channel->refcount > 0)
- return 0;
-
-#ifndef NO_IO_CACHE
- retval = flush_cached_blocks(channel, data, 0);
-#endif
-
- if (close(data->dev) < 0)
- retval = errno;
- free_cache(data);
-
- ext2fs_free_mem(&channel->private_data);
- ext2fs_free_mem(&channel->name);
- ext2fs_free_mem(&channel);
- return retval;
-}
-
-static errcode_t unix_set_blksize(io_channel channel, int blksize)
-{
- struct unix_private_data *data;
- errcode_t retval;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
- if (channel->block_size != blksize) {
-#ifndef NO_IO_CACHE
- if ((retval = flush_cached_blocks(channel, data, 0)))
- return retval;
-#endif
-
- channel->block_size = blksize;
- free_cache(data);
- if ((retval = alloc_cache(channel, data)))
- return retval;
- }
- return 0;
-}
-
-
-static errcode_t unix_read_blk(io_channel channel, unsigned long block,
- int count, void *buf)
-{
- struct unix_private_data *data;
- struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
- errcode_t retval;
- char *cp;
- int i, j;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
-#ifdef NO_IO_CACHE
- return raw_read_blk(channel, data, block, count, buf);
-#else
- /*
- * If we're doing an odd-sized read or a very large read,
- * flush out the cache and then do a direct read.
- */
- if (count < 0 || count > WRITE_DIRECT_SIZE) {
- if ((retval = flush_cached_blocks(channel, data, 0)))
- return retval;
- return raw_read_blk(channel, data, block, count, buf);
- }
-
- cp = buf;
- while (count > 0) {
- /* If it's in the cache, use it! */
- if ((cache = find_cached_block(data, block, &reuse[0]))) {
-#ifdef DEBUG
- printf("Using cached block %lu\n", block);
-#endif
- memcpy(cp, cache->buf, channel->block_size);
- count--;
- block++;
- cp += channel->block_size;
- continue;
- }
- /*
- * Find the number of uncached blocks so we can do a
- * single read request
- */
- for (i=1; i < count; i++)
- if (find_cached_block(data, block+i, &reuse[i]))
- break;
-#ifdef DEBUG
- printf("Reading %d blocks starting at %lu\n", i, block);
-#endif
- if ((retval = raw_read_blk(channel, data, block, i, cp)))
- return retval;
-
- /* Save the results in the cache */
- for (j=0; j < i; j++) {
- count--;
- cache = reuse[j];
- reuse_cache(channel, data, cache, block++);
- memcpy(cache->buf, cp, channel->block_size);
- cp += channel->block_size;
- }
- }
- return 0;
-#endif /* NO_IO_CACHE */
-}
-
-static errcode_t unix_write_blk(io_channel channel, unsigned long block,
- int count, const void *buf)
-{
- struct unix_private_data *data;
- struct unix_cache *cache, *reuse;
- errcode_t retval = 0;
- const char *cp;
- int writethrough;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
-#ifdef NO_IO_CACHE
- return raw_write_blk(channel, data, block, count, buf);
-#else
- /*
- * If we're doing an odd-sized write or a very large write,
- * flush out the cache completely and then do a direct write.
- */
- if (count < 0 || count > WRITE_DIRECT_SIZE) {
- if ((retval = flush_cached_blocks(channel, data, 1)))
- return retval;
- return raw_write_blk(channel, data, block, count, buf);
- }
-
- /*
- * For a moderate-sized multi-block write, first force a write
- * if we're in write-through cache mode, and then fill the
- * cache with the blocks.
- */
- writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
- if (writethrough)
- retval = raw_write_blk(channel, data, block, count, buf);
-
- cp = buf;
- while (count > 0) {
- cache = find_cached_block(data, block, &reuse);
- if (!cache) {
- cache = reuse;
- reuse_cache(channel, data, cache, block);
- }
- memcpy(cache->buf, cp, channel->block_size);
- cache->dirty = !writethrough;
- count--;
- block++;
- cp += channel->block_size;
- }
- return retval;
-#endif /* NO_IO_CACHE */
-}
-
-static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
- int size, const void *buf)
-{
- struct unix_private_data *data;
- errcode_t retval = 0;
- ssize_t actual;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
-#ifndef NO_IO_CACHE
- /*
- * Flush out the cache completely
- */
- if ((retval = flush_cached_blocks(channel, data, 1)))
- return retval;
-#endif
-
- if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
- return errno;
-
- actual = write(data->dev, buf, size);
- if (actual != size)
- return EXT2_ET_SHORT_WRITE;
-
- return 0;
-}
-
-/*
- * Flush data buffers to disk.
- */
-static errcode_t unix_flush(io_channel channel)
-{
- struct unix_private_data *data;
- errcode_t retval = 0;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
-#ifndef NO_IO_CACHE
- retval = flush_cached_blocks(channel, data, 0);
-#endif
- fsync(data->dev);
- return retval;
-}
-
-static errcode_t unix_set_option(io_channel channel, const char *option,
- const char *arg)
-{
- struct unix_private_data *data;
- unsigned long tmp;
- char *end;
-
- EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
- data = (struct unix_private_data *) channel->private_data;
- EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
-
- if (!strcmp(option, "offset")) {
- if (!arg)
- return EXT2_ET_INVALID_ARGUMENT;
-
- tmp = strtoul(arg, &end, 0);
- if (*end)
- return EXT2_ET_INVALID_ARGUMENT;
- data->offset = tmp;
- return 0;
- }
- return EXT2_ET_INVALID_ARGUMENT;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c b/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
deleted file mode 100644
index 71a9ffc..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
+++ b/dev/null
@@ -1,99 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * unlink.c --- delete links in a ext2fs directory
- *
- * Copyright (C) 1993, 1994, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <string.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-struct link_struct {
- const char *name;
- int namelen;
- ext2_ino_t inode;
- int flags;
- struct ext2_dir_entry *prev;
- int done;
-};
-
-#ifdef __TURBOC__
-# pragma argsused
-#endif
-static int unlink_proc(struct ext2_dir_entry *dirent,
- int offset EXT2FS_ATTR((unused)),
- int blocksize EXT2FS_ATTR((unused)),
- char *buf EXT2FS_ATTR((unused)),
- void *priv_data)
-{
- struct link_struct *ls = (struct link_struct *) priv_data;
- struct ext2_dir_entry *prev;
-
- prev = ls->prev;
- ls->prev = dirent;
-
- if (ls->name) {
- if ((dirent->name_len & 0xFF) != ls->namelen)
- return 0;
- if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF))
- return 0;
- }
- if (ls->inode) {
- if (dirent->inode != ls->inode)
- return 0;
- } else {
- if (!dirent->inode)
- return 0;
- }
-
- if (prev)
- prev->rec_len += dirent->rec_len;
- else
- dirent->inode = 0;
- ls->done++;
- return DIRENT_ABORT|DIRENT_CHANGED;
-}
-
-#ifdef __TURBOC__
- #pragma argsused
-#endif
-errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
- const char *name, ext2_ino_t ino,
- int flags EXT2FS_ATTR((unused)))
-{
- errcode_t retval;
- struct link_struct ls;
-
- EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
- if (!name && !ino)
- return EXT2_ET_INVALID_ARGUMENT;
-
- if (!(fs->flags & EXT2_FLAG_RW))
- return EXT2_ET_RO_FILSYS;
-
- ls.name = name;
- ls.namelen = name ? strlen(name) : 0;
- ls.inode = ino;
- ls.flags = 0;
- ls.done = 0;
- ls.prev = 0;
-
- retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
- 0, unlink_proc, &ls);
- if (retval)
- return retval;
-
- return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c b/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
deleted file mode 100644
index 8ed77ae..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
+++ b/dev/null
@@ -1,57 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * valid_blk.c --- does the inode have valid blocks?
- *
- * Copyright 1997 by Theodore Ts'o
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- *
- */
-
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <time.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-/*
- * This function returns 1 if the inode's block entries actually
- * contain block entries.
- */
-int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
-{
- /*
- * Only directories, regular files, and some symbolic links
- * have valid block entries.
- */
- if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
- !LINUX_S_ISLNK(inode->i_mode))
- return 0;
-
- /*
- * If the symbolic link is a "fast symlink", then the symlink
- * target is stored in the block entries.
- */
- if (LINUX_S_ISLNK (inode->i_mode)) {
- if (inode->i_file_acl == 0) {
- /* With no EA block, we can rely on i_blocks */
- if (inode->i_blocks == 0)
- return 0;
- } else {
- /* With an EA block, life gets more tricky */
- if (inode->i_size >= EXT2_N_BLOCKS*4)
- return 1; /* definitely using i_block[] */
- if (inode->i_size > 4 && inode->i_block[1] == 0)
- return 1; /* definitely using i_block[] */
- return 0; /* Probably a fast symlink */
- }
- }
- return 1;
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/version.c b/e2fsprogs/old_e2fsprogs/ext2fs/version.c
deleted file mode 100644
index d2981e8..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/version.c
+++ b/dev/null
@@ -1,51 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * version.c --- Return the version of the ext2 library
- *
- * Copyright (C) 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-static const char *lib_version = E2FSPROGS_VERSION;
-static const char *lib_date = E2FSPROGS_DATE;
-
-int ext2fs_parse_version_string(const char *ver_string)
-{
- const char *cp;
- int version = 0;
-
- for (cp = ver_string; *cp; cp++) {
- if (*cp == '.')
- continue;
- if (!isdigit(*cp))
- break;
- version = (version * 10) + (*cp - '0');
- }
- return version;
-}
-
-
-int ext2fs_get_library_version(const char **ver_string,
- const char **date_string)
-{
- if (ver_string)
- *ver_string = lib_version;
- if (date_string)
- *date_string = lib_date;
-
- return ext2fs_parse_version_string(lib_version);
-}
diff --git a/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c b/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
deleted file mode 100644
index 5b19eef..0000000
--- a/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
+++ b/dev/null
@@ -1,35 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * write_bb_file.c --- write a list of bad blocks to a FILE *
- *
- * Copyright (C) 1994, 1995 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-#include <stdio.h>
-
-#include "ext2_fs.h"
-#include "ext2fs.h"
-
-errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
- unsigned int flags EXT2FS_ATTR((unused)),
- FILE *f)
-{
- badblocks_iterate bb_iter;
- blk_t blk;
- errcode_t retval;
-
- retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
- if (retval)
- return retval;
-
- while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) {
- fprintf(f, "%d\n", blk);
- }
- ext2fs_badblocks_list_iterate_end(bb_iter);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/fsck.c b/e2fsprogs/old_e2fsprogs/fsck.c
deleted file mode 100644
index 91cce97..0000000
--- a/e2fsprogs/old_e2fsprogs/fsck.c
+++ b/dev/null
@@ -1,1375 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * pfsck --- A generic, parallelizing front-end for the fsck program.
- * It will automatically try to run fsck programs in parallel if the
- * devices are on separate spindles. It is based on the same ideas as
- * the generic front end for fsck by David Engel and Fred van Kempen,
- * but it has been completely rewritten from scratch to support
- * parallel execution.
- *
- * Written by Theodore Ts'o, <tytso@mit.edu>
- *
- * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
- * o Changed -t fstype to behave like with mount when -A (all file
- * systems) or -M (like mount) is specified.
- * o fsck looks if it can find the fsck.type program to decide
- * if it should ignore the fs type. This way more fsck programs
- * can be added without changing this front-end.
- * o -R flag skip root file system.
- *
- * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <paths.h>
-#include <unistd.h>
-#include <errno.h>
-#include <signal.h>
-
-#include "fsck.h"
-#include "blkid/blkid.h"
-
-#include "e2fsbb.h"
-
-#include "libbb.h"
-
-#ifndef _PATH_MNTTAB
-#define _PATH_MNTTAB "/etc/fstab"
-#endif
-
-/*
- * fsck.h
- */
-
-#ifndef DEFAULT_FSTYPE
-#define DEFAULT_FSTYPE "ext2"
-#endif
-
-#define MAX_DEVICES 32
-#define MAX_ARGS 32
-
-/*
- * Internal structure for mount tabel entries.
- */
-
-struct fs_info {
- char *device;
- char *mountpt;
- char *type;
- char *opts;
- int freq;
- int passno;
- int flags;
- struct fs_info *next;
-};
-
-#define FLAG_DONE 1
-#define FLAG_PROGRESS 2
-
-/*
- * Structure to allow exit codes to be stored
- */
-struct fsck_instance {
- int pid;
- int flags;
- int exit_status;
- time_t start_time;
- char * prog;
- char * type;
- char * device;
- char * base_device;
- struct fsck_instance *next;
-};
-
-/*
- * base_device.c
- *
- * Return the "base device" given a particular device; this is used to
- * assure that we only fsck one partition on a particular drive at any
- * one time. Otherwise, the disk heads will be seeking all over the
- * place. If the base device cannot be determined, return NULL.
- *
- * The base_device() function returns an allocated string which must
- * be freed.
- *
- */
-
-
-#ifdef CONFIG_FEATURE_DEVFS
-/*
- * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
- * pathames.
- */
-static const char *const devfs_hier[] = {
- "host", "bus", "target", "lun", 0
-};
-#endif
-
-static char *base_device(const char *device)
-{
- char *str, *cp;
-#ifdef CONFIG_FEATURE_DEVFS
- const char *const *hier;
- const char *disk;
- int len;
-#endif
-
- cp = str = xstrdup(device);
-
- /* Skip over /dev/; if it's not present, give up. */
- if (strncmp(cp, "/dev/", 5) != 0)
- goto errout;
- cp += 5;
-
- /*
- * For md devices, we treat them all as if they were all
- * on one disk, since we don't know how to parallelize them.
- */
- if (cp[0] == 'm' && cp[1] == 'd') {
- *(cp+2) = 0;
- return str;
- }
-
- /* Handle DAC 960 devices */
- if (strncmp(cp, "rd/", 3) == 0) {
- cp += 3;
- if (cp[0] != 'c' || cp[2] != 'd' ||
- !isdigit(cp[1]) || !isdigit(cp[3]))
- goto errout;
- *(cp+4) = 0;
- return str;
- }
-
- /* Now let's handle /dev/hd* and /dev/sd* devices.... */
- if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) {
- cp += 2;
- /* If there's a single number after /dev/hd, skip it */
- if (isdigit(*cp))
- cp++;
- /* What follows must be an alpha char, or give up */
- if (!isalpha(*cp))
- goto errout;
- *(cp + 1) = 0;
- return str;
- }
-
-#ifdef CONFIG_FEATURE_DEVFS
- /* Now let's handle devfs (ugh) names */
- len = 0;
- if (strncmp(cp, "ide/", 4) == 0)
- len = 4;
- if (strncmp(cp, "scsi/", 5) == 0)
- len = 5;
- if (len) {
- cp += len;
- /*
- * Now we proceed down the expected devfs hierarchy.
- * i.e., .../host1/bus2/target3/lun4/...
- * If we don't find the expected token, followed by
- * some number of digits at each level, abort.
- */
- for (hier = devfs_hier; *hier; hier++) {
- len = strlen(*hier);
- if (strncmp(cp, *hier, len) != 0)
- goto errout;
- cp += len;
- while (*cp != '/' && *cp != 0) {
- if (!isdigit(*cp))
- goto errout;
- cp++;
- }
- cp++;
- }
- *(cp - 1) = 0;
- return str;
- }
-
- /* Now handle devfs /dev/disc or /dev/disk names */
- disk = 0;
- if (strncmp(cp, "discs/", 6) == 0)
- disk = "disc";
- else if (strncmp(cp, "disks/", 6) == 0)
- disk = "disk";
- if (disk) {
- cp += 6;
- if (strncmp(cp, disk, 4) != 0)
- goto errout;
- cp += 4;
- while (*cp != '/' && *cp != 0) {
- if (!isdigit(*cp))
- goto errout;
- cp++;
- }
- *cp = 0;
- return str;
- }
-#endif
-
-errout:
- free(str);
- return NULL;
-}
-
-
-static const char *const ignored_types[] = {
- "ignore",
- "iso9660",
- "nfs",
- "proc",
- "sw",
- "swap",
- "tmpfs",
- "devpts",
- NULL
-};
-
-static const char *const really_wanted[] = {
- "minix",
- "ext2",
- "ext3",
- "jfs",
- "reiserfs",
- "xiafs",
- "xfs",
- NULL
-};
-
-#define BASE_MD "/dev/md"
-
-/*
- * Global variables for options
- */
-static char *devices[MAX_DEVICES];
-static char *args[MAX_ARGS];
-static int num_devices, num_args;
-
-static int verbose;
-static int doall;
-static int noexecute;
-static int serialize;
-static int skip_root;
-static int like_mount;
-static int notitle;
-static int parallel_root;
-static int progress;
-static int progress_fd;
-static int force_all_parallel;
-static int num_running;
-static int max_running;
-static volatile int cancel_requested;
-static int kill_sent;
-static char *fstype;
-static struct fs_info *filesys_info, *filesys_last;
-static struct fsck_instance *instance_list;
-static char *fsck_path;
-static blkid_cache cache;
-
-static char *string_copy(const char *s)
-{
- char *ret;
-
- if (!s)
- return 0;
- ret = xstrdup(s);
- return ret;
-}
-
-static int string_to_int(const char *s)
-{
- long l;
- char *p;
-
- l = strtol(s, &p, 0);
- if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
- return -1;
- else
- return (int) l;
-}
-
-static char *skip_over_blank(char *cp)
-{
- while (*cp && isspace(*cp))
- cp++;
- return cp;
-}
-
-static char *skip_over_word(char *cp)
-{
- while (*cp && !isspace(*cp))
- cp++;
- return cp;
-}
-
-static void strip_line(char *line)
-{
- char *p;
-
- while (*line) {
- p = line + strlen(line) - 1;
- if ((*p == '\n') || (*p == '\r'))
- *p = 0;
- else
- break;
- }
-}
-
-static char *parse_word(char **buf)
-{
- char *word, *next;
-
- word = *buf;
- if (*word == 0)
- return 0;
-
- word = skip_over_blank(word);
- next = skip_over_word(word);
- if (*next)
- *next++ = 0;
- *buf = next;
- return word;
-}
-
-static void parse_escape(char *word)
-{
- char *q, c;
- const char *p;
-
- if (!word)
- return;
-
- strcpy_and_process_escape_sequences(word, word);
-}
-
-static void free_instance(struct fsck_instance *i)
-{
- if (i->prog)
- free(i->prog);
- if (i->device)
- free(i->device);
- if (i->base_device)
- free(i->base_device);
- free(i);
-}
-
-static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
- const char *type, const char *opts,
- int freq, int passno)
-{
- struct fs_info *fs;
-
- fs = xmalloc(sizeof(struct fs_info));
-
- fs->device = string_copy(device);
- fs->mountpt = string_copy(mntpnt);
- fs->type = string_copy(type);
- fs->opts = string_copy(opts ? opts : "");
- fs->freq = freq;
- fs->passno = passno;
- fs->flags = 0;
- fs->next = NULL;
-
- if (!filesys_info)
- filesys_info = fs;
- else
- filesys_last->next = fs;
- filesys_last = fs;
-
- return fs;
-}
-
-
-
-static int parse_fstab_line(char *line, struct fs_info **ret_fs)
-{
- char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
- struct fs_info *fs;
-
- *ret_fs = 0;
- strip_line(line);
- if ((cp = strchr(line, '#')))
- *cp = 0; /* Ignore everything after the comment char */
- cp = line;
-
- device = parse_word(&cp);
- mntpnt = parse_word(&cp);
- type = parse_word(&cp);
- opts = parse_word(&cp);
- freq = parse_word(&cp);
- passno = parse_word(&cp);
-
- if (!device)
- return 0; /* Allow blank lines */
-
- if (!mntpnt || !type)
- return -1;
-
- parse_escape(device);
- parse_escape(mntpnt);
- parse_escape(type);
- parse_escape(opts);
- parse_escape(freq);
- parse_escape(passno);
-
- dev = blkid_get_devname(cache, device, NULL);
- if (dev)
- device = dev;
-
- if (strchr(type, ','))
- type = 0;
-
- fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
- freq ? atoi(freq) : -1,
- passno ? atoi(passno) : -1);
- if (dev)
- free(dev);
-
- if (!fs)
- return -1;
- *ret_fs = fs;
- return 0;
-}
-
-static void interpret_type(struct fs_info *fs)
-{
- char *t;
-
- if (strcmp(fs->type, "auto") != 0)
- return;
- t = blkid_get_tag_value(cache, "TYPE", fs->device);
- if (t) {
- free(fs->type);
- fs->type = t;
- }
-}
-
-/*
- * Load the filesystem database from /etc/fstab
- */
-static void load_fs_info(const char *filename)
-{
- FILE *f;
- char buf[1024];
- int lineno = 0;
- int old_fstab = 1;
- struct fs_info *fs;
-
- if ((f = fopen_or_warn(filename, "r")) == NULL) {
- return;
- }
- while (!feof(f)) {
- lineno++;
- if (!fgets(buf, sizeof(buf), f))
- break;
- buf[sizeof(buf)-1] = 0;
- if (parse_fstab_line(buf, &fs) < 0) {
- bb_error_msg("WARNING: bad format "
- "on line %d of %s\n", lineno, filename);
- continue;
- }
- if (!fs)
- continue;
- if (fs->passno < 0)
- fs->passno = 0;
- else
- old_fstab = 0;
- }
-
- fclose(f);
-
- if (old_fstab) {
- fputs("\007\007\007"
- "WARNING: Your /etc/fstab does not contain the fsck passno\n"
- " field. I will kludge around things for you, but you\n"
- " should fix your /etc/fstab file as soon as you can.\n\n", stderr);
-
- for (fs = filesys_info; fs; fs = fs->next) {
- fs->passno = 1;
- }
- }
-}
-
-/* Lookup filesys in /etc/fstab and return the corresponding entry. */
-static struct fs_info *lookup(char *filesys)
-{
- struct fs_info *fs;
-
- /* No filesys name given. */
- if (filesys == NULL)
- return NULL;
-
- for (fs = filesys_info; fs; fs = fs->next) {
- if (!strcmp(filesys, fs->device) ||
- (fs->mountpt && !strcmp(filesys, fs->mountpt)))
- break;
- }
-
- return fs;
-}
-
-/* Find fsck program for a given fs type. */
-static char *find_fsck(char *type)
-{
- char *s;
- const char *tpl;
- char *p = string_copy(fsck_path);
- struct stat st;
-
- /* Are we looking for a program or just a type? */
- tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
-
- for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
- s = xasprintf(tpl, s, type);
- if (stat(s, &st) == 0) break;
- free(s);
- }
- free(p);
- return s;
-}
-
-static int progress_active(void)
-{
- struct fsck_instance *inst;
-
- for (inst = instance_list; inst; inst = inst->next) {
- if (inst->flags & FLAG_DONE)
- continue;
- if (inst->flags & FLAG_PROGRESS)
- return 1;
- }
- return 0;
-}
-
-/*
- * Execute a particular fsck program, and link it into the list of
- * child processes we are waiting for.
- */
-static int execute(const char *type, const char *device, const char *mntpt,
- int interactive)
-{
- char *s, *argv[80];
- char *prog;
- int argc, i;
- struct fsck_instance *inst, *p;
- pid_t pid;
-
- inst = xzalloc(sizeof(struct fsck_instance));
-
- prog = xasprintf("fsck.%s", type);
- argv[0] = prog;
- argc = 1;
-
- for (i=0; i <num_args; i++)
- argv[argc++] = string_copy(args[i]);
-
- if (progress && !progress_active()) {
- if ((strcmp(type, "ext2") == 0) ||
- (strcmp(type, "ext3") == 0)) {
- char tmp[80];
- snprintf(tmp, 80, "-C%d", progress_fd);
- argv[argc++] = string_copy(tmp);
- inst->flags |= FLAG_PROGRESS;
- }
- }
-
- argv[argc++] = string_copy(device);
- argv[argc] = 0;
-
- s = find_fsck(prog);
- if (s == NULL) {
- bb_error_msg("%s: not found", prog);
- return ENOENT;
- }
-
- if (verbose || noexecute) {
- printf("[%s (%d) -- %s] ", s, num_running,
- mntpt ? mntpt : device);
- for (i=0; i < argc; i++)
- printf("%s ", argv[i]);
- bb_putchar('\n');
- }
-
- /* Fork and execute the correct program. */
- if (noexecute)
- pid = -1;
- else if ((pid = fork()) < 0) {
- perror("vfork"+1);
- return errno;
- } else if (pid == 0) {
- if (!interactive)
- close(0);
- (void) execv(s, argv);
- bb_simple_perror_msg_and_die(argv[0]);
- }
-
- for (i = 1; i < argc; i++)
- free(argv[i]);
-
- free(s);
- inst->pid = pid;
- inst->prog = prog;
- inst->type = string_copy(type);
- inst->device = string_copy(device);
- inst->base_device = base_device(device);
- inst->start_time = time(0);
- inst->next = NULL;
-
- /*
- * Find the end of the list, so we add the instance on at the end.
- */
- for (p = instance_list; p && p->next; p = p->next);
-
- if (p)
- p->next = inst;
- else
- instance_list = inst;
-
- return 0;
-}
-
-/*
- * Send a signal to all outstanding fsck child processes
- */
-static int kill_all(int signum)
-{
- struct fsck_instance *inst;
- int n = 0;
-
- for (inst = instance_list; inst; inst = inst->next) {
- if (inst->flags & FLAG_DONE)
- continue;
- kill(inst->pid, signum);
- n++;
- }
- return n;
-}
-
-/*
- * Wait for one child process to exit; when it does, unlink it from
- * the list of executing child processes, and return it.
- */
-static struct fsck_instance *wait_one(int flags)
-{
- int status;
- int sig;
- struct fsck_instance *inst, *inst2, *prev;
- pid_t pid;
-
- if (!instance_list)
- return NULL;
-
- if (noexecute) {
- inst = instance_list;
- prev = 0;
-#ifdef RANDOM_DEBUG
- while (inst->next && (random() & 1)) {
- prev = inst;
- inst = inst->next;
- }
-#endif
- inst->exit_status = 0;
- goto ret_inst;
- }
-
- /*
- * gcc -Wall fails saving throw against stupidity
- * (inst and prev are thought to be uninitialized variables)
- */
- inst = prev = NULL;
-
- do {
- pid = waitpid(-1, &status, flags);
- if (cancel_requested && !kill_sent) {
- kill_all(SIGTERM);
- kill_sent++;
- }
- if ((pid == 0) && (flags & WNOHANG))
- return NULL;
- if (pid < 0) {
- if ((errno == EINTR) || (errno == EAGAIN))
- continue;
- if (errno == ECHILD) {
- bb_error_msg("wait: no more child process?!?");
- return NULL;
- }
- perror("wait");
- continue;
- }
- for (prev = 0, inst = instance_list;
- inst;
- prev = inst, inst = inst->next) {
- if (inst->pid == pid)
- break;
- }
- } while (!inst);
-
- if (WIFEXITED(status))
- status = WEXITSTATUS(status);
- else if (WIFSIGNALED(status)) {
- sig = WTERMSIG(status);
- if (sig == SIGINT) {
- status = EXIT_UNCORRECTED;
- } else {
- printf("Warning... %s for device %s exited "
- "with signal %d.\n",
- inst->prog, inst->device, sig);
- status = EXIT_ERROR;
- }
- } else {
- printf("%s %s: status is %x, should never happen.\n",
- inst->prog, inst->device, status);
- status = EXIT_ERROR;
- }
- inst->exit_status = status;
- if (progress && (inst->flags & FLAG_PROGRESS) &&
- !progress_active()) {
- for (inst2 = instance_list; inst2; inst2 = inst2->next) {
- if (inst2->flags & FLAG_DONE)
- continue;
- if (strcmp(inst2->type, "ext2") &&
- strcmp(inst2->type, "ext3"))
- continue;
- /*
- * If we've just started the fsck, wait a tiny
- * bit before sending the kill, to give it
- * time to set up the signal handler
- */
- if (inst2->start_time < time(0)+2) {
- if (fork() == 0) {
- sleep(1);
- kill(inst2->pid, SIGUSR1);
- exit(0);
- }
- } else
- kill(inst2->pid, SIGUSR1);
- inst2->flags |= FLAG_PROGRESS;
- break;
- }
- }
-ret_inst:
- if (prev)
- prev->next = inst->next;
- else
- instance_list = inst->next;
- if (verbose > 1)
- printf("Finished with %s (exit status %d)\n",
- inst->device, inst->exit_status);
- num_running--;
- return inst;
-}
-
-#define FLAG_WAIT_ALL 0
-#define FLAG_WAIT_ATLEAST_ONE 1
-/*
- * Wait until all executing child processes have exited; return the
- * logical OR of all of their exit code values.
- */
-static int wait_many(int flags)
-{
- struct fsck_instance *inst;
- int global_status = 0;
- int wait_flags = 0;
-
- while ((inst = wait_one(wait_flags))) {
- global_status |= inst->exit_status;
- free_instance(inst);
-#ifdef RANDOM_DEBUG
- if (noexecute && (flags & WNOHANG) && !(random() % 3))
- break;
-#endif
- if (flags & FLAG_WAIT_ATLEAST_ONE)
- wait_flags = WNOHANG;
- }
- return global_status;
-}
-
-/*
- * Run the fsck program on a particular device
- *
- * If the type is specified using -t, and it isn't prefixed with "no"
- * (as in "noext2") and only one filesystem type is specified, then
- * use that type regardless of what is specified in /etc/fstab.
- *
- * If the type isn't specified by the user, then use either the type
- * specified in /etc/fstab, or DEFAULT_FSTYPE.
- */
-static void fsck_device(struct fs_info *fs, int interactive)
-{
- const char *type;
- int retval;
-
- interpret_type(fs);
-
- if (strcmp(fs->type, "auto") != 0)
- type = fs->type;
- else if (fstype && strncmp(fstype, "no", 2) &&
- strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
- !strchr(fstype, ','))
- type = fstype;
- else
- type = DEFAULT_FSTYPE;
-
- num_running++;
- retval = execute(type, fs->device, fs->mountpt, interactive);
- if (retval) {
- bb_error_msg("error %d while executing fsck.%s for %s",
- retval, type, fs->device);
- num_running--;
- }
-}
-
-
-/*
- * Deal with the fsck -t argument.
- */
-struct fs_type_compile {
- char **list;
- int *type;
- int negate;
-} fs_type_compiled;
-
-#define FS_TYPE_NORMAL 0
-#define FS_TYPE_OPT 1
-#define FS_TYPE_NEGOPT 2
-
-static const char fs_type_syntax_error[] =
-"Either all or none of the filesystem types passed to -t must be prefixed\n"
- "with 'no' or '!'.";
-
-static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
-{
- char *cp, *list, *s;
- int num = 2;
- int negate, first_negate = 1;
-
- if (fs_type) {
- for (cp=fs_type; *cp; cp++) {
- if (*cp == ',')
- num++;
- }
- }
-
- cmp->list = xzalloc(num * sizeof(char *));
- cmp->type = xzalloc(num * sizeof(int));
- cmp->negate = 0;
-
- if (!fs_type)
- return;
-
- list = string_copy(fs_type);
- num = 0;
- s = strtok(list, ",");
- while (s) {
- negate = 0;
- if (strncmp(s, "no", 2) == 0) {
- s += 2;
- negate = 1;
- } else if (*s == '!') {
- s++;
- negate = 1;
- }
- if (strcmp(s, "loop") == 0)
- /* loop is really short-hand for opts=loop */
- goto loop_special_case;
- else if (strncmp(s, "opts=", 5) == 0) {
- s += 5;
- loop_special_case:
- cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
- } else {
- if (first_negate) {
- cmp->negate = negate;
- first_negate = 0;
- }
- if ((negate && !cmp->negate) ||
- (!negate && cmp->negate)) {
- bb_error_msg_and_die("%s", fs_type_syntax_error);
- }
- }
- cmp->list[num++] = string_copy(s);
- s = strtok(NULL, ",");
- }
- free(list);
-}
-
-/*
- * This function returns true if a particular option appears in a
- * comma-delimited options list
- */
-static int opt_in_list(char *opt, char *optlist)
-{
- char *list, *s;
-
- if (!optlist)
- return 0;
- list = string_copy(optlist);
-
- s = strtok(list, ",");
- while (s) {
- if (strcmp(s, opt) == 0) {
- free(list);
- return 1;
- }
- s = strtok(NULL, ",");
- }
- free(list);
- return 0;
-}
-
-/* See if the filesystem matches the criteria given by the -t option */
-static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
-{
- int n, ret = 0, checked_type = 0;
- char *cp;
-
- if (cmp->list == 0 || cmp->list[0] == 0)
- return 1;
-
- for (n=0; (cp = cmp->list[n]); n++) {
- switch (cmp->type[n]) {
- case FS_TYPE_NORMAL:
- checked_type++;
- if (strcmp(cp, fs->type) == 0) {
- ret = 1;
- }
- break;
- case FS_TYPE_NEGOPT:
- if (opt_in_list(cp, fs->opts))
- return 0;
- break;
- case FS_TYPE_OPT:
- if (!opt_in_list(cp, fs->opts))
- return 0;
- break;
- }
- }
- if (checked_type == 0)
- return 1;
- return (cmp->negate ? !ret : ret);
-}
-
-/* Check if we should ignore this filesystem. */
-static int ignore(struct fs_info *fs)
-{
- int wanted;
- char *s;
-
- /*
- * If the pass number is 0, ignore it.
- */
- if (fs->passno == 0)
- return 1;
-
- interpret_type(fs);
-
- /*
- * If a specific fstype is specified, and it doesn't match,
- * ignore it.
- */
- if (!fs_match(fs, &fs_type_compiled)) return 1;
-
- /* Are we ignoring this type? */
- if (index_in_str_array(ignored_types, fs->type) >= 0)
- return 1;
-
- /* Do we really really want to check this fs? */
- wanted = index_in_str_array(really_wanted, fs->type) >= 0;
-
- /* See if the <fsck.fs> program is available. */
- s = find_fsck(fs->type);
- if (s == NULL) {
- if (wanted)
- bb_error_msg("can't check %s: fsck.%s not found",
- fs->device, fs->type);
- return 1;
- }
- free(s);
-
- /* We can and want to check this file system type. */
- return 0;
-}
-
-/*
- * Returns TRUE if a partition on the same disk is already being
- * checked.
- */
-static int device_already_active(char *device)
-{
- struct fsck_instance *inst;
- char *base;
-
- if (force_all_parallel)
- return 0;
-
-#ifdef BASE_MD
- /* Don't check a soft raid disk with any other disk */
- if (instance_list &&
- (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
- !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
- return 1;
-#endif
-
- base = base_device(device);
- /*
- * If we don't know the base device, assume that the device is
- * already active if there are any fsck instances running.
- */
- if (!base)
- return (instance_list != 0);
- for (inst = instance_list; inst; inst = inst->next) {
- if (!inst->base_device || !strcmp(base, inst->base_device)) {
- free(base);
- return 1;
- }
- }
- free(base);
- return 0;
-}
-
-/* Check all file systems, using the /etc/fstab table. */
-static int check_all(void)
-{
- struct fs_info *fs = NULL;
- int status = EXIT_OK;
- int not_done_yet = 1;
- int passno = 1;
- int pass_done;
-
- if (verbose)
- fputs("Checking all file systems.\n", stdout);
-
- /*
- * Do an initial scan over the filesystem; mark filesystems
- * which should be ignored as done, and resolve any "auto"
- * filesystem types (done as a side-effect of calling ignore()).
- */
- for (fs = filesys_info; fs; fs = fs->next) {
- if (ignore(fs))
- fs->flags |= FLAG_DONE;
- }
-
- /*
- * Find and check the root filesystem.
- */
- if (!parallel_root) {
- for (fs = filesys_info; fs; fs = fs->next) {
- if (LONE_CHAR(fs->mountpt, '/'))
- break;
- }
- if (fs) {
- if (!skip_root && !ignore(fs)) {
- fsck_device(fs, 1);
- status |= wait_many(FLAG_WAIT_ALL);
- if (status > EXIT_NONDESTRUCT)
- return status;
- }
- fs->flags |= FLAG_DONE;
- }
- }
- /*
- * This is for the bone-headed user who enters the root
- * filesystem twice. Skip root will skep all root entries.
- */
- if (skip_root)
- for (fs = filesys_info; fs; fs = fs->next)
- if (LONE_CHAR(fs->mountpt, '/'))
- fs->flags |= FLAG_DONE;
-
- while (not_done_yet) {
- not_done_yet = 0;
- pass_done = 1;
-
- for (fs = filesys_info; fs; fs = fs->next) {
- if (cancel_requested)
- break;
- if (fs->flags & FLAG_DONE)
- continue;
- /*
- * If the filesystem's pass number is higher
- * than the current pass number, then we don't
- * do it yet.
- */
- if (fs->passno > passno) {
- not_done_yet++;
- continue;
- }
- /*
- * If a filesystem on a particular device has
- * already been spawned, then we need to defer
- * this to another pass.
- */
- if (device_already_active(fs->device)) {
- pass_done = 0;
- continue;
- }
- /*
- * Spawn off the fsck process
- */
- fsck_device(fs, serialize);
- fs->flags |= FLAG_DONE;
-
- /*
- * Only do one filesystem at a time, or if we
- * have a limit on the number of fsck's extant
- * at one time, apply that limit.
- */
- if (serialize ||
- (max_running && (num_running >= max_running))) {
- pass_done = 0;
- break;
- }
- }
- if (cancel_requested)
- break;
- if (verbose > 1)
- printf("--waiting-- (pass %d)\n", passno);
- status |= wait_many(pass_done ? FLAG_WAIT_ALL :
- FLAG_WAIT_ATLEAST_ONE);
- if (pass_done) {
- if (verbose > 1)
- printf("----------------------------------\n");
- passno++;
- } else
- not_done_yet++;
- }
- if (cancel_requested && !kill_sent) {
- kill_all(SIGTERM);
- kill_sent++;
- }
- status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
- return status;
-}
-
-static void signal_cancel(int sig FSCK_ATTR((unused)))
-{
- cancel_requested++;
-}
-
-static void PRS(int argc, char **argv)
-{
- int i, j;
- char *arg, *dev, *tmp = NULL;
- char options[128];
- int opt = 0;
- int opts_for_fsck = 0;
- struct sigaction sa;
-
- /*
- * Set up signal action
- */
- memset(&sa, 0, sizeof(struct sigaction));
- sa.sa_handler = signal_cancel;
- sigaction(SIGINT, &sa, 0);
- sigaction(SIGTERM, &sa, 0);
-
- num_devices = 0;
- num_args = 0;
- instance_list = 0;
-
- for (i=1; i < argc; i++) {
- arg = argv[i];
- if (!arg)
- continue;
- if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
- if (num_devices >= MAX_DEVICES) {
- bb_error_msg_and_die("too many devices");
- }
- dev = blkid_get_devname(cache, arg, NULL);
- if (!dev && strchr(arg, '=')) {
- /*
- * Check to see if we failed because
- * /proc/partitions isn't found.
- */
- if (access("/proc/partitions", R_OK) < 0) {
- bb_perror_msg_and_die("can't open /proc/partitions "
- "(is /proc mounted?)");
- }
- /*
- * Check to see if this is because
- * we're not running as root
- */
- if (geteuid())
- bb_error_msg_and_die(
- "must be root to scan for matching filesystems: %s\n", arg);
- else
- bb_error_msg_and_die(
- "can't find matching filesystem: %s", arg);
- }
- devices[num_devices++] = dev ? dev : string_copy(arg);
- continue;
- }
- if (arg[0] != '-' || opts_for_fsck) {
- if (num_args >= MAX_ARGS) {
- bb_error_msg_and_die("too many arguments");
- }
- args[num_args++] = string_copy(arg);
- continue;
- }
- for (j=1; arg[j]; j++) {
- if (opts_for_fsck) {
- options[++opt] = arg[j];
- continue;
- }
- switch (arg[j]) {
- case 'A':
- doall++;
- break;
- case 'C':
- progress++;
- if (arg[j+1]) {
- progress_fd = string_to_int(arg+j+1);
- if (progress_fd < 0)
- progress_fd = 0;
- else
- goto next_arg;
- } else if ((i+1) < argc
- && argv[i+1][0] != '-') {
- progress_fd = string_to_int(argv[i]);
- if (progress_fd < 0)
- progress_fd = 0;
- else {
- goto next_arg;
- }
- }
- break;
- case 'V':
- verbose++;
- break;
- case 'N':
- noexecute++;
- break;
- case 'R':
- skip_root++;
- break;
- case 'T':
- notitle++;
- break;
- case 'M':
- like_mount++;
- break;
- case 'P':
- parallel_root++;
- break;
- case 's':
- serialize++;
- break;
- case 't':
- tmp = 0;
- if (fstype)
- bb_show_usage();
- if (arg[j+1])
- tmp = arg+j+1;
- else if ((i+1) < argc)
- tmp = argv[++i];
- else
- bb_show_usage();
- fstype = string_copy(tmp);
- compile_fs_type(fstype, &fs_type_compiled);
- goto next_arg;
- case '-':
- opts_for_fsck++;
- break;
- case '?':
- bb_show_usage();
- break;
- default:
- options[++opt] = arg[j];
- break;
- }
- }
- next_arg:
- if (opt) {
- options[0] = '-';
- options[++opt] = '\0';
- if (num_args >= MAX_ARGS) {
- bb_error_msg("too many arguments");
- }
- args[num_args++] = string_copy(options);
- opt = 0;
- }
- }
- if (getenv("FSCK_FORCE_ALL_PARALLEL"))
- force_all_parallel++;
- if ((tmp = getenv("FSCK_MAX_INST")))
- max_running = atoi(tmp);
-}
-
-int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int fsck_main(int argc, char **argv)
-{
- int i, status = 0;
- int interactive = 0;
- const char *fstab;
- struct fs_info *fs;
-
- setvbuf(stdout, NULL, _IONBF, BUFSIZ);
- setvbuf(stderr, NULL, _IONBF, BUFSIZ);
-
- blkid_get_cache(&cache, NULL);
- PRS(argc, argv);
-
- if (!notitle)
- printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
-
- fstab = getenv("FSTAB_FILE");
- if (!fstab)
- fstab = _PATH_MNTTAB;
- load_fs_info(fstab);
-
- fsck_path = e2fs_set_sbin_path();
-
- if ((num_devices == 1) || (serialize))
- interactive = 1;
-
- /* If -A was specified ("check all"), do that! */
- if (doall)
- return check_all();
-
- if (num_devices == 0) {
- serialize++;
- interactive++;
- return check_all();
- }
- for (i = 0; i < num_devices; i++) {
- if (cancel_requested) {
- if (!kill_sent) {
- kill_all(SIGTERM);
- kill_sent++;
- }
- break;
- }
- fs = lookup(devices[i]);
- if (!fs) {
- fs = create_fs_device(devices[i], 0, "auto",
- 0, -1, -1);
- if (!fs)
- continue;
- }
- fsck_device(fs, interactive);
- if (serialize ||
- (max_running && (num_running >= max_running))) {
- struct fsck_instance *inst;
-
- inst = wait_one(0);
- if (inst) {
- status |= inst->exit_status;
- free_instance(inst);
- }
- if (verbose > 1)
- printf("----------------------------------\n");
- }
- }
- status |= wait_many(FLAG_WAIT_ALL);
- blkid_put_cache(cache);
- return status;
-}
diff --git a/e2fsprogs/old_e2fsprogs/fsck.h b/e2fsprogs/old_e2fsprogs/fsck.h
deleted file mode 100644
index 2ca2af7..0000000
--- a/e2fsprogs/old_e2fsprogs/fsck.h
+++ b/dev/null
@@ -1,16 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * fsck.h
- */
-
-#define FSCK_ATTR(x) __attribute__(x)
-
-#define EXIT_OK 0
-#define EXIT_NONDESTRUCT 1
-#define EXIT_DESTRUCT 2
-#define EXIT_UNCORRECTED 4
-#define EXIT_ERROR 8
-#define EXIT_USAGE 16
-#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
-
-extern char *e2fs_set_sbin_path(void);
diff --git a/e2fsprogs/old_e2fsprogs/lsattr.c b/e2fsprogs/old_e2fsprogs/lsattr.c
deleted file mode 100644
index 9eab68b..0000000
--- a/e2fsprogs/old_e2fsprogs/lsattr.c
+++ b/dev/null
@@ -1,129 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * lsattr.c - List file attributes on an ext2 file system
- *
- * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * This file can be redistributed under the terms of the GNU General
- * Public License
- */
-
-/*
- * History:
- * 93/10/30 - Creation
- * 93/11/13 - Replace stat() calls by lstat() to avoid loops
- * 94/02/27 - Integrated in Ted's distribution
- * 98/12/29 - Display version info only when -V specified (G M Sipe)
- */
-
-#include <sys/types.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include "ext2fs/ext2_fs.h"
-#include "e2fsbb.h"
-#include "e2p/e2p.h"
-
-#define OPT_RECUR 1
-#define OPT_ALL 2
-#define OPT_DIRS_OPT 4
-#define OPT_PF_LONG 8
-#define OPT_GENERATION 16
-static int flags;
-
-static void list_attributes(const char *name)
-{
- unsigned long fsflags;
- unsigned long generation;
-
- if (fgetflags(name, &fsflags) == -1)
- goto read_err;
- if (flags & OPT_GENERATION) {
- if (fgetversion(name, &generation) == -1)
- goto read_err;
- printf("%5lu ", generation);
- }
-
- if (flags & OPT_PF_LONG) {
- printf("%-28s ", name);
- print_e2flags(stdout, fsflags, PFOPT_LONG);
- bb_putchar('\n');
- } else {
- print_e2flags(stdout, fsflags, 0);
- printf(" %s\n", name);
- }
-
- return;
-read_err:
- bb_perror_msg("reading %s", name);
-}
-
-static int lsattr_dir_proc(const char *, struct dirent *, void *);
-
-static void lsattr_args(const char *name)
-{
- struct stat st;
-
- if (lstat(name, &st) == -1) {
- bb_perror_msg("stating %s", name);
- } else {
- if (S_ISDIR(st.st_mode) && !(flags & OPT_DIRS_OPT))
- iterate_on_dir(name, lsattr_dir_proc, NULL);
- else
- list_attributes(name);
- }
-}
-
-static int lsattr_dir_proc(const char *dir_name, struct dirent *de,
- void *private)
-{
- struct stat st;
- char *path;
-
- path = concat_path_file(dir_name, de->d_name);
-
- if (lstat(path, &st) == -1)
- bb_simple_perror_msg(path);
- else {
- if (de->d_name[0] != '.' || (flags & OPT_ALL)) {
- list_attributes(path);
- if (S_ISDIR(st.st_mode) && (flags & OPT_RECUR) &&
- (de->d_name[0] != '.' && (de->d_name[1] != '\0' ||
- (de->d_name[1] != '.' && de->d_name[2] != '\0')))) {
- printf("\n%s:\n", path);
- iterate_on_dir(path, lsattr_dir_proc, NULL);
- bb_putchar('\n');
- }
- }
- }
-
- free(path);
-
- return 0;
-}
-
-int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int lsattr_main(int argc, char **argv)
-{
- int i;
-
- flags = getopt32(argv, "Radlv");
-
- if (optind > argc - 1)
- lsattr_args(".");
- else
- for (i = optind; i < argc; i++)
- lsattr_args(argv[i]);
-
- return EXIT_SUCCESS;
-}
diff --git a/e2fsprogs/old_e2fsprogs/mke2fs.c b/e2fsprogs/old_e2fsprogs/mke2fs.c
deleted file mode 100644
index ebcb46c..0000000
--- a/e2fsprogs/old_e2fsprogs/mke2fs.c
+++ b/dev/null
@@ -1,1333 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * mke2fs.c - Make a ext2fs filesystem.
- *
- * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- * 2003, 2004, 2005 by Theodore Ts'o.
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-
-/* Usage: mke2fs [options] device
- *
- * The device may be a block device or a image of one, but this isn't
- * enforced (but it's not much fun on a character device :-).
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <time.h>
-#include <getopt.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <mntent.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-
-#include "e2fsbb.h"
-#include "ext2fs/ext2_fs.h"
-#include "uuid/uuid.h"
-#include "e2p/e2p.h"
-#include "ext2fs/ext2fs.h"
-#include "util.h"
-
-#define STRIDE_LENGTH 8
-
-#ifndef __sparc__
-#define ZAP_BOOTBLOCK
-#endif
-
-static const char * device_name;
-
-/* Command line options */
-static int cflag;
-static int quiet;
-static int super_only;
-static int force;
-static int noaction;
-static int journal_size;
-static int journal_flags;
-static const char *bad_blocks_filename;
-static __u32 fs_stride;
-
-static struct ext2_super_block param;
-static char *creator_os;
-static char *volume_label;
-static char *mount_dir;
-static char *journal_device = NULL;
-static int sync_kludge; /* Set using the MKE2FS_SYNC env. option */
-
-static int sys_page_size = 4096;
-static int linux_version_code = 0;
-
-static int int_log2(int arg)
-{
- int l = 0;
-
- arg >>= 1;
- while (arg) {
- l++;
- arg >>= 1;
- }
- return l;
-}
-
-static int int_log10(unsigned int arg)
-{
- int l;
-
- for (l = 0; arg; l++)
- arg = arg / 10;
- return l;
-}
-
-/*
- * This function sets the default parameters for a filesystem
- *
- * The type is specified by the user. The size is the maximum size
- * (in megabytes) for which a set of parameters applies, with a size
- * of zero meaning that it is the default parameter for the type.
- * Note that order is important in the table below.
- */
-#define DEF_MAX_BLOCKSIZE -1
-static const char default_str[] = "default";
-struct mke2fs_defaults {
- const char *type;
- int size;
- int blocksize;
- int inode_ratio;
-};
-
-static const struct mke2fs_defaults settings[] = {
- { default_str, 0, 4096, 8192 },
- { default_str, 512, 1024, 4096 },
- { default_str, 3, 1024, 8192 },
- { "journal", 0, 4096, 8192 },
- { "news", 0, 4096, 4096 },
- { "largefile", 0, 4096, 1024 * 1024 },
- { "largefile4", 0, 4096, 4096 * 1024 },
- { 0, 0, 0, 0},
-};
-
-static void set_fs_defaults(const char *fs_type,
- struct ext2_super_block *super,
- int blocksize, int sector_size,
- int *inode_ratio)
-{
- int megs;
- int ratio = 0;
- const struct mke2fs_defaults *p;
- int use_bsize = 1024;
-
- megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024;
- if (inode_ratio)
- ratio = *inode_ratio;
- if (!fs_type)
- fs_type = default_str;
- for (p = settings; p->type; p++) {
- if ((strcmp(p->type, fs_type) != 0) &&
- (strcmp(p->type, default_str) != 0))
- continue;
- if ((p->size != 0) && (megs > p->size))
- continue;
- if (ratio == 0)
- *inode_ratio = p->inode_ratio < blocksize ?
- blocksize : p->inode_ratio;
- use_bsize = p->blocksize;
- }
- if (blocksize <= 0) {
- if (use_bsize == DEF_MAX_BLOCKSIZE) {
- use_bsize = sys_page_size;
- if ((linux_version_code < (2*65536 + 6*256)) &&
- (use_bsize > 4096))
- use_bsize = 4096;
- }
- if (sector_size && use_bsize < sector_size)
- use_bsize = sector_size;
- if ((blocksize < 0) && (use_bsize < (-blocksize)))
- use_bsize = -blocksize;
- blocksize = use_bsize;
- super->s_blocks_count /= blocksize / 1024;
- }
- super->s_log_frag_size = super->s_log_block_size =
- int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
-}
-
-
-/*
- * Helper function for read_bb_file and test_disk
- */
-static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
-{
- bb_error_msg("Bad block %u out of range; ignored", blk);
-}
-
-/*
- * Busybox stuff
- */
-static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
-static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...)
-{
- va_list ap;
-
- if (retval) {
- va_start(ap, fmt);
- fprintf(stderr, "\nCould not ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- exit(EXIT_FAILURE);
- }
-}
-
-static void mke2fs_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
-static void mke2fs_verbose(const char *fmt, ...)
-{
- va_list ap;
-
- if (!quiet) {
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- fflush(stdout);
- va_end(ap);
- }
-}
-
-static void mke2fs_verbose_done(void)
-{
- mke2fs_verbose("done\n");
-}
-
-static void mke2fs_warning_msg(int retval, char *fmt, ... ) __attribute__ ((format (printf, 2, 3)));
-static void mke2fs_warning_msg(int retval, char *fmt, ... )
-{
- va_list ap;
-
- if (retval) {
- va_start(ap, fmt);
- fprintf(stderr, "\nWarning: ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap);
- }
-}
-
-/*
- * Reads the bad blocks list from a file
- */
-static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list,
- const char *bad_blocks_file)
-{
- FILE *f;
- errcode_t retval;
-
- f = xfopen_for_read(bad_blocks_file);
- retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
- fclose (f);
- mke2fs_error_msg_and_die(retval, "read bad blocks from list");
-}
-
-/*
- * Runs the badblocks program to test the disk
- */
-static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
-{
- FILE *f;
- errcode_t retval;
- char buf[1024];
-
- sprintf(buf, "badblocks -b %u %s%s%s %d", fs->blocksize,
- quiet ? "" : "-s ", (cflag > 1) ? "-w " : "",
- fs->device_name, fs->super->s_blocks_count);
- mke2fs_verbose("Running command: %s\n", buf);
- f = popen(buf, "r");
- if (!f) {
- bb_perror_msg_and_die("can't run '%s'", buf);
- }
- retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
- pclose(f);
- mke2fs_error_msg_and_die(retval, "read bad blocks from program");
-}
-
-static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list)
-{
- dgrp_t i;
- blk_t j;
- unsigned must_be_good;
- blk_t blk;
- badblocks_iterate bb_iter;
- errcode_t retval;
- blk_t group_block;
- int group;
- int group_bad;
-
- if (!bb_list)
- return;
-
- /*
- * The primary superblock and group descriptors *must* be
- * good; if not, abort.
- */
- must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks;
- for (i = fs->super->s_first_data_block; i <= must_be_good; i++) {
- if (ext2fs_badblocks_list_test(bb_list, i)) {
- bb_error_msg_and_die(
- "Block %d in primary superblock/group descriptor area bad\n"
- "Blocks %d through %d must be good in order to build a filesystem\n"
- "Aborting ...", i, fs->super->s_first_data_block, must_be_good);
- }
- }
-
- /*
- * See if any of the bad blocks are showing up in the backup
- * superblocks and/or group descriptors. If so, issue a
- * warning and adjust the block counts appropriately.
- */
- group_block = fs->super->s_first_data_block +
- fs->super->s_blocks_per_group;
-
- for (i = 1; i < fs->group_desc_count; i++) {
- group_bad = 0;
- for (j=0; j < fs->desc_blocks+1; j++) {
- if (ext2fs_badblocks_list_test(bb_list,
- group_block + j)) {
- mke2fs_warning_msg(!group_bad,
- "the backup superblock/group descriptors at block %d contain\n"
- "bad blocks\n", group_block);
- group_bad++;
- group = ext2fs_group_of_blk(fs, group_block+j);
- fs->group_desc[group].bg_free_blocks_count++;
- fs->super->s_free_blocks_count++;
- }
- }
- group_block += fs->super->s_blocks_per_group;
- }
-
- /*
- * Mark all the bad blocks as used...
- */
- retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
- mke2fs_error_msg_and_die(retval, "mark bad blocks as used");
-
- while (ext2fs_badblocks_list_iterate(bb_iter, &blk))
- ext2fs_mark_block_bitmap(fs->block_map, blk);
- ext2fs_badblocks_list_iterate_end(bb_iter);
-}
-
-/*
- * These functions implement a generalized progress meter.
- */
-struct progress_struct {
- char format[20];
- char backup[80];
- __u32 max;
- int skip_progress;
-};
-
-static void progress_init(struct progress_struct *progress,
- const char *label,__u32 max)
-{
- int i;
-
- memset(progress, 0, sizeof(struct progress_struct));
- if (quiet)
- return;
-
- /*
- * Figure out how many digits we need
- */
- i = int_log10(max);
- sprintf(progress->format, "%%%dd/%%%dld", i, i);
- memset(progress->backup, '\b', sizeof(progress->backup)-1);
- progress->backup[sizeof(progress->backup)-1] = 0;
- if ((2*i)+1 < (int) sizeof(progress->backup))
- progress->backup[(2*i)+1] = 0;
- progress->max = max;
-
- progress->skip_progress = 0;
- if (getenv("MKE2FS_SKIP_PROGRESS"))
- progress->skip_progress++;
-
- fputs(label, stdout);
- fflush(stdout);
-}
-
-static void progress_update(struct progress_struct *progress, __u32 val)
-{
- if ((progress->format[0] == 0) || progress->skip_progress)
- return;
- printf(progress->format, val, progress->max);
- fputs(progress->backup, stdout);
-}
-
-static void progress_close(struct progress_struct *progress)
-{
- if (progress->format[0] == 0)
- return;
- printf("%-28s\n", "done");
-}
-
-
-/*
- * Helper function which zeros out _num_ blocks starting at _blk_. In
- * case of an error, the details of the error is returned via _ret_blk_
- * and _ret_count_ if they are non-NULL pointers. Returns 0 on
- * success, and an error code on an error.
- *
- * As a special case, if the first argument is NULL, then it will
- * attempt to free the static zeroizing buffer. (This is to keep
- * programs that check for memory leaks happy.)
- */
-static errcode_t zero_blocks(ext2_filsys fs, blk_t blk, int num,
- struct progress_struct *progress,
- blk_t *ret_blk, int *ret_count)
-{
- int j, count, next_update;
- static char *buf;
- errcode_t retval;
-
- /* If fs is null, clean up the static buffer and return */
- if (!fs) {
- if (buf) {
- free(buf);
- buf = 0;
- }
- return 0;
- }
- /* Allocate the zeroizing buffer if necessary */
- if (!buf) {
- buf = xzalloc(fs->blocksize * STRIDE_LENGTH);
- }
- /* OK, do the write loop */
- next_update = 0;
-
- for (j=0; j < num; j += STRIDE_LENGTH, blk += STRIDE_LENGTH) {
- count = num - j;
- if (count > STRIDE_LENGTH)
- count = STRIDE_LENGTH;
- retval = io_channel_write_blk(fs->io, blk, count, buf);
- if (retval) {
- if (ret_count)
- *ret_count = count;
- if (ret_blk)
- *ret_blk = blk;
- return retval;
- }
- if (progress && j > next_update) {
- next_update += num / 100;
- progress_update(progress, blk);
- }
- }
- return 0;
-}
-
-static void write_inode_tables(ext2_filsys fs)
-{
- errcode_t retval;
- blk_t blk;
- dgrp_t i;
- int num;
- struct progress_struct progress;
-
- if (quiet)
- memset(&progress, 0, sizeof(progress));
- else
- progress_init(&progress, "Writing inode tables: ",
- fs->group_desc_count);
-
- for (i = 0; i < fs->group_desc_count; i++) {
- progress_update(&progress, i);
-
- blk = fs->group_desc[i].bg_inode_table;
- num = fs->inode_blocks_per_group;
-
- retval = zero_blocks(fs, blk, num, 0, &blk, &num);
- mke2fs_error_msg_and_die(retval,
- "write %d blocks in inode table starting at %d.",
- num, blk);
- if (sync_kludge) {
- if (sync_kludge == 1)
- sync();
- else if ((i % sync_kludge) == 0)
- sync();
- }
- }
- zero_blocks(0, 0, 0, 0, 0, 0);
- progress_close(&progress);
-}
-
-static void create_root_dir(ext2_filsys fs)
-{
- errcode_t retval;
- struct ext2_inode inode;
-
- retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0);
- mke2fs_error_msg_and_die(retval, "create root dir");
- if (geteuid()) {
- retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode);
- mke2fs_error_msg_and_die(retval, "read root inode");
- inode.i_uid = getuid();
- if (inode.i_uid)
- inode.i_gid = getgid();
- retval = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
- mke2fs_error_msg_and_die(retval, "set root inode ownership");
- }
-}
-
-static void create_lost_and_found(ext2_filsys fs)
-{
- errcode_t retval;
- ext2_ino_t ino;
- const char *name = "lost+found";
- int i = 1;
- char *msg = "create";
- int lpf_size = 0;
-
- fs->umask = 077;
- retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name);
- if (retval) {
- goto CREATE_LOST_AND_FOUND_ERROR;
- }
-
- retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino);
- if (retval) {
- msg = "lookup";
- goto CREATE_LOST_AND_FOUND_ERROR;
- }
-
- for (; i < EXT2_NDIR_BLOCKS; i++) {
- if ((lpf_size += fs->blocksize) >= 16*1024)
- break;
- retval = ext2fs_expand_dir(fs, ino);
- msg = "expand";
-CREATE_LOST_AND_FOUND_ERROR:
- mke2fs_error_msg_and_die(retval, "%s %s", msg, name);
- }
-}
-
-static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list)
-{
- errcode_t retval;
-
- ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO);
- fs->group_desc[0].bg_free_inodes_count--;
- fs->super->s_free_inodes_count--;
- retval = ext2fs_update_bb_inode(fs, bb_list);
- mke2fs_error_msg_and_die(retval, "set bad block inode");
-}
-
-static void reserve_inodes(ext2_filsys fs)
-{
- ext2_ino_t i;
- int group;
-
- for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) {
- ext2fs_mark_inode_bitmap(fs->inode_map, i);
- group = ext2fs_group_of_ino(fs, i);
- fs->group_desc[group].bg_free_inodes_count--;
- fs->super->s_free_inodes_count--;
- }
- ext2fs_mark_ib_dirty(fs);
-}
-
-#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */
-#define BSD_MAGICDISK (0x57455682UL) /* The disk magic number reversed */
-#define BSD_LABEL_OFFSET 64
-
-static void zap_sector(ext2_filsys fs, int sect, int nsect)
-{
- char *buf;
- char *fmt = "could not %s %d";
- int retval;
- unsigned int *magic;
-
- buf = xmalloc(512*nsect);
-
- if (sect == 0) {
- /* Check for a BSD disklabel, and don't erase it if so */
- retval = io_channel_read_blk(fs->io, 0, -512, buf);
- if (retval)
- mke2fs_warning_msg(retval, fmt, "read block", 0);
- else {
- magic = (unsigned int *) (buf + BSD_LABEL_OFFSET);
- if ((*magic == BSD_DISKMAGIC) ||
- (*magic == BSD_MAGICDISK))
- return;
- }
- }
-
- memset(buf, 0, 512*nsect);
- io_channel_set_blksize(fs->io, 512);
- retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf);
- io_channel_set_blksize(fs->io, fs->blocksize);
- free(buf);
- mke2fs_warning_msg(retval, fmt, "erase sector", sect);
-}
-
-static void create_journal_dev(ext2_filsys fs)
-{
- struct progress_struct progress;
- errcode_t retval;
- char *buf;
- char *fmt = "%s journal superblock";
- blk_t blk;
- int count;
-
- retval = ext2fs_create_journal_superblock(fs,
- fs->super->s_blocks_count, 0, &buf);
- mke2fs_error_msg_and_die(retval, fmt, "init");
- if (quiet)
- memset(&progress, 0, sizeof(progress));
- else
- progress_init(&progress, "Zeroing journal device: ",
- fs->super->s_blocks_count);
-
- retval = zero_blocks(fs, 0, fs->super->s_blocks_count,
- &progress, &blk, &count);
- mke2fs_error_msg_and_die(retval, "zero journal device (block %u, count %d)",
- blk, count);
- zero_blocks(0, 0, 0, 0, 0, 0);
-
- retval = io_channel_write_blk(fs->io,
- fs->super->s_first_data_block+1,
- 1, buf);
- mke2fs_error_msg_and_die(retval, fmt, "write");
- progress_close(&progress);
-}
-
-static void show_stats(ext2_filsys fs)
-{
- struct ext2_super_block *s = fs->super;
- char *os;
- blk_t group_block;
- dgrp_t i;
- int need, col_left;
-
- mke2fs_warning_msg((param.s_blocks_count != s->s_blocks_count),
- "%d blocks unused\n", param.s_blocks_count - s->s_blocks_count);
- os = e2p_os2string(fs->super->s_creator_os);
- printf( "Filesystem label=%.*s\n"
- "OS type: %s\n"
- "Block size=%u (log=%u)\n"
- "Fragment size=%u (log=%u)\n"
- "%u inodes, %u blocks\n"
- "%u blocks (%2.2f%%) reserved for the super user\n"
- "First data block=%u\n",
- (int) sizeof(s->s_volume_name),
- s->s_volume_name,
- os,
- fs->blocksize, s->s_log_block_size,
- fs->fragsize, s->s_log_frag_size,
- s->s_inodes_count, s->s_blocks_count,
- s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count,
- s->s_first_data_block);
- free(os);
- if (s->s_reserved_gdt_blocks) {
- printf("Maximum filesystem blocks=%lu\n",
- (s->s_reserved_gdt_blocks + fs->desc_blocks) *
- (fs->blocksize / sizeof(struct ext2_group_desc)) *
- s->s_blocks_per_group);
- }
- printf( "%u block group%s\n"
- "%u blocks per group, %u fragments per group\n"
- "%u inodes per group\n",
- fs->group_desc_count, (fs->group_desc_count > 1) ? "s" : "",
- s->s_blocks_per_group, s->s_frags_per_group,
- s->s_inodes_per_group);
- if (fs->group_desc_count == 1) {
- bb_putchar('\n');
- return;
- }
-
- printf("Superblock backups stored on blocks: ");
- group_block = s->s_first_data_block;
- col_left = 0;
- for (i = 1; i < fs->group_desc_count; i++) {
- group_block += s->s_blocks_per_group;
- if (!ext2fs_bg_has_super(fs, i))
- continue;
- if (i != 1)
- printf(", ");
- need = int_log10(group_block) + 2;
- if (need > col_left) {
- printf("\n\t");
- col_left = 72;
- }
- col_left -= need;
- printf("%u", group_block);
- }
- puts("\n");
-}
-
-/*
- * Set the S_CREATOR_OS field. Return true if OS is known,
- * otherwise, 0.
- */
-static int set_os(struct ext2_super_block *sb, char *os)
-{
- if (isdigit (*os)) {
- sb->s_creator_os = atoi(os);
- return 1;
- }
-
- if ((sb->s_creator_os = e2p_string2os(os)) >= 0) {
- return 1;
- } else if (!strcasecmp("GNU", os)) {
- sb->s_creator_os = EXT2_OS_HURD;
- return 1;
- }
- return 0;
-}
-
-static void parse_extended_opts(struct ext2_super_block *sb_param,
- const char *opts)
-{
- char *buf, *token, *next, *p, *arg;
- int r_usage = 0;
-
- buf = xstrdup(opts);
- for (token = buf; token && *token; token = next) {
- p = strchr(token, ',');
- next = 0;
- if (p) {
- *p = 0;
- next = p+1;
- }
- arg = strchr(token, '=');
- if (arg) {
- *arg = 0;
- arg++;
- }
- if (strcmp(token, "stride") == 0) {
- if (!arg) {
- r_usage++;
- continue;
- }
- fs_stride = strtoul(arg, &p, 0);
- if (*p || (fs_stride == 0)) {
- bb_error_msg("Invalid stride parameter: %s", arg);
- r_usage++;
- continue;
- }
- } else if (!strcmp(token, "resize")) {
- unsigned long resize, bpg, rsv_groups;
- unsigned long group_desc_count, desc_blocks;
- unsigned int gdpb, blocksize;
- int rsv_gdb;
-
- if (!arg) {
- r_usage++;
- continue;
- }
-
- resize = parse_num_blocks(arg,
- sb_param->s_log_block_size);
-
- if (resize == 0) {
- bb_error_msg("Invalid resize parameter: %s", arg);
- r_usage++;
- continue;
- }
- if (resize <= sb_param->s_blocks_count) {
- bb_error_msg("The resize maximum must be greater "
- "than the filesystem size");
- r_usage++;
- continue;
- }
-
- blocksize = EXT2_BLOCK_SIZE(sb_param);
- bpg = sb_param->s_blocks_per_group;
- if (!bpg)
- bpg = blocksize * 8;
- gdpb = blocksize / sizeof(struct ext2_group_desc);
- group_desc_count = (sb_param->s_blocks_count +
- bpg - 1) / bpg;
- desc_blocks = (group_desc_count +
- gdpb - 1) / gdpb;
- rsv_groups = (resize + bpg - 1) / bpg;
- rsv_gdb = (rsv_groups + gdpb - 1) / gdpb -
- desc_blocks;
- if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb_param))
- rsv_gdb = EXT2_ADDR_PER_BLOCK(sb_param);
-
- if (rsv_gdb > 0) {
- sb_param->s_feature_compat |=
- EXT2_FEATURE_COMPAT_RESIZE_INO;
-
- sb_param->s_reserved_gdt_blocks = rsv_gdb;
- }
- } else
- r_usage++;
- }
- if (r_usage) {
- bb_error_msg_and_die(
- "\nBad options specified.\n\n"
- "Extended options are separated by commas, "
- "and may take an argument which\n"
- "\tis set off by an equals ('=') sign.\n\n"
- "Valid extended options are:\n"
- "\tstride=<stride length in blocks>\n"
- "\tresize=<resize maximum size in blocks>\n");
- }
-}
-
-static __u32 ok_features[3] = {
- EXT3_FEATURE_COMPAT_HAS_JOURNAL |
- EXT2_FEATURE_COMPAT_RESIZE_INO |
- EXT2_FEATURE_COMPAT_DIR_INDEX, /* Compat */
- EXT2_FEATURE_INCOMPAT_FILETYPE| /* Incompat */
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
- EXT2_FEATURE_INCOMPAT_META_BG,
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER /* R/O compat */
-};
-
-static int PRS(int argc, char **argv)
-{
- int c;
- int size;
- char * tmp;
- int blocksize = 0;
- int inode_ratio = 0;
- int inode_size = 0;
- int reserved_ratio = 5;
- int sector_size = 0;
- int show_version_only = 0;
- ext2_ino_t num_inodes = 0;
- errcode_t retval;
- char * extended_opts = NULL;
- const char * fs_type = NULL;
- blk_t dev_size;
- long sysval;
-
- /* Update our PATH to include /sbin */
- e2fs_set_sbin_path();
-
- tmp = getenv("MKE2FS_SYNC");
- if (tmp)
- sync_kludge = atoi(tmp);
-
- /* Determine the system page size if possible */
-#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
-#define _SC_PAGESIZE _SC_PAGE_SIZE
-#endif
-#ifdef _SC_PAGESIZE
- sysval = sysconf(_SC_PAGESIZE);
- if (sysval > 0)
- sys_page_size = sysval;
-#endif /* _SC_PAGESIZE */
-
- setbuf(stdout, NULL);
- setbuf(stderr, NULL);
- memset(&param, 0, sizeof(struct ext2_super_block));
- param.s_rev_level = 1; /* Create revision 1 filesystems now */
- param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE;
- param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
-
-#ifdef __linux__
- linux_version_code = get_linux_version_code();
- if (linux_version_code && linux_version_code < KERNEL_VERSION(2,2,0)) {
- param.s_rev_level = 0;
- param.s_feature_incompat = 0;
- param.s_feature_compat = 0;
- param.s_feature_ro_compat = 0;
- }
-#endif
-
- /* If called as mkfs.ext3, create a journal inode */
- if (last_char_is(applet_name, '3'))
- journal_size = -1;
-
- while ((c = getopt (argc, argv,
- "b:cE:f:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF) {
- switch (c) {
- case 'b':
- blocksize = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
- mke2fs_warning_msg((blocksize > 4096),
- "blocksize %d not usable on most systems",
- blocksize);
- param.s_log_block_size =
- int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
- break;
- case 'c': /* Check for bad blocks */
- case 't': /* deprecated */
- cflag++;
- break;
- case 'f':
- size = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
- param.s_log_frag_size =
- int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE);
- mke2fs_warning_msg(1, "fragments not supported. Ignoring -f option");
- break;
- case 'g':
- param.s_blocks_per_group = xatou32(optarg);
- if ((param.s_blocks_per_group % 8) != 0) {
- bb_error_msg_and_die("blocks per group must be multiple of 8");
- }
- break;
- case 'i':
- /* Huh? is "* 1024" correct? */
- inode_ratio = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE * 1024);
- break;
- case 'J':
- parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
- break;
- case 'j':
- param.s_feature_compat |=
- EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- if (!journal_size)
- journal_size = -1;
- break;
- case 'l':
- bad_blocks_filename = optarg;
- break;
- case 'm':
- reserved_ratio = xatou_range(optarg, 0, 50);
- break;
- case 'n':
- noaction++;
- break;
- case 'o':
- creator_os = optarg;
- break;
- case 'r':
- param.s_rev_level = xatoi_positive(optarg);
- if (param.s_rev_level == EXT2_GOOD_OLD_REV) {
- param.s_feature_incompat = 0;
- param.s_feature_compat = 0;
- param.s_feature_ro_compat = 0;
- }
- break;
- case 's': /* deprecated */
- if (xatou(optarg))
- param.s_feature_ro_compat |=
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- else
- param.s_feature_ro_compat &=
- ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- break;
-#ifdef EXT2_DYNAMIC_REV
- case 'I':
- inode_size = xatoi_positive(optarg);
- break;
-#endif
- case 'N':
- num_inodes = xatoi_positive(optarg);
- break;
- case 'v':
- quiet = 0;
- break;
- case 'q':
- quiet = 1;
- break;
- case 'F':
- force = 1;
- break;
- case 'L':
- volume_label = optarg;
- break;
- case 'M':
- mount_dir = optarg;
- break;
- case 'O':
- if (!strcmp(optarg, "none")) {
- param.s_feature_compat = 0;
- param.s_feature_incompat = 0;
- param.s_feature_ro_compat = 0;
- break;
- }
- if (e2p_edit_feature(optarg,
- &param.s_feature_compat,
- ok_features)) {
- bb_error_msg_and_die("Invalid filesystem option set: %s", optarg);
- }
- break;
- case 'E':
- case 'R':
- extended_opts = optarg;
- break;
- case 'S':
- super_only = 1;
- break;
- case 'T':
- fs_type = optarg;
- break;
- case 'V':
- /* Print version number and exit */
- show_version_only = 1;
- quiet = 0;
- break;
- default:
- bb_show_usage();
- }
- }
- if ((optind == argc) /*&& !show_version_only*/)
- bb_show_usage();
- device_name = argv[optind++];
-
- mke2fs_verbose("mke2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
-
- if (show_version_only) {
- return 0;
- }
-
- /*
- * If there's no blocksize specified and there is a journal
- * device, use it to figure out the blocksize
- */
- if (blocksize <= 0 && journal_device) {
- ext2_filsys jfs;
- io_manager io_ptr;
-
-#ifdef CONFIG_TESTIO_DEBUG
- io_ptr = test_io_manager;
- test_io_backing_manager = unix_io_manager;
-#else
- io_ptr = unix_io_manager;
-#endif
- retval = ext2fs_open(journal_device,
- EXT2_FLAG_JOURNAL_DEV_OK, 0,
- 0, io_ptr, &jfs);
- mke2fs_error_msg_and_die(retval, "open journal device %s", journal_device);
- if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) {
- bb_error_msg_and_die(
- "Journal dev blocksize (%d) smaller than "
- "minimum blocksize %d\n", jfs->blocksize,
- -blocksize);
- }
- blocksize = jfs->blocksize;
- param.s_log_block_size =
- int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
- ext2fs_close(jfs);
- }
-
- if (blocksize > sys_page_size) {
- mke2fs_warning_msg(1, "%d-byte blocks too big for system (max %d)",
- blocksize, sys_page_size);
- if (!force) {
- proceed_question();
- }
- bb_error_msg("Forced to continue");
- }
- mke2fs_warning_msg(((blocksize > 4096) &&
- (param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)),
- "some 2.4 kernels do not support "
- "blocksizes greater than 4096 using ext3.\n"
- "Use -b 4096 if this is an issue for you\n");
-
- if (optind < argc) {
- param.s_blocks_count = parse_num_blocks(argv[optind++],
- param.s_log_block_size);
- mke2fs_error_msg_and_die(!param.s_blocks_count, "invalid blocks count - %s", argv[optind - 1]);
- }
- if (optind < argc)
- bb_show_usage();
-
- if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
- if (!fs_type)
- fs_type = "journal";
- reserved_ratio = 0;
- param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV;
- param.s_feature_compat = 0;
- param.s_feature_ro_compat = 0;
- }
- if (param.s_rev_level == EXT2_GOOD_OLD_REV &&
- (param.s_feature_compat || param.s_feature_ro_compat ||
- param.s_feature_incompat))
- param.s_rev_level = 1; /* Create a revision 1 filesystem */
-
- check_plausibility(device_name , force);
- check_mount(device_name, force, "filesystem");
-
- param.s_log_frag_size = param.s_log_block_size;
-
- if (noaction && param.s_blocks_count) {
- dev_size = param.s_blocks_count;
- retval = 0;
- } else {
- retry:
- retval = ext2fs_get_device_size(device_name,
- EXT2_BLOCK_SIZE(&param),
- &dev_size);
- if ((retval == EFBIG) &&
- (blocksize == 0) &&
- (param.s_log_block_size == 0)) {
- param.s_log_block_size = 2;
- blocksize = 4096;
- goto retry;
- }
- }
-
- mke2fs_error_msg_and_die((retval && (retval != EXT2_ET_UNIMPLEMENTED)),"determine filesystem size");
-
- if (!param.s_blocks_count) {
- if (retval == EXT2_ET_UNIMPLEMENTED) {
- mke2fs_error_msg_and_die(1,
- "determine device size; you "
- "must specify\nthe size of the "
- "filesystem");
- } else {
- if (dev_size == 0) {
- bb_error_msg_and_die(
- "Device size reported to be zero. "
- "Invalid partition specified, or\n\t"
- "partition table wasn't reread "
- "after running fdisk, due to\n\t"
- "a modified partition being busy "
- "and in use. You may need to reboot\n\t"
- "to re-read your partition table.\n"
- );
- }
- param.s_blocks_count = dev_size;
- if (sys_page_size > EXT2_BLOCK_SIZE(&param))
- param.s_blocks_count &= ~((sys_page_size /
- EXT2_BLOCK_SIZE(&param))-1);
- }
-
- } else if (!force && (param.s_blocks_count > dev_size)) {
- bb_error_msg("Filesystem larger than apparent device size");
- proceed_question();
- }
-
- /*
- * If the user asked for HAS_JOURNAL, then make sure a journal
- * gets created.
- */
- if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
- !journal_size)
- journal_size = -1;
-
- /* Set first meta blockgroup via an environment variable */
- /* (this is mostly for debugging purposes) */
- if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
- ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
- param.s_first_meta_bg = atoi(tmp);
-
- /* Get the hardware sector size, if available */
- retval = ext2fs_get_device_sectsize(device_name, &sector_size);
- mke2fs_error_msg_and_die(retval, "determine hardware sector size");
-
- if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL)
- sector_size = atoi(tmp);
-
- set_fs_defaults(fs_type, &param, blocksize, sector_size, &inode_ratio);
- blocksize = EXT2_BLOCK_SIZE(&param);
-
- if (extended_opts)
- parse_extended_opts(&param, extended_opts);
-
- /* Since sparse_super is the default, we would only have a problem
- * here if it was explicitly disabled.
- */
- if ((param.s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) &&
- !(param.s_feature_ro_compat&EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
- bb_error_msg_and_die("reserved online resize blocks not supported "
- "on non-sparse filesystem");
- }
-
- if (param.s_blocks_per_group) {
- if (param.s_blocks_per_group < 256 ||
- param.s_blocks_per_group > 8 * (unsigned) blocksize) {
- bb_error_msg_and_die("blocks per group count out of range");
- }
- }
-
- if (!force && param.s_blocks_count >= (1 << 31)) {
- bb_error_msg_and_die("Filesystem too large. No more than 2**31-1 blocks\n"
- "\t (8TB using a blocksize of 4k) are currently supported.");
- }
-
- if (inode_size) {
- if (inode_size < EXT2_GOOD_OLD_INODE_SIZE ||
- inode_size > EXT2_BLOCK_SIZE(&param) ||
- inode_size & (inode_size - 1)) {
- bb_error_msg_and_die("invalid inode size %d (min %d/max %d)",
- inode_size, EXT2_GOOD_OLD_INODE_SIZE,
- blocksize);
- }
- mke2fs_warning_msg((inode_size != EXT2_GOOD_OLD_INODE_SIZE),
- "%d-byte inodes not usable on most systems",
- inode_size);
- param.s_inode_size = inode_size;
- }
-
- /*
- * Calculate number of inodes based on the inode ratio
- */
- param.s_inodes_count = num_inodes ? num_inodes :
- ((__u64) param.s_blocks_count * blocksize)
- / inode_ratio;
-
- /*
- * Calculate number of blocks to reserve
- */
- param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100;
- return 1;
-}
-
-static void mke2fs_clean_up(void)
-{
- if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
-}
-
-int mke2fs_main (int argc, char **argv);
-int mke2fs_main (int argc, char **argv)
-{
- errcode_t retval;
- ext2_filsys fs;
- badblocks_list bb_list = 0;
- unsigned int i;
- int val;
- io_manager io_ptr;
-
- if (ENABLE_FEATURE_CLEAN_UP)
- atexit(mke2fs_clean_up);
- if (!PRS(argc, argv))
- return 0;
-
-#ifdef CONFIG_TESTIO_DEBUG
- io_ptr = test_io_manager;
- test_io_backing_manager = unix_io_manager;
-#else
- io_ptr = unix_io_manager;
-#endif
-
- /*
- * Initialize the superblock....
- */
- retval = ext2fs_initialize(device_name, 0, &param,
- io_ptr, &fs);
- mke2fs_error_msg_and_die(retval, "set up superblock");
-
- /*
- * Wipe out the old on-disk superblock
- */
- if (!noaction)
- zap_sector(fs, 2, 6);
-
- /*
- * Generate a UUID for it...
- */
- uuid_generate(fs->super->s_uuid);
-
- /*
- * Initialize the directory index variables
- */
- fs->super->s_def_hash_version = EXT2_HASH_TEA;
- uuid_generate((unsigned char *) fs->super->s_hash_seed);
-
- /*
- * Add "jitter" to the superblock's check interval so that we
- * don't check all the filesystems at the same time. We use a
- * kludgy hack of using the UUID to derive a random jitter value.
- */
- for (i = 0, val = 0; i < sizeof(fs->super->s_uuid); i++)
- val += fs->super->s_uuid[i];
- fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT;
-
- /*
- * Override the creator OS, if applicable
- */
- if (creator_os && !set_os(fs->super, creator_os)) {
- bb_error_msg_and_die("unknown os - %s", creator_os);
- }
-
- /*
- * For the Hurd, we will turn off filetype since it doesn't
- * support it.
- */
- if (fs->super->s_creator_os == EXT2_OS_HURD)
- fs->super->s_feature_incompat &=
- ~EXT2_FEATURE_INCOMPAT_FILETYPE;
-
- /*
- * Set the volume label...
- */
- if (volume_label) {
- snprintf(fs->super->s_volume_name, sizeof(fs->super->s_volume_name), "%s", volume_label);
- }
-
- /*
- * Set the last mount directory
- */
- if (mount_dir) {
- snprintf(fs->super->s_last_mounted, sizeof(fs->super->s_last_mounted), "%s", mount_dir);
- }
-
- if (!quiet || noaction)
- show_stats(fs);
-
- if (noaction)
- return 0;
-
- if (fs->super->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
- create_journal_dev(fs);
- return (ext2fs_close(fs) ? 1 : 0);
- }
-
- if (bad_blocks_filename)
- read_bb_file(fs, &bb_list, bad_blocks_filename);
- if (cflag)
- test_disk(fs, &bb_list);
-
- handle_bad_blocks(fs, bb_list);
- fs->stride = fs_stride;
- retval = ext2fs_allocate_tables(fs);
- mke2fs_error_msg_and_die(retval, "allocate filesystem tables");
- if (super_only) {
- fs->super->s_state |= EXT2_ERROR_FS;
- fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
- } else {
- /* rsv must be a power of two (64kB is MD RAID sb alignment) */
- unsigned int rsv = 65536 / fs->blocksize;
- unsigned long blocks = fs->super->s_blocks_count;
- unsigned long start;
- blk_t ret_blk;
-
-#ifdef ZAP_BOOTBLOCK
- zap_sector(fs, 0, 2);
-#endif
-
- /*
- * Wipe out any old MD RAID (or other) metadata at the end
- * of the device. This will also verify that the device is
- * as large as we think. Be careful with very small devices.
- */
- start = (blocks & ~(rsv - 1));
- if (start > rsv)
- start -= rsv;
- if (start > 0)
- retval = zero_blocks(fs, start, blocks - start,
- NULL, &ret_blk, NULL);
-
- mke2fs_warning_msg(retval, "can't zero block %u at end of filesystem", ret_blk);
- write_inode_tables(fs);
- create_root_dir(fs);
- create_lost_and_found(fs);
- reserve_inodes(fs);
- create_bad_block_inode(fs, bb_list);
- if (fs->super->s_feature_compat &
- EXT2_FEATURE_COMPAT_RESIZE_INO) {
- retval = ext2fs_create_resize_inode(fs);
- mke2fs_error_msg_and_die(retval, "reserve blocks for online resize");
- }
- }
-
- if (journal_device) {
- make_journal_device(journal_device, fs, quiet, force);
- } else if (journal_size) {
- make_journal_blocks(fs, journal_size, journal_flags, quiet);
- }
-
- mke2fs_verbose("Writing superblocks and filesystem accounting information: ");
- retval = ext2fs_flush(fs);
- mke2fs_warning_msg(retval, "had trouble writing out superblocks");
- mke2fs_verbose_done();
- if (!quiet && !getenv("MKE2FS_SKIP_CHECK_MSG"))
- print_check_message(fs);
- val = ext2fs_close(fs);
- return (retval || val) ? 1 : 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/tune2fs.c b/e2fsprogs/old_e2fsprogs/tune2fs.c
deleted file mode 100644
index bbe30e5..0000000
--- a/e2fsprogs/old_e2fsprogs/tune2fs.c
+++ b/dev/null
@@ -1,710 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * tune2fs.c - Change the file system parameters on an ext2 file system
- *
- * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
- * Laboratoire MASI, Institut Blaise Pascal
- * Universite Pierre et Marie Curie (Paris VI)
- *
- * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-
-/*
- * History:
- * 93/06/01 - Creation
- * 93/10/31 - Added the -c option to change the maximal mount counts
- * 93/12/14 - Added -l flag to list contents of superblock
- * M.J.E. Mol (marcel@duteca.et.tudelft.nl)
- * F.W. ten Wolde (franky@duteca.et.tudelft.nl)
- * 93/12/29 - Added the -e option to change errors behavior
- * 94/02/27 - Ported to use the ext2fs library
- * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
- */
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <getopt.h>
-
-#include "e2fsbb.h"
-#include "ext2fs/ext2_fs.h"
-#include "ext2fs/ext2fs.h"
-#include "uuid/uuid.h"
-#include "e2p/e2p.h"
-#include "ext2fs/kernel-jbd.h"
-#include "util.h"
-#include "blkid/blkid.h"
-
-#include "libbb.h"
-
-static char * device_name = NULL;
-static char * new_label, *new_last_mounted, *new_UUID;
-static char * io_options;
-static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
-static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
-static time_t last_check_time;
-static int print_label;
-static int max_mount_count, mount_count, mount_flags;
-static unsigned long interval, reserved_blocks;
-static unsigned reserved_ratio;
-static unsigned long resgid, resuid;
-static unsigned short errors;
-static int open_flag;
-static char *features_cmd;
-static char *mntopts_cmd;
-
-static int journal_size, journal_flags;
-static char *journal_device = NULL;
-
-static const char *please_fsck = "Please run e2fsck on the filesystem\n";
-
-static __u32 ok_features[3] = {
- EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX,
- EXT2_FEATURE_INCOMPAT_FILETYPE,
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
-};
-
-/*
- * Remove an external journal from the filesystem
- */
-static void remove_journal_device(ext2_filsys fs)
-{
- char *journal_path;
- ext2_filsys jfs;
- char buf[1024];
- journal_superblock_t *jsb;
- int i, nr_users;
- errcode_t retval;
- int commit_remove_journal = 0;
- io_manager io_ptr;
-
- if (f_flag)
- commit_remove_journal = 1; /* force removal even if error */
-
- uuid_unparse(fs->super->s_journal_uuid, buf);
- journal_path = blkid_get_devname(NULL, "UUID", buf);
-
- if (!journal_path) {
- journal_path =
- ext2fs_find_block_device(fs->super->s_journal_dev);
- if (!journal_path)
- return;
- }
-
- io_ptr = unix_io_manager;
- retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
- EXT2_FLAG_JOURNAL_DEV_OK, 0,
- fs->blocksize, io_ptr, &jfs);
- if (retval) {
- bb_error_msg("Failed to open external journal");
- goto no_valid_journal;
- }
- if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
- bb_error_msg("%s is not a journal device", journal_path);
- goto no_valid_journal;
- }
-
- /* Get the journal superblock */
- if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
- bb_error_msg("Failed to read journal superblock");
- goto no_valid_journal;
- }
-
- jsb = (journal_superblock_t *) buf;
- if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
- (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
- bb_error_msg("Journal superblock not found!");
- goto no_valid_journal;
- }
-
- /* Find the filesystem UUID */
- nr_users = ntohl(jsb->s_nr_users);
- for (i=0; i < nr_users; i++) {
- if (memcmp(fs->super->s_uuid,
- &jsb->s_users[i*16], 16) == 0)
- break;
- }
- if (i >= nr_users) {
- bb_error_msg("Filesystem's UUID not found on journal device");
- commit_remove_journal = 1;
- goto no_valid_journal;
- }
- nr_users--;
- for (i=0; i < nr_users; i++)
- memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
- jsb->s_nr_users = htonl(nr_users);
-
- /* Write back the journal superblock */
- if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
- bb_error_msg("Failed to write journal superblock");
- goto no_valid_journal;
- }
-
- commit_remove_journal = 1;
-
-no_valid_journal:
- if (commit_remove_journal == 0)
- bb_error_msg_and_die("Journal NOT removed");
- fs->super->s_journal_dev = 0;
- uuid_clear(fs->super->s_journal_uuid);
- ext2fs_mark_super_dirty(fs);
- puts("Journal removed");
- free(journal_path);
-}
-
-/* Helper function for remove_journal_inode */
-static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
- int blockcnt EXT2FS_ATTR((unused)),
- void *private EXT2FS_ATTR((unused)))
-{
- blk_t block;
- int group;
-
- block = *blocknr;
- ext2fs_unmark_block_bitmap(fs->block_map,block);
- group = ext2fs_group_of_blk(fs, block);
- fs->group_desc[group].bg_free_blocks_count++;
- fs->super->s_free_blocks_count++;
- return 0;
-}
-
-/*
- * Remove the journal inode from the filesystem
- */
-static void remove_journal_inode(ext2_filsys fs)
-{
- struct ext2_inode inode;
- errcode_t retval;
- ino_t ino = fs->super->s_journal_inum;
- char *msg = "to read";
- char *s = "journal inode";
-
- retval = ext2fs_read_inode(fs, ino, &inode);
- if (retval)
- goto REMOVE_JOURNAL_INODE_ERROR;
- if (ino == EXT2_JOURNAL_INO) {
- retval = ext2fs_read_bitmaps(fs);
- if (retval) {
- msg = "to read bitmaps";
- s = "";
- goto REMOVE_JOURNAL_INODE_ERROR;
- }
- retval = ext2fs_block_iterate(fs, ino, 0, NULL,
- release_blocks_proc, NULL);
- if (retval) {
- msg = "clearing";
- goto REMOVE_JOURNAL_INODE_ERROR;
- }
- memset(&inode, 0, sizeof(inode));
- ext2fs_mark_bb_dirty(fs);
- fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
- } else
- inode.i_flags &= ~EXT2_IMMUTABLE_FL;
- retval = ext2fs_write_inode(fs, ino, &inode);
- if (retval) {
- msg = "writing";
-REMOVE_JOURNAL_INODE_ERROR:
- bb_error_msg_and_die("Failed %s %s", msg, s);
- }
- fs->super->s_journal_inum = 0;
- ext2fs_mark_super_dirty(fs);
-}
-
-/*
- * Update the default mount options
- */
-static void update_mntopts(ext2_filsys fs, char *mntopts)
-{
- struct ext2_super_block *sb= fs->super;
-
- if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0))
- bb_error_msg_and_die("Invalid mount option set: %s", mntopts);
- ext2fs_mark_super_dirty(fs);
-}
-
-/*
- * Update the feature set as provided by the user.
- */
-static void update_feature_set(ext2_filsys fs, char *features)
-{
- int sparse, old_sparse, filetype, old_filetype;
- int journal, old_journal, dxdir, old_dxdir;
- struct ext2_super_block *sb= fs->super;
- __u32 old_compat, old_incompat, old_ro_compat;
-
- old_compat = sb->s_feature_compat;
- old_ro_compat = sb->s_feature_ro_compat;
- old_incompat = sb->s_feature_incompat;
-
- old_sparse = sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- old_filetype = sb->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE;
- old_journal = sb->s_feature_compat &
- EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- old_dxdir = sb->s_feature_compat &
- EXT2_FEATURE_COMPAT_DIR_INDEX;
- if (e2p_edit_feature(features, &sb->s_feature_compat, ok_features))
- bb_error_msg_and_die("Invalid filesystem option set: %s", features);
- sparse = sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- filetype = sb->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE;
- journal = sb->s_feature_compat &
- EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- dxdir = sb->s_feature_compat &
- EXT2_FEATURE_COMPAT_DIR_INDEX;
- if (old_journal && !journal) {
- if ((mount_flags & EXT2_MF_MOUNTED) &&
- !(mount_flags & EXT2_MF_READONLY)) {
- bb_error_msg_and_die(
- "The has_journal flag may only be "
- "cleared when the filesystem is\n"
- "unmounted or mounted "
- "read-only");
- }
- if (sb->s_feature_incompat &
- EXT3_FEATURE_INCOMPAT_RECOVER) {
- bb_error_msg_and_die(
- "The needs_recovery flag is set. "
- "%s before clearing the has_journal flag.",
- please_fsck);
- }
- if (sb->s_journal_inum) {
- remove_journal_inode(fs);
- }
- if (sb->s_journal_dev) {
- remove_journal_device(fs);
- }
- }
- if (journal && !old_journal) {
- /*
- * If adding a journal flag, let the create journal
- * code below handle creating setting the flag and
- * creating the journal. We supply a default size if
- * necessary.
- */
- if (!journal_size)
- journal_size = -1;
- sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- }
- if (dxdir && !old_dxdir) {
- if (!sb->s_def_hash_version)
- sb->s_def_hash_version = EXT2_HASH_TEA;
- if (uuid_is_null((unsigned char *) sb->s_hash_seed))
- uuid_generate((unsigned char *) sb->s_hash_seed);
- }
-
- if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
- (sb->s_feature_compat || sb->s_feature_ro_compat ||
- sb->s_feature_incompat))
- ext2fs_update_dynamic_rev(fs);
- if ((sparse != old_sparse) ||
- (filetype != old_filetype)) {
- sb->s_state &= ~EXT2_VALID_FS;
- printf("\n%s\n", please_fsck);
- }
- if ((old_compat != sb->s_feature_compat) ||
- (old_ro_compat != sb->s_feature_ro_compat) ||
- (old_incompat != sb->s_feature_incompat))
- ext2fs_mark_super_dirty(fs);
-}
-
-/*
- * Add a journal to the filesystem.
- */
-static void add_journal(ext2_filsys fs)
-{
- if (fs->super->s_feature_compat &
- EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
- bb_error_msg_and_die("The filesystem already has a journal");
- }
- if (journal_device) {
- make_journal_device(journal_device, fs, 0, 0);
- } else if (journal_size) {
- make_journal_blocks(fs, journal_size, journal_flags, 0);
- /*
- * If the filesystem wasn't mounted, we need to force
- * the block group descriptors out.
- */
- if ((mount_flags & EXT2_MF_MOUNTED) == 0)
- fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
- }
- print_check_message(fs);
-}
-
-/*
- * Busybox stuff
- */
-static char * x_blkid_get_devname(const char *token)
-{
- char * dev_name;
-
- if (!(dev_name = blkid_get_devname(NULL, token, NULL)))
- bb_error_msg_and_die("Unable to resolve '%s'", token);
- return dev_name;
-}
-
-#ifdef CONFIG_E2LABEL
-static void parse_e2label_options(int argc, char ** argv)
-{
- if ((argc < 2) || (argc > 3))
- bb_show_usage();
- io_options = strchr(argv[1], '?');
- if (io_options)
- *io_options++ = 0;
- device_name = x_blkid_get_devname(argv[1]);
- if (argc == 3) {
- open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK;
- L_flag = 1;
- new_label = argv[2];
- } else
- print_label++;
-}
-#else
-#define parse_e2label_options(x,y)
-#endif
-
-static time_t parse_time(char *str)
-{
- struct tm ts;
-
- if (strcmp(str, "now") == 0) {
- return time(0);
- }
- memset(&ts, 0, sizeof(ts));
-#ifdef HAVE_STRPTIME
- strptime(str, "%Y%m%d%H%M%S", &ts);
-#else
- sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
- &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
- ts.tm_year -= 1900;
- ts.tm_mon -= 1;
- if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
- ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
- ts.tm_min > 59 || ts.tm_sec > 61)
- ts.tm_mday = 0;
-#endif
- if (ts.tm_mday == 0) {
- bb_error_msg_and_die("can't parse date/time specifier: %s", str);
- }
- return mktime(&ts);
-}
-
-static void parse_tune2fs_options(int argc, char **argv)
-{
- int c;
- char * tmp;
-
- printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
- while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF)
- switch (c)
- {
- case 'c':
- max_mount_count = xatou_range(optarg, 0, 16000);
- if (max_mount_count == 0)
- max_mount_count = -1;
- c_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'C':
- mount_count = xatou_range(optarg, 0, 16000);
- C_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'e':
- if (strcmp (optarg, "continue") == 0)
- errors = EXT2_ERRORS_CONTINUE;
- else if (strcmp (optarg, "remount-ro") == 0)
- errors = EXT2_ERRORS_RO;
- else if (strcmp (optarg, "panic") == 0)
- errors = EXT2_ERRORS_PANIC;
- else {
- bb_error_msg_and_die("bad error behavior - %s", optarg);
- }
- e_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'f': /* Force */
- f_flag = 1;
- break;
- case 'g':
- resgid = bb_strtoul(optarg, NULL, 10);
- if (errno)
- resgid = xgroup2gid(optarg);
- g_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'i':
- interval = strtoul(optarg, &tmp, 0);
- switch (*tmp) {
- case 's':
- tmp++;
- break;
- case '\0':
- case 'd':
- case 'D': /* days */
- interval *= 86400;
- if (*tmp != '\0')
- tmp++;
- break;
- case 'm':
- case 'M': /* months! */
- interval *= 86400 * 30;
- tmp++;
- break;
- case 'w':
- case 'W': /* weeks */
- interval *= 86400 * 7;
- tmp++;
- break;
- }
- if (*tmp || interval > (365 * 86400)) {
- bb_error_msg_and_die("bad interval - %s", optarg);
- }
- i_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'j':
- if (!journal_size)
- journal_size = -1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'J':
- parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
- open_flag = EXT2_FLAG_RW;
- break;
- case 'l':
- l_flag = 1;
- break;
- case 'L':
- new_label = optarg;
- L_flag = 1;
- open_flag = EXT2_FLAG_RW |
- EXT2_FLAG_JOURNAL_DEV_OK;
- break;
- case 'm':
- reserved_ratio = xatou_range(optarg, 0, 50);
- m_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'M':
- new_last_mounted = optarg;
- M_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'o':
- if (mntopts_cmd) {
- bb_error_msg_and_die("-o may only be specified once");
- }
- mntopts_cmd = optarg;
- open_flag = EXT2_FLAG_RW;
- break;
-
- case 'O':
- if (features_cmd) {
- bb_error_msg_and_die("-O may only be specified once");
- }
- features_cmd = optarg;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'r':
- reserved_blocks = xatoul(optarg);
- r_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 's':
- s_flag = atoi(optarg);
- open_flag = EXT2_FLAG_RW;
- break;
- case 'T':
- T_flag = 1;
- last_check_time = parse_time(optarg);
- open_flag = EXT2_FLAG_RW;
- break;
- case 'u':
- resuid = bb_strtoul(optarg, NULL, 10);
- if (errno)
- resuid = xuname2uid(optarg);
- u_flag = 1;
- open_flag = EXT2_FLAG_RW;
- break;
- case 'U':
- new_UUID = optarg;
- U_flag = 1;
- open_flag = EXT2_FLAG_RW |
- EXT2_FLAG_JOURNAL_DEV_OK;
- break;
- default:
- bb_show_usage();
- }
- if (optind < argc - 1 || optind == argc)
- bb_show_usage();
- if (!open_flag && !l_flag)
- bb_show_usage();
- io_options = strchr(argv[optind], '?');
- if (io_options)
- *io_options++ = 0;
- device_name = x_blkid_get_devname(argv[optind]);
-}
-
-static void tune2fs_clean_up(void)
-{
- if (ENABLE_FEATURE_CLEAN_UP && device_name) free(device_name);
- if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
-}
-
-int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int tune2fs_main(int argc, char **argv)
-{
- errcode_t retval;
- ext2_filsys fs;
- struct ext2_super_block *sb;
- io_manager io_ptr;
-
- if (ENABLE_FEATURE_CLEAN_UP)
- atexit(tune2fs_clean_up);
-
- if (ENABLE_E2LABEL && (applet_name[0] == 'e')) /* e2label */
- parse_e2label_options(argc, argv);
- else
- parse_tune2fs_options(argc, argv); /* tune2fs */
-
- io_ptr = unix_io_manager;
- retval = ext2fs_open2(device_name, io_options, open_flag,
- 0, 0, io_ptr, &fs);
- if (retval)
- bb_error_msg_and_die("No valid superblock on %s", device_name);
- sb = fs->super;
- if (print_label) {
- /* For e2label emulation */
- printf("%.*s\n", (int) sizeof(sb->s_volume_name),
- sb->s_volume_name);
- return 0;
- }
- retval = ext2fs_check_if_mounted(device_name, &mount_flags);
- if (retval)
- bb_error_msg_and_die("can't determine if %s is mounted", device_name);
- /* Normally we only need to write out the superblock */
- fs->flags |= EXT2_FLAG_SUPER_ONLY;
-
- if (c_flag) {
- sb->s_max_mnt_count = max_mount_count;
- ext2fs_mark_super_dirty(fs);
- printf("Setting maximal mount count to %d\n", max_mount_count);
- }
- if (C_flag) {
- sb->s_mnt_count = mount_count;
- ext2fs_mark_super_dirty(fs);
- printf("Setting current mount count to %d\n", mount_count);
- }
- if (e_flag) {
- sb->s_errors = errors;
- ext2fs_mark_super_dirty(fs);
- printf("Setting error behavior to %u\n", errors);
- }
- if (g_flag) {
- sb->s_def_resgid = resgid;
- ext2fs_mark_super_dirty(fs);
- printf("Setting reserved blocks gid to %lu\n", resgid);
- }
- if (i_flag) {
- sb->s_checkinterval = interval;
- ext2fs_mark_super_dirty(fs);
- printf("Setting interval between check %lu seconds\n", interval);
- }
- if (m_flag) {
- sb->s_r_blocks_count = (sb->s_blocks_count / 100)
- * reserved_ratio;
- ext2fs_mark_super_dirty(fs);
- printf("Setting reserved blocks percentage to %u (%u blocks)\n",
- reserved_ratio, sb->s_r_blocks_count);
- }
- if (r_flag) {
- if (reserved_blocks >= sb->s_blocks_count/2)
- bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks);
- sb->s_r_blocks_count = reserved_blocks;
- ext2fs_mark_super_dirty(fs);
- printf("Setting reserved blocks count to %lu\n", reserved_blocks);
- }
- if (s_flag == 1) {
- if (sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
- bb_error_msg("\nThe filesystem already has sparse superblocks");
- else {
- sb->s_feature_ro_compat |=
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- sb->s_state &= ~EXT2_VALID_FS;
- ext2fs_mark_super_dirty(fs);
- printf("\nSparse superblock flag set. %s", please_fsck);
- }
- }
- if (s_flag == 0) {
- if (!(sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
- bb_error_msg("\nThe filesystem already has sparse superblocks disabled");
- else {
- sb->s_feature_ro_compat &=
- ~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
- sb->s_state &= ~EXT2_VALID_FS;
- fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
- ext2fs_mark_super_dirty(fs);
- printf("\nSparse superblock flag cleared. %s", please_fsck);
- }
- }
- if (T_flag) {
- sb->s_lastcheck = last_check_time;
- ext2fs_mark_super_dirty(fs);
- printf("Setting time filesystem last checked to %s\n",
- ctime(&last_check_time));
- }
- if (u_flag) {
- sb->s_def_resuid = resuid;
- ext2fs_mark_super_dirty(fs);
- printf("Setting reserved blocks uid to %lu\n", resuid);
- }
- if (L_flag) {
- if (strlen(new_label) > sizeof(sb->s_volume_name))
- bb_error_msg("Warning: label too long, truncating");
- memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
- safe_strncpy(sb->s_volume_name, new_label,
- sizeof(sb->s_volume_name));
- ext2fs_mark_super_dirty(fs);
- }
- if (M_flag) {
- memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
- safe_strncpy(sb->s_last_mounted, new_last_mounted,
- sizeof(sb->s_last_mounted));
- ext2fs_mark_super_dirty(fs);
- }
- if (mntopts_cmd)
- update_mntopts(fs, mntopts_cmd);
- if (features_cmd)
- update_feature_set(fs, features_cmd);
- if (journal_size || journal_device)
- add_journal(fs);
-
- if (U_flag) {
- if ((strcasecmp(new_UUID, "null") == 0) ||
- (strcasecmp(new_UUID, "clear") == 0)) {
- uuid_clear(sb->s_uuid);
- } else if (strcasecmp(new_UUID, "time") == 0) {
- uuid_generate_time(sb->s_uuid);
- } else if (strcasecmp(new_UUID, "random") == 0) {
- uuid_generate(sb->s_uuid);
- } else if (uuid_parse(new_UUID, sb->s_uuid)) {
- bb_error_msg_and_die("Invalid UUID format");
- }
- ext2fs_mark_super_dirty(fs);
- }
-
- if (l_flag)
- list_super (sb);
- return (ext2fs_close (fs) ? 1 : 0);
-}
diff --git a/e2fsprogs/old_e2fsprogs/util.c b/e2fsprogs/old_e2fsprogs/util.c
deleted file mode 100644
index 3e7ee8e..0000000
--- a/e2fsprogs/old_e2fsprogs/util.c
+++ b/dev/null
@@ -1,263 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * util.c --- helper functions used by tune2fs and mke2fs
- *
- * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <linux/major.h>
-#include <sys/stat.h>
-
-#include "e2fsbb.h"
-#include "e2p/e2p.h"
-#include "ext2fs/ext2_fs.h"
-#include "ext2fs/ext2fs.h"
-#include "blkid/blkid.h"
-#include "util.h"
-
-void proceed_question(void)
-{
- fputs("Proceed anyway? (y,n) ", stdout);
- if (bb_ask_confirmation() == 0)
- exit(1);
-}
-
-void check_plausibility(const char *device, int force)
-{
- int val;
- struct stat s;
- val = stat(device, &s);
- if (force)
- return;
- if (val == -1)
- bb_perror_msg_and_die("can't stat '%s'", device);
- if (!S_ISBLK(s.st_mode)) {
- printf("%s is not a block special device.\n", device);
- proceed_question();
- return;
- }
-
-#ifdef HAVE_LINUX_MAJOR_H
-#ifndef MAJOR
-#define MAJOR(dev) ((dev)>>8)
-#define MINOR(dev) ((dev) & 0xff)
-#endif
-#ifndef SCSI_BLK_MAJOR
-#ifdef SCSI_DISK0_MAJOR
-#ifdef SCSI_DISK8_MAJOR
-#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
- ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
- ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
-#else
-#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
- ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR))
-#endif /* defined(SCSI_DISK8_MAJOR) */
-#define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR)
-#else
-#define SCSI_BLK_MAJOR(M) ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR)
-#endif /* defined(SCSI_DISK0_MAJOR) */
-#endif /* defined(SCSI_BLK_MAJOR) */
- if (((MAJOR(s.st_rdev) == HD_MAJOR &&
- MINOR(s.st_rdev)%64 == 0) ||
- (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) &&
- MINOR(s.st_rdev)%16 == 0))) {
- printf("%s is entire device, not just one partition!\n", device);
- proceed_question();
- }
-#endif
-}
-
-void check_mount(const char *device, int force, const char *type)
-{
- errcode_t retval;
- int mount_flags;
-
- retval = ext2fs_check_if_mounted(device, &mount_flags);
- if (retval) {
- bb_error_msg("can't determine if %s is mounted", device);
- return;
- }
- if (mount_flags & EXT2_MF_MOUNTED) {
- bb_error_msg("%s is mounted !", device);
-force_check:
- if (force)
- bb_error_msg("badblocks forced anyways");
- else
- bb_error_msg_and_die("it's not safe to run badblocks!");
- }
-
- if (mount_flags & EXT2_MF_BUSY) {
- bb_error_msg("%s is apparently in use by the system", device);
- goto force_check;
- }
-}
-
-void parse_journal_opts(char **journal_device, int *journal_flags,
- int *journal_size, const char *opts)
-{
- char *buf, *token, *next, *p, *arg;
- int journal_usage = 0;
- buf = xstrdup(opts);
- for (token = buf; token && *token; token = next) {
- p = strchr(token, ',');
- next = 0;
- if (p) {
- *p = 0;
- next = p+1;
- }
- arg = strchr(token, '=');
- if (arg) {
- *arg = 0;
- arg++;
- }
- if (strcmp(token, "device") == 0) {
- *journal_device = blkid_get_devname(NULL, arg, NULL);
- if (!*journal_device) {
- journal_usage++;
- continue;
- }
- } else if (strcmp(token, "size") == 0) {
- if (!arg) {
- journal_usage++;
- continue;
- }
- (*journal_size) = strtoul(arg, &p, 0);
- if (*p)
- journal_usage++;
- } else if (strcmp(token, "v1_superblock") == 0) {
- (*journal_flags) |= EXT2_MKJOURNAL_V1_SUPER;
- continue;
- } else
- journal_usage++;
- }
- if (journal_usage)
- bb_error_msg_and_die(
- "\nBad journal options specified.\n\n"
- "Journal options are separated by commas, "
- "and may take an argument which\n"
- "\tis set off by an equals ('=') sign.\n\n"
- "Valid journal options are:\n"
- "\tsize=<journal size in megabytes>\n"
- "\tdevice=<journal device>\n\n"
- "The journal size must be between "
- "1024 and 102400 filesystem blocks.\n\n");
-}
-
-/*
- * Determine the number of journal blocks to use, either via
- * user-specified # of megabytes, or via some intelligently selected
- * defaults.
- *
- * Find a reasonable journal file size (in blocks) given the number of blocks
- * in the filesystem. For very small filesystems, it is not reasonable to
- * have a journal that fills more than half of the filesystem.
- */
-int figure_journal_size(int size, ext2_filsys fs)
-{
- blk_t j_blocks;
-
- if (fs->super->s_blocks_count < 2048) {
- bb_error_msg("Filesystem too small for a journal");
- return 0;
- }
-
- if (size >= 0) {
- j_blocks = size * 1024 / (fs->blocksize / 1024);
- if (j_blocks < 1024 || j_blocks > 102400)
- bb_error_msg_and_die("\nThe requested journal "
- "size is %d blocks;\n it must be "
- "between 1024 and 102400 blocks; Aborting",
- j_blocks);
- if (j_blocks > fs->super->s_free_blocks_count)
- bb_error_msg_and_die("Journal size too big for filesystem");
- return j_blocks;
- }
-
- if (fs->super->s_blocks_count < 32768)
- j_blocks = 1024;
- else if (fs->super->s_blocks_count < 256*1024)
- j_blocks = 4096;
- else if (fs->super->s_blocks_count < 512*1024)
- j_blocks = 8192;
- else if (fs->super->s_blocks_count < 1024*1024)
- j_blocks = 16384;
- else
- j_blocks = 32768;
-
- return j_blocks;
-}
-
-void print_check_message(ext2_filsys fs)
-{
- printf("This filesystem will be automatically "
- "checked every %d mounts or\n"
- "%g days, whichever comes first. "
- "Use tune2fs -c or -i to override.\n",
- fs->super->s_max_mnt_count,
- (double)fs->super->s_checkinterval / (3600 * 24));
-}
-
-void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force)
-{
- errcode_t retval;
- ext2_filsys jfs;
- io_manager io_ptr;
-
- check_plausibility(journal_device, force);
- check_mount(journal_device, force, "journal");
- io_ptr = unix_io_manager;
- retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
- EXT2_FLAG_JOURNAL_DEV_OK, 0,
- fs->blocksize, io_ptr, &jfs);
- if (retval)
- bb_error_msg_and_die("can't journal device %s", journal_device);
- if (!quiet)
- printf("Adding journal to device %s: ", journal_device);
- fflush(stdout);
- retval = ext2fs_add_journal_device(fs, jfs);
- if (retval)
- bb_error_msg_and_die("\nFailed to add journal to device %s", journal_device);
- if (!quiet)
- puts("done");
- ext2fs_close(jfs);
-}
-
-void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet)
-{
- unsigned long journal_blocks;
- errcode_t retval;
-
- journal_blocks = figure_journal_size(journal_size, fs);
- if (!journal_blocks) {
- fs->super->s_feature_compat &=
- ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
- return;
- }
- if (!quiet)
- printf("Creating journal (%lu blocks): ", journal_blocks);
- fflush(stdout);
- retval = ext2fs_add_journal_inode(fs, journal_blocks,
- journal_flags);
- if (retval)
- bb_error_msg_and_die("can't create journal");
- if (!quiet)
- puts("done");
-}
-
-char *e2fs_set_sbin_path(void)
-{
- char *oldpath = getenv("PATH");
- /* Update our PATH to include /sbin */
-#define PATH_SET "/sbin"
- if (oldpath)
- oldpath = xasprintf("%s:%s", PATH_SET, oldpath);
- else
- oldpath = PATH_SET;
- putenv(oldpath);
- return oldpath;
-}
diff --git a/e2fsprogs/old_e2fsprogs/util.h b/e2fsprogs/old_e2fsprogs/util.h
deleted file mode 100644
index 80d2417..0000000
--- a/e2fsprogs/old_e2fsprogs/util.h
+++ b/dev/null
@@ -1,22 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * util.h --- header file defining prototypes for helper functions
- * used by tune2fs and mke2fs
- *
- * Copyright 2000 by Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-
-extern void proceed_question(void);
-extern void check_plausibility(const char *device, int force);
-extern void parse_journal_opts(char **, int *, int *, const char *opts);
-extern void check_mount(const char *device, int force, const char *type);
-extern int figure_journal_size(int size, ext2_filsys fs);
-extern void print_check_message(ext2_filsys fs);
-extern void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force);
-extern void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet);
-extern char *e2fs_set_sbin_path(void);
diff --git a/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src b/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src
deleted file mode 100644
index b8c687d..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src
+++ b/dev/null
@@ -1,16 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
-#
-# Licensed under GPLv2, see file LICENSE in this source tree.
-
-NEEDED-$(CONFIG_E2FSCK) = y
-NEEDED-$(CONFIG_FSCK) = y
-NEEDED-$(CONFIG_MKE2FS) = y
-NEEDED-$(CONFIG_TUNE2FS) = y
-
-lib-y:=
-
-INSERT
-lib-$(NEEDED-y) += compare.o gen_uuid.o pack.o parse.o unpack.o unparse.o \
- uuid_time.o
diff --git a/e2fsprogs/old_e2fsprogs/uuid/compare.c b/e2fsprogs/old_e2fsprogs/uuid/compare.c
deleted file mode 100644
index 348ea7c..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/compare.c
+++ b/dev/null
@@ -1,55 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * compare.c --- compare whether or not two UUID's are the same
- *
- * Returns 0 if the two UUID's are different, and 1 if they are the same.
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include "uuidP.h"
-#include <string.h>
-
-#define UUCMP(u1,u2) if (u1 != u2) return (u1 < u2) ? -1 : 1;
-
-int uuid_compare(const uuid_t uu1, const uuid_t uu2)
-{
- struct uuid uuid1, uuid2;
-
- uuid_unpack(uu1, &uuid1);
- uuid_unpack(uu2, &uuid2);
-
- UUCMP(uuid1.time_low, uuid2.time_low);
- UUCMP(uuid1.time_mid, uuid2.time_mid);
- UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
- UUCMP(uuid1.clock_seq, uuid2.clock_seq);
- return memcmp(uuid1.node, uuid2.node, 6);
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c b/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
deleted file mode 100644
index 4310c17..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
+++ b/dev/null
@@ -1,304 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * gen_uuid.c --- generate a DCE-compatible uuid
- *
- * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/file.h>
-#include <sys/time.h>
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-#include <sys/socket.h>
-#ifdef HAVE_SYS_SOCKIO_H
-#include <sys/sockio.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NET_IF_DL_H
-#include <net/if_dl.h>
-#endif
-
-#include "uuidP.h"
-
-#ifdef HAVE_SRANDOM
-#define srand(x) srandom(x)
-#define rand() random()
-#endif
-
-static int get_random_fd(void)
-{
- struct timeval tv;
- static int fd = -2;
- int i;
-
- if (fd == -2) {
- gettimeofday(&tv, 0);
- fd = open("/dev/urandom", O_RDONLY);
- if (fd == -1)
- fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
- srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
- }
- /* Crank the random number generator a few times */
- gettimeofday(&tv, 0);
- for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
- rand();
- return fd;
-}
-
-
-/*
- * Generate a series of random bytes. Use /dev/urandom if possible,
- * and if not, use srandom/random.
- */
-static void get_random_bytes(void *buf, int nbytes)
-{
- int i, n = nbytes, fd = get_random_fd();
- int lose_counter = 0;
- unsigned char *cp = (unsigned char *) buf;
-
- if (fd >= 0) {
- while (n > 0) {
- i = read(fd, cp, n);
- if (i <= 0) {
- if (lose_counter++ > 16)
- break;
- continue;
- }
- n -= i;
- cp += i;
- lose_counter = 0;
- }
- }
-
- /*
- * We do this all the time, but this is the only source of
- * randomness if /dev/random/urandom is out to lunch.
- */
- for (cp = buf, i = 0; i < nbytes; i++)
- *cp++ ^= (rand() >> 7) & 0xFF;
-}
-
-/*
- * Get the ethernet hardware address, if we can find it...
- */
-static int get_node_id(unsigned char *node_id)
-{
-#ifdef HAVE_NET_IF_H
- int sd;
- struct ifreq ifr, *ifrp;
- struct ifconf ifc;
- char buf[1024];
- int n, i;
- unsigned char *a;
-#ifdef HAVE_NET_IF_DL_H
- struct sockaddr_dl *sdlp;
-#endif
-
-/*
- * BSD 4.4 defines the size of an ifreq to be
- * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
- * However, under earlier systems, sa_len isn't present, so the size is
- * just sizeof(struct ifreq)
- */
-#ifdef HAVE_SA_LEN
-#ifndef max
-#define max(a,b) ((a) > (b) ? (a) : (b))
-#endif
-#define ifreq_size(i) max(sizeof(struct ifreq),\
- sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
-#else
-#define ifreq_size(i) sizeof(struct ifreq)
-#endif /* HAVE_SA_LEN*/
-
- sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
- if (sd < 0) {
- return -1;
- }
- memset(buf, 0, sizeof(buf));
- ifc.ifc_len = sizeof(buf);
- ifc.ifc_buf = buf;
- if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
- close(sd);
- return -1;
- }
- n = ifc.ifc_len;
- for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
- ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
- strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name);
-#ifdef SIOCGIFHWADDR
- if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
- continue;
- a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
-#else
-#ifdef SIOCGENADDR
- if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
- continue;
- a = (unsigned char *) ifr.ifr_enaddr;
-#else
-#ifdef HAVE_NET_IF_DL_H
- sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
- if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
- continue;
- a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
-#else
- /*
- * XXX we don't have a way of getting the hardware
- * address
- */
- close(sd);
- return 0;
-#endif /* HAVE_NET_IF_DL_H */
-#endif /* SIOCGENADDR */
-#endif /* SIOCGIFHWADDR */
- if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
- continue;
- if (node_id) {
- memcpy(node_id, a, 6);
- close(sd);
- return 1;
- }
- }
- close(sd);
-#endif
- return 0;
-}
-
-/* Assume that the gettimeofday() has microsecond granularity */
-#define MAX_ADJUSTMENT 10
-
-static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq)
-{
- static int adjustment = 0;
- static struct timeval last = {0, 0};
- static uint16_t clock_seq;
- struct timeval tv;
- unsigned long long clock_reg;
-
-try_again:
- gettimeofday(&tv, 0);
- if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
- get_random_bytes(&clock_seq, sizeof(clock_seq));
- clock_seq &= 0x3FFF;
- last = tv;
- last.tv_sec--;
- }
- if ((tv.tv_sec < last.tv_sec) ||
- ((tv.tv_sec == last.tv_sec) &&
- (tv.tv_usec < last.tv_usec))) {
- clock_seq = (clock_seq+1) & 0x3FFF;
- adjustment = 0;
- last = tv;
- } else if ((tv.tv_sec == last.tv_sec) &&
- (tv.tv_usec == last.tv_usec)) {
- if (adjustment >= MAX_ADJUSTMENT)
- goto try_again;
- adjustment++;
- } else {
- adjustment = 0;
- last = tv;
- }
-
- clock_reg = tv.tv_usec*10 + adjustment;
- clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
- clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
-
- *clock_high = clock_reg >> 32;
- *clock_low = clock_reg;
- *ret_clock_seq = clock_seq;
- return 0;
-}
-
-void uuid_generate_time(uuid_t out)
-{
- static unsigned char node_id[6];
- static int has_init = 0;
- struct uuid uu;
- uint32_t clock_mid;
-
- if (!has_init) {
- if (get_node_id(node_id) <= 0) {
- get_random_bytes(node_id, 6);
- /*
- * Set multicast bit, to prevent conflicts
- * with IEEE 802 addresses obtained from
- * network cards
- */
- node_id[0] |= 0x01;
- }
- has_init = 1;
- }
- get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
- uu.clock_seq |= 0x8000;
- uu.time_mid = (uint16_t) clock_mid;
- uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
- memcpy(uu.node, node_id, 6);
- uuid_pack(&uu, out);
-}
-
-void uuid_generate_random(uuid_t out)
-{
- uuid_t buf;
- struct uuid uu;
-
- get_random_bytes(buf, sizeof(buf));
- uuid_unpack(buf, &uu);
-
- uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
- uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
- uuid_pack(&uu, out);
-}
-
-/*
- * This is the generic front-end to uuid_generate_random and
- * uuid_generate_time. It uses uuid_generate_random only if
- * /dev/urandom is available, since otherwise we won't have
- * high-quality randomness.
- */
-void uuid_generate(uuid_t out)
-{
- if (get_random_fd() >= 0)
- uuid_generate_random(out);
- else
- uuid_generate_time(out);
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/pack.c b/e2fsprogs/old_e2fsprogs/uuid/pack.c
deleted file mode 100644
index 217cfce..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/pack.c
+++ b/dev/null
@@ -1,69 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Internal routine for packing UUID's
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <string.h>
-#include "uuidP.h"
-
-void uuid_pack(const struct uuid *uu, uuid_t ptr)
-{
- uint32_t tmp;
- unsigned char *out = ptr;
-
- tmp = uu->time_low;
- out[3] = (unsigned char) tmp;
- tmp >>= 8;
- out[2] = (unsigned char) tmp;
- tmp >>= 8;
- out[1] = (unsigned char) tmp;
- tmp >>= 8;
- out[0] = (unsigned char) tmp;
-
- tmp = uu->time_mid;
- out[5] = (unsigned char) tmp;
- tmp >>= 8;
- out[4] = (unsigned char) tmp;
-
- tmp = uu->time_hi_and_version;
- out[7] = (unsigned char) tmp;
- tmp >>= 8;
- out[6] = (unsigned char) tmp;
-
- tmp = uu->clock_seq;
- out[9] = (unsigned char) tmp;
- tmp >>= 8;
- out[8] = (unsigned char) tmp;
-
- memcpy(out+10, uu->node, 6);
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/parse.c b/e2fsprogs/old_e2fsprogs/uuid/parse.c
deleted file mode 100644
index 9a3f9cb..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/parse.c
+++ b/dev/null
@@ -1,80 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * parse.c --- UUID parsing
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "uuidP.h"
-
-int uuid_parse(const char *in, uuid_t uu)
-{
- struct uuid uuid;
- int i;
- const char *cp;
- char buf[3];
-
- if (strlen(in) != 36)
- return -1;
- for (i=0, cp = in; i <= 36; i++,cp++) {
- if ((i == 8) || (i == 13) || (i == 18) ||
- (i == 23)) {
- if (*cp == '-')
- continue;
- else
- return -1;
- }
- if (i== 36)
- if (*cp == 0)
- continue;
- if (!isxdigit(*cp))
- return -1;
- }
- uuid.time_low = strtoul(in, NULL, 16);
- uuid.time_mid = strtoul(in+9, NULL, 16);
- uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
- uuid.clock_seq = strtoul(in+19, NULL, 16);
- cp = in+24;
- buf[2] = 0;
- for (i=0; i < 6; i++) {
- buf[0] = *cp++;
- buf[1] = *cp++;
- uuid.node[i] = strtoul(buf, NULL, 16);
- }
-
- uuid_pack(&uuid, uu);
- return 0;
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/unpack.c b/e2fsprogs/old_e2fsprogs/uuid/unpack.c
deleted file mode 100644
index 95d3aab..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/unpack.c
+++ b/dev/null
@@ -1,63 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Internal routine for unpacking UUID
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <string.h>
-#include "uuidP.h"
-
-void uuid_unpack(const uuid_t in, struct uuid *uu)
-{
- const uint8_t *ptr = in;
- uint32_t tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_low = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_mid = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->time_hi_and_version = tmp;
-
- tmp = *ptr++;
- tmp = (tmp << 8) | *ptr++;
- uu->clock_seq = tmp;
-
- memcpy(uu->node, ptr, 6);
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/unparse.c b/e2fsprogs/old_e2fsprogs/uuid/unparse.c
deleted file mode 100644
index d2948fe..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/unparse.c
+++ b/dev/null
@@ -1,77 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * unparse.c -- convert a UUID to string
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <stdio.h>
-
-#include "uuidP.h"
-
-static const char *fmt_lower =
- "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
-
-static const char *fmt_upper =
- "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
-
-#ifdef UUID_UNPARSE_DEFAULT_UPPER
-#define FMT_DEFAULT fmt_upper
-#else
-#define FMT_DEFAULT fmt_lower
-#endif
-
-static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
-{
- struct uuid uuid;
-
- uuid_unpack(uu, &uuid);
- sprintf(out, fmt,
- uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
- uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
- uuid.node[0], uuid.node[1], uuid.node[2],
- uuid.node[3], uuid.node[4], uuid.node[5]);
-}
-
-void uuid_unparse_lower(const uuid_t uu, char *out)
-{
- uuid_unparse_x(uu, out, fmt_lower);
-}
-
-void uuid_unparse_upper(const uuid_t uu, char *out)
-{
- uuid_unparse_x(uu, out, fmt_upper);
-}
-
-void uuid_unparse(const uuid_t uu, char *out)
-{
- uuid_unparse_x(uu, out, FMT_DEFAULT);
-}
diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuid.h b/e2fsprogs/old_e2fsprogs/uuid/uuid.h
deleted file mode 100644
index 7a97064..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/uuid.h
+++ b/dev/null
@@ -1,103 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Public include file for the UUID library
- *
- * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-#ifndef UUID_UUID_H
-#define UUID_UUID_H 1
-
-#include <sys/types.h>
-#include <time.h>
-
-typedef unsigned char uuid_t[16];
-
-/* UUID Variant definitions */
-#define UUID_VARIANT_NCS 0
-#define UUID_VARIANT_DCE 1
-#define UUID_VARIANT_MICROSOFT 2
-#define UUID_VARIANT_OTHER 3
-
-/* UUID Type definitions */
-#define UUID_TYPE_DCE_TIME 1
-#define UUID_TYPE_DCE_RANDOM 4
-
-/* Allow UUID constants to be defined */
-#ifdef __GNUC__
-#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
- static const uuid_t name UNUSED_PARAM = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
-#else
-#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
- static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* clear.c */
-/*void uuid_clear(uuid_t uu);*/
-#define uuid_clear(uu) memset(uu, 0, sizeof(uu))
-
-/* compare.c */
-int uuid_compare(const uuid_t uu1, const uuid_t uu2);
-
-/* copy.c */
-/*void uuid_copy(uuid_t dst, const uuid_t src);*/
-#define uuid_copy(dst,src) memcpy(dst, src, sizeof(dst))
-
-/* gen_uuid.c */
-void uuid_generate(uuid_t out);
-void uuid_generate_random(uuid_t out);
-void uuid_generate_time(uuid_t out);
-
-/* isnull.c */
-/*int uuid_is_null(const uuid_t uu);*/
-#define uuid_is_null(uu) (!memcmp(uu, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(uu)))
-
-/* parse.c */
-int uuid_parse(const char *in, uuid_t uu);
-
-/* unparse.c */
-void uuid_unparse(const uuid_t uu, char *out);
-void uuid_unparse_lower(const uuid_t uu, char *out);
-void uuid_unparse_upper(const uuid_t uu, char *out);
-
-/* uuid_time.c */
-time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
-int uuid_type(const uuid_t uu);
-int uuid_variant(const uuid_t uu);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _UUID_UUID_H */
diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuidP.h b/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
deleted file mode 100644
index 87041ef..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
+++ b/dev/null
@@ -1,60 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * uuid.h -- private header file for uuids
- *
- * Copyright (C) 1996, 1997 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <inttypes.h>
-#include <sys/types.h>
-
-#include "uuid.h"
-
-/*
- * Offset between 15-Oct-1582 and 1-Jan-70
- */
-#define TIME_OFFSET_HIGH 0x01B21DD2
-#define TIME_OFFSET_LOW 0x13814000
-
-struct uuid {
- uint32_t time_low;
- uint16_t time_mid;
- uint16_t time_hi_and_version;
- uint16_t clock_seq;
- uint8_t node[6];
-};
-
-
-/*
- * prototypes
- */
-void uuid_pack(const struct uuid *uu, uuid_t ptr);
-void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c b/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
deleted file mode 100644
index b6f73e6..0000000
--- a/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
+++ b/dev/null
@@ -1,161 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * uuid_time.c --- Interpret the time field from a uuid. This program
- * violates the UUID abstraction barrier by reaching into the guts
- * of a UUID and interpreting it.
- *
- * Copyright (C) 1998, 1999 Theodore Ts'o.
- *
- * %Begin-Header%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, and the entire permission notice in its entirety,
- * including the disclaimer of warranties.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
- * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- * %End-Header%
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include "uuidP.h"
-
-time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
-{
- struct uuid uuid;
- uint32_t high;
- struct timeval tv;
- unsigned long long clock_reg;
-
- uuid_unpack(uu, &uuid);
-
- high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
- clock_reg = uuid.time_low | ((unsigned long long) high << 32);
-
- clock_reg -= (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
- tv.tv_sec = clock_reg / 10000000;
- tv.tv_usec = (clock_reg % 10000000) / 10;
-
- if (ret_tv)
- *ret_tv = tv;
-
- return tv.tv_sec;
-}
-
-int uuid_type(const uuid_t uu)
-{
- struct uuid uuid;
-
- uuid_unpack(uu, &uuid);
- return ((uuid.time_hi_and_version >> 12) & 0xF);
-}
-
-int uuid_variant(const uuid_t uu)
-{
- struct uuid uuid;
- int var;
-
- uuid_unpack(uu, &uuid);
- var = uuid.clock_seq;
-
- if ((var & 0x8000) == 0)
- return UUID_VARIANT_NCS;
- if ((var & 0x4000) == 0)
- return UUID_VARIANT_DCE;
- if ((var & 0x2000) == 0)
- return UUID_VARIANT_MICROSOFT;
- return UUID_VARIANT_OTHER;
-}
-
-#ifdef DEBUG
-static const char *variant_string(int variant)
-{
- switch (variant) {
- case UUID_VARIANT_NCS:
- return "NCS";
- case UUID_VARIANT_DCE:
- return "DCE";
- case UUID_VARIANT_MICROSOFT:
- return "Microsoft";
- default:
- return "Other";
- }
-}
-
-
-int
-main(int argc, char **argv)
-{
- uuid_t buf;
- time_t time_reg;
- struct timeval tv;
- int type, variant;
-
- if (argc != 2) {
- fprintf(stderr, "Usage: %s uuid\n", argv[0]);
- exit(1);
- }
- if (uuid_parse(argv[1], buf)) {
- fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
- exit(1);
- }
- variant = uuid_variant(buf);
- type = uuid_type(buf);
- time_reg = uuid_time(buf, &tv);
-
- printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
- if (variant != UUID_VARIANT_DCE) {
- printf("Warning: This program only knows how to interpret "
- "DCE UUIDs.\n\tThe rest of the output is likely "
- "to be incorrect!!\n");
- }
- printf("UUID type is %d", type);
- switch (type) {
- case 1:
- printf(" (time based)\n");
- break;
- case 2:
- printf(" (DCE)\n");
- break;
- case 3:
- printf(" (name-based)\n");
- break;
- case 4:
- printf(" (random)\n");
- break;
- default:
- bb_putchar('\n');
- }
- if (type != 1) {
- printf("Warning: not a time-based UUID, so UUID time "
- "decoding will likely not work!\n");
- }
- printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
- ctime(&time_reg));
-
- return 0;
-}
-#endif
diff --git a/e2fsprogs/tune2fs.c b/e2fsprogs/tune2fs.c
index 119460a..0485e82 100644
--- a/e2fsprogs/tune2fs.c
+++ b/e2fsprogs/tune2fs.c
@@ -6,6 +6,35 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config TUNE2FS
+//config: bool "tune2fs"
+//config: default n # off: it is too limited compared to upstream version
+//config: help
+//config: tune2fs allows the system administrator to adjust various tunable
+//config: filesystem parameters on Linux ext2/ext3 filesystems.
+
+//applet:IF_TUNE2FS(APPLET(tune2fs, BB_DIR_SBIN, BB_SUID_DROP))
+
+//TODO alias to "tune2fs -L LABEL": //applet:IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label))
+
+//kbuild:lib-$(CONFIG_TUNE2FS) += tune2fs.o
+
+//usage:#define tune2fs_trivial_usage
+//usage: "[-c MAX_MOUNT_COUNT] "
+////usage: "[-e errors-behavior] [-g group] "
+//usage: "[-i DAYS] "
+////usage: "[-j] [-J journal-options] [-l] [-s sparse-flag] "
+////usage: "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] "
+////usage: "[-r reserved-blocks-count] [-u user] "
+//usage: "[-C MOUNT_COUNT] "
+//usage: "[-L LABEL] "
+////usage: "[-M last-mounted-dir] [-O [^]feature[,...]] "
+////usage: "[-T last-check-time] [-U UUID] "
+//usage: "BLOCKDEV"
+//usage:
+//usage:#define tune2fs_full_usage "\n\n"
+//usage: "Adjust filesystem options on ext[23] filesystems"
+
#include "libbb.h"
#include <linux/fs.h>
#include "bb_e2fs_defs.h"
@@ -27,22 +56,6 @@ do { \
#define FETCH_LE32(field) \
(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
-//usage:#define tune2fs_trivial_usage
-//usage: "[-c MAX_MOUNT_COUNT] "
-////usage: "[-e errors-behavior] [-g group] "
-//usage: "[-i DAYS] "
-////usage: "[-j] [-J journal-options] [-l] [-s sparse-flag] "
-////usage: "[-m reserved-blocks-percent] [-o [^]mount-options[,...]] "
-////usage: "[-r reserved-blocks-count] [-u user] "
-//usage: "[-C MOUNT_COUNT] "
-//usage: "[-L LABEL] "
-////usage: "[-M last-mounted-dir] [-O [^]feature[,...]] "
-////usage: "[-T last-check-time] [-U UUID] "
-//usage: "BLOCKDEV"
-//usage:
-//usage:#define tune2fs_full_usage "\n\n"
-//usage: "Adjust filesystem options on ext[23] filesystems"
-
enum {
OPT_L = 1 << 0, // label
OPT_c = 1 << 1, // max mount count
diff --git a/editors/awk.c b/editors/awk.c
index e2527ff..b1dfe9e 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -72,12 +72,9 @@
#define OPTSTR_AWK \
- "F:v:f:" \
- IF_FEATURE_AWK_GNU_EXTENSIONS("e:") \
+ "F:v:*f:*" \
+ IF_FEATURE_AWK_GNU_EXTENSIONS("e:*") \
"W:"
-#define OPTCOMPLSTR_AWK \
- "v::f::" \
- IF_FEATURE_AWK_GNU_EXTENSIONS("e::")
enum {
OPTBIT_F, /* define field separator */
OPTBIT_v, /* define variable */
@@ -207,7 +204,7 @@ typedef struct tsplitter_s {
/* simple token classes */
/* Order and hex values are very important!!! See next_token() */
-#define TC_SEQSTART 1 /* ( */
+#define TC_SEQSTART (1 << 0) /* ( */
#define TC_SEQTERM (1 << 1) /* ) */
#define TC_REGEXP (1 << 2) /* /.../ */
#define TC_OUTRDR (1 << 3) /* | > >> */
@@ -227,16 +224,22 @@ typedef struct tsplitter_s {
#define TC_WHILE (1 << 17)
#define TC_ELSE (1 << 18)
#define TC_BUILTIN (1 << 19)
-#define TC_GETLINE (1 << 20)
-#define TC_FUNCDECL (1 << 21) /* `function' `func' */
-#define TC_BEGIN (1 << 22)
-#define TC_END (1 << 23)
-#define TC_EOF (1 << 24)
-#define TC_VARIABLE (1 << 25)
-#define TC_ARRAY (1 << 26)
-#define TC_FUNCTION (1 << 27)
-#define TC_STRING (1 << 28)
-#define TC_NUMBER (1 << 29)
+/* This costs ~50 bytes of code.
+ * A separate class to support deprecated "length" form. If we don't need that
+ * (i.e. if we demand that only "length()" with () is valid), then TC_LENGTH
+ * can be merged with TC_BUILTIN:
+ */
+#define TC_LENGTH (1 << 20)
+#define TC_GETLINE (1 << 21)
+#define TC_FUNCDECL (1 << 22) /* `function' `func' */
+#define TC_BEGIN (1 << 23)
+#define TC_END (1 << 24)
+#define TC_EOF (1 << 25)
+#define TC_VARIABLE (1 << 26)
+#define TC_ARRAY (1 << 27)
+#define TC_FUNCTION (1 << 28)
+#define TC_STRING (1 << 29)
+#define TC_NUMBER (1 << 30)
#define TC_UOPPRE (TC_UOPPRE1 | TC_UOPPRE2)
@@ -244,14 +247,16 @@ typedef struct tsplitter_s {
#define TC_BINOP (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
//#define TC_UNARYOP (TC_UOPPRE | TC_UOPPOST)
#define TC_OPERAND (TC_VARIABLE | TC_ARRAY | TC_FUNCTION \
- | TC_BUILTIN | TC_GETLINE | TC_SEQSTART | TC_STRING | TC_NUMBER)
+ | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
+ | TC_SEQSTART | TC_STRING | TC_NUMBER)
#define TC_STATEMNT (TC_STATX | TC_WHILE)
#define TC_OPTERM (TC_SEMICOL | TC_NEWLINE)
/* word tokens, cannot mean something else if not expected */
-#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN \
- | TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
+#define TC_WORD (TC_IN | TC_STATEMNT | TC_ELSE \
+ | TC_BUILTIN | TC_LENGTH | TC_GETLINE \
+ | TC_FUNCDECL | TC_BEGIN | TC_END)
/* discard newlines after these */
#define TC_NOTERM (TC_COMMA | TC_GRPSTART | TC_GRPTERM \
@@ -346,54 +351,54 @@ enum {
#define NTC "\377" /* switch to next token class (tc<<1) */
#define NTCC '\377'
-#define OC_B OC_BUILTIN
-
static const char tokenlist[] ALIGN1 =
- "\1(" NTC
- "\1)" NTC
- "\1/" NTC /* REGEXP */
- "\2>>" "\1>" "\1|" NTC /* OUTRDR */
- "\2++" "\2--" NTC /* UOPPOST */
- "\2++" "\2--" "\1$" NTC /* UOPPRE1 */
- "\2==" "\1=" "\2+=" "\2-=" /* BINOPX */
+ "\1(" NTC /* TC_SEQSTART */
+ "\1)" NTC /* TC_SEQTERM */
+ "\1/" NTC /* TC_REGEXP */
+ "\2>>" "\1>" "\1|" NTC /* TC_OUTRDR */
+ "\2++" "\2--" NTC /* TC_UOPPOST */
+ "\2++" "\2--" "\1$" NTC /* TC_UOPPRE1 */
+ "\2==" "\1=" "\2+=" "\2-=" /* TC_BINOPX */
"\2*=" "\2/=" "\2%=" "\2^="
"\1+" "\1-" "\3**=" "\2**"
"\1/" "\1%" "\1^" "\1*"
"\2!=" "\2>=" "\2<=" "\1>"
"\1<" "\2!~" "\1~" "\2&&"
"\2||" "\1?" "\1:" NTC
- "\2in" NTC
- "\1," NTC
- "\1|" NTC
- "\1+" "\1-" "\1!" NTC /* UOPPRE2 */
- "\1]" NTC
- "\1{" NTC
- "\1}" NTC
- "\1;" NTC
- "\1\n" NTC
- "\2if" "\2do" "\3for" "\5break" /* STATX */
+ "\2in" NTC /* TC_IN */
+ "\1," NTC /* TC_COMMA */
+ "\1|" NTC /* TC_PIPE */
+ "\1+" "\1-" "\1!" NTC /* TC_UOPPRE2 */
+ "\1]" NTC /* TC_ARRTERM */
+ "\1{" NTC /* TC_GRPSTART */
+ "\1}" NTC /* TC_GRPTERM */
+ "\1;" NTC /* TC_SEMICOL */
+ "\1\n" NTC /* TC_NEWLINE */
+ "\2if" "\2do" "\3for" "\5break" /* TC_STATX */
"\10continue" "\6delete" "\5print"
"\6printf" "\4next" "\10nextfile"
"\6return" "\4exit" NTC
- "\5while" NTC
- "\4else" NTC
-
- "\3and" "\5compl" "\6lshift" "\2or"
+ "\5while" NTC /* TC_WHILE */
+ "\4else" NTC /* TC_ELSE */
+ "\3and" "\5compl" "\6lshift" "\2or" /* TC_BUILTIN */
"\6rshift" "\3xor"
- "\5close" "\6system" "\6fflush" "\5atan2" /* BUILTIN */
+ "\5close" "\6system" "\6fflush" "\5atan2"
"\3cos" "\3exp" "\3int" "\3log"
"\4rand" "\3sin" "\4sqrt" "\5srand"
- "\6gensub" "\4gsub" "\5index" "\6length"
+ "\6gensub" "\4gsub" "\5index" /* "\6length" was here */
"\5match" "\5split" "\7sprintf" "\3sub"
"\6substr" "\7systime" "\10strftime" "\6mktime"
"\7tolower" "\7toupper" NTC
- "\7getline" NTC
- "\4func" "\10function" NTC
- "\5BEGIN" NTC
- "\3END"
+ "\6length" NTC /* TC_LENGTH */
+ "\7getline" NTC /* TC_GETLINE */
+ "\4func" "\10function" NTC /* TC_FUNCDECL */
+ "\5BEGIN" NTC /* TC_BEGIN */
+ "\3END" /* TC_END */
/* compiler adds trailing "\0" */
;
+#define OC_B OC_BUILTIN
+
static const uint32_t tokeninfo[] = {
0,
0,
@@ -408,7 +413,7 @@ static const uint32_t tokeninfo[] = {
OC_COMPARE|VV|P(39)|4, OC_COMPARE|VV|P(39)|3, OC_COMPARE|VV|P(39)|0, OC_COMPARE|VV|P(39)|1,
OC_COMPARE|VV|P(39)|2, OC_MATCH|Sx|P(45)|'!', OC_MATCH|Sx|P(45)|'~', OC_LAND|Vx|P(55),
OC_LOR|Vx|P(59), OC_TERNARY|Vx|P(64)|'?', OC_COLON|xx|P(67)|':',
- OC_IN|SV|P(49), /* in */
+ OC_IN|SV|P(49), /* TC_IN */
OC_COMMA|SS|P(80),
OC_PGETLINE|SV|P(37),
OC_UNARY|xV|P(19)|'+', OC_UNARY|xV|P(19)|'-', OC_UNARY|xV|P(19)|'!',
@@ -423,20 +428,20 @@ static const uint32_t tokeninfo[] = {
OC_RETURN|Vx, OC_EXIT|Nx,
ST_WHILE,
0, /* else */
-
OC_B|B_an|P(0x83), OC_B|B_co|P(0x41), OC_B|B_ls|P(0x83), OC_B|B_or|P(0x83),
OC_B|B_rs|P(0x83), OC_B|B_xo|P(0x83),
OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
OC_FBLTIN|F_rn, OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
- OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
+ OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), /* OC_FBLTIN|Sx|F_le, was here */
OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF, OC_B|B_su|P(0xb6),
OC_B|B_ss|P(0x8f), OC_FBLTIN|F_ti, OC_B|B_ti|P(0x0b), OC_B|B_mt|P(0x0b),
OC_B|B_lo|P(0x49), OC_B|B_up|P(0x49),
+ OC_FBLTIN|Sx|F_le, /* TC_LENGTH */
OC_GETLINE|SV|P(0),
0, 0,
0,
- 0 /* END */
+ 0 /* TC_END */
};
/* internal variable names and their initial values */
@@ -1060,12 +1065,10 @@ static uint32_t next_token(uint32_t expected)
if (t_rollback) {
t_rollback = FALSE;
-
} else if (concat_inserted) {
concat_inserted = FALSE;
t_tclass = save_tclass;
t_info = save_info;
-
} else {
p = g_pos;
readnext:
@@ -1081,7 +1084,6 @@ static uint32_t next_token(uint32_t expected)
if (*p == '\0') {
tc = TC_EOF;
debug_printf_parse("%s: token found: TC_EOF\n", __func__);
-
} else if (*p == '\"') {
/* it's a string */
t_string = s = ++p;
@@ -1097,7 +1099,6 @@ static uint32_t next_token(uint32_t expected)
*s = '\0';
tc = TC_STRING;
debug_printf_parse("%s: token found:'%s' TC_STRING\n", __func__, t_string);
-
} else if ((expected & TC_REGEXP) && *p == '/') {
/* it's regexp */
t_string = s = ++p;
@@ -1130,7 +1131,6 @@ static uint32_t next_token(uint32_t expected)
syntax_error(EMSG_UNEXP_TOKEN);
tc = TC_NUMBER;
debug_printf_parse("%s: token found:%f TC_NUMBER\n", __func__, t_double);
-
} else {
/* search for something known */
tl = tokenlist;
@@ -1207,9 +1207,10 @@ static uint32_t next_token(uint32_t expected)
ltclass = t_tclass;
/* Are we ready for this? */
- if (!(ltclass & expected))
+ if (!(ltclass & expected)) {
syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+ }
return ltclass;
#undef concat_inserted
@@ -1376,6 +1377,16 @@ static node *parse_expr(uint32_t iexp)
debug_printf_parse("%s: TC_BUILTIN\n", __func__);
cn->l.n = condition();
break;
+
+ case TC_LENGTH:
+ debug_printf_parse("%s: TC_LENGTH\n", __func__);
+ next_token(TC_SEQSTART | TC_OPTERM | TC_GRPTERM);
+ rollback_token();
+ if (t_tclass & TC_SEQSTART) {
+ /* It was a "(" token. Handle just like TC_BUILTIN */
+ cn->l.n = condition();
+ }
+ break;
}
}
}
@@ -1503,7 +1514,7 @@ static void chain_group(void)
next_token(TC_SEQSTART);
n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
if (t_tclass & TC_SEQTERM) { /* for-in */
- if ((n2->info & OPCLSMASK) != OC_IN)
+ if (!n2 || (n2->info & OPCLSMASK) != OC_IN)
syntax_error(EMSG_UNEXP_TOKEN);
n = chain_node(OC_WALKINIT | VV);
n->l.n = n2->l.n;
@@ -1540,12 +1551,14 @@ static void chain_group(void)
debug_printf_parse("%s: OC_BREAK\n", __func__);
n = chain_node(OC_EXEC);
n->a.n = break_ptr;
+ chain_expr(t_info);
break;
case OC_CONTINUE:
debug_printf_parse("%s: OC_CONTINUE\n", __func__);
n = chain_node(OC_EXEC);
n->a.n = continue_ptr;
+ chain_expr(t_info);
break;
/* delete, next, nextfile, return, exit */
@@ -1578,12 +1591,10 @@ static void parse_program(char *p)
debug_printf_parse("%s: TC_BEGIN\n", __func__);
seq = &beginseq;
chain_group();
-
} else if (tclass & TC_END) {
debug_printf_parse("%s: TC_END\n", __func__);
seq = &endseq;
chain_group();
-
} else if (tclass & TC_FUNCDECL) {
debug_printf_parse("%s: TC_FUNCDECL\n", __func__);
next_token(TC_FUNCTION);
@@ -1601,7 +1612,6 @@ static void parse_program(char *p)
seq = &f->body;
chain_group();
clear_array(ahash);
-
} else if (tclass & TC_OPSEQ) {
debug_printf_parse("%s: TC_OPSEQ\n", __func__);
rollback_token();
@@ -1616,7 +1626,6 @@ static void parse_program(char *p)
chain_node(OC_PRINT);
}
cn->r.n = mainseq.last;
-
} else /* if (tclass & TC_GRPSTART) */ {
debug_printf_parse("%s: TC_GRPSTART(?)\n", __func__);
rollback_token();
@@ -1877,13 +1886,10 @@ static void handle_special(var *v)
split_f0();
mk_splitter(getvar_s(v), &fsplitter);
-
} else if (v == intvar[RS]) {
mk_splitter(getvar_s(v), &rsplitter);
-
} else if (v == intvar[IGNORECASE]) {
icase = istrue(v);
-
} else { /* $n */
n = getvar_i(intvar[NF]);
setvar_i(intvar[NF], n > v-Fields ? n : v-Fields+1);
@@ -3200,7 +3206,6 @@ int awk_main(int argc, char **argv)
*s1 = '=';
}
}
- opt_complementary = OPTCOMPLSTR_AWK;
opt = getopt32(argv, OPTSTR_AWK, &opt_F, &list_v, &list_f, IF_FEATURE_AWK_GNU_EXTENSIONS(&list_e,) NULL);
argv += optind;
argc -= optind;
diff --git a/editors/diff.c b/editors/diff.c
index 1961de6..b275a21 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -125,6 +125,7 @@
//usage: "\n -w Ignore all whitespace"
#include "libbb.h"
+#include "common_bufsiz.h"
#if 0
# define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
@@ -363,7 +364,7 @@ static void stone(const int *a, int n, const int *b, int *J, int pref)
}
struct line {
- /* 'serial' is not used in the begining, so we reuse it
+ /* 'serial' is not used in the beginning, so we reuse it
* to store line offsets, thus reducing memory pressure
*/
union {
@@ -433,7 +434,7 @@ static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch)
for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) {
int c = fgetc(ft->ft_fp);
if (c == EOF) {
- printf("\n\\ No newline at end of file\n");
+ puts("\n\\ No newline at end of file");
return;
}
ft->ft_pos++;
@@ -658,8 +659,8 @@ static bool diff(FILE* fp[2], char *file[2])
}
for (j = 0; j < 2; j++)
- for (k = v[j].a; k < v[j].b; k++)
- nonempty |= (ix[j][k+1] - ix[j][k] != 1);
+ for (k = v[j].a; k <= v[j].b; k++)
+ nonempty |= (ix[j][k] - ix[j][k - 1] != 1);
vec = xrealloc_vector(vec, 6, ++idx);
memcpy(vec[idx], v, sizeof(v));
@@ -692,7 +693,7 @@ static bool diff(FILE* fp[2], char *file[2])
continue;
printf(",%d", (a < b) ? b - a + 1 : 0);
}
- printf(" @@\n");
+ puts(" @@");
/*
* Output changes in "unified" diff format--the old and new lines
* are printed together.
@@ -747,13 +748,15 @@ static int diffreg(char *file[2])
unlink(name);
if (bb_copyfd_eof(fd, fd_tmp) < 0)
xfunc_die();
- if (fd) /* Prevents closing of stdin */
+ if (fd != STDIN_FILENO)
close(fd);
fd = fd_tmp;
+ xlseek(fd, 0, SEEK_SET);
}
fp[i] = fdopen(fd, "r");
}
+ setup_common_bufsiz();
while (1) {
const size_t sz = COMMON_BUFSIZE / 2;
char *const buf0 = bb_common_bufsiz1;
@@ -986,11 +989,11 @@ int diff_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
/* exactly 2 params; collect multiple -L <label>; -U N */
- opt_complementary = "=2:L::U+";
+ opt_complementary = "=2";
#if ENABLE_FEATURE_DIFF_LONG_OPTIONS
applet_long_options = diff_longopts;
#endif
- getopt32(argv, "abdiL:NqrsS:tTU:wupBE",
+ getopt32(argv, "abdiL:*NqrsS:tTU:+wupBE",
&L_arg, &s_start, &opt_U_context);
argv += optind;
while (L_arg)
diff --git a/editors/ed.c b/editors/ed.c
index 3087fb0..c028b78 100644
--- a/editors/ed.c
+++ b/editors/ed.c
@@ -23,6 +23,7 @@
//usage:#define ed_full_usage ""
#include "libbb.h"
+#include "common_bufsiz.h"
typedef struct LINE {
struct LINE *next;
@@ -35,8 +36,8 @@ typedef struct LINE {
#define searchString bb_common_bufsiz1
enum {
- USERSIZE = sizeof(searchString) > 1024 ? 1024
- : sizeof(searchString) - 1, /* max line length typed in by user */
+ USERSIZE = COMMON_BUFSIZE > 1024 ? 1024
+ : COMMON_BUFSIZE - 1, /* max line length typed in by user */
INITBUF_SIZE = 1024, /* initial buffer size */
};
@@ -66,6 +67,7 @@ struct globals {
#define lines (G.lines )
#define marks (G.marks )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)
@@ -206,7 +208,7 @@ static void doCommands(void)
if (fileName)
printf("\"%s\"\n", fileName);
else
- printf("No file name\n");
+ puts("No file name");
break;
}
free(fileName);
@@ -732,7 +734,6 @@ static int readLines(const char *file, int num)
cc = safe_read(fd, bufPtr, bufSize - bufUsed);
bufUsed += cc;
bufPtr = bufBase;
-
} while (cc > 0);
if (cc < 0) {
diff --git a/editors/patch.c b/editors/patch.c
index 4cdc394..d297542 100644
--- a/editors/patch.c
+++ b/editors/patch.c
@@ -247,7 +247,7 @@ static int apply_one_hunk(void)
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
- if (data && !strcmp(data, plist->data+1)) {
+ if (data && strcmp(data, plist->data+1) == 0) {
if (!backwarn) {
backwarn = TT.linenum;
if (option_mask32 & FLAG_IGNORE) {
@@ -290,12 +290,24 @@ static int apply_one_hunk(void)
// out of buffer.
for (;;) {
+ while (plist && *plist->data == "+-"[reverse]) {
+ if (strcmp(check->data, plist->data+1) == 0
+ && !backwarn
+ ) {
+ backwarn = TT.linenum;
+ if (option_mask32 & FLAG_IGNORE) {
+ dummy_revert = 1;
+ reverse ^= 1;
+ }
+ }
+ plist = plist->next;
+ }
if (!plist || strcmp(check->data, plist->data+1)) {
// Match failed. Write out first line of buffered data and
// recheck remaining buffered data for a new match.
if (PATCH_DEBUG)
- fdprintf(2, "NOT: %s\n", plist->data);
+ fdprintf(2, "NOT: %s\n", plist ? plist->data : "EOF");
TT.state = 3;
check = buf;
@@ -345,6 +357,8 @@ done:
// state 1: Found +++ file indicator, look for @@
// state 2: In hunk: counting initial context lines
// state 3: In hunk: getting body
+// Like GNU patch, we don't require a --- line before the +++, and
+// also allow the --- after the +++ line.
int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int patch_main(int argc UNUSED_PARAM, char **argv)
@@ -370,10 +384,6 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
xmove_fd(xopen_stdin(argv[1]), STDIN_FILENO);
}
}
- if (argv[0]) {
- oldname = xstrdup(argv[0]);
- newname = xstrdup(argv[0]);
- }
// Loop through the lines in the patch
for(;;) {
@@ -412,7 +422,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
}
// Open a new file?
- if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) {
+ if (is_prefixed_with(patchline, "--- ") || is_prefixed_with(patchline, "+++ ")) {
char *s, **name = reverse ? &newname : &oldname;
int i;
@@ -444,7 +454,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
// Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@
// but a missing ,value means the value is 1.
- } else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
+ } else if (state == 1 && is_prefixed_with(patchline, "@@ -")) {
int i;
char *s = patchline+4;
@@ -462,6 +472,14 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
TT.context = 0;
state = 2;
+ // If the --- line is missing or malformed, either oldname
+ // or (for -R) newname could be NULL -- but not both. Like
+ // GNU patch, proceed based on the +++ line, and avoid SEGVs.
+ if (!oldname)
+ oldname = xstrdup("MISSING_FILENAME");
+ if (!newname)
+ newname = xstrdup("MISSING_FILENAME");
+
// If this is the first hunk, open the file.
if (TT.filein == -1) {
int oldsum, newsum, empty = 0;
@@ -474,12 +492,12 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
// We're deleting oldname if new file is /dev/null (before -p)
// or if new hunk is empty (zero context) after patching
- if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) {
+ if (strcmp(name, "/dev/null") == 0 || !(reverse ? oldsum : newsum)) {
name = reverse ? newname : oldname;
- empty++;
+ empty = 1;
}
- // handle -p path truncation.
+ // Handle -p path truncation.
for (i = 0, s = name; *s;) {
if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i)
break;
@@ -490,6 +508,9 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
i++;
name = s;
}
+ // If "patch FILE_TO_PATCH", completely ignore name from patch
+ if (argv[0])
+ name = argv[0];
if (empty) {
// File is empty after the patches have been applied
@@ -507,7 +528,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
struct stat statbuf;
// If the old file was null, we're creating a new one.
- if (!strcmp(oldname, "/dev/null") || !oldsum) {
+ if (strcmp(oldname, "/dev/null") == 0 || !oldsum) {
printf("creating %s\n", name);
s = strrchr(name, '/');
if (s) {
diff --git a/editors/patch_toybox.c b/editors/patch_toybox.c
index a60bf07..5174acd 100644
--- a/editors/patch_toybox.c
+++ b/editors/patch_toybox.c
@@ -335,7 +335,7 @@ static int apply_one_hunk(void)
// Figure out which line of hunk to compare with next. (Skip lines
// of the hunk we'd be adding.)
while (plist && *plist->data == "+-"[reverse]) {
- if (data && !strcmp(data, plist->data+1)) {
+ if (data && strcmp(data, plist->data+1) == 0) {
if (!backwarn) {
fdprintf(2,"Possibly reversed hunk %d at %ld\n",
TT.hunknum, TT.linenum);
@@ -529,8 +529,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
// We're deleting oldname if new file is /dev/null (before -p)
// or if new hunk is empty (zero context) after patching
- if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum))
- {
+ if (strcmp(name, "/dev/null") == 0 || !(reverse ? oldsum : newsum)) {
name = reverse ? newname : oldname;
del++;
}
@@ -551,7 +550,7 @@ int patch_main(int argc UNUSED_PARAM, char **argv)
// If we've got a file to open, do so.
} else if (!(option_mask32 & FLAG_PATHLEN) || i <= TT.prefix) {
// If the old file was null, we're creating a new one.
- if (!strcmp(oldname, "/dev/null") || !oldsum) {
+ if (strcmp(oldname, "/dev/null") == 0 || !oldsum) {
printf("creating %s\n", name);
s = strrchr(name, '/');
if (s) {
diff --git a/editors/sed.c b/editors/sed.c
index e18e48a..637a685 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -53,6 +53,7 @@
* Reference
* http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
+ * http://sed.sourceforge.net/sedfaq3.html
*/
//config:config SED
@@ -85,6 +86,7 @@
//usage: "bar\n"
#include "libbb.h"
+#include "common_bufsiz.h"
#include "xregex.h"
#if 0
@@ -109,9 +111,10 @@ typedef struct sed_cmd_s {
regex_t *sub_match; /* For 's/sub_match/string/' */
int beg_line; /* 'sed 1p' 0 == apply commands to all lines */
int beg_line_orig; /* copy of the above, needed for -i */
- int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($) */
+ int end_line; /* 'sed 1,3p' 0 == one line only. -1 = last line ($). -2-N = +N */
+ int end_line_orig;
- FILE *sw_file; /* File (sw) command writes to, -1 for none. */
+ FILE *sw_file; /* File (sw) command writes to, NULL for none. */
char *string; /* Data string for (saicytb) commands. */
unsigned which_match; /* (s) Which match to replace (0 for all) */
@@ -159,11 +162,10 @@ struct globals {
int len; /* Space allocated */
} pipeline;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
G.sed_cmd_tail = &G.sed_cmd_head; \
} while (0)
@@ -179,7 +181,7 @@ static void sed_free_and_close_stuff(void)
sed_cmd_t *sed_cmd_next = sed_cmd->next;
if (sed_cmd->sw_file)
- xprint_and_close_file(sed_cmd->sw_file);
+ fclose(sed_cmd->sw_file);
if (sed_cmd->beg_match) {
regfree(sed_cmd->beg_match);
@@ -216,23 +218,33 @@ static void cleanup_outname(void)
/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
-static void parse_escapes(char *dest, const char *string, int len, char from, char to)
+static unsigned parse_escapes(char *dest, const char *string, int len, char from, char to)
{
+ char *d = dest;
int i = 0;
+ if (len == -1)
+ len = strlen(string);
+
while (i < len) {
if (string[i] == '\\') {
if (!to || string[i+1] == from) {
- *dest++ = to ? to : string[i+1];
+ if ((*d = to ? to : string[i+1]) == '\0')
+ return d - dest;
i += 2;
+ d++;
continue;
}
- *dest++ = string[i++];
+ i++; /* skip backslash in string[] */
+ *d++ = '\\';
+ /* fall through: copy next char verbatim */
}
- /* TODO: is it safe wrt a string with trailing '\\' ? */
- *dest++ = string[i++];
+ if ((*d = string[i++]) == '\0')
+ return d - dest;
+ d++;
}
- *dest = '\0';
+ *d = '\0';
+ return d - dest;
}
static char *copy_parsing_escapes(const char *string, int len)
@@ -243,9 +255,8 @@ static char *copy_parsing_escapes(const char *string, int len)
/* sed recognizes \n */
/* GNU sed also recognizes \t and \r */
for (s = "\nn\tt\rr"; *s; s += 2) {
- parse_escapes(dest, string, len, s[1], s[0]);
+ len = parse_escapes(dest, string, len, s[1], s[0]);
string = dest;
- len = strlen(dest);
}
return dest;
}
@@ -395,7 +406,9 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
/* process the flags */
sed_cmd->which_match = 1;
+ dbg("s flags:'%s'", substr + idx + 1);
while (substr[++idx]) {
+ dbg("s flag:'%c'", substr[idx]);
/* Parse match number */
if (isdigit(substr[idx])) {
if (match[0] != '^') {
@@ -403,7 +416,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
const char *pos = substr + idx;
/* FIXME: error check? */
sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
- idx = pos - substr;
+ idx = pos - substr - 1;
}
continue;
}
@@ -424,8 +437,11 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
/* Write to file */
case 'w':
{
- char *temp;
- idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
+ char *fname;
+ idx += parse_file_cmd(/*sed_cmd,*/ substr+idx+1, &fname);
+ sed_cmd->sw_file = xfopen_for_write(fname);
+ sed_cmd->sw_last_char = '\n';
+ free(fname);
break;
}
/* Ignore case (gnu exension) */
@@ -443,6 +459,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
case '}':
goto out;
default:
+ dbg("s bad flags:'%s'", substr + idx);
bb_error_msg_and_die("bad option in substitution expression");
}
}
@@ -465,7 +482,7 @@ static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
*/
static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
{
- static const char cmd_letters[] = "saicrw:btTydDgGhHlnNpPqx={}";
+ static const char cmd_letters[] ALIGN1 = "saicrw:btTydDgGhHlnNpPqx={}";
enum {
IDX_s = 0,
IDX_a,
@@ -496,9 +513,11 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
IDX_rbrace,
IDX_nul
};
- struct chk { char chk[sizeof(cmd_letters)-1 == IDX_nul ? 1 : -1]; };
+ unsigned idx;
- unsigned idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
+ BUILD_BUG_ON(sizeof(cmd_letters)-1 != IDX_nul);
+
+ idx = strchrnul(cmd_letters, sed_cmd->cmd) - cmd_letters;
/* handle (s)ubstitution command */
if (idx == IDX_s) {
@@ -506,6 +525,8 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
}
/* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
else if (idx <= IDX_c) { /* a,i,c */
+ unsigned len;
+
if (idx < IDX_c) { /* a,i */
if (sed_cmd->end_line || sed_cmd->end_match)
bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
@@ -519,10 +540,11 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
break;
cmdstr++;
}
- sed_cmd->string = xstrdup(cmdstr);
+ len = strlen(cmdstr);
+ sed_cmd->string = copy_parsing_escapes(cmdstr, len);
+ cmdstr += len;
/* "\anychar" -> "anychar" */
- parse_escapes(sed_cmd->string, sed_cmd->string, strlen(cmdstr), '\0', '\0');
- cmdstr += strlen(cmdstr);
+ parse_escapes(sed_cmd->string, sed_cmd->string, -1, '\0', '\0');
}
/* handle file cmds: (r)ead */
else if (idx <= IDX_w) { /* r,w */
@@ -554,8 +576,8 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
/* \n already parsed, but \delimiter needs unescaping. */
- parse_escapes(match, match, strlen(match), i, i);
- parse_escapes(replace, replace, strlen(replace), i, i);
+ parse_escapes(match, match, -1, i, i);
+ parse_escapes(replace, replace, -1, i, i);
sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
for (i = 0; match[i] && replace[i]; i++) {
@@ -637,10 +659,29 @@ static void add_cmd(const char *cmdstr)
int idx;
cmdstr++;
- idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
- if (!idx)
+ if (*cmdstr == '+' && isdigit(cmdstr[1])) {
+ /* http://sed.sourceforge.net/sedfaq3.html#s3.3
+ * Under GNU sed 3.02+, ssed, and sed15+, <address2>
+ * may also be a notation of the form +num,
+ * indicating the next num lines after <address1> is
+ * matched.
+ * GNU sed 4.2.1 accepts even "+" (meaning "+0").
+ * We don't (we check for isdigit, see above), think
+ * about the "+-3" case.
+ */
+ char *end;
+ /* code is smaller compared to using &cmdstr here: */
+ idx = strtol(cmdstr+1, &end, 10);
+ sed_cmd->end_line = -2 - idx;
+ cmdstr = end;
+ } else {
+ idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
+ cmdstr += idx;
+ idx--; /* if 0, trigger error check below */
+ }
+ if (idx < 0)
bb_error_msg_and_die("no address after comma");
- cmdstr += idx;
+ sed_cmd->end_line_orig = sed_cmd->end_line;
}
/* skip whitespace before the command */
@@ -851,7 +892,10 @@ static sed_cmd_t *branch_to(char *label)
sed_cmd_t *sed_cmd;
for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
- if (sed_cmd->cmd == ':' && sed_cmd->string && !strcmp(sed_cmd->string, label)) {
+ if (sed_cmd->cmd == ':'
+ && sed_cmd->string
+ && strcmp(sed_cmd->string, label) == 0
+ ) {
return sed_cmd;
}
}
@@ -915,13 +959,22 @@ static void puts_maybe_newline(char *s, FILE *file, char *last_puts_char, char l
*last_puts_char = lpc;
}
-static void flush_append(char *last_puts_char, char last_gets_char)
+static void flush_append(char *last_puts_char)
{
char *data;
/* Output appended lines. */
- while ((data = (char *)llist_pop(&G.append_head))) {
- puts_maybe_newline(data, G.nonstdout, last_puts_char, last_gets_char);
+ while ((data = (char *)llist_pop(&G.append_head)) != NULL) {
+ /* Append command does not respect "nonterminated-ness"
+ * of last line. Try this:
+ * $ echo -n "woot" | sed -e '/woot/a woo' -
+ * woot
+ * woo
+ * (both lines are terminated with \n)
+ * Therefore we do not propagate "last_gets_char" here,
+ * pass '\n' instead:
+ */
+ puts_maybe_newline(data, G.nonstdout, last_puts_char, '\n');
free(data);
}
}
@@ -929,13 +982,13 @@ static void flush_append(char *last_puts_char, char last_gets_char)
/* Get next line of input from G.input_file_list, flushing append buffer and
* noting if we ran out of files without a newline on the last line we read.
*/
-static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets_char)
+static char *get_next_line(char *gets_char, char *last_puts_char)
{
char *temp = NULL;
int len;
char gc;
- flush_append(last_puts_char, last_gets_char);
+ flush_append(last_puts_char);
/* will be returned if last line in the file
* doesn't end with either '\n' or '\0' */
@@ -1013,7 +1066,7 @@ static void process_files(void)
int substituted;
/* Prime the pump */
- next_line = get_next_line(&next_gets_char, &last_puts_char, '\n' /*last_gets_char*/);
+ next_line = get_next_line(&next_gets_char, &last_puts_char);
/* Go through every line in each file */
again:
@@ -1027,7 +1080,7 @@ static void process_files(void)
/* Read one line in advance so we can act on the last line,
* the '$' address */
- next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
+ next_line = get_next_line(&next_gets_char, &last_puts_char);
linenum++;
/* For every line, go through all the commands */
@@ -1086,10 +1139,19 @@ static void process_files(void)
/* Is this line the end of the current match? */
if (matched) {
+ if (sed_cmd->end_line <= -2) {
+ /* address2 is +N, i.e. N lines from beg_line */
+ sed_cmd->end_line = linenum + (-sed_cmd->end_line - 2);
+ }
/* once matched, "n,xxx" range is dead, disabling it */
if (sed_cmd->beg_line > 0) {
sed_cmd->beg_line = -2;
}
+ dbg("end1:%d", sed_cmd->end_line ? sed_cmd->end_line == -1
+ ? !next_line : (sed_cmd->end_line <= linenum)
+ : !sed_cmd->end_match);
+ dbg("end2:%d", sed_cmd->end_match && old_matched
+ && !regexec(sed_cmd->end_match,pattern_space, 0, NULL, 0));
sed_cmd->in_match = !(
/* has the ending line come, or is this a single address command? */
(sed_cmd->end_line
@@ -1241,16 +1303,17 @@ static void process_files(void)
case 'n':
if (!G.be_quiet)
sed_puts(pattern_space, last_gets_char);
- if (next_line) {
- free(pattern_space);
- pattern_space = next_line;
- last_gets_char = next_gets_char;
- next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
- substituted = 0;
- linenum++;
- break;
+ if (next_line == NULL) {
+ /* If no next line, jump to end of script and exit. */
+ goto discard_line;
}
- /* fall through */
+ free(pattern_space);
+ pattern_space = next_line;
+ last_gets_char = next_gets_char;
+ next_line = get_next_line(&next_gets_char, &last_puts_char);
+ substituted = 0;
+ linenum++;
+ break;
/* Quit. End of script, end of input. */
case 'q':
@@ -1281,7 +1344,7 @@ static void process_files(void)
pattern_space[len] = '\n';
strcpy(pattern_space + len+1, next_line);
last_gets_char = next_gets_char;
- next_line = get_next_line(&next_gets_char, &last_puts_char, last_gets_char);
+ next_line = get_next_line(&next_gets_char, &last_puts_char);
linenum++;
break;
}
@@ -1385,7 +1448,7 @@ static void process_files(void)
/* Delete and such jump here. */
discard_line:
- flush_append(&last_puts_char, last_gets_char);
+ flush_append(&last_puts_char /*,last_gets_char*/);
free(pattern_space);
goto again;
@@ -1444,8 +1507,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
/* do normal option parsing */
opt_e = opt_f = NULL;
opt_i = NULL;
- opt_complementary = "e::f::" /* can occur multiple times */
- "nn"; /* count -n */
+ opt_complementary = "nn"; /* count -n */
IF_LONG_OPTS(applet_long_options = sed_longopts);
@@ -1454,7 +1516,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
* GNU sed 4.2.1 mentions it in neither --help
* nor manpage, but does recognize it.
*/
- opt = getopt32(argv, "i::rEne:f:", &opt_i, &opt_e, &opt_f,
+ opt = getopt32(argv, "i::rEne:*f:*", &opt_i, &opt_e, &opt_f,
&G.be_quiet); /* counter for -n */
//argc -= optind;
argv += optind;
@@ -1471,12 +1533,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
while (opt_f) { // -f
char *line;
FILE *cmdfile;
- cmdfile = xfopen_for_read(llist_pop(&opt_f));
+ cmdfile = xfopen_stdin(llist_pop(&opt_f));
while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
add_cmd(line);
free(line);
}
- fclose(cmdfile);
+ fclose_if_not_stdin(cmdfile);
}
/* if we didn't get a pattern from -e or -f, use argv[0] */
if (!(opt & 0x30)) {
@@ -1519,12 +1581,16 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
/* -i: process each FILE separately: */
+ if (stat(*argv, &statbuf) != 0) {
+ bb_simple_perror_msg(*argv);
+ G.exitcode = EXIT_FAILURE;
+ G.current_input_file++;
+ continue;
+ }
G.outname = xasprintf("%sXXXXXX", *argv);
nonstdoutfd = xmkstemp(G.outname);
G.nonstdout = xfdopen_for_write(nonstdoutfd);
-
/* Set permissions/owner of output file */
- stat(*argv, &statbuf);
/* chmod'ing AFTER chown would preserve suid/sgid bits,
* but GNU sed 4.2.1 does not preserve them either */
fchmod(nonstdoutfd, statbuf.st_mode);
@@ -1544,9 +1610,10 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
free(G.outname);
G.outname = NULL;
- /* Re-enable disabled range matches */
+ /* Fix disabled range matches and mangled ",+N" ranges */
for (sed_cmd = G.sed_cmd_head; sed_cmd; sed_cmd = sed_cmd->next) {
sed_cmd->beg_line = sed_cmd->beg_line_orig;
+ sed_cmd->end_line = sed_cmd->end_line_orig;
}
}
/* Here, to handle "sed 'cmds' nonexistent_file" case we did:
diff --git a/editors/vi.c b/editors/vi.c
index ccf53a9..432fba8 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -17,7 +17,6 @@
* it would be easier to change the mark when add/delete lines
* More intelligence in refresh()
* ":r !cmd" and "!cmd" to filter text through an external command
- * A true "undo" facility
* An "ex" line oriented mode- maybe using "cmdedit"
*/
@@ -136,6 +135,36 @@
//config: cursor position using "ESC [ 6 n" escape sequence, then read stdin.
//config:
//config: This is not clean but helps a lot on serial lines and such.
+//config:config FEATURE_VI_UNDO
+//config: bool "Support undo command 'u'"
+//config: default y
+//config: depends on VI
+//config: help
+//config: Support the 'u' command to undo insertion, deletion, and replacement
+//config: of text.
+//config:config FEATURE_VI_UNDO_QUEUE
+//config: bool "Enable undo operation queuing"
+//config: default y
+//config: depends on FEATURE_VI_UNDO
+//config: help
+//config: The vi undo functions can use an intermediate queue to greatly lower
+//config: malloc() calls and overhead. When the maximum size of this queue is
+//config: reached, the contents of the queue are committed to the undo stack.
+//config: This increases the size of the undo code and allows some undo
+//config: operations (especially un-typing/backspacing) to be far more useful.
+//config:config FEATURE_VI_UNDO_QUEUE_MAX
+//config: int "Maximum undo character queue size"
+//config: default 256
+//config: range 32 65536
+//config: depends on FEATURE_VI_UNDO_QUEUE
+//config: help
+//config: This option sets the number of bytes used at runtime for the queue.
+//config: Smaller values will create more undo objects and reduce the amount
+//config: of typed or backspaced characters that are grouped into one undo
+//config: operation; larger values increase the potential size of each undo
+//config: and will generally malloc() larger objects and less frequently.
+//config: Unless you want more (or less) frequent "undo points" while typing,
+//config: you should probably leave this unchanged.
//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
@@ -222,7 +251,7 @@ enum {
// cmds modifying text[]
// vda: removed "aAiIs" as they switch us into insert mode
// and remembering input for replay after them makes no sense
-static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
+static const char modifying_cmds[] ALIGN1 = "cCdDJoOpPrRxX<>~";
#endif
enum {
@@ -277,8 +306,8 @@ struct globals {
smallint editing; // >0 while we are editing a file
// [code audit says "can be 0, 1 or 2 only"]
smallint cmd_mode; // 0=command 1=insert 2=replace
- int file_modified; // buffer contents changed (counter, not flag!)
- int last_file_modified; // = -1;
+ int modified_count; // buffer contents changed if !0
+ int last_modified_count; // = -1;
int save_argc; // how many file names on cmd line
int cmdcnt; // repetition count
int rows, columns; // the terminal screen is this size
@@ -347,6 +376,41 @@ struct globals {
char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
+#if ENABLE_FEATURE_VI_UNDO
+// undo_push() operations
+#define UNDO_INS 0
+#define UNDO_DEL 1
+#define UNDO_INS_CHAIN 2
+#define UNDO_DEL_CHAIN 3
+// UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG
+#define UNDO_QUEUED_FLAG 4
+#define UNDO_INS_QUEUED 4
+#define UNDO_DEL_QUEUED 5
+#define UNDO_USE_SPOS 32
+#define UNDO_EMPTY 64
+// Pass-through flags for functions that can be undone
+#define NO_UNDO 0
+#define ALLOW_UNDO 1
+#define ALLOW_UNDO_CHAIN 2
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+#define ALLOW_UNDO_QUEUED 3
+ char undo_queue_state;
+ int undo_q;
+ char *undo_queue_spos; // Start position of queued operation
+ char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
+# else
+// If undo queuing disabled, don't invoke the missing queue logic
+#define ALLOW_UNDO_QUEUED 1
+# endif
+
+ struct undo_object {
+ struct undo_object *prev; // Linking back avoids list traversal (LIFO)
+ int start; // Offset where the data should be restored/deleted
+ int length; // total data size
+ uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped
+ char undo_text[1]; // text that was deleted (if deletion)
+ } *undo_stack_tail;
+#endif /* ENABLE_FEATURE_VI_UNDO */
};
#define G (*ptr_to_globals)
#define text (G.text )
@@ -358,8 +422,8 @@ struct globals {
#define vi_setops (G.vi_setops )
#define editing (G.editing )
#define cmd_mode (G.cmd_mode )
-#define file_modified (G.file_modified )
-#define last_file_modified (G.last_file_modified )
+#define modified_count (G.modified_count )
+#define last_modified_count (G.last_modified_count)
#define save_argc (G.save_argc )
#define cmdcnt (G.cmdcnt )
#define rows (G.rows )
@@ -408,15 +472,24 @@ struct globals {
#define last_modifying_cmd (G.last_modifying_cmd )
#define get_input_line__buf (G.get_input_line__buf)
+#if ENABLE_FEATURE_VI_UNDO
+#define undo_stack_tail (G.undo_stack_tail )
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+#define undo_queue_state (G.undo_queue_state)
+#define undo_q (G.undo_q )
+#define undo_queue (G.undo_queue )
+#define undo_queue_spos (G.undo_queue_spos )
+# endif
+#endif
+
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
- last_file_modified = -1; \
+ last_modified_count = -1; \
/* "" but has space for 2 chars: */ \
IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
} while (0)
-static int init_text_buffer(char *); // init from file or create new
static void edit_file(char *); // edit one file
static void do_cmd(int); // execute a command
static int next_tabstop(int);
@@ -427,7 +500,7 @@ static char *prev_line(char *); // return pointer to prev line B-o-l
static char *next_line(char *); // return pointer to next line B-o-l
static char *end_screen(void); // get pointer to last char on screen
static int count_lines(char *, char *); // count line from start to stop
-static char *find_line(int); // find begining of line #li
+static char *find_line(int); // find beginning of line #li
static char *move_to_col(char *, int); // move "p" to column l
static void dot_left(void); // move dot left- dont leave line
static void dot_right(void); // move dot right- dont leave line
@@ -437,10 +510,12 @@ static void dot_next(void); // move dot to next line B-o-l
static void dot_prev(void); // move dot to prev line B-o-l
static void dot_scroll(int, int); // move the screen up or down
static void dot_skip_over_ws(void); // move dot pat WS
-static void dot_delete(void); // delete the char at 'dot'
static char *bound_dot(char *); // make sure text[0] <= P < "end"
static char *new_screen(int, int); // malloc virtual screen memory
-static char *char_insert(char *, char); // insert the char c at 'p'
+#if !ENABLE_FEATURE_VI_UNDO
+#define char_insert(a,b,c) char_insert(a,b)
+#endif
+static char *char_insert(char *, char, int); // insert the char c at 'p'
// might reallocate text[]! use p += stupid_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
@@ -448,11 +523,17 @@ static int find_range(char **, char **, char); // return pointers for an object
static int st_test(char *, int, int, char *); // helper for skip_thing()
static char *skip_thing(char *, int, int, int); // skip some object
static char *find_pair(char *, char); // find matching pair () [] {}
-static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
+#if !ENABLE_FEATURE_VI_UNDO
+#define text_hole_delete(a,b,c) text_hole_delete(a,b)
+#endif
+static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole
// might reallocate text[]! use p += text_hole_make(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
-static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
+#if !ENABLE_FEATURE_VI_UNDO
+#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
+#endif
+static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete
static void show_help(void); // display some help info
static void rawmode(void); // set "raw" mode on tty
static void cookmode(void); // return to "cooked" mode on tty
@@ -460,10 +541,6 @@ static void cookmode(void); // return to "cooked" mode on tty
static int mysleep(int);
static int readit(void); // read (maybe cursor) key from stdin
static int get_one_char(void); // read 1 char from stdin
-static int file_size(const char *); // what is the byte size of "fn"
-#if !ENABLE_FEATURE_VI_READONLY
-#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
-#endif
// file_insert might reallocate text[]!
static int file_insert(const char *, char *, int);
static int file_write(char *, char *, char *);
@@ -485,8 +562,7 @@ static void redraw(int); // force a full screen refresh
static char* format_line(char* /*, int*/);
static void refresh(int); // update the terminal from screen[]
-static void Indicate_Error(void); // use flash or beep to indicate error
-#define indicate_error(c) Indicate_Error()
+static void indicate_error(void); // use flash or beep to indicate error
static void Hit_Return(void);
#if ENABLE_FEATURE_VI_SEARCH
@@ -495,8 +571,8 @@ static char *char_search(char *, const char *, int, int); // search for pattern
#if ENABLE_FEATURE_VI_COLON
static char *get_one_address(char *, int *); // get colon addr, if present
static char *get_address(char *, int *, int *); // get two colon addrs, if present
-static void colon(char *); // execute the "colon" mode cmds
#endif
+static void colon(char *); // execute the "colon" mode cmds
#if ENABLE_FEATURE_VI_USE_SIGNALS
static void winch_sig(int); // catch window size changes
static void suspend_sig(int); // catch ctrl-Z
@@ -514,20 +590,36 @@ static void showmatching(char *); // show the matching pair () [] {}
#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
// might reallocate text[]! use p += string_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t string_insert(char *, const char *); // insert the string at 'p'
+# if !ENABLE_FEATURE_VI_UNDO
+#define string_insert(a,b,c) string_insert(a,b)
+# endif
+static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p'
#endif
#if ENABLE_FEATURE_VI_YANKMARK
static char *text_yank(char *, char *, int); // save copy of "p" into a register
static char what_reg(void); // what is letter of current YDreg
static void check_context(char); // remember context for '' command
#endif
+#if ENABLE_FEATURE_VI_UNDO
+static void flush_undo_data(void);
+static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack
+static void undo_pop(void); // Undo the last operation
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+static void undo_queue_commit(void); // Flush any queued objects to the undo stack
+# else
+# define undo_queue_commit() ((void)0)
+# endif
+#else
+#define flush_undo_data() ((void)0)
+#define undo_queue_commit() ((void)0)
+#endif
+
#if ENABLE_FEATURE_VI_CRASHME
static void crash_dummy();
static void crash_test();
static int crashme = 0;
#endif
-
static void write1(const char *out)
{
fputs(out, stdout);
@@ -540,6 +632,14 @@ int vi_main(int argc, char **argv)
INIT_G();
+#if ENABLE_FEATURE_VI_UNDO
+ /* undo_stack_tail = NULL; - already is */
+#if ENABLE_FEATURE_VI_UNDO_QUEUE
+ undo_queue_state = UNDO_EMPTY;
+ /* undo_q = 0; - already is */
+#endif
+#endif
+
#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
my_pid = getpid();
#endif
@@ -618,30 +718,29 @@ int vi_main(int argc, char **argv)
static int init_text_buffer(char *fn)
{
int rc;
- int size = file_size(fn); // file size. -1 means does not exist.
+
+ flush_undo_data();
+ modified_count = 0;
+ last_modified_count = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+ /* init the marks */
+ memset(mark, 0, sizeof(mark));
+#endif
/* allocate/reallocate text buffer */
free(text);
- text_size = size + 10240;
+ text_size = 10240;
screenbegin = dot = end = text = xzalloc(text_size);
if (fn != current_filename) {
free(current_filename);
current_filename = xstrdup(fn);
}
- if (size < 0) {
- // file dont exist. Start empty buf with dummy line
- char_insert(text, '\n');
- rc = 0;
- } else {
- rc = file_insert(fn, text, 1);
+ rc = file_insert(fn, text, 1);
+ if (rc < 0) {
+ // file doesnt exist. Start empty buf with dummy line
+ char_insert(text, '\n', NO_UNDO);
}
- file_modified = 0;
- last_file_modified = -1;
-#if ENABLE_FEATURE_VI_YANKMARK
- /* init the marks. */
- memset(mark, 0, sizeof(mark));
-#endif
return rc;
}
@@ -758,7 +857,7 @@ static void edit_file(char *fn)
crash_dummy(); // generate a random command
} else {
crashme = 0;
- string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
+ string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string
dot = text;
refresh(FALSE);
}
@@ -911,13 +1010,71 @@ static void setops(const char *args, const char *opname, int flg_no,
}
#endif
+#endif /* FEATURE_VI_COLON */
+
// buf must be no longer than MAX_INPUT_LEN!
static void colon(char *buf)
{
+#if !ENABLE_FEATURE_VI_COLON
+ /* Simple ":cmd" handler with minimal set of commands */
+ char *p = buf;
+ int cnt;
+
+ if (*p == ':')
+ p++;
+ cnt = strlen(p);
+ if (cnt == 0)
+ return;
+ if (strncmp(p, "quit", cnt) == 0
+ || strncmp(p, "q!", cnt) == 0
+ ) {
+ if (modified_count && p[1] != '!') {
+ status_line_bold("No write since last change (:%s! overrides)", p);
+ } else {
+ editing = 0;
+ }
+ return;
+ }
+ if (strncmp(p, "write", cnt) == 0
+ || strncmp(p, "wq", cnt) == 0
+ || strncmp(p, "wn", cnt) == 0
+ || (p[0] == 'x' && !p[1])
+ ) {
+ cnt = file_write(current_filename, text, end - 1);
+ if (cnt < 0) {
+ if (cnt == -1)
+ status_line_bold("Write error: %s", strerror(errno));
+ } else {
+ modified_count = 0;
+ last_modified_count = -1;
+ status_line("'%s' %dL, %dC",
+ current_filename,
+ count_lines(text, end - 1), cnt
+ );
+ if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
+ || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
+ ) {
+ editing = 0;
+ }
+ }
+ return;
+ }
+ if (strncmp(p, "file", cnt) == 0) {
+ last_status_cksum = 0; // force status update
+ return;
+ }
+ if (sscanf(p, "%d", &cnt) > 0) {
+ dot = find_line(cnt);
+ dot_skip_over_ws();
+ return;
+ }
+ not_implemented(p);
+#else
+
char c, *orig_buf, *buf1, *q, *r;
char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
- int i, l, li, ch, b, e;
- int useforce, forced = FALSE;
+ int i, l, li, b, e;
+ int useforce;
// :3154 // if (-e line 3154) goto it else stay put
// :4,33w! foo // write a portion of buffer to file "foo"
@@ -939,7 +1096,7 @@ static void colon(char *buf)
if (*buf == ':')
buf++; // move past the ':'
- li = ch = i = 0;
+ li = i = 0;
b = e = -1;
q = text; // assume 1,$ for the range
r = end - 1;
@@ -1017,11 +1174,13 @@ static void colon(char *buf)
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
- dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
+ dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines
dot_skip_over_ws();
} else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
+ int size;
+
// don't edit, if the current file has been modified
- if (file_modified && !useforce) {
+ if (modified_count && !useforce) {
status_line_bold("No write since last change (:%s! overrides)", cmd);
goto ret;
}
@@ -1037,8 +1196,7 @@ static void colon(char *buf)
goto ret;
}
- if (init_text_buffer(fn) < 0)
- goto ret;
+ size = init_text_buffer(fn);
#if ENABLE_FEATURE_VI_YANKMARK
if (Ureg >= 0 && Ureg < 28) {
@@ -1054,12 +1212,14 @@ static void colon(char *buf)
li = count_lines(text, end - 1);
status_line("'%s'%s"
IF_FEATURE_VI_READONLY("%s")
- " %dL, %dC", current_filename,
- (file_size(fn) < 0 ? " [New file]" : ""),
+ " %dL, %dC",
+ current_filename,
+ (size < 0 ? " [New file]" : ""),
IF_FEATURE_VI_READONLY(
((readonly_mode) ? " [Readonly]" : ""),
)
- li, ch);
+ li, (int)(end - text)
+ );
} else if (strncmp(cmd, "file", i) == 0) { // what File is this
if (b != -1 || e != -1) {
status_line_bold("No address allowed on this command");
@@ -1124,7 +1284,7 @@ static void colon(char *buf)
goto ret;
}
// don't exit if the file been modified
- if (file_modified) {
+ if (modified_count) {
status_line_bold("No write since last change (:%s! overrides)", cmd);
goto ret;
}
@@ -1148,6 +1308,8 @@ static void colon(char *buf)
}
editing = 0;
} else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
+ int size;
+
fn = args;
if (!fn[0]) {
status_line_bold("No filename given");
@@ -1157,30 +1319,35 @@ static void colon(char *buf)
q = begin_line(dot); // assume "dot"
}
// read after current line- unless user said ":0r foo"
- if (b != 0)
+ if (b != 0) {
q = next_line(q);
+ // read after last line
+ if (q == end-1)
+ ++q;
+ }
{ // dance around potentially-reallocated text[]
uintptr_t ofs = q - text;
- ch = file_insert(fn, q, 0);
+ size = file_insert(fn, q, 0);
q = text + ofs;
}
- if (ch < 0)
+ if (size < 0)
goto ret; // nothing was inserted
// how many lines in text[]?
- li = count_lines(q, q + ch - 1);
+ li = count_lines(q, q + size - 1);
status_line("'%s'"
IF_FEATURE_VI_READONLY("%s")
- " %dL, %dC", fn,
+ " %dL, %dC",
+ fn,
IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
- li, ch);
- if (ch > 0) {
+ li, size
+ );
+ if (size > 0) {
// if the insert is before "dot" then we need to update
if (q <= dot)
- dot += ch;
- /*file_modified++; - done by file_insert */
+ dot += size;
}
} else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
- if (file_modified && !useforce) {
+ if (modified_count && !useforce) {
status_line_bold("No write since last change (:%s! overrides)", cmd);
} else {
// reset the filenames to edit
@@ -1237,6 +1404,9 @@ static void colon(char *buf)
char *F, *R, *flags;
size_t len_F, len_R;
int gflag; // global replace flag
+#if ENABLE_FEATURE_VI_UNDO
+ int dont_chain_first_item = ALLOW_UNDO;
+#endif
// F points to the "find" pattern
// R points to the "replace" pattern
@@ -1271,9 +1441,13 @@ static void colon(char *buf)
if (found) {
uintptr_t bias;
// we found the "find" pattern - delete it
- text_hole_delete(found, found + len_F - 1);
- // inset the "replace" patern
- bias = string_insert(found, R); // insert the string
+ // For undo support, the first item should not be chained
+ text_hole_delete(found, found + len_F - 1, dont_chain_first_item);
+#if ENABLE_FEATURE_VI_UNDO
+ dont_chain_first_item = ALLOW_UNDO_CHAIN;
+#endif
+ // insert the "replace" patern
+ bias = string_insert(found, R, ALLOW_UNDO_CHAIN);
found += bias;
ls += bias;
/*q += bias; - recalculated anyway */
@@ -1295,6 +1469,9 @@ static void colon(char *buf)
|| strncmp(cmd, "wn", i) == 0
|| (cmd[0] == 'x' && !cmd[1])
) {
+ int size;
+ //int forced = FALSE;
+
// is there a file name to write to?
if (args[0]) {
fn = args;
@@ -1307,34 +1484,33 @@ static void colon(char *buf)
#endif
// how many lines in text[]?
li = count_lines(q, r);
- ch = r - q + 1;
- // see if file exists- if not, its just a new file request
- if (useforce) {
+ size = r - q + 1;
+ //if (useforce) {
// if "fn" is not write-able, chmod u+w
// sprintf(syscmd, "chmod u+w %s", fn);
// system(syscmd);
- forced = TRUE;
- }
+ // forced = TRUE;
+ //}
l = file_write(fn, q, r);
- if (useforce && forced) {
+ //if (useforce && forced) {
// chmod u-w
// sprintf(syscmd, "chmod u-w %s", fn);
// system(syscmd);
- forced = FALSE;
- }
+ // forced = FALSE;
+ //}
if (l < 0) {
if (l == -1)
status_line_bold_errno(fn);
} else {
status_line("'%s' %dL, %dC", fn, li, l);
- if (q == text && r == end - 1 && l == ch) {
- file_modified = 0;
- last_file_modified = -1;
+ if (q == text && r == end - 1 && l == size) {
+ modified_count = 0;
+ last_modified_count = -1;
}
if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
|| cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
)
- && l == ch
+ && l == size
) {
editing = 0;
}
@@ -1361,9 +1537,8 @@ static void colon(char *buf)
colon_s_fail:
status_line(":s expression missing delimiters");
#endif
-}
-
#endif /* FEATURE_VI_COLON */
+}
static void Hit_Return(void)
{
@@ -1510,10 +1685,10 @@ static char *dollar_line(char *p) // return pointer to just before NL line
static char *prev_line(char *p) // return pointer first char prev line
{
- p = begin_line(p); // goto begining of cur line
+ p = begin_line(p); // goto beginning of cur line
if (p > text && p[-1] == '\n')
p--; // step to prev line
- p = begin_line(p); // goto begining of prev line
+ p = begin_line(p); // goto beginning of prev line
return p;
}
@@ -1561,7 +1736,7 @@ static int count_lines(char *start, char *stop)
return cnt;
}
-static char *find_line(int li) // find begining of line #li
+static char *find_line(int li) // find beginning of line #li
{
char *q;
@@ -1574,23 +1749,27 @@ static char *find_line(int li) // find begining of line #li
//----- Dot Movement Routines ----------------------------------
static void dot_left(void)
{
+ undo_queue_commit();
if (dot > text && dot[-1] != '\n')
dot--;
}
static void dot_right(void)
{
+ undo_queue_commit();
if (dot < end - 1 && *dot != '\n')
dot++;
}
static void dot_begin(void)
{
+ undo_queue_commit();
dot = begin_line(dot); // return pointer to first char cur line
}
static void dot_end(void)
{
+ undo_queue_commit();
dot = end_line(dot); // return pointer to last char cur line
}
@@ -1616,11 +1795,13 @@ static char *move_to_col(char *p, int l)
static void dot_next(void)
{
+ undo_queue_commit();
dot = next_line(dot);
}
static void dot_prev(void)
{
+ undo_queue_commit();
dot = prev_line(dot);
}
@@ -1628,6 +1809,7 @@ static void dot_scroll(int cnt, int dir)
{
char *q;
+ undo_queue_commit();
for (; cnt > 0; cnt--) {
if (dir < 0) {
// scroll Backwards
@@ -1655,20 +1837,15 @@ static void dot_skip_over_ws(void)
dot++;
}
-static void dot_delete(void) // delete the char at 'dot'
-{
- text_hole_delete(dot, dot);
-}
-
static char *bound_dot(char *p) // make sure text[0] <= P < "end"
{
if (p >= end && end > text) {
p = end - 1;
- indicate_error('1');
+ indicate_error();
}
if (p < text) {
p = text;
- indicate_error('2');
+ indicate_error();
}
return p;
}
@@ -1806,17 +1983,34 @@ static char *char_search(char *p, const char *pat, int dir, int range)
#endif /* FEATURE_VI_SEARCH */
-static char *char_insert(char *p, char c) // insert the char c at 'p'
+static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
{
if (c == 22) { // Is this an ctrl-V?
p += stupid_insert(p, '^'); // use ^ to indicate literal next
refresh(FALSE); // show the ^
c = get_one_char();
*p = c;
+#if ENABLE_FEATURE_VI_UNDO
+ switch (undo) {
+ case ALLOW_UNDO:
+ undo_push(p, 1, UNDO_INS);
+ break;
+ case ALLOW_UNDO_CHAIN:
+ undo_push(p, 1, UNDO_INS_CHAIN);
+ break;
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+ case ALLOW_UNDO_QUEUED:
+ undo_push(p, 1, UNDO_INS_QUEUED);
+ break;
+# endif
+ }
+#else
+ modified_count++;
+#endif /* ENABLE_FEATURE_VI_UNDO */
p++;
- file_modified++;
} else if (c == 27) { // Is this an ESC?
cmd_mode = 0;
+ undo_queue_commit();
cmdcnt = 0;
end_cmd_q(); // stop adding to q
last_status_cksum = 0; // force status update
@@ -1824,26 +2018,39 @@ static char *char_insert(char *p, char c) // insert the char c at 'p'
p--;
}
} else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
- // 123456789
- if ((p[-1] != '\n') && (dot>text)) {
+ if (p > text) {
p--;
- p = text_hole_delete(p, p); // shrink buffer 1 char
+ p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
}
} else {
-#if ENABLE_FEATURE_VI_SETOPTS
// insert a char into text[]
- char *sp; // "save p"
-#endif
-
if (c == 13)
c = '\n'; // translate \r to \n
-#if ENABLE_FEATURE_VI_SETOPTS
- sp = p; // remember addr of insert
-#endif
+#if ENABLE_FEATURE_VI_UNDO
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+ if (c == '\n')
+ undo_queue_commit();
+# endif
+ switch (undo) {
+ case ALLOW_UNDO:
+ undo_push(p, 1, UNDO_INS);
+ break;
+ case ALLOW_UNDO_CHAIN:
+ undo_push(p, 1, UNDO_INS_CHAIN);
+ break;
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+ case ALLOW_UNDO_QUEUED:
+ undo_push(p, 1, UNDO_INS_QUEUED);
+ break;
+# endif
+ }
+#else
+ modified_count++;
+#endif /* ENABLE_FEATURE_VI_UNDO */
p += 1 + stupid_insert(p, c); // insert the char
#if ENABLE_FEATURE_VI_SETOPTS
- if (showmatch && strchr(")]}", *sp) != NULL) {
- showmatching(sp);
+ if (showmatch && strchr(")]}", c) != NULL) {
+ showmatching(p - 1);
}
if (autoindent && c == '\n') { // auto indent the new line
char *q;
@@ -1855,6 +2062,9 @@ static char *char_insert(char *p, char c) // insert the char c at 'p'
bias = text_hole_make(p, len);
p += bias;
q += bias;
+#if ENABLE_FEATURE_VI_UNDO
+ undo_push(p, len, UNDO_INS);
+#endif
memcpy(p, q, len);
p += len;
}
@@ -1872,7 +2082,6 @@ static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at
bias = text_hole_make(p, 1);
p += bias;
*p = c;
- //file_modified++; - done by text_hole_make()
return bias;
}
@@ -2001,34 +2210,32 @@ static char *skip_thing(char *p, int linecnt, int dir, int type)
}
// find matching char of pair () [] {}
+// will crash if c is not one of these
static char *find_pair(char *p, const char c)
{
- char match, *q;
+ const char *braces = "()[]{}";
+ char match;
int dir, level;
- match = ')';
+ dir = strchr(braces, c) - braces;
+ dir ^= 1;
+ match = braces[dir];
+ dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */
+
+ // look for match, count levels of pairs (( ))
level = 1;
- dir = 1; // assume forward
- switch (c) {
- case '(': match = ')'; break;
- case '[': match = ']'; break;
- case '{': match = '}'; break;
- case ')': match = '('; dir = -1; break;
- case ']': match = '['; dir = -1; break;
- case '}': match = '{'; dir = -1; break;
- }
- for (q = p + dir; text <= q && q < end; q += dir) {
- // look for match, count levels of pairs (( ))
- if (*q == c)
+ for (;;) {
+ p += dir;
+ if (p < text || p >= end)
+ return NULL;
+ if (*p == c)
level++; // increase pair levels
- if (*q == match)
+ if (*p == match) {
level--; // reduce pair level
- if (level == 0)
- break; // found matching pair
+ if (level == 0)
+ return p; // found matching pair
+ }
}
- if (level != 0)
- q = NULL; // indicate no match
- return q;
}
#if ENABLE_FEATURE_VI_SETOPTS
@@ -2040,7 +2247,7 @@ static void showmatching(char *p)
// we found half of a pair
q = find_pair(p, *p); // get loc of matching char
if (q == NULL) {
- indicate_error('3'); // no matching char
+ indicate_error(); // no matching char
} else {
// "q" now points to matching pair
save_dot = dot; // remember where we are
@@ -2053,6 +2260,199 @@ static void showmatching(char *p)
}
#endif /* FEATURE_VI_SETOPTS */
+#if ENABLE_FEATURE_VI_UNDO
+static void flush_undo_data(void)
+{
+ struct undo_object *undo_entry;
+
+ while (undo_stack_tail) {
+ undo_entry = undo_stack_tail;
+ undo_stack_tail = undo_entry->prev;
+ free(undo_entry);
+ }
+}
+
+// Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
+static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack
+{
+ struct undo_object *undo_entry;
+
+ // "u_type" values
+ // UNDO_INS: insertion, undo will remove from buffer
+ // UNDO_DEL: deleted text, undo will restore to buffer
+ // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete
+ // The CHAIN operations are for handling multiple operations that the user
+ // performs with a single action, i.e. REPLACE mode or find-and-replace commands
+ // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
+ // for the INS/DEL operation. The raw values should be equal to the values of
+ // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG
+
+#if ENABLE_FEATURE_VI_UNDO_QUEUE
+ // This undo queuing functionality groups multiple character typing or backspaces
+ // into a single large undo object. This greatly reduces calls to malloc() for
+ // single-character operations while typing and has the side benefit of letting
+ // an undo operation remove chunks of text rather than a single character.
+ switch (u_type) {
+ case UNDO_EMPTY: // Just in case this ever happens...
+ return;
+ case UNDO_DEL_QUEUED:
+ if (length != 1)
+ return; // Only queue single characters
+ switch (undo_queue_state) {
+ case UNDO_EMPTY:
+ undo_queue_state = UNDO_DEL;
+ case UNDO_DEL:
+ undo_queue_spos = src;
+ undo_q++;
+ undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src;
+ // If queue is full, dump it into an object
+ if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
+ undo_queue_commit();
+ return;
+ case UNDO_INS:
+ // Switch from storing inserted text to deleted text
+ undo_queue_commit();
+ undo_push(src, length, UNDO_DEL_QUEUED);
+ return;
+ }
+ break;
+ case UNDO_INS_QUEUED:
+ if (length != 1)
+ return;
+ switch (undo_queue_state) {
+ case UNDO_EMPTY:
+ undo_queue_state = UNDO_INS;
+ undo_queue_spos = src;
+ case UNDO_INS:
+ undo_q++; // Don't need to save any data for insertions
+ if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
+ undo_queue_commit();
+ return;
+ case UNDO_DEL:
+ // Switch from storing deleted text to inserted text
+ undo_queue_commit();
+ undo_push(src, length, UNDO_INS_QUEUED);
+ return;
+ }
+ break;
+ }
+#else
+ // If undo queuing is disabled, ignore the queuing flag entirely
+ u_type = u_type & ~UNDO_QUEUED_FLAG;
+#endif
+
+ // Allocate a new undo object
+ if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) {
+ // For UNDO_DEL objects, save deleted text
+ if ((src + length) == end)
+ length--;
+ // If this deletion empties text[], strip the newline. When the buffer becomes
+ // zero-length, a newline is added back, which requires this to compensate.
+ undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length);
+ memcpy(undo_entry->undo_text, src, length);
+ } else {
+ undo_entry = xzalloc(sizeof(*undo_entry));
+ }
+ undo_entry->length = length;
+#if ENABLE_FEATURE_VI_UNDO_QUEUE
+ if ((u_type & UNDO_USE_SPOS) != 0) {
+ undo_entry->start = undo_queue_spos - text; // use start position from queue
+ } else {
+ undo_entry->start = src - text; // use offset from start of text buffer
+ }
+ u_type = (u_type & ~UNDO_USE_SPOS);
+#else
+ undo_entry->start = src - text;
+#endif
+ undo_entry->u_type = u_type;
+
+ // Push it on undo stack
+ undo_entry->prev = undo_stack_tail;
+ undo_stack_tail = undo_entry;
+ modified_count++;
+}
+
+static void undo_pop(void) // Undo the last operation
+{
+ int repeat;
+ char *u_start, *u_end;
+ struct undo_object *undo_entry;
+
+ // Commit pending undo queue before popping (should be unnecessary)
+ undo_queue_commit();
+
+ undo_entry = undo_stack_tail;
+ // Check for an empty undo stack
+ if (!undo_entry) {
+ status_line("Already at oldest change");
+ return;
+ }
+
+ switch (undo_entry->u_type) {
+ case UNDO_DEL:
+ case UNDO_DEL_CHAIN:
+ // make hole and put in text that was deleted; deallocate text
+ u_start = text + undo_entry->start;
+ text_hole_make(u_start, undo_entry->length);
+ memcpy(u_start, undo_entry->undo_text, undo_entry->length);
+ status_line("Undo [%d] %s %d chars at position %d",
+ modified_count, "restored",
+ undo_entry->length, undo_entry->start
+ );
+ break;
+ case UNDO_INS:
+ case UNDO_INS_CHAIN:
+ // delete what was inserted
+ u_start = undo_entry->start + text;
+ u_end = u_start - 1 + undo_entry->length;
+ text_hole_delete(u_start, u_end, NO_UNDO);
+ status_line("Undo [%d] %s %d chars at position %d",
+ modified_count, "deleted",
+ undo_entry->length, undo_entry->start
+ );
+ break;
+ }
+ repeat = 0;
+ switch (undo_entry->u_type) {
+ // If this is the end of a chain, lower modification count and refresh display
+ case UNDO_DEL:
+ case UNDO_INS:
+ dot = (text + undo_entry->start);
+ refresh(FALSE);
+ break;
+ case UNDO_DEL_CHAIN:
+ case UNDO_INS_CHAIN:
+ repeat = 1;
+ break;
+ }
+ // Deallocate the undo object we just processed
+ undo_stack_tail = undo_entry->prev;
+ free(undo_entry);
+ modified_count--;
+ // For chained operations, continue popping all the way down the chain.
+ if (repeat) {
+ undo_pop(); // Follow the undo chain if one exists
+ }
+}
+
+#if ENABLE_FEATURE_VI_UNDO_QUEUE
+static void undo_queue_commit(void) // Flush any queued objects to the undo stack
+{
+ // Pushes the queue object onto the undo stack
+ if (undo_q > 0) {
+ // Deleted character undo events grow from the end
+ undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
+ undo_q,
+ (undo_queue_state | UNDO_USE_SPOS)
+ );
+ undo_queue_state = UNDO_EMPTY;
+ undo_q = 0;
+ }
+}
+#endif
+
+#endif /* ENABLE_FEATURE_VI_UNDO */
+
// open a hole in text[]
// might reallocate text[]! use p += text_hole_make(p, ...),
// and be careful to not use pointers into potentially freed text[]!
@@ -2084,12 +2484,12 @@ static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte
}
memmove(p + size, p, end - size - p);
memset(p, ' ', size); // clear new hole
- file_modified++;
return bias;
}
// close a hole in text[]
-static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
+// "undo" value indicates if this operation should be undo-able
+static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive
{
char *src, *dest;
int cnt, hole_size;
@@ -2104,10 +2504,29 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu
}
hole_size = q - p + 1;
cnt = end - src;
+#if ENABLE_FEATURE_VI_UNDO
+ switch (undo) {
+ case NO_UNDO:
+ break;
+ case ALLOW_UNDO:
+ undo_push(p, hole_size, UNDO_DEL);
+ break;
+ case ALLOW_UNDO_CHAIN:
+ undo_push(p, hole_size, UNDO_DEL_CHAIN);
+ break;
+# if ENABLE_FEATURE_VI_UNDO_QUEUE
+ case ALLOW_UNDO_QUEUED:
+ undo_push(p, hole_size, UNDO_DEL_QUEUED);
+ break;
+# endif
+ }
+ modified_count--;
+#endif
if (src < text || src > end)
goto thd0;
if (dest < text || dest >= end)
goto thd0;
+ modified_count++;
if (src >= end)
goto thd_atend; // just delete the end of the buffer
memmove(dest, src, cnt);
@@ -2117,7 +2536,6 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu
dest = end - 1; // make sure dest in below end-1
if (end <= text)
dest = end = text; // keep pointers valid
- file_modified++;
thd0:
return dest;
}
@@ -2125,7 +2543,7 @@ static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclu
// copy text into register, then delete text.
// if dist <= 0, do not include, or go past, a NewLine
//
-static char *yank_delete(char *start, char *stop, int dist, int yf)
+static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
{
char *p;
@@ -2154,7 +2572,7 @@ static char *yank_delete(char *start, char *stop, int dist, int yf)
text_yank(start, stop, YDreg);
#endif
if (yf == YANKDEL) {
- p = text_hole_delete(start, stop);
+ p = text_hole_delete(start, stop, undo);
} // delete lines
return p;
}
@@ -2220,12 +2638,22 @@ static void end_cmd_q(void)
|| ENABLE_FEATURE_VI_CRASHME
// might reallocate text[]! use p += string_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
-static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
+static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
{
uintptr_t bias;
int i;
i = strlen(s);
+#if ENABLE_FEATURE_VI_UNDO
+ switch (undo) {
+ case ALLOW_UNDO:
+ undo_push(p, i, UNDO_INS);
+ break;
+ case ALLOW_UNDO_CHAIN:
+ undo_push(p, i, UNDO_INS_CHAIN);
+ break;
+ }
+#endif
bias = text_hole_make(p, i);
p += bias;
memcpy(p, s, i);
@@ -2296,9 +2724,8 @@ static char *swap_context(char *p) // goto new context for '' command make this
// only swap context if other context is valid
if (text <= mark[27] && mark[27] <= end - 1) {
tmp = mark[27];
- mark[27] = mark[26];
- mark[26] = tmp;
- p = mark[26]; // where we are going- previous context
+ mark[27] = p;
+ mark[26] = p = tmp;
context_start = prev_line(prev_line(prev_line(p)));
context_end = next_line(next_line(next_line(p)));
}
@@ -2378,6 +2805,9 @@ static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
{
struct pollfd pfd[1];
+ if (hund != 0)
+ fflush_all();
+
pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
return safe_poll(pfd, 1, hund*10) > 0;
@@ -2474,62 +2904,50 @@ static char *get_input_line(const char *prompt)
#undef buf
}
-static int file_size(const char *fn) // what is the byte size of "fn"
-{
- struct stat st_buf;
- int cnt;
-
- cnt = -1;
- if (fn && stat(fn, &st_buf) == 0) // see if file exists
- cnt = (int) st_buf.st_size;
- return cnt;
-}
-
// might reallocate text[]!
-static int file_insert(const char *fn, char *p, int update_ro_status)
+static int file_insert(const char *fn, char *p, int initial)
{
int cnt = -1;
int fd, size;
struct stat statbuf;
+ if (p < text)
+ p = text;
+ if (p > end)
+ p = end;
+
+ fd = open(fn, O_RDONLY);
+ if (fd < 0) {
+ if (!initial)
+ status_line_bold_errno(fn);
+ return cnt;
+ }
+
/* Validate file */
- if (stat(fn, &statbuf) < 0) {
+ if (fstat(fd, &statbuf) < 0) {
status_line_bold_errno(fn);
- goto fi0;
+ goto fi;
}
if (!S_ISREG(statbuf.st_mode)) {
- // This is not a regular file
status_line_bold("'%s' is not a regular file", fn);
- goto fi0;
- }
- if (p < text || p > end) {
- status_line_bold("Trying to insert file outside of memory");
- goto fi0;
- }
-
- // read file to buffer
- fd = open(fn, O_RDONLY);
- if (fd < 0) {
- status_line_bold_errno(fn);
- goto fi0;
+ goto fi;
}
size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
p += text_hole_make(p, size);
- cnt = safe_read(fd, p, size);
+ cnt = full_read(fd, p, size);
if (cnt < 0) {
status_line_bold_errno(fn);
- p = text_hole_delete(p, p + size - 1); // un-do buffer insert
+ p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
} else if (cnt < size) {
- // There was a partial read, shrink unused space text[]
- p = text_hole_delete(p + cnt, p + size - 1); // un-do buffer insert
+ // There was a partial read, shrink unused space
+ p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
status_line_bold("can't read '%s'", fn);
}
- if (cnt >= size)
- file_modified++;
+ fi:
close(fd);
- fi0:
+
#if ENABLE_FEATURE_VI_READONLY
- if (update_ro_status
+ if (initial
&& ((access(fn, W_OK) < 0) ||
/* root will always have access()
* so we check fileperms too */
@@ -2562,7 +2980,7 @@ static int file_write(char *fn, char *first, char *last)
ftruncate(fd, charcnt);
if (charcnt == cnt) {
// good write
- //file_modified = FALSE;
+ //modified_count = FALSE;
} else {
charcnt = 0;
}
@@ -2635,7 +3053,7 @@ static void flash(int h)
redraw(TRUE);
}
-static void Indicate_Error(void)
+static void indicate_error(void)
{
#if ENABLE_FEATURE_VI_CRASHME
if (crashme > 0)
@@ -2784,7 +3202,7 @@ static int format_edit_status(void)
int cur, percent, ret, trunc_at;
- // file_modified is now a counter rather than a flag. this
+ // modified_count is now a counter rather than a flag. this
// helps reduce the amount of line counting we need to do.
// (this will cause a mis-reporting of modified status
// once every MAXINT editing operations.)
@@ -2794,11 +3212,12 @@ static int format_edit_status(void)
// we're on, then we shouldn't have to do this count_lines()
cur = count_lines(text, dot);
- // reduce counting -- the total lines can't have
- // changed if we haven't done any edits.
- if (file_modified != last_file_modified) {
+ // count_lines() is expensive.
+ // Call it only if something was changed since last time
+ // we were here:
+ if (modified_count != last_modified_count) {
tot = cur + count_lines(dot, end - 1) - 1;
- last_file_modified = file_modified;
+ last_modified_count = modified_count;
}
// current line percent
@@ -2825,7 +3244,7 @@ static int format_edit_status(void)
#if ENABLE_FEATURE_VI_READONLY
(readonly_mode ? " [Readonly]" : ""),
#endif
- (file_modified ? " [Modified]" : ""),
+ (modified_count ? " [Modified]" : ""),
cur, tot, percent);
if (ret >= 0 && ret < trunc_at)
@@ -2938,7 +3357,7 @@ static void refresh(int full_screen)
tp = t + 1;
}
- // see if there are any changes between vitual screen and out_buf
+ // see if there are any changes between virtual screen and out_buf
changed = FALSE; // assume no change
cs = 0;
ce = columns - 1;
@@ -2975,7 +3394,7 @@ static void refresh(int full_screen)
if (cs < 0) cs = 0;
if (ce > columns - 1) ce = columns - 1;
if (cs > ce) { cs = 0; ce = columns - 1; }
- // is there a change between vitual screen and out_buf
+ // is there a change between virtual screen and out_buf
if (changed) {
// copy changed part of buffer to virtual screen
memcpy(sp+cs, out_buf+cs, ce-cs+1);
@@ -3050,11 +3469,12 @@ static void do_cmd(int c)
if (*dot == '\n') {
// don't Replace past E-o-l
cmd_mode = 1; // convert to insert
+ undo_queue_commit();
} else {
if (1 <= c || Isprint(c)) {
if (c != 27)
- dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
- dot = char_insert(dot, c); // insert new char
+ dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
+ dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
}
goto dc1;
}
@@ -3064,7 +3484,7 @@ static void do_cmd(int c)
if (c == KEYCODE_INSERT) goto dc5;
// insert the char c at "dot"
if (1 <= c || Isprint(c)) {
- dot = char_insert(dot, c);
+ dot = char_insert(dot, c, ALLOW_UNDO_QUEUED);
}
goto dc1;
}
@@ -3110,7 +3530,6 @@ static void do_cmd(int c)
//case ']': // ]-
//case '_': // _-
//case '`': // `-
- //case 'u': // u- FIXME- there is no undo
//case 'v': // v-
default: // unrecognized command
buf[0] = c;
@@ -3177,8 +3596,9 @@ static void do_cmd(int c)
break;
case 27: // esc
if (cmd_mode == 0)
- indicate_error(c);
+ indicate_error();
cmd_mode = 0; // stop insrting
+ undo_queue_commit();
end_cmd_q();
last_status_cksum = 0; // force status update
break;
@@ -3195,12 +3615,13 @@ static void do_cmd(int c)
if ((unsigned)c1 <= 25) { // a-z?
YDreg = c1;
} else {
- indicate_error(c);
+ indicate_error();
}
break;
case '\'': // '- goto a specific mark
- c1 = (get_one_char() | 0x20) - 'a';
- if ((unsigned)c1 <= 25) { // a-z?
+ c1 = (get_one_char() | 0x20);
+ if ((unsigned)(c1 - 'a') <= 25) { // a-z?
+ c1 = (c1 - 'a');
// get the b-o-l
q = mark[c1];
if (text <= q && q < end) {
@@ -3213,7 +3634,7 @@ static void do_cmd(int c)
dot_begin(); // go to B-o-l
dot_skip_over_ws();
} else {
- indicate_error(c);
+ indicate_error();
}
break;
case 'm': // m- Mark a line
@@ -3226,7 +3647,7 @@ static void do_cmd(int c)
// remember the line
mark[c1] = dot;
} else {
- indicate_error(c);
+ indicate_error();
}
break;
case 'P': // P- Put register before
@@ -3253,20 +3674,25 @@ static void do_cmd(int c)
if (c == 'p')
dot_right(); // move to right, can move to NL
}
- string_insert(dot, p); // insert the string
+ string_insert(dot, p, ALLOW_UNDO); // insert the string
end_cmd_q(); // stop adding to q
break;
case 'U': // U- Undo; replace current line with original version
if (reg[Ureg] != NULL) {
p = begin_line(dot);
q = end_line(dot);
- p = text_hole_delete(p, q); // delete cur line
- p += string_insert(p, reg[Ureg]); // insert orig line
+ p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line
+ p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
dot = p;
dot_skip_over_ws();
}
break;
#endif /* FEATURE_VI_YANKMARK */
+#if ENABLE_FEATURE_VI_UNDO
+ case 'u': // u- undo last operation
+ undo_pop();
+ break;
+#endif
case '$': // $- goto end of line
case KEYCODE_END: // Cursor Key End
for (;;) {
@@ -3282,7 +3708,7 @@ static void do_cmd(int c)
// we found half of a pair
p = find_pair(q, *q);
if (p == NULL) {
- indicate_error(c);
+ indicate_error();
} else {
dot = p;
}
@@ -3290,7 +3716,7 @@ static void do_cmd(int c)
}
}
if (*q == '\n')
- indicate_error(c);
+ indicate_error();
break;
case 'f': // f- forward to a user specified char
last_forward_char = get_one_char(); // get the search char
@@ -3419,7 +3845,7 @@ static void do_cmd(int c)
}
break;
#endif /* FEATURE_VI_SEARCH */
- case '0': // 0- goto begining of line
+ case '0': // 0- goto beginning of line
case '1': // 1-
case '2': // 2-
case '3': // 3-
@@ -3437,57 +3863,14 @@ static void do_cmd(int c)
break;
case ':': // :- the colon mode commands
p = get_input_line(":"); // get input line- use "status line"
-#if ENABLE_FEATURE_VI_COLON
colon(p); // execute the command
-#else
- if (*p == ':')
- p++; // move past the ':'
- cnt = strlen(p);
- if (cnt <= 0)
- break;
- if (strncmp(p, "quit", cnt) == 0
- || strncmp(p, "q!", cnt) == 0 // delete lines
- ) {
- if (file_modified && p[1] != '!') {
- status_line_bold("No write since last change (:%s! overrides)", p);
- } else {
- editing = 0;
- }
- } else if (strncmp(p, "write", cnt) == 0
- || strncmp(p, "wq", cnt) == 0
- || strncmp(p, "wn", cnt) == 0
- || (p[0] == 'x' && !p[1])
- ) {
- cnt = file_write(current_filename, text, end - 1);
- if (cnt < 0) {
- if (cnt == -1)
- status_line_bold("Write error: %s", strerror(errno));
- } else {
- file_modified = 0;
- last_file_modified = -1;
- status_line("'%s' %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
- if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
- || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
- ) {
- editing = 0;
- }
- }
- } else if (strncmp(p, "file", cnt) == 0) {
- last_status_cksum = 0; // force status update
- } else if (sscanf(p, "%d", &j) > 0) {
- dot = find_line(j); // go to line # j
- dot_skip_over_ws();
- } else { // unrecognized cmd
- not_implemented(p);
- }
-#endif /* !FEATURE_VI_COLON */
break;
case '<': // <- Left shift something
case '>': // >- Right shift something
cnt = count_lines(text, dot); // remember what line we are on
c1 = get_one_char(); // get the type of thing to delete
find_range(&p, &q, c1);
- yank_delete(p, q, 1, YANKONLY); // save copy before change
+ yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change
p = begin_line(p);
q = end_line(q);
i = count_lines(p, q); // # of lines we are shifting
@@ -3496,16 +3879,16 @@ static void do_cmd(int c)
// shift left- remove tab or 8 spaces
if (*p == '\t') {
// shrink buffer 1 char
- text_hole_delete(p, p);
+ text_hole_delete(p, p, NO_UNDO);
} else if (*p == ' ') {
// we should be calculating columns, not just SPACE
for (j = 0; *p == ' ' && j < tabstop; j++) {
- text_hole_delete(p, p);
+ text_hole_delete(p, p, NO_UNDO);
}
}
} else if (c == '>') {
// shift right -- add tab or 8 spaces
- char_insert(p, '\t');
+ char_insert(p, '\t', ALLOW_UNDO);
}
}
dot = find_line(cnt); // what line were we on
@@ -3540,7 +3923,7 @@ static void do_cmd(int c)
save_dot = dot;
dot = dollar_line(dot); // move to before NL
// copy text into a register and delete
- dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
+ dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l
if (c == 'C')
goto dc_i; // start inserting
#if ENABLE_FEATURE_VI_DOT_CMD
@@ -3552,7 +3935,9 @@ static void do_cmd(int c)
c1 = get_one_char();
if (c1 != 'g') {
buf[0] = 'g';
- buf[1] = c1; // TODO: if Unicode?
+ // c1 < 0 if the key was special. Try "g<up-arrow>"
+ // TODO: if Unicode?
+ buf[1] = (c1 >= 0 ? c1 : '*');
buf[2] = '\0';
not_implemented(buf);
break;
@@ -3585,15 +3970,22 @@ static void do_cmd(int c)
case KEYCODE_INSERT: // Cursor Key Insert
dc_i:
cmd_mode = 1; // start inserting
+ undo_queue_commit(); // commit queue when cmd_mode changes
break;
case 'J': // J- join current and next lines together
do {
dot_end(); // move to NL
if (dot < end - 1) { // make sure not last char in text[]
+#if ENABLE_FEATURE_VI_UNDO
+ undo_push(dot, 1, UNDO_DEL);
*dot++ = ' '; // replace NL with space
- file_modified++;
+ undo_push((dot - 1), 1, UNDO_INS_CHAIN);
+#else
+ *dot++ = ' ';
+ modified_count++;
+#endif
while (isblank(*dot)) { // delete leading WS
- dot_delete();
+ text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN);
}
}
} while (--cmdcnt > 0);
@@ -3622,10 +4014,10 @@ static void do_cmd(int c)
dot_prev();
case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
dot_end();
- dot = char_insert(dot, '\n');
+ dot = char_insert(dot, '\n', ALLOW_UNDO);
} else {
dot_begin(); // 0
- dot = char_insert(dot, '\n'); // i\n ESC
+ dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC
dot_prev(); // -
}
goto dc_i;
@@ -3633,10 +4025,12 @@ static void do_cmd(int c)
case 'R': // R- continuous Replace char
dc5:
cmd_mode = 2;
+ undo_queue_commit();
break;
case KEYCODE_DELETE:
- c = 'x';
- // fall through
+ if (dot < end - 1)
+ dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO);
+ break;
case 'X': // X- delete char before dot
case 'x': // x- delete the current char
case 's': // s- substitute the current char
@@ -3647,7 +4041,7 @@ static void do_cmd(int c)
if (dot[dir] != '\n') {
if (c == 'X')
dot--; // delete prev char
- dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
+ dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char
}
} while (--cmdcnt > 0);
end_cmd_q(); // stop adding to q
@@ -3658,10 +4052,10 @@ static void do_cmd(int c)
// ZZ means to save file (if necessary), then exit
c1 = get_one_char();
if (c1 != 'Z') {
- indicate_error(c);
+ indicate_error();
break;
}
- if (file_modified) {
+ if (modified_count) {
if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
status_line_bold("'%s' is read only", current_filename);
break;
@@ -3718,6 +4112,7 @@ static void do_cmd(int c)
c1 = get_one_char(); // get the type of thing to delete
// determine range, and whether it spans lines
ml = find_range(&p, &q, c1);
+ place_cursor(0, 0);
if (c1 == 27) { // ESC- user changed mind and wants out
c = c1 = 27; // Escape- do nothing
} else if (strchr("wW", c1)) {
@@ -3729,23 +4124,23 @@ static void do_cmd(int c)
q--;
}
}
- dot = yank_delete(p, q, ml, yf); // delete word
+ dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
} else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
// partial line copy text into a register and delete
- dot = yank_delete(p, q, ml, yf); // delete word
+ dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
} else if (strchr("cdykjHL+-{}\r\n", c1)) {
// whole line copy text into a register and delete
- dot = yank_delete(p, q, ml, yf); // delete lines
+ dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
whole = 1;
} else {
// could not recognize object
c = c1 = 27; // error-
ml = 0;
- indicate_error(c);
+ indicate_error();
}
if (ml && whole) {
if (c == 'c') {
- dot = char_insert(dot, '\n');
+ dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
// on the last line of file don't move to prev line
if (whole && dot != (end-1)) {
dot_prev();
@@ -3791,8 +4186,14 @@ static void do_cmd(int c)
case 'r': // r- replace the current char with user input
c1 = get_one_char(); // get the replacement char
if (*dot != '\n') {
+#if ENABLE_FEATURE_VI_UNDO
+ undo_push(dot, 1, UNDO_DEL);
*dot = c1;
- file_modified++;
+ undo_push(dot, 1, UNDO_INS_CHAIN);
+#else
+ *dot = c1;
+ modified_count++;
+#endif
}
end_cmd_q(); // stop adding to q
break;
@@ -3832,13 +4233,25 @@ static void do_cmd(int c)
break;
case '~': // ~- flip the case of letters a-z -> A-Z
do {
+#if ENABLE_FEATURE_VI_UNDO
if (islower(*dot)) {
+ undo_push(dot, 1, UNDO_DEL);
*dot = toupper(*dot);
- file_modified++;
+ undo_push(dot, 1, UNDO_INS_CHAIN);
} else if (isupper(*dot)) {
+ undo_push(dot, 1, UNDO_DEL);
*dot = tolower(*dot);
- file_modified++;
+ undo_push(dot, 1, UNDO_INS_CHAIN);
}
+#else
+ if (islower(*dot)) {
+ *dot = toupper(*dot);
+ modified_count++;
+ } else if (isupper(*dot)) {
+ *dot = tolower(*dot);
+ modified_count++;
+ }
+#endif
dot_right();
} while (--cmdcnt > 0);
end_cmd_q(); // stop adding to q
@@ -3868,7 +4281,7 @@ static void do_cmd(int c)
dc1:
// if text[] just became empty, add back an empty line
if (end == text) {
- char_insert(text, '\n'); // start empty buf with dummy line
+ char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line
dot = text;
}
// it is OK for dot to exactly equal to end, otherwise check dot validity
diff --git a/examples/android-build b/examples/android-build
index 89f3b63..123ba96 100755
--- a/examples/android-build
+++ b/examples/android-build
@@ -29,4 +29,6 @@ else
LDLIBS="dl m c gcc"
fi
+# It's possible with newer version
+# you need to use CFLAGS_busybox instead of EXTRA_LDFLAGS below:
make EXTRA_LDFLAGS="$LDFLAGS" LDLIBS="$LDLIBS" "$@"
diff --git a/examples/mdev.conf b/examples/mdev.conf
index 5179569..fab1dc4 100644
--- a/examples/mdev.conf
+++ b/examples/mdev.conf
@@ -34,3 +34,5 @@ fd[0-9]* 0:11 660
sd[a-z]* 0:6 660
hd[a-z]* 0:6 660
+
+hw_random 0:0 600 =hwrng
diff --git a/examples/mdev_fat.conf b/examples/mdev_fat.conf
index f2a15f3..630d270 100644
--- a/examples/mdev_fat.conf
+++ b/examples/mdev_fat.conf
@@ -19,7 +19,7 @@
# support module loading on hotplug
$MODALIAS=.* root:root 660 @modprobe "$MODALIAS"
-# null may already exist; therefore ownership has to be changed with command
+# null may already exist; therefore mode has to be changed with command
null root:root 666 @chmod 666 $MDEV
zero root:root 666
full root:root 666
@@ -31,7 +31,7 @@ grsec root:root 660
kmem root:root 640
mem root:root 640
port root:root 640
-# console may already exist; therefore ownership has to be changed with command
+# console may already exist; therefore mode has to be changed with command
console root:tty 600 @chmod 600 $MDEV
ptmx root:tty 666
pty.* root:tty 660
@@ -63,6 +63,12 @@ control.* root:audio 660 =snd/
midi.* root:audio 660 =snd/
seq root:audio 660 =snd/
timer root:audio 660 =snd/
+# for kernels/mdevs which expose full path of alsa devices:
+snd/pcm.* root:audio 660
+snd/control.* root:audio 660
+snd/midi.* root:audio 660
+snd/seq root:audio 660
+snd/timer root:audio 660
adsp root:audio 660 >sound/
audio root:audio 660 >sound/
@@ -139,6 +145,6 @@ dahdi!(.*) root:dialout 660 =dahdi/%1
# We are hooking to the last events. To avoid having two scripts running,
# we check for DISK_MEDIA_CHANGE=1 (prev to last event has it,
# and it's the _only_ difference in env for these two events as of kernel 3.7.7)
-# Unfortunately, there is no event for "user pressed [Turn USB storage] btn"!
+# Unfortunately, there is no event for "user pressed [Turn USB storage on] btn"!
# Script merely backgrounds and tries to rescan partition table for 1 minute:
ACTION=change;SUBSYSTEM=block;DISK_MEDIA_CHANGE=1;.* 0:0 660 */etc/mdev.conf.change_blockdev.sh
diff --git a/examples/udhcp/sample.bound b/examples/udhcp/sample.bound
index bd3569c..7c9d857 100755
--- a/examples/udhcp/sample.bound
+++ b/examples/udhcp/sample.bound
@@ -22,10 +22,14 @@ then
done
fi
-echo -n > $RESOLV_CONF
-[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
-for i in $dns
-do
- echo adding dns $i
- echo nameserver $i >> $RESOLV_CONF
-done
+# Only replace resolv.conf is we have at least one DNS server
+if [ -n "$dns" ]
+then
+ echo -n > $RESOLV_CONF
+ [ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+ for i in $dns
+ do
+ echo adding dns $i
+ echo nameserver $i >> $RESOLV_CONF
+ done
+fi
diff --git a/examples/udhcp/sample.renew b/examples/udhcp/sample.renew
index ea368fc..4dce848 100755
--- a/examples/udhcp/sample.renew
+++ b/examples/udhcp/sample.renew
@@ -22,10 +22,14 @@ then
done
fi
-echo -n > $RESOLV_CONF
-[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
-for i in $dns
-do
- echo adding dns $i
- echo nameserver $i >> $RESOLV_CONF
-done
+# Only replace resolv.conf is we have at least one DNS server
+if [ -n "$dns" ]
+then
+ echo -n > $RESOLV_CONF
+ [ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+ for i in $dns
+ do
+ echo adding dns $i
+ echo nameserver $i >> $RESOLV_CONF
+ done
+fi
diff --git a/examples/undeb b/examples/undeb
index 37104e9..c30baf3 100755
--- a/examples/undeb
+++ b/examples/undeb
@@ -5,49 +5,55 @@
# Requires the programs (ar, tar, gzip, and the pager more or less).
#
usage() {
-echo "Usage: undeb -c package.deb <Print control file info>"
-echo " undeb -l package.deb <List contents of deb package>"
-echo " undeb -x package.deb /foo/boo <Extract deb package to this directory,"
-echo " put . for current directory>"
-exit
+ cat <<EOF
+Usage: undeb -c package.deb <Print control file info>
+ undeb -l package.deb <List contents of deb package>
+ undeb -x package.deb /foo/boo <Extract deb package to this directory,
+ put . for current directory>
+EOF
+ exit
}
deb=$2
exist() {
-if [ "$deb" = "" ]; then
-usage
-elif [ ! -s "$deb" ]; then
-echo "Can't find $deb!"
-exit
-fi
+ if [ -z "${deb}" ]; then
+ usage
+ elif [ ! -s "${deb}" ]; then
+ echo "Can't find ${deb}!"
+ exit 1
+ fi
}
-if [ "$1" = "" ]; then
-usage
+if [ -z "$1" ]; then
+ usage
elif [ "$1" = "-l" ]; then
-exist
-type more >/dev/null 2>&1 && pager=more
-type less >/dev/null 2>&1 && pager=less
-[ "$pager" = "" ] && echo "No pager found!" && exit
-(ar -p $deb control.tar.gz | tar -xzO *control ; echo -e "\nPress enter to scroll, q to Quit!\n" ; ar -p $deb data.tar.gz | tar -tzv) | $pager
-exit
+ exist
+ type more >/dev/null 2>&1 && pager=more
+ type less >/dev/null 2>&1 && pager=less
+ [ -z "${pager}" ] && echo "No pager found!" && exit 1
+ (
+ ar -p "${deb}" control.tar.gz | tar -xzO *control
+ printf "\nPress enter to scroll, q to Quit!\n\n"
+ ar -p "${deb}" data.tar.gz | tar -tzv
+ ) | ${pager}
+ exit
elif [ "$1" = "-c" ]; then
-exist
-ar -p $deb control.tar.gz | tar -xzO *control
-exit
+ exist
+ ar -p "${deb}" control.tar.gz | tar -xzO *control
+ exit
elif [ "$1" = "-x" ]; then
-exist
-if [ "$3" = "" ]; then
-usage
-elif [ ! -d "$3" ]; then
-echo "No such directory $3!"
-exit
-fi
-ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit
-echo
-echo "Extracted $deb to $3!"
-exit
+ exist
+ if [ -z "$3" ]; then
+ usage
+ elif [ ! -d "$3" ]; then
+ echo "No such directory $3!"
+ exit 1
+ fi
+ ar -p "${deb}" data.tar.gz | tar -xzvpf - -C "$3" || exit
+ echo
+ echo "Extracted ${deb} to $3!"
+ exit
else
-usage
+ usage
fi
diff --git a/examples/unrpm b/examples/unrpm
index 7fd3676..f48550b 100755
--- a/examples/unrpm
+++ b/examples/unrpm
@@ -5,44 +5,49 @@
# Requires the programs (cpio, gzip, and the pager more or less).
#
usage() {
-echo "Usage: unrpm -l package.rpm <List contents of rpm package>"
-echo " unrpm -x package.rpm /foo/boo <Extract rpm package to this directory,"
-echo " put . for current directory>"
-exit
+ cat <<EOF
+Usage: unrpm -l package.rpm <List contents of rpm package>
+ unrpm -x package.rpm /foo/boo <Extract rpm package to this directory,
+ put . for current directory>
+EOF
+ exit
}
rpm=$2
exist() {
-if [ "$rpm" = "" ]; then
-usage
-elif [ ! -s "$rpm" ]; then
-echo "Can't find $rpm!"
-exit
-fi
+ if [ -z "${rpm}" ]; then
+ usage
+ elif [ ! -s "${rpm}" ]; then
+ echo "Can't find ${rpm}!"
+ exit 1
+ fi
}
-if [ "$1" = "" ]; then
-usage
+if [ -z "$1" ]; then
+ usage
elif [ "$1" = "-l" ]; then
-exist
-type more >/dev/null 2>&1 && pager=more
-type less >/dev/null 2>&1 && pager=less
-[ "$pager" = "" ] && echo "No pager found!" && exit
-(echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager
-exit
+ exist
+ type more >/dev/null 2>&1 && pager=more
+ type less >/dev/null 2>&1 && pager=less
+ [ "$pager" = "" ] && echo "No pager found!" && exit
+ (
+ printf "\nPress enter to scroll, q to Quit!\n\n"
+ rpm2cpio "${rpm}" | cpio -tv --quiet
+ ) | ${pager}
+ exit
elif [ "$1" = "-x" ]; then
-exist
-if [ "$3" = "" ]; then
-usage
-elif [ ! -d "$3" ]; then
-echo "No such directory $3!"
-exit
-fi
-rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit
-echo
-echo "Extracted $rpm to $3!"
-exit
+ exist
+ if [ -z "$3" ]; then
+ usage
+ elif [ ! -d "$3" ]; then
+ echo "No such directory $3!"
+ exit 1
+ fi
+ rpm2cpio "${rpm}" | (umask 0 ; cd "$3" ; cpio -idmuv) || exit
+ echo
+ echo "Extracted ${rpm} to $3!"
+ exit
else
-usage
+ usage
fi
diff --git a/examples/var_service/README b/examples/var_service/README
index 06817c8..938cce9 100644
--- a/examples/var_service/README
+++ b/examples/var_service/README
@@ -1,45 +1,158 @@
+ Daemontools and runit
+
+Tired of PID files, needing root access, and writing init scripts just
+to have your UNIX apps start when your server boots? Want a simpler,
+better alternative that will also restart them if they crash? If so,
+this is an introduction to process supervision with runit/daemontools.
+
+
+ Background
+
+Classic init scripts, e.g. /etc/init.d/apache, are widely used for
+starting processes at system boot time, when they are executed by init.
+Sadly, init scripts are cumbersome and error-prone to write, they must
+typically be edited and run as root, and the processes they launch do
+not get restarted automatically if they crash.
+
+In an alternative scheme called "process supervision", each important
+process is looked after by a tiny supervising process, which deals with
+starting and stopping the important process on request, and re-starting
+it when it exits unexpectedly. Those supervising processes can in turn
+be supervised by other supervising processes.
+
+Dan Bernstein wrote the process supervision toolkit, "daemontools",
+which is a set of small, reliable programs that cooperate in the
+UNIX tradition to manage process supervision trees.
+
+Runit is a more conveniently licensed and more actively maintained
+reimplementation of daemontools, written by Gerrit Pape.
+
+Here I’ll use runit, however, the ideas are the same for other
+daemontools-like projects (there are several).
+
+
+ Service directories and scripts
+
+In runit parlance a "service" is simply a directory containing a script
+named "run".
+
+There are just two key programs in runit. Firstly, runsv supervises the
+process for an individual service. Service directories themselves sit
+inside a containing directory, and the runsvdir program supervises that
+directory, running one child runsv process for the service in each
+subdirectory. A typical choice is to start an instance of runsvdir
+which supervises services in subdirectories of /var/service/.
+
+If /var/service/log/ exists, runsv will supervise two services,
+and will connect stdout of main service to the stdin of log service.
+This is primarily used for logging.
+
+You can debug an individual service by running its SERVICE_DIR/run script.
+In this case, its stdout and stderr go to your terminal.
+
+You can also run "runsv SERVICE_DIR", which runs both the service
+and its logger service (SERVICE_DIR/log/run) if logger service exists.
+If logger service exists, the output will go to it instead of the terminal.
+
+"runsvdir /var/service" merely runs "runsv SERVICE_DIR" for every subdirectory
+in /var/service.
+
+
+ Examples
+
+This directory contains some examples of services:
+
+ var_service/getty_<tty>
+
+Runs a getty on <tty>. (run script looks at $PWD and extracts suffix
+after "_" as tty name). Create copies (or symlinks) of this directory
+with different names to run many gettys on many ttys.
+
+ var_service/gpm
+
+Runs gpm, the cut and paste utility and mouse server for text consoles.
+
+ var_service/inetd
+
+Runs inetd. This is an example of a service with log. Log service
+writes timestamped, rotated log data to /var/log/service/inetd/*
+using "svlogd -tt". p_log and w_log scripts demonstrage how you can
+"page log" and "watch log".
+
+Other services which have logs handle them in the same way.
+
+ var_service/nmeter
+
+Runs nmeter '%t %c ....' with output to /dev/tty9. This gives you
+a 1-second sampling of server load and health on a dedicated text console.
+
+
+ Networking examples
+
In many cases, network configuration makes it necessary to run several daemons:
dhcp, zeroconf, ppp, openvpn and such. They need to be controlled,
-and in many cases you also want to babysit them. runsvdir is a good tool for this.
-examples/var_service directory provides a few examples. It is meant to be used
-this way: copy it somewhere (say, /var/service) and run something like
+and in many cases you also want to babysit them.
-env - PATH=... <other vars=...> runsvdir /var/service &
+They present a case where different services need to control (start, stop,
+restart) each other.
-from one of system startup scripts. (Google "man runsvdir" and "man runsv"
-for more info about these tools).
+ var_service/dhcp_if
-Some existing examples:
-
-var_service/dhcp_if -
controls a udhcpc instance which provides dhpc-assigned IP
address on interface named "if". Copy/rename this directory as needed to run
udhcpc on other interfaces (var_service/dhcp_if/run script uses _foo suffix
-of the parent directory as interface name). When IP address is obtained or lost,
-var_service/dhcp_if/dhcp_handler is run. It saves new config data to
-/var/run/service/fw/dhcp_if.ipconf and (re)starts /var/service/fw service.
-This example can be used as a template for other dynamic network link services
-(ppp/vpn/zcip).
-
-var_service/ifplugd_if -
-watches link status of interface if. Downs and ups /var/service/dhcp_if
+of the parent directory as interface name).
+
+When IP address is obtained or lost, var_service/dhcp_if/dhcp_handler is run.
+It saves new config data to /var/run/service/fw/dhcp_if.ipconf and (re)starts
+/var/service/fw service. This example can be used as a template for other
+dynamic network link services (ppp/vpn/zcip).
+
+This is an example of service with has a "finish" script. If downed ("sv d"),
+"finish" is executed. For this service, it removes DHCP address from
+the interface. This is useful when ifplugd detects that the the link is dead
+(cable is no longer attached anywhere) and downs us - keeping DHCP configured
+addresses on the interface would make kernel still try to use it.
+
+ var_service/zcip_if
+
+Zeroconf IP service: assigns a 169.254.x.y/16 address to interface "if".
+This allows to talk to other devices on a network without DHCP server
+(if they also assign 169.254 addresses to themselves).
+
+ var_service/ifplugd_if
+
+Watches link status of interface "if". Downs and ups /var/service/dhcp_if
service accordingly. In effect, it allows you to unplug/plug-to-different-network
and have your IP properly re-negotiated at once.
-var_service/dhcp_if_pinger -
-Uses var_service/dhcp_if's data (/var/service/dhcp_if/dhcp_if.out file)
-to determine router IP. Pings it. If ping fails, restarts /var/service/dhcp_if
-service. Basically, an example of watchdog service for networks
-which are not reliable and need babysitting.
+ var_service/dhcp_if_pinger
+
+Uses var_service/dhcp_if's data to determine router IP. Pings it.
+If ping fails, restarts /var/service/dhcp_if service.
+Basically, an example of watchdog service for networks which are not reliable
+and need babysitting.
+
+ var_service/supplicant_if
-var_service/fw -
-A *one-shot* service which reconfigures network based on current known state
-of ALL interfaces. Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf
+Wireless supplicant (wifi association and encryption daemon) service for
+interface "if".
+
+ var_service/fw
+
+"Firewall" script, although it is tasked with much more than setting up firewall.
+It is responsible for all aspects of network configuration.
+
+This is an example of *one-shot* service.
+
+It reconfigures network based on current known state of ALL interfaces.
+Uses conf/*.ipconf (static config) and /var/run/service/fw/*.ipconf
(dynamic config from dhcp/ppp/vpn/etc) to determine what to do.
+
One-shot-ness of this service means that it shuts itself off after single run.
IOW: it is not a constantly running daemon sort of thing.
It starts, it configures the network, it shuts down, all done
-(unlike infamous NetworkManagers which sit in RAM forever, doing hell knows what).
+(unlike infamous NetworkManagers which sit in RAM forever).
However, any dhcp/ppp/vpn or similar service can restart it anytime
when it senses the change in network configuration.
@@ -47,13 +160,77 @@ This even works while fw service runs: if dhcp signals fw to (re)start
while fw runs, fw will not stop after its execution, but will re-execute once,
picking up dhcp's new configuration.
This is achieved very simply by having
-# Make ourself one-shot
-sv o .
+ # Make ourself one-shot
+ sv o .
at the very beginning of fw/run script, not at the end.
+
Therefore, any "sv u /var/run/service/fw" command by any other
script "undoes" o(ne-shot) command if fw still runs, thus
runsv will rerun it; or start it in a normal way if fw is not running.
+This mechanism is the reason why fw is a service, not just a script.
+
System administrators are expected to edit fw/run script, since
network configuration needs are likely to be very complex and different
for non-trivial installations.
+
+ var_service/ftpd
+ var_service/httpd
+ var_service/tftpd
+ var_service/ntpd
+
+Examples of typical network daemons.
+
+
+ Process tree
+
+Here is an example of the process tree from a live system with these services
+(and a few others). An interesting detail are ftpd and vpnc services, where
+you can see only logger process. These services are "downed" at the moment:
+their daemons are not launched.
+
+PID TIME COMMAND
+553 0:04 runsvdir -P /var/service
+561 0:00 runsv sshd
+576 0:00 svlogd -tt /var/log/service/sshd
+589 0:00 /usr/sbin/sshd -D -e -p22 -u0 -h /var/service/sshd/ssh_host_rsa_key
+562 0:00 runsv dhcp_eth0
+568 0:00 svlogd -tt /var/log/service/dhcp_eth0
+850 0:00 udhcpc -vv --foreground --interface=eth0
+ --pidfile=/var/service/dhcp_eth0/udhcpc.pid
+ --script=/var/service/dhcp_eth0/dhcp_handler -x hostname bbox
+563 0:00 runsv ntpd
+573 0:01 svlogd -tt /var/log/service/ntpd
+845 0:00 busybox ntpd -dddnNl -S ./ntp.script -p 10.x.x.x -p 10.x.x.x
+564 0:00 runsv ifplugd_wlan0
+598 0:00 svlogd -tt /var/log/service/ifplugd_wlan0
+614 0:05 ifplugd -apqns -t3 -u0 -d0 -i wlan0
+ -r /var/service/ifplugd_wlan0/ifplugd_handler
+565 0:08 runsv dhcp_wlan0_pinger
+911 0:00 sleep 67
+566 0:00 runsv unscd
+583 0:03 svlogd -tt /var/log/service/unscd
+599 0:02 nscd -dddd
+567 0:00 runsv dhcp_wlan0
+591 0:00 svlogd -tt /var/log/service/dhcp_wlan0
+802 0:00 udhcpc -vv -C -o -V --foreground --interface=wlan0
+ --pidfile=/var/service/dhcp_wlan0/udhcpc.pid
+ --script=/var/service/dhcp_wlan0/dhcp_handler
+569 0:00 runsv fw
+570 0:00 runsv ifplugd_eth0
+597 0:00 svlogd -tt /var/log/service/ifplugd_eth0
+612 0:05 ifplugd -apqns -t3 -u8 -d8 -i eth0
+ -r /var/service/ifplugd_eth0/ifplugd_handler
+571 0:00 runsv zcip_eth0
+590 0:00 svlogd -tt /var/log/service/zcip_eth0
+607 0:01 zcip -fvv eth0 /var/service/zcip_eth0/zcip_handler
+572 0:00 runsv ftpd
+604 0:00 svlogd -tt /var/log/service/ftpd
+574 0:00 runsv vpnc
+603 0:00 svlogd -tt /var/log/service/vpnc
+575 0:00 runsv httpd
+602 0:00 svlogd -tt /var/log/service/httpd
+622 0:00 busybox httpd -p80 -vvv -f -h /home/httpd_root
+577 0:00 runsv supplicant_wlan0
+627 0:00 svlogd -tt /var/log/service/supplicant_wlan0
+638 0:03 wpa_supplicant -i wlan0 -c /var/service/supplicant_wlan0/wpa_supplicant.conf -d
diff --git a/examples/var_service/README_distro_proposal.txt b/examples/var_service/README_distro_proposal.txt
new file mode 100644
index 0000000..9b3fe04
--- a/dev/null
+++ b/examples/var_service/README_distro_proposal.txt
@@ -0,0 +1,291 @@
+ A distro which already uses runit
+
+I installed Void Linux, in order to see what do they have.
+Xfce desktop looks fairly okay, network is up.
+ps tells me they did put X, dbus, NM and udev into runsvdir-supervised tree:
+
+ 1 ? 00:00:01 runit
+ 623 ? 00:00:00 runsvdir
+ 629 ? 00:00:00 runsv
+ 650 tty1 00:00:00 agetty
+ 630 ? 00:00:00 runsv
+ 644 ? 00:00:09 NetworkManager
+ 1737 ? 00:00:00 dhclient
+ 631 ? 00:00:00 runsv
+ 639 tty4 00:00:00 agetty
+ 632 ? 00:00:00 runsv
+ 640 ? 00:00:00 sshd
+ 1804 ? 00:00:00 sshd
+ 1809 pts/3 00:00:00 sh
+ 1818 pts/3 00:00:00 ps
+ 633 ? 00:00:00 runsv
+ 637 tty5 00:00:00 agetty
+ 634 ? 00:00:00 runsv
+ 796 ? 00:00:00 dhclient
+ 635 ? 00:00:00 runsv
+ 649 ? 00:00:00 uuidd
+ 636 ? 00:00:00 runsv
+ 647 ? 00:00:00 acpid
+ 638 ? 00:00:00 runsv
+ 652 ? 00:00:00 console-kit-dae
+ 641 ? 00:00:00 runsv
+ 651 tty6 00:00:00 agetty
+ 642 ? 00:00:00 runsv
+ 660 tty2 00:00:00 agetty
+ 643 ? 00:00:00 runsv
+ 657 ? 00:00:02 dbus-daemon
+ 645 ? 00:00:00 runsv
+ 658 ? 00:00:00 cgmanager
+ 648 ? 00:00:00 runsv
+ 656 tty3 00:00:00 agetty
+ 653 ? 00:00:00 runsv
+ 655 ? 00:00:00 lxdm-binary
+ 698 tty7 00:00:14 Xorg
+ 729 ? 00:00:00 lxdm-session
+ 956 ? 00:00:00 sh
+ 982 ? 00:00:00 xfce4-session
+ 1006 ? 00:00:04 nm-applet
+ 654 ? 00:00:00 runsv
+ 659 ? 00:00:00 udevd
+
+Here is a link to Vod Linux's wiki:
+
+ https://wiki.voidlinux.eu/Runit
+
+Void Linux packages install their services as subdirectories of /etc/rc,
+such as /etc/sv/sshd, with a script file, "run", and a link
+"supervise" -> /run/runit/supervise.sshd
+
+For sshd, "run" contains:
+
+ #!/bin/sh
+ ssh-keygen -A >/dev/null 2>&1 # generate host keys if they don't exist
+ [ -r conf ] && . ./conf
+ exec /usr/bin/sshd -D $OPTS
+
+That's it from the POV of the packager.
+
+This is pretty minimalistic, and yet, it is already distro-specific:
+the link to /run/runit/* is conceptually wrong, it requires packagers
+to know that /etc/rc should not be mutable and thus they need to use
+a different location in filesystem for supervise/ directory.
+
+I think a good thing would be to require just one file: the "run" script.
+The rest should be handled by distro tooling, not by packager.
+
+A similar issue is arising with logging. It would be ideal if packagers
+would not need to know how a particular distro manages logs.
+Whatever their daemons print to stdout/stderr, should be automagically logged
+in a way distro prefers.
+
+* * * * * * * *
+
+ Proposed "standard" on how distros should use runit
+
+The original idea of services-as-directories belongs to D.J.Bernstein (djb),
+and his project to implement it is daemontools: https://cr.yp.to/daemontools.html
+
+There are several reimplementations of daemontools:
+- runit: by Gerrit Pape, http://smarden.org/runit/
+ (busybox has it included)
+- s6: by Laurent Bercot, http://skarnet.org/software/s6/
+
+
+It is not required that a specific clone should be used. Let evolution work.
+
+ Terminology
+
+daemon: any long running background program. Common examples are sshd, getty,
+ntpd, dhcp client...
+
+service: same as "daemon"
+
+service directory: a directory with an executable file (script) named "run"
+which (usually) execs daemon (possibly after some preparatory steps).
+It should start it not as a child or daemonized process, but by exec'ing it
+(inheriting the same PID and the place in the process tree).
+
+service monitor: a tool which watches a set of service directories.
+In daemontools package, it is called "svscan". In runit, it is called
+"runsvdir". In s6, it is called "s6-svscan".
+Service monitor starts a supervisor for each service directory.
+If it dies, it restarts it. If service directory disappears,
+service monitor will not be restarted if it dies.
+runit's service monitor (runsvdir) sends SIGTERM to supervisors
+whose directories disappeared.
+
+supervisor: a tool which monitors one service directory.
+It runs "run" script as its child. It restarts it if it dies.
+It can be instructed to start/top/signal its child.
+In daemontools package, it is called "supervise". In runit, it is called
+"runsv". In s6, it is called "s6-supervise".
+
+Conceptually, a daemontools clone can be designed such that it does not *have*
+the supervisor component: service monitor can directly monitor all its daemons
+(for example, this may be a good idea for memory-constrained systems).
+However all three existing projects (daemontools/runit/s6) do have a per-service
+supervisor process.
+
+log service: a service which is exclusively tasked with logging
+the output of another service. It is implemented as log/ subdirectory
+in a service directory. It has the same structure as "normal"
+service dirs: it has a "run" script which starts a logging tool.
+
+If log service exists, stdout of its "main" service is piped
+to log service. Stops/restarts of either of them do not sever the pipe
+between them.
+
+If log service exists, daemontools and s6 run a pair of supervisors
+(one for the daemon, one for the logger); runit runs only one supervisor
+per service, which is handling both of them (presumably this is done
+to use fewer processes and thus, fewer resources).
+
+
+ User API
+
+"Users" of service monitoring are authors of software which has daemons.
+They need to package their daemons to be installed as services at package
+install time. And they need to do this for many distros.
+The less distros diverge, the easier users' lives are.
+
+System-wide service dirs reside in a distro-specific location.
+The recommended location is /var/service. (However, since it is not
+a mandatory location, avoid depending on it in your run scripts).
+
+The install location for service dirs is /etc/rc:
+when e.g. ntpd daemon is installed, it creates the /etc/rc/ntpd
+directory with (minimally) one executable file (script) named "run"
+which starts ntpd daemon. It can have other files there.
+
+At boot, distro should copy /etc/rc/* to a suitable writable
+directory (common choice are /var/service, /run/service etc).
+It should create log/ directories in each subdirectory
+and create "run" files in them with suitable (for this particular distro)
+logging tool invocation, unless this directory chose to channel
+all logging from all daemons through service monitor process
+and log all of them into one file/database/whatever,
+in which case log/ directories should not be created.
+
+It is allowable for a distro to directly use /etc/rc/ as the only
+location of its service directories. (For example,
+/var/service may be a symlink to /etc/rc).
+However, it poses some problems:
+
+(1) Supervision tools will need to write to subdirectories:
+the control of running daemons is implemented via some files and fifos
+in automatically created supervise/ subdirectory in each /etc/rc/DIR.
+
+(2) Creation of a new service can race with the rescanning of /etc/rc/
+by service monitor: service monitor may see a directory with only some files
+present. If it attempts to start the service in this state, all sorts
+of bad things may happen. This may be worked around by various
+heuristics in service monitor which give new service a few seconds
+of "grace time" to be fully populated; but this is not yet
+implemented in any of three packages.
+
+Daemons' output file descriptors are handled somewhat awkwardly
+by various daemontools implementations. For example, for runit tools,
+daemons' stdout goes to wherever runsdir's stdout was directied;
+stderr goes to runsvdir, which in turn "rotates" it on its command line
+(which is visible in ps output).
+
+Hopefully this get changed/standardized; while it is not, the "run" file
+should start with a
+
+ exec 2>&1
+
+command, making stderr equivalent to stdout.
+An especially primitive service which does not want its output to be logged
+with standard tools can do
+
+ exec >LOGFILE 2>&1
+
+or even
+
+ exec >/dev/null 2>&1
+
+To prevent creation of distro-specific log/ directory, a service directory
+in /etc/rc can contain an empty "log" file.
+
+
+ Controlling daemons
+
+The "svc" tool is available for admins and scripts to control services.
+In particular, often one service needs to control another:
+e.g. ifplugd can detect that the network cable was just plugged in,
+and it needs to (re)start DHCP service for this network device.
+
+The name of this tool is not standard either, which is an obvious problem.
+I propose to fix this by implementing a tool with fixed name and API by all
+daemontools clones. Lets use original daemontools name and API. Thus:
+
+The following form must work:
+
+ svc -udopchaitkx DIR
+
+Options map to up/down/once/STOP/CONT/HUP/ALRM/INT/TERM/KILL/exit
+commands to the daemon being controlled.
+
+The form with one option letter must work. If multiple-option form
+is supported, there is no guarantee in which order they take effect:
+svc -it DIR can deliver TERM and INT in any order.
+
+If more than one DIR can be specified (which is not a requirement),
+there is no guarantee in which order commands are sent to them.
+
+If DIR has no slash and is not "." or "..", it is assumed to be
+relative to the system-wide service directory.
+
+The "svok DIR" tool exits 0 if service is running, and nonzero if not.
+
+The "svstat DIR1 DIR2..." prints one human-readable line for each directory,
+saying whether supervise is successfully running in that directory,
+and reporting the status information maintained by supervise.
+
+Other tools with different names and APIs may exist; however
+for portability scripts should use the above tools.
+
+Creation of a new service on a running system should be done atomically.
+To this end, first create and populate a new /etc/rc/DIR.
+
+Then "activate" it by running ??????? - this copies (or symlinks,
+depending on the distro) its files to the "live" service directory,
+whereever it is located on this distro.
+
+Removal of the service should be done as follows:
+svc -d DIR [DIR/log], then remove the service directory
+(this makes service monitor SIGTERM per-directory supervisors
+(if they exist in the implementation))
+
+
+ Implementation details
+
+Top-level service monitor program name is not standardized.
+[svscan, runsvdir, s6-svscan ...]
+
+It may run one per-directory supervisor, or two supervisors
+(one for DIR/ and one for DIR/log/); for memory-constrained systems
+an implementation is possible which itself controls all services, without
+intermediate supervisors.
+[runsvdir runs one "runsv DIR" per DIR, runsv handles DIR/log/ if that exists]
+[svscan runs a pair of "superwise DIR" and "superwise DIR/log"]
+
+Directores are remembered by device+inode numbers, not names. Renaming a directory
+does not affect the running service (unless it is renamed to a .dotdir).
+
+Removal (or .dotdiring) of a directory sends SIGTERM to any running services.
+
+Standard output of non-logged services goes to standard output of service monitor.
+Standard output of logger services goes to standard output of service monitor.
+Standard error of them always goes to standard error of service monitor.
+
+If you want to log standard error of your logged service along with its stdout, use
+"exec 2>&1" in the beginning of your "run" script.
+
+Whether stdout/stderr of service monitor is discarded (>/dev/null)
+or logged in some way is system-dependent.
+
+
+ Containers
+
+[What do containers need?]
diff --git a/examples/var_service/dhcp_if/README b/examples/var_service/dhcp_if/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/dhcp_if/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/dhcp_if/convert2ntpconf b/examples/var_service/dhcp_if/convert2ntpconf
index debf1eb..e9d8293 100755
--- a/examples/var_service/dhcp_if/convert2ntpconf
+++ b/examples/var_service/dhcp_if/convert2ntpconf
@@ -29,6 +29,6 @@ test "$ip" || exit 1
{
for n in $ntpsrv; do
echo "let cfg=cfg+1"
- echo "ntpip[\$cfg]='$n'";
+ echo "ntpip[\$cfg]='$n'"
done
} >"$1"
diff --git a/examples/var_service/dhcp_if/dhcp_handler b/examples/var_service/dhcp_if/dhcp_handler
index 927e02a..3d2a5cb 100755
--- a/examples/var_service/dhcp_if/dhcp_handler
+++ b/examples/var_service/dhcp_if/dhcp_handler
@@ -36,7 +36,7 @@ service=${PWD##*/}
file_ipconf="$service.ipconf"
file_ntpconf="$service.ntpconf"
dir_ipconf="/var/run/service/fw"
-dir_ntpconf="/var/run/service/ntp"
+dir_ntpconf="/var/run/service/ntpd"
exec >/dev/null
#exec >>"$0.out" #debug
@@ -47,7 +47,7 @@ echo "`date`: Params: $*"
if test x"$1" != x"bound" && test x"$1" != x"renew" ; then
# Reconfigure network with this interface disabled
echo "Deconfiguring"
- rm "$service.out"
+ rm "env.out"
rm "$file_ipconf"
rm "$file_ntpconf"
rm "$dir_ipconf/$file_ipconf"
@@ -57,7 +57,8 @@ if test x"$1" != x"bound" && test x"$1" != x"renew" ; then
fi
# Bound: we've got the lease
-#env >"$service.out" # debug
+# Record information for e.g. dhcp_$IF_pinger service
+env >"env.out"
./convert2ipconf "$file_ipconf"
# Reconfigure routing and firewall if needed
@@ -69,7 +70,7 @@ if test $? != 0; then
sv u /var/service/fw
fi
-if test -d /var/service/ntp; then
+if test -d /var/service/ntpd; then
./convert2ntpconf "$file_ntpconf"
# Reconfigure ntp server addresses if needed
diff --brief "$file_ntpconf" "$dir_ntpconf/$file_ntpconf" >/dev/null 2>&1
@@ -77,7 +78,7 @@ if test -d /var/service/ntp; then
echo "Reconfiguring ntp"
mkdir -p "$dir_ntpconf" 2>/dev/null
cp "$file_ntpconf" "$dir_ntpconf/$file_ntpconf"
- sv t /var/service/ntp
- sv u /var/service/ntp
+ sv t /var/service/ntpd
+ sv u /var/service/ntpd
fi
fi
diff --git a/examples/var_service/dhcp_if/finish b/examples/var_service/dhcp_if/finish
new file mode 100755
index 0000000..5e7667a
--- a/dev/null
+++ b/examples/var_service/dhcp_if/finish
@@ -0,0 +1,17 @@
+#!/bin/sh
+# executed when service is taken down ("sv d .")
+
+service=${PWD##*/}
+file_ipconf="$service.ipconf"
+file_ntpconf="$service.ntpconf"
+dir_ipconf="/var/run/service/fw"
+dir_ntpconf="/var/run/service/ntpd"
+
+# Reconfigure network with this interface disabled
+echo "Finish: deconfiguring"
+rm "env.out"
+rm "$file_ipconf"
+rm "$file_ntpconf"
+rm "$dir_ipconf/$file_ipconf"
+rm "$dir_ntpconf/$file_ntpconf"
+sv u /var/service/fw
diff --git a/examples/var_service/dhcp_if/log/run b/examples/var_service/dhcp_if/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/dhcp_if/log/run
+++ b/examples/var_service/dhcp_if/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/dhcp_if_pinger/README b/examples/var_service/dhcp_if_pinger/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/dhcp_if_pinger/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/dhcp_if_pinger/run b/examples/var_service/dhcp_if_pinger/run
index 20b2fc5..e0e87a1 100755
--- a/examples/var_service/dhcp_if_pinger/run
+++ b/examples/var_service/dhcp_if_pinger/run
@@ -1,23 +1,47 @@
#!/bin/sh
-delay=67
-
+# How often to test, seconds
+ping_time=67
+# "One ping, must have reply in 1 sec"
+ping_opts="-c1 -W1 -w1"
+# If ping failed, how soon to retry
+retry_time=5
+# Reinit after this many consecutive ping error
+max_fail=5
+# Interface whose DHCP data to use
if=${PWD##*/dhcp_}
if=${if%%_pinger}
+msg() {
+ echo "`date '+%Y-%m-%d %H:%M:%S'` $*" >>"$0.log"
+}
+
if test -f "$0.log"; then
tail -999 "$0.log" >"$0.log.new"
mv "$0.log.new" "$0.log"
fi
-test -f "/var/service/dhcp_$if/dhcp_$if.out" || exec env - sleep "$delay"
-. "/var/service/dhcp_$if/dhcp_$if.out"
-test x"$router" != x"" || exec env - sleep "$delay"
+test -f "/var/service/dhcp_$if/env.out" || exec env - sleep "$ping_time"
-#echo "`date '+%Y-%m-%d %H:%M:%S'` Testing ping -c3 $router" >>"$0.log"
-ping -c3 "$router" && exec env - sleep "$delay"
+. "/var/service/dhcp_$if/env.out"
+test x"$router" != x"" || exec env - sleep "$ping_time"
-echo "`date '+%Y-%m-%d %H:%M:%S'` Restarting /var/service/dhcp_$if" >>"$0.log"
-sv t "/var/service/dhcp_$if"
+#msg "Pinging $router"
+failcnt=0
+while true; do
+ ping $ping_opts "$router" && exec env - sleep "$ping_time"
+ : $((failcnt++))
+ msg "Failed to ping $router, fail count:$failcnt"
+ test $failcnt -ge $max_fail && break
+ env - sleep "$retry_time"
+done
-exec env - sleep "$delay"
+test -d "/var/service/dhcp_$if" && {
+ msg "Restarting /var/service/dhcp_$if"
+ sv t "/var/service/dhcp_$if"
+}
+test -d "/var/service/supplicant_$if" && {
+ msg "Restarting /var/service/supplicant_$if"
+ sv t "/var/service/supplicant_$if"
+}
+exec env - sleep "$ping_time"
diff --git a/examples/var_service/dhcpd_if/README b/examples/var_service/dhcpd_if/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/dhcpd_if/log/run b/examples/var_service/dhcpd_if/log/run
new file mode 100755
index 0000000..69d74b7
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm -rf logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/examples/var_service/dhcpd_if/p_log b/examples/var_service/dhcpd_if/p_log
new file mode 100755
index 0000000..a2521be
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/examples/var_service/dhcpd_if/run b/examples/var_service/dhcpd_if/run
new file mode 100755
index 0000000..de85dec
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/run
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+exec 2>&1
+exec </dev/null
+
+pwd="$PWD"
+
+if="${PWD##*/dhcpd_}"
+
+echo "* Upping iface $if"
+ip link set dev $if up
+
+>>udhcpd.leases
+sed 's/^interface.*$/interface '"$if/" -i udhcpc.conf
+
+echo "* Starting udhcpd"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid root \
+udhcpd -f -vv udhcpc.conf
+
+exit $?
diff --git a/examples/var_service/dhcpd_if/udhcpc.conf b/examples/var_service/dhcpd_if/udhcpc.conf
new file mode 100644
index 0000000..a819259
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/udhcpc.conf
@@ -0,0 +1,28 @@
+# Directives with defaults:
+# start 192.168.0.20
+# end 192.168.0.254
+# interface eth0
+# max_leases 235
+# auto_time 7200
+# decline_time 3600
+# conflict_time 3600
+# offer_time 60
+# min_lease 60
+# lease_file /var/lib/misc/udhcpd.leases
+# pidfile /var/run/udhcpd.pid
+# siaddr 0.0.0.0
+#
+# Directives with no defaults (or with empty defaults):
+# option/opt NAME VALUE
+# notify_file /path/to/script_to_run_after_leasefile_is_written
+# (it is run with $1 = lease_file_name)
+# sname dhcp_packet_sname_field_contents
+# boot_file dhcp_packet_bootfile_field_contents
+# static_lease XX:XX:XX:XX:XX:XX IP.ADD.RE.SS
+
+interface if
+pidfile /dev/null
+lease_file udhcpd.leases
+option subnet 255.255.255.0
+option lease 3600
+#option router 192.168.0.1
diff --git a/examples/var_service/dhcpd_if/w_dumpleases b/examples/var_service/dhcpd_if/w_dumpleases
new file mode 100755
index 0000000..ff77205
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/w_dumpleases
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+watch -n1 'dumpleases -af udhcpd.leases'
diff --git a/examples/var_service/dhcpd_if/w_dumpleases_countdown b/examples/var_service/dhcpd_if/w_dumpleases_countdown
new file mode 100755
index 0000000..7fcd960
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/w_dumpleases_countdown
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+watch -n1 'dumpleases -f udhcpd.leases'
diff --git a/examples/var_service/dhcpd_if/w_log b/examples/var_service/dhcpd_if/w_log
new file mode 100755
index 0000000..dba76c6
--- a/dev/null
+++ b/examples/var_service/dhcpd_if/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir
+watch -n1 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b0-$((w-2))'
diff --git a/examples/var_service/ftpd/README b/examples/var_service/ftpd/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/ftpd/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/ftpd/log/run b/examples/var_service/ftpd/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/ftpd/log/run
+++ b/examples/var_service/ftpd/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/fw/README b/examples/var_service/fw/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/fw/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/fw/run b/examples/var_service/fw/run
index 396b678..1fd71cc 100755
--- a/examples/var_service/fw/run
+++ b/examples/var_service/fw/run
@@ -1,18 +1,20 @@
#!/bin/bash
# (using bashism: arrays)
-service="${PWD##*/}"
-rundir="/var/run/service/$service"
-
-user=root
-extif=if
-ext_open_tcp="21 22 80" # space-separated
+user="root"
+reset_all_netdevs=true
+preferred_default_route_iface="if"
+extif="if"
+ext_open_tcp="22 80 88" # space-separated
# Make ourself one-shot
sv o .
# Debug
#date '+%Y-%m-%d %H:%M:%S' >>"$0.log"
+service=`basename $PWD`
+rundir="/var/run/service/$service"
+
### filter This is the default table (if no -t option is passed). It contains
### the built-in chains INPUT (for packets coming into the box itself),
### FORWARD (for packets being routed through the box), and OUTPUT (for
@@ -62,7 +64,7 @@ umask 077
# Make sure rundir/ exists
mkdir -p "$rundir" 2>/dev/null
-chown -R "$user:" "$rundir"
+chown -R "$user": "$rundir"
chmod -R a=rX "$rundir"
rm -rf rundir 2>/dev/null
ln -s "$rundir" rundir
@@ -70,7 +72,6 @@ ln -s "$rundir" rundir
# Timestamping
date '+%Y-%m-%d %H:%M:%S'
-
echo; echo "* Reading IP config"
cfg=-1
# static cfg dhcp,zeroconf etc
@@ -86,11 +87,19 @@ echo; echo "* Configuring hardware"
#doit ethtool -K if rx off tx off sg off tso off
echo; echo "* Resetting address and routing info"
-doit ip a f dev lo
-i=0; while test "${if[$i]}"; do
- doit ip a f dev "${if[$i]}"
- doit ip r f dev "${if[$i]}" root 0/0
-let i++; done
+if $reset_all_netdevs; then
+ devs=`sed -n 's/ //g;s/:.*$//p' </proc/net/dev`
+ for iface in $devs; do
+ doit ip a f dev "$iface"
+ doit ip r f dev "$iface" root 0/0
+ done
+else
+ doit ip a f dev lo
+ i=0; while test "${if[$i]}"; do
+ doit ip a f dev "${if[$i]}"
+ doit ip r f dev "${if[$i]}" root 0/0
+ let i++; done
+fi
echo; echo "* Configuring addresses"
doit ip a a dev lo 127.0.0.1/8 scope host
@@ -103,7 +112,22 @@ i=0; while test "${if[$i]}"; do
let i++; done
echo; echo "* Configuring routes"
+# If several ifaces are configured via DHCP, they often both have 0/0 route.
+# They have no way of knowing that this route is offered on more than one iface.
+# Often, it's desirable to prefer one iface: say, wired eth over wireless.
+# if preferred_default_route_iface is not set, 0/0 route will be assigned randomly.
+if test "$preferred_default_route_iface"; then
+ i=0; while test "${if[$i]}"; do
+ if test "${if[$i]}" = "$preferred_default_route_iface" \
+ && test "${net[$i]}" = "0/0" \
+ && test "${gw[$i]}"; then
+ echo "+ default route through ${if[$i]}, ${gw[$i]}:"
+ doit ip r a "${net[$i]}" via "${gw[$i]}"
+ fi
+ let i++; done
+fi
i=0; while test "${if[$i]}"; do
+ #echo $i:"${if[$i]}"
if test "${net[$i]}" && test "${gw[$i]}"; then
doit ip r a "${net[$i]}" via "${gw[$i]}"
fi
diff --git a/examples/var_service/getty_tty1/README b/examples/var_service/getty_tty1/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/getty_tty1/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/gpm/README b/examples/var_service/gpm/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/gpm/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/httpd/README b/examples/var_service/httpd/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/httpd/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/httpd/log/run b/examples/var_service/httpd/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/httpd/log/run
+++ b/examples/var_service/httpd/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/ifplugd_if/README b/examples/var_service/ifplugd_if/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/ifplugd_if/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/ifplugd_if/log/run b/examples/var_service/ifplugd_if/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/ifplugd_if/log/run
+++ b/examples/var_service/ifplugd_if/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/ifplugd_if/run b/examples/var_service/ifplugd_if/run
index 2781cf9..c4f766e 100755
--- a/examples/var_service/ifplugd_if/run
+++ b/examples/var_service/ifplugd_if/run
@@ -8,6 +8,9 @@ pwd="$PWD"
if="${PWD##*/ifplugd_}"
+echo "* Upping iface $if"
+ip link set dev "$if" up
+
echo "* Starting ifplugd on $if [$$]"
exec \
env - PATH="$PATH" \
diff --git a/examples/var_service/inetd/README b/examples/var_service/inetd/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/inetd/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/inetd/log/run b/examples/var_service/inetd/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/inetd/log/run
+++ b/examples/var_service/inetd/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/nmeter/README b/examples/var_service/nmeter/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/nmeter/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/ntpd/README b/examples/var_service/ntpd/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/ntpd/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/ntpd/log/run b/examples/var_service/ntpd/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/ntpd/log/run
+++ b/examples/var_service/ntpd/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/ntpd/ntp.script b/examples/var_service/ntpd/ntp.script
index 76c34bf..8542181 100755
--- a/examples/var_service/ntpd/ntp.script
+++ b/examples/var_service/ntpd/ntp.script
@@ -10,12 +10,30 @@
dt=`date '+%Y-%m-%d %H:%M:%S'`
+echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
+
+if test x"$1" = x"unsync" \
+; then
+ # No replies for our NTP requests were seen for some time.
+ #
+ # Among more mundate cases like network outages, this happens
+ # if we ran for a LONG time (days) and ntp server's IP has changed.
+ # ntpd has no code to re-resolve peers' addresses to IPs,
+ # we need to help it:
+ #
+ echo "$dt: $1"\
+ "syncronization lost, restarting ntpd"\
+ >>"$0.log.$$"
+ mv -- "$0.log.$$" "$0.log"
+ kill $PPID
+ exit
+fi
+
if test x"$stratum" != x"" \
&& test x"$poll_interval" != x"" \
&& test 4 -ge "$stratum" \
&& test 128 -le "$poll_interval" \
; then
- echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
echo "$dt: $1"\
"freq_drift_ppm=$freq_drift_ppm"\
"offset=$offset"\
@@ -27,7 +45,6 @@ if test x"$stratum" != x"" \
exec hwclock --systohc
fi
-echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
echo "$dt: $1"\
"freq_drift_ppm=$freq_drift_ppm"\
"offset=$offset"\
diff --git a/examples/var_service/ntpd/p_log_important b/examples/var_service/ntpd/p_log_important
new file mode 100755
index 0000000..09b248f
--- a/dev/null
+++ b/examples/var_service/ntpd/p_log_important
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | grep -v -eolder -esending -esockets -e'reply from.*offset' -executing | $PAGER
diff --git a/examples/var_service/ntpd/run b/examples/var_service/ntpd/run
index 581d231..6f2a681 100755
--- a/examples/var_service/ntpd/run
+++ b/examples/var_service/ntpd/run
@@ -15,7 +15,7 @@ default_p_opt="-p 0.$pool -p 1.$pool -p 2.$pool -p 3.$pool"
# Make sure rundir/ exists
mkdir -p "$rundir" 2>/dev/null
-chown -R "$user:" "$rundir"
+chown -R "$user": "$rundir"
chmod -R a=rX "$rundir"
rm -rf rundir 2>/dev/null
ln -s "$rundir" rundir
diff --git a/examples/var_service/supplicant_if/README b/examples/var_service/supplicant_if/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/supplicant_if/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/supplicant_if/log/run b/examples/var_service/supplicant_if/log/run
new file mode 100755
index 0000000..69d74b7
--- a/dev/null
+++ b/examples/var_service/supplicant_if/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm -rf logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/examples/var_service/supplicant_if/p_log b/examples/var_service/supplicant_if/p_log
new file mode 100755
index 0000000..a2521be
--- a/dev/null
+++ b/examples/var_service/supplicant_if/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/examples/var_service/supplicant_if/run b/examples/var_service/supplicant_if/run
new file mode 100755
index 0000000..45211e0
--- a/dev/null
+++ b/examples/var_service/supplicant_if/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+exec 2>&1
+exec </dev/null
+
+pwd="$PWD"
+
+if="${PWD##*/dhcp_}"
+
+echo "* Upping iface $if"
+ip link set dev "$if" up
+
+##echo "* Powersave disable on $if"
+##iw dev "$if" set power_save off
+
+echo "* Starting wpa_supplicant"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid root \
+wpa_supplicant -i "$if" -c "$pwd/wpa_supplicant.conf" -d
diff --git a/examples/var_service/supplicant_if/w_log b/examples/var_service/supplicant_if/w_log
new file mode 100755
index 0000000..aa36ef1
--- a/dev/null
+++ b/examples/var_service/supplicant_if/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/examples/var_service/supplicant_if/wpa_supplicant.conf b/examples/var_service/supplicant_if/wpa_supplicant.conf
new file mode 100644
index 0000000..e317e2e
--- a/dev/null
+++ b/examples/var_service/supplicant_if/wpa_supplicant.conf
@@ -0,0 +1,28 @@
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=DIR=/var/run/wpa_supplicant
+#GROUP=wheel
+
+# Typical minimal wifi setup:
+network={
+ ssid="--your-ESSID--"
+ key_mgmt=WPA-PSK
+ psk="--your-password--"
+}
+
+# Other fields:
+# scan_ssid=1
+# key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+# pairwise=CCMP TKIP
+# group=CCMP TKIP WEP104 WEP40
+# eap=TTLS PEAP TLS
+# identity="user@example.com"
+# password="foobar"
+# ca_cert="/etc/cert/ca.pem"
+# client_cert="/etc/cert/user.pem"
+# private_key="/etc/cert/user.prv"
+# private_key_passwd="password"
+# phase1="peaplabel=0"
+# ca_cert2="/etc/cert/ca2.pem"
+# client_cert2="/etc/cer/user.pem"
+# private_key2="/etc/cer/user.prv"
+# private_key2_passwd="password"
diff --git a/examples/var_service/tftpd/README b/examples/var_service/tftpd/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/tftpd/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/tftpd/log/run b/examples/var_service/tftpd/log/run
index 560d1b1..69d74b7 100755
--- a/examples/var_service/tftpd/log/run
+++ b/examples/var_service/tftpd/log/run
@@ -6,7 +6,7 @@ logdir="/var/log/service/`(cd ..;basename $PWD)`"
mkdir -p "$logdir" 2>/dev/null
chown -R "$user": "$logdir"
chmod -R go-rwxst,u+rwX "$logdir"
-rm logdir
+rm -rf logdir
ln -s "$logdir" logdir
# make this dir accessible to logger
diff --git a/examples/var_service/zcip_if/README b/examples/var_service/zcip_if/README
new file mode 100644
index 0000000..4ddccb2
--- a/dev/null
+++ b/examples/var_service/zcip_if/README
@@ -0,0 +1,5 @@
+The real README file is one directory up.
+
+This directory's run script can have useful comments.
+If it doesn't but you feel it should, please send a patch
+to busybox's mailing list.
diff --git a/examples/var_service/zcip_if/convert2ipconf b/examples/var_service/zcip_if/convert2ipconf
new file mode 100755
index 0000000..c858723
--- a/dev/null
+++ b/examples/var_service/zcip_if/convert2ipconf
@@ -0,0 +1,24 @@
+#!/bin/sh
+# convert:
+
+#interface=eth1
+#ip=169.254.x.y
+
+# into:
+
+#let cfg=cfg+1
+#if[$cfg]=...; ip[$cfg]=...; ipmask[$cfg]=.../...; gw[$cfg]=...; net[$cfg]=... dns[$cfg]=...
+
+exec >/dev/null
+#exec >"$0.out" # debug
+exec 2>&1
+
+test "$interface" || exit 1
+test "$ip" || exit 1
+
+{
+echo "let cfg=cfg+1"
+test "$interface" && echo "if[\$cfg]='$interface'"
+test "$ip" && echo "ip[\$cfg]='$ip'"
+test "$ip" && echo "ipmask[\$cfg]='$ip/16'"
+} >"$1"
diff --git a/examples/var_service/zcip_if/finish b/examples/var_service/zcip_if/finish
new file mode 100755
index 0000000..95995cf
--- a/dev/null
+++ b/examples/var_service/zcip_if/finish
@@ -0,0 +1,13 @@
+#!/bin/sh
+# executed when service is taken down ("sv d .")
+
+service=${PWD##*/}
+file_ipconf="$service.ipconf"
+dir_ipconf="/var/run/service/fw"
+
+# Reconfigure network with this interface disabled
+echo "Finish: deconfiguring"
+rm "env.out"
+rm "$file_ipconf"
+rm "$dir_ipconf/$file_ipconf"
+sv u /var/service/fw
diff --git a/examples/var_service/zcip_if/log/run b/examples/var_service/zcip_if/log/run
new file mode 100755
index 0000000..69d74b7
--- a/dev/null
+++ b/examples/var_service/zcip_if/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm -rf logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/examples/var_service/zcip_if/p_log b/examples/var_service/zcip_if/p_log
new file mode 100755
index 0000000..a2521be
--- a/dev/null
+++ b/examples/var_service/zcip_if/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/examples/var_service/zcip_if/run b/examples/var_service/zcip_if/run
new file mode 100755
index 0000000..94a8754
--- a/dev/null
+++ b/examples/var_service/zcip_if/run
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+exec 2>&1
+exec </dev/null
+
+pwd="$PWD"
+
+if="${PWD##*/zcip_}"
+
+echo "* Upping iface $if"
+ip link set dev "$if" up
+
+echo "* Starting zcip"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid root \
+zcip -fvv \
+ "$if" \
+ "$pwd/zcip_handler"
diff --git a/examples/var_service/zcip_if/w_log b/examples/var_service/zcip_if/w_log
new file mode 100755
index 0000000..aa36ef1
--- a/dev/null
+++ b/examples/var_service/zcip_if/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/examples/var_service/zcip_if/zcip_handler b/examples/var_service/zcip_if/zcip_handler
new file mode 100755
index 0000000..3c6ca78
--- a/dev/null
+++ b/examples/var_service/zcip_if/zcip_handler
@@ -0,0 +1,47 @@
+#!/bin/sh
+# executed by zcip
+# parameters: $1 and environment
+# $1 is:
+#
+# init: zcip starts. Environment:
+# interface=eth0
+#
+# config: Address is obtained.
+# interface=eth0
+# ip=169.254.a.b
+#
+# deconfig: Conflict or link went down.
+# interface=eth0
+
+service=${PWD##*/}
+file_ipconf="$service.ipconf"
+dir_ipconf="/var/run/service/fw"
+
+exec >/dev/null
+#exec >>"$0.out" #debug
+exec 2>&1
+
+echo "`date`: Params: $*"
+
+if test x"$1" != x"config"; then
+ # Reconfigure network with this interface disabled
+ echo "Deconfiguring"
+ rm "$file_ipconf"
+ rm "$dir_ipconf/$file_ipconf"
+ sv u /var/service/fw
+ exit
+fi
+
+# "config": we've got the address
+# Record information for e.g. dhcp_$IF_pinger service
+env >"env.out"
+
+./convert2ipconf "$file_ipconf"
+# Reconfigure routing and firewall if needed
+diff --brief "$file_ipconf" "$dir_ipconf/$file_ipconf" >/dev/null 2>&1
+if test $? != 0; then
+ echo "Reconfiguring fw"
+ mkdir -p "$dir_ipconf" 2>/dev/null
+ cp "$file_ipconf" "$dir_ipconf/$file_ipconf"
+ sv u /var/service/fw
+fi
diff --git a/findutils/find.c b/findutils/find.c
index 4c9f9d8..4f976d5 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -137,6 +137,16 @@
//config: Support the 'find -exec' option for executing commands based upon
//config: the files matched.
//config:
+//config:config FEATURE_FIND_EXEC_PLUS
+//config: bool "Enable -exec ... {} +"
+//config: default y
+//config: depends on FEATURE_FIND_EXEC
+//config: help
+//config: Support the 'find -exec ... {} +' option for executing commands
+//config: for all matched files at once.
+//config: Without this option, -exec + is a synonym for -exec ;
+//config: (IOW: it works correctly, but without expected speedup)
+//config:
//config:config FEATURE_FIND_USER
//config: bool "Enable -user: username/uid matching"
//config: default y
@@ -319,6 +329,9 @@
//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by"
//usage: "\n file name. Fails if CMD exits with nonzero"
//usage: )
+//usage: IF_FEATURE_FIND_EXEC_PLUS(
+//usage: "\n -exec CMD ARG + Run CMD with {} replaced by list of file names"
+//usage: )
//usage: IF_FEATURE_FIND_DELETE(
//usage: "\n -delete Delete current file/directory. Turns on -depth option"
//usage: )
@@ -329,6 +342,7 @@
#include <fnmatch.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_FIND_REGEX
# include "xregex.h"
#endif
@@ -337,8 +351,12 @@
# define FNM_CASEFOLD 0
#endif
-#define dbg(...) ((void)0)
-/* #define dbg(...) bb_error_msg(__VA_ARGS__) */
+#if 1
+# define dbg(...) ((void)0)
+#else
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#endif
+
/* This is a NOEXEC applet. Be very careful! */
@@ -375,7 +393,20 @@ IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
IF_FEATURE_FIND_PRUNE( ACTS(prune))
IF_FEATURE_FIND_DELETE( ACTS(delete))
-IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;))
+IF_FEATURE_FIND_EXEC( ACTS(exec,
+ char **exec_argv; /* -exec ARGS */
+ unsigned *subst_count;
+ int exec_argc; /* count of ARGS */
+ IF_FEATURE_FIND_EXEC_PLUS(
+ /*
+ * filelist is NULL if "exec ;"
+ * non-NULL if "exec +"
+ */
+ char **filelist;
+ int filelist_idx;
+ int file_len;
+ )
+ ))
IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; unsigned links_count;))
@@ -389,49 +420,20 @@ struct globals {
smallint need_print;
smallint xdev_on;
recurse_flags_t recurse_flags;
+ IF_FEATURE_FIND_EXEC_PLUS(unsigned max_argv_len;)
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
- struct G_sizecheck { \
- char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
- }; \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
/* we have to zero it out because of NOEXEC */ \
memset(&G, 0, sizeof(G)); \
IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
+ IF_FEATURE_FIND_EXEC_PLUS(G.max_argv_len = bb_arg_max() - 2048;) \
G.need_print = 1; \
G.recurse_flags = ACTION_RECURSE; \
} while (0)
-#if ENABLE_FEATURE_FIND_EXEC
-static unsigned count_subst(const char *str)
-{
- unsigned count = 0;
- while ((str = strstr(str, "{}")) != NULL) {
- count++;
- str++;
- }
- return count;
-}
-
-
-static char* subst(const char *src, unsigned count, const char* filename)
-{
- char *buf, *dst, *end;
- size_t flen = strlen(filename);
- /* we replace each '{}' with filename: growth by strlen-2 */
- buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
- while ((end = strstr(src, "{}"))) {
- memcpy(dst, src, end - src);
- dst += end - src;
- src = end + 2;
- memcpy(dst, filename, flen);
- dst += flen;
- }
- strcpy(dst, src);
- return buf;
-}
-#endif
-
/* Return values of ACTFs ('action functions') are a bit mask:
* bit 1=1: prune (use SKIP constant for setting it)
* bit 0=1: matched successfully (TRUE)
@@ -482,7 +484,6 @@ static int exec_actions(action ***appp, const char *fileName, const struct stat
return rc ^ TRUE; /* restore TRUE bit */
}
-
#if !FNM_CASEFOLD
static char *strcpy_upcase(char *dst, const char *src)
{
@@ -501,26 +502,54 @@ static char *strcpy_upcase(char *dst, const char *src)
ACTF(name)
{
+ int r;
const char *tmp = bb_basename(fileName);
- if (tmp != fileName && *tmp == '\0') {
- /* "foo/bar/". Oh no... go back to 'b' */
- tmp--;
- while (tmp != fileName && *--tmp != '/')
- continue;
- if (*tmp == '/')
- tmp++;
+ /* GNU findutils: find DIR/ -name DIR
+ * prints "DIR/" (DIR// prints "DIR//" etc).
+ * Need to strip trailing "/".
+ * Such names can come only from top-level names, but
+ * we can't do this before recursive_action() call,
+ * since then "find FILE/ -name FILE"
+ * would also work (on non-directories), which is wrong.
+ */
+ char *trunc_slash = NULL;
+
+ if (*tmp == '\0') {
+ /* "foo/bar/[//...]" */
+ while (tmp != fileName && tmp[-1] == '/')
+ tmp--;
+ if (tmp == fileName) { /* entire fileName is "//.."? */
+ /* yes, convert "//..." to "/"
+ * Testcases:
+ * find / -maxdepth 1 -name /: prints /
+ * find // -maxdepth 1 -name /: prints //
+ * find / -maxdepth 1 -name //: prints nothing
+ * find // -maxdepth 1 -name //: prints nothing
+ */
+ if (tmp[1])
+ trunc_slash = (char*)tmp + 1;
+ } else {
+ /* no, it's "foo/bar/[//...]", go back to 'b' */
+ trunc_slash = (char*)tmp;
+ while (tmp != fileName && tmp[-1] != '/')
+ tmp--;
+ }
}
+
/* Was using FNM_PERIOD flag too,
* but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
* find -name '*foo' should match .foo too:
*/
+ if (trunc_slash) *trunc_slash = '\0';
#if FNM_CASEFOLD
- return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
+ r = fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0));
#else
if (ap->iname)
tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
- return fnmatch(ap->pattern, tmp, 0) == 0;
+ r = fnmatch(ap->pattern, tmp, 0);
#endif
+ if (trunc_slash) *trunc_slash = '/';
+ return r == 0;
}
#if ENABLE_FEATURE_FIND_PATH
@@ -557,8 +586,8 @@ ACTF(type)
#if ENABLE_FEATURE_FIND_PERM
ACTF(perm)
{
- /* -perm +mode: at least one of perm_mask bits are set */
- if (ap->perm_char == '+')
+ /* -perm [+/]mode: at least one of perm_mask bits are set */
+ if (ap->perm_char == '+' || ap->perm_char == '/')
return (statbuf->st_mode & ap->perm_mask) != 0;
/* -perm -mode: all of perm_mask are set */
if (ap->perm_char == '-')
@@ -606,17 +635,57 @@ ACTF(inum)
}
#endif
#if ENABLE_FEATURE_FIND_EXEC
-ACTF(exec)
+static int do_exec(action_exec *ap, const char *fileName)
{
int i, rc;
-#if ENABLE_USE_PORTABLE_CODE
- char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
-#else /* gcc 4.3.1 generates smaller code: */
- char *argv[ap->exec_argc + 1];
-#endif
- for (i = 0; i < ap->exec_argc; i++)
- argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
- argv[i] = NULL; /* terminate the list */
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ int size = ap->exec_argc + ap->filelist_idx + 1;
+# else
+ int size = ap->exec_argc + 1;
+# endif
+# if ENABLE_USE_PORTABLE_CODE
+ char **argv = alloca(sizeof(char*) * size);
+# else /* gcc 4.3.1 generates smaller code: */
+ char *argv[size];
+# endif
+ char **pp = argv;
+
+ for (i = 0; i < ap->exec_argc; i++) {
+ const char *arg = ap->exec_argv[i];
+
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ if (ap->filelist) {
+ /* Handling "-exec +"
+ * Only one exec_argv[i] has substitution in it.
+ * Expand that one exec_argv[i] into file list.
+ */
+ if (ap->subst_count[i] == 0) {
+ *pp++ = xstrdup(arg);
+ } else {
+ int j = 0;
+ while (ap->filelist[j]) {
+ /* 2nd arg here should be ap->subst_count[i], but it is always 1: */
+ *pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]);
+ free(ap->filelist[j]);
+ j++;
+ }
+ }
+ } else
+# endif
+ {
+ /* Handling "-exec ;" */
+ *pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName);
+ }
+ }
+ *pp = NULL; /* terminate the list */
+
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ if (ap->filelist) {
+ ap->filelist[0] = NULL;
+ ap->filelist_idx = 0;
+ ap->file_len = 0;
+ }
+# endif
rc = spawn_and_wait(argv);
if (rc < 0)
@@ -627,6 +696,48 @@ ACTF(exec)
free(argv[i++]);
return rc == 0; /* return 1 if exitcode 0 */
}
+ACTF(exec)
+{
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ if (ap->filelist) {
+ int rc;
+
+ ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx);
+ ap->filelist[ap->filelist_idx++] = xstrdup(fileName);
+ ap->file_len += strlen(fileName) + sizeof(char*) + 1;
+ /* If we have lots of files already, exec the command */
+ rc = 1;
+ if (ap->file_len >= G.max_argv_len)
+ rc = do_exec(ap, NULL);
+ return rc;
+ }
+# endif
+ return do_exec(ap, fileName);
+}
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+static int flush_exec_plus(void)
+{
+ action *ap;
+ action **app;
+ action ***appp = G.actions;
+ while ((app = *appp++) != NULL) {
+ while ((ap = *app++) != NULL) {
+ if (ap->f == (action_fp)func_exec) {
+ action_exec *ae = (void*)ap;
+ if (ae->filelist_idx != 0) {
+ int rc = do_exec(ae, NULL);
+# if ENABLE_FEATURE_FIND_NOT
+ if (ap->invert) rc = !rc;
+# endif
+ if (rc == 0)
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+# endif
#endif
#if ENABLE_FEATURE_FIND_USER
ACTF(user)
@@ -685,7 +796,10 @@ ACTF(delete)
{
int rc;
if (S_ISDIR(statbuf->st_mode)) {
- rc = rmdir(fileName);
+ /* "find . -delete" skips rmdir(".") */
+ rc = 0;
+ if (NOT_LONE_CHAR(fileName, '.'))
+ rc = rmdir(fileName);
} else {
rc = unlink(fileName);
}
@@ -798,7 +912,7 @@ static int find_type(const char *type)
mask = S_IFSOCK;
if (mask == 0 || type[1] != '\0')
- bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, type, "-type");
return mask;
}
@@ -1067,6 +1181,7 @@ static action*** parse_params(char **argv)
else if (parm == PARM_exec) {
int i;
action_exec *ap;
+ IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;)
dbg("%d", __LINE__);
G.need_print = 0;
ap = ALLOC_ACTION(exec);
@@ -1079,10 +1194,13 @@ static action*** parse_params(char **argv)
// executes "echo Foo >FILENAME<",
// find -exec echo Foo ">{}<" "+"
// executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
- // TODO (so far we treat "+" just like ";")
if ((argv[0][0] == ';' || argv[0][0] == '+')
&& argv[0][1] == '\0'
) {
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ if (argv[0][0] == '+')
+ ap->filelist = xzalloc(sizeof(ap->filelist[0]));
+# endif
break;
}
argv++;
@@ -1092,8 +1210,17 @@ static action*** parse_params(char **argv)
bb_error_msg_and_die(bb_msg_requires_arg, arg);
ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
i = ap->exec_argc;
- while (i--)
- ap->subst_count[i] = count_subst(ap->exec_argv[i]);
+ while (i--) {
+ ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}");
+ IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];)
+ }
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ /*
+ * coreutils expects {} to appear only once in "-exec +"
+ */
+ if (all_subst != 1 && ap->filelist)
+ bb_error_msg_and_die("only one '{}' allowed for -exec +");
+# endif
}
#endif
#if ENABLE_FEATURE_FIND_PAREN
@@ -1156,16 +1283,17 @@ static action*** parse_params(char **argv)
/* -perm BITS File's mode bits are exactly BITS (octal or symbolic).
* Symbolic modes use mode 0 as a point of departure.
* -perm -BITS All of the BITS are set in file's mode.
- * -perm +BITS At least one of the BITS is set in file's mode.
+ * -perm [+/]BITS At least one of the BITS is set in file's mode.
*/
else if (parm == PARM_perm) {
action_perm *ap;
dbg("%d", __LINE__);
ap = ALLOC_ACTION(perm);
ap->perm_char = arg1[0];
- arg1 = plus_minus_num(arg1);
+ arg1 = (arg1[0] == '/' ? arg1+1 : plus_minus_num(arg1));
/*ap->perm_mask = 0; - ALLOC_ACTION did it */
- if (!bb_parse_mode(arg1, &ap->perm_mask))
+ ap->perm_mask = bb_parse_mode(arg1, ap->perm_mask);
+ if (ap->perm_mask == (mode_t)-1)
bb_error_msg_and_die("invalid mode '%s'", arg1);
}
#endif
@@ -1364,9 +1492,10 @@ int find_main(int argc UNUSED_PARAM, char **argv)
NULL, /* user data */
0) /* depth */
) {
- status = EXIT_FAILURE;
+ status |= EXIT_FAILURE;
}
}
+ IF_FEATURE_FIND_EXEC_PLUS(status |= flush_exec_plus();)
return status;
}
diff --git a/findutils/grep.c b/findutils/grep.c
index 22101f3..547e3d5 100644
--- a/findutils/grep.c
+++ b/findutils/grep.c
@@ -18,54 +18,49 @@
* (C) 2006 Jac Goudsmit added -o option
*/
-//applet:IF_GREP(APPLET(grep, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, BB_DIR_BIN, BB_SUID_DROP, egrep))
-//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, BB_DIR_BIN, BB_SUID_DROP, fgrep))
-
-//kbuild:lib-$(CONFIG_GREP) += grep.o
-
//config:config GREP
//config: bool "grep"
//config: default y
//config: help
//config: grep is used to search files for a specified pattern.
//config:
-//config:config FEATURE_GREP_EGREP_ALIAS
-//config: bool "Enable extended regular expressions (egrep & grep -E)"
+//config:config EGREP
+//config: bool "egrep"
//config: default y
-//config: depends on GREP
//config: help
-//config: Enabled support for extended regular expressions. Extended
-//config: regular expressions allow for alternation (foo|bar), grouping,
-//config: and various repetition operators.
+//config: Alias to "grep -E"
//config:
-//config:config FEATURE_GREP_FGREP_ALIAS
-//config: bool "Alias fgrep to grep -F"
+//config:config FGREP
+//config: bool "fgrep"
//config: default y
-//config: depends on GREP
//config: help
-//config: fgrep sees the search pattern as a normal string rather than
-//config: regular expressions.
-//config: grep -F always works, this just creates the fgrep alias.
+//config: Alias to "grep -F"
//config:
//config:config FEATURE_GREP_CONTEXT
//config: bool "Enable before and after context flags (-A, -B and -C)"
//config: default y
-//config: depends on GREP
+//config: depends on GREP || EGREP
//config: help
//config: Print the specified number of leading (-B) and/or trailing (-A)
//config: context surrounding our matching lines.
//config: Print the specified number of context lines (-C).
+//applet:IF_GREP(APPLET(grep, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_EGREP(APPLET_ODDNAME(egrep, grep, BB_DIR_BIN, BB_SUID_DROP, egrep))
+//applet:IF_FGREP(APPLET_ODDNAME(fgrep, grep, BB_DIR_BIN, BB_SUID_DROP, fgrep))
+
+//kbuild:lib-$(CONFIG_GREP) += grep.o
+//kbuild:lib-$(CONFIG_EGREP) += grep.o
+//kbuild:lib-$(CONFIG_FGREP) += grep.o
+
#include "libbb.h"
+#include "common_bufsiz.h"
#include "xregex.h"
/* options */
//usage:#define grep_trivial_usage
-//usage: "[-HhnlLoqvsriw"
-//usage: "F"
-//usage: IF_FEATURE_GREP_EGREP_ALIAS("E")
+//usage: "[-HhnlLoqvsriwFE"
//usage: IF_EXTRA_COMPAT("z")
//usage: "] [-m N] "
//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
@@ -87,9 +82,7 @@
//usage: "\n -w Match whole words only"
//usage: "\n -x Match whole lines only"
//usage: "\n -F PATTERN is a literal (not regexp)"
-//usage: IF_FEATURE_GREP_EGREP_ALIAS(
//usage: "\n -E PATTERN is an extended regexp"
-//usage: )
//usage: IF_EXTRA_COMPAT(
//usage: "\n -z Input is NUL terminated"
//usage: )
@@ -114,9 +107,9 @@
//usage:#define fgrep_full_usage ""
#define OPTSTR_GREP \
- "lnqvscFiHhe:f:Lorm:wx" \
- IF_FEATURE_GREP_CONTEXT("A:B:C:") \
- IF_FEATURE_GREP_EGREP_ALIAS("E") \
+ "lnqvscFiHhe:*f:*Lorm:+wx" \
+ IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \
+ "E" \
IF_EXTRA_COMPAT("z") \
"aI"
/* ignored: -a "assume all files to be text" */
@@ -143,7 +136,7 @@ enum {
IF_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */
IF_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */
IF_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */
- IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
+ OPTBIT_E, /* extended regexp */
IF_EXTRA_COMPAT( OPTBIT_z ,) /* input is NUL terminated */
OPT_l = 1 << OPTBIT_l,
OPT_n = 1 << OPTBIT_n,
@@ -166,7 +159,7 @@ enum {
OPT_A = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0,
OPT_B = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0,
OPT_C = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0,
- OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
+ OPT_E = 1 << OPTBIT_E,
OPT_z = IF_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0,
};
@@ -201,11 +194,10 @@ struct globals {
llist_t *pattern_head; /* growable list of patterns to match */
const char *cur_file; /* the current file we are reading */
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
- struct G_sizecheck { \
- char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
- }; \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
} while (0)
#define max_matches (G.max_matches )
#if !ENABLE_EXTRA_COMPAT
@@ -247,7 +239,7 @@ typedef struct grep_list_data_t {
#endif
#define ALLOCATED 1
#define COMPILED 2
- int flg_mem_alocated_compiled;
+ int flg_mem_allocated_compiled;
} grep_list_data_t;
#if !ENABLE_EXTRA_COMPAT
@@ -337,7 +329,7 @@ static int grep_file(FILE *file)
#endif
) {
llist_t *pattern_ptr = pattern_head;
- static grep_list_data_t *gl;
+ grep_list_data_t *gl = gl; /* for gcc */
linenum++;
found = 0;
@@ -375,11 +367,13 @@ static int grep_file(FILE *file)
} else {
#if ENABLE_EXTRA_COMPAT
unsigned start_pos;
+#else
+ int match_flg;
#endif
char *match_at;
- if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
- gl->flg_mem_alocated_compiled |= COMPILED;
+ if (!(gl->flg_mem_allocated_compiled & COMPILED)) {
+ gl->flg_mem_allocated_compiled |= COMPILED;
#if !ENABLE_EXTRA_COMPAT
xregcomp(&gl->compiled_regex, gl->pattern, reflags);
#else
@@ -392,6 +386,7 @@ static int grep_file(FILE *file)
#if !ENABLE_EXTRA_COMPAT
gl->matched_range.rm_so = 0;
gl->matched_range.rm_eo = 0;
+ match_flg = 0;
#else
start_pos = 0;
#endif
@@ -400,7 +395,7 @@ static int grep_file(FILE *file)
//bb_error_msg("'%s' start_pos:%d line_len:%d", match_at, start_pos, line_len);
if (
#if !ENABLE_EXTRA_COMPAT
- regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, 0) == 0
+ regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, match_flg) == 0
#else
re_search(&gl->compiled_regex, match_at, line_len,
start_pos, /*range:*/ line_len,
@@ -415,13 +410,15 @@ static int grep_file(FILE *file)
found = 1;
} else {
char c = ' ';
- if (gl->matched_range.rm_so)
+ if (match_at > line || gl->matched_range.rm_so != 0) {
c = match_at[gl->matched_range.rm_so - 1];
+ }
if (!isalnum(c) && c != '_') {
c = match_at[gl->matched_range.rm_eo];
- if (!c || (!isalnum(c) && c != '_')) {
- found = 1;
- } else {
+ }
+ if (!isalnum(c) && c != '_') {
+ found = 1;
+ } else {
/*
* Why check gl->matched_range.rm_eo?
* Zero-length match makes -w skip the line:
@@ -430,17 +427,17 @@ static int grep_file(FILE *file)
* Without such check, we can loop forever.
*/
#if !ENABLE_EXTRA_COMPAT
- if (gl->matched_range.rm_eo != 0) {
- match_at += gl->matched_range.rm_eo;
- goto opt_w_again;
- }
+ if (gl->matched_range.rm_eo != 0) {
+ match_at += gl->matched_range.rm_eo;
+ match_flg |= REG_NOTBOL;
+ goto opt_w_again;
+ }
#else
- if (gl->matched_range.rm_eo > start_pos) {
- start_pos = gl->matched_range.rm_eo;
- goto opt_w_again;
- }
-#endif
+ if (gl->matched_range.rm_eo > start_pos) {
+ start_pos = gl->matched_range.rm_eo;
+ goto opt_w_again;
}
+#endif
}
}
}
@@ -614,9 +611,9 @@ static char *add_grep_list_data(char *pattern)
grep_list_data_t *gl = xzalloc(sizeof(*gl));
gl->pattern = pattern;
#if ENABLE_FEATURE_CLEAN_UP
- gl->flg_mem_alocated_compiled = flg_used_mem;
+ gl->flg_mem_allocated_compiled = flg_used_mem;
#else
- /*gl->flg_mem_alocated_compiled = 0;*/
+ /*gl->flg_mem_allocated_compiled = 0;*/
#endif
return (char *)gl;
}
@@ -663,7 +660,7 @@ static int grep_dir(const char *dir)
int matched = 0;
recursive_action(dir,
/* recurse=yes */ ACTION_RECURSE |
- /* followLinks=no */
+ /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 |
/* depthFirst=yes */ ACTION_DEPTHFIRST,
/* fileAction= */ file_action_grep,
/* dirAction= */ NULL,
@@ -678,14 +675,19 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
FILE *file;
int matched;
llist_t *fopt = NULL;
-
- /* do normal option parsing */
#if ENABLE_FEATURE_GREP_CONTEXT
int Copt, opts;
+#endif
+ INIT_G();
+
+ /* For grep, exitcode of 1 is "not found". Other errors are 2: */
+ xfunc_error_retval = 2;
+ /* do normal option parsing */
+#if ENABLE_FEATURE_GREP_CONTEXT
/* -H unsets -h; -C unsets -A,-B; -e,-f are lists;
* -m,-A,-B,-C have numeric param */
- opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
+ opt_complementary = "H-h:C-AB";
opts = getopt32(argv,
OPTSTR_GREP,
&pattern_head, &fopt, &max_matches,
@@ -705,7 +707,7 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
lines_before = 0;
lines_after = 0;
} else if (lines_before > 0) {
- if ((unsigned) lines_before > (unsigned) (INT_MAX / sizeof(long long)))
+ if (lines_before > INT_MAX / sizeof(long long))
lines_before = INT_MAX / sizeof(long long);
/* overflow in (lines_before * sizeof(x)) is prevented (above) */
before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
@@ -714,7 +716,7 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
#else
/* with auto sanity checks */
/* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
- opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
+ opt_complementary = "H-h:c-n:q-n:l-n:";
getopt32(argv, OPTSTR_GREP,
&pattern_head, &fopt, &max_matches);
#endif
@@ -734,7 +736,7 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
}
}
- if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
+ if (ENABLE_FGREP && applet_name[0] == 'f')
option_mask32 |= OPT_F;
#if !ENABLE_EXTRA_COMPAT
@@ -742,8 +744,8 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
reflags = REG_NOSUB;
#endif
- if (ENABLE_FEATURE_GREP_EGREP_ALIAS
- && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
+ if ((ENABLE_EGREP && applet_name[0] == 'e')
+ || (option_mask32 & OPT_E)
) {
reflags |= REG_EXTENDED;
}
@@ -827,9 +829,9 @@ int grep_main(int argc UNUSED_PARAM, char **argv)
grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
pattern_head = pattern_head->link;
- if (gl->flg_mem_alocated_compiled & ALLOCATED)
+ if (gl->flg_mem_allocated_compiled & ALLOCATED)
free(gl->pattern);
- if (gl->flg_mem_alocated_compiled & COMPILED)
+ if (gl->flg_mem_allocated_compiled & COMPILED)
regfree(&gl->compiled_regex);
free(gl);
free(pattern_head_ptr);
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 0d1bb43..ae01a49 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -53,12 +53,20 @@
//config: Support -0: input items are terminated by a NUL character
//config: instead of whitespace, and the quotes and backslash
//config: are not special.
+//config:
+//config:config FEATURE_XARGS_SUPPORT_REPL_STR
+//config: bool "Enable -I STR: string to replace"
+//config: default y
+//config: depends on XARGS
+//config: help
+//config: Support -I STR and -i[STR] options.
//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, BB_DIR_USR_BIN, BB_SUID_DROP, xargs))
//kbuild:lib-$(CONFIG_XARGS) += xargs.o
#include "libbb.h"
+#include "common_bufsiz.h"
/* This is a NOEXEC applet. Be very careful! */
@@ -85,19 +93,23 @@
struct globals {
char **args;
+#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
+ char **argv;
+ const char *repl_str;
+ char eol_ch;
+#endif
const char *eof_str;
int idx;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
} while (0)
-/*
- * This function has special algorithm.
- * Don't use fork and include to main!
- */
static int xargs_exec(void)
{
int status;
@@ -112,7 +124,7 @@ static int xargs_exec(void)
return 124;
}
if (status >= 0x180) {
- bb_error_msg("%s: terminated by signal %d",
+ bb_error_msg("'%s' terminated by signal %d",
G.args[0], status - 0x180);
return 125;
}
@@ -141,7 +153,7 @@ static void store_param(char *s)
* is seen, store the address of a new parameter to args[].
* If reading discovers that last chars do not form the complete
* parameter, the pointer to the first such "tail character" is returned.
- * (buf has extra byte at the end to accomodate terminating NUL
+ * (buf has extra byte at the end to accommodate terminating NUL
* of "tail characters" string).
* Otherwise, the returned pointer points to NUL byte.
* On entry, buf[] may contain some "seed chars" which are to become
@@ -301,7 +313,7 @@ static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
c = '\0';
}
*p++ = c;
- if (c == '\0') { /* word's delimiter or EOF detected */
+ if (c == '\0') { /* NUL or EOF detected */
/* A full word is loaded */
store_param(s);
dbg_msg("args[]:'%s'", s);
@@ -323,10 +335,71 @@ static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
}
#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
+#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
+/*
+ * Used if -I<repl> was specified.
+ * In this mode, words aren't appended to PROG ARGS.
+ * Instead, entire input line is read, then <repl> string
+ * in every PROG and ARG is replaced with the line:
+ * echo -e "ho ho\nhi" | xargs -I_ cmd __ _
+ * results in "cmd 'ho hoho ho' 'ho ho'"; "cmd 'hihi' 'hi'".
+ * -n MAX_ARGS seems to be ignored.
+ * Tested with GNU findutils 4.5.10.
+ */
+//FIXME: n_max_chars is not handled the same way as in GNU findutils.
+//FIXME: quoting is not implemented.
+static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg UNUSED_PARAM, char *buf)
+{
+ int i;
+ char *end, *p;
+
+ /* Free strings from last invocation, if any */
+ for (i = 0; G.args && G.args[i]; i++)
+ if (G.args[i] != G.argv[i])
+ free(G.args[i]);
+
+ end = buf + n_max_chars;
+ p = buf;
+
+ while (1) {
+ int c = getchar();
+ if (c == EOF || c == G.eol_ch) {
+ if (p == buf)
+ goto ret; /* empty line */
+ c = '\0';
+ }
+ *p++ = c;
+ if (c == '\0') { /* EOL or EOF detected */
+ i = 0;
+ while (G.argv[i]) {
+ char *arg = G.argv[i];
+ int count = count_strstr(arg, G.repl_str);
+ if (count != 0)
+ arg = xmalloc_substitute_string(arg, count, G.repl_str, buf);
+ store_param(arg);
+ dbg_msg("args[]:'%s'", arg);
+ i++;
+ }
+ p = buf;
+ goto ret;
+ }
+ if (p == end) {
+ goto ret;
+ }
+ }
+ ret:
+ *p = '\0';
+ /* store_param(NULL) - caller will do it */
+ dbg_msg("return:'%s'", buf);
+ return buf;
+}
+#endif
+
#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
/* Prompt the user for a response, and
- if the user responds affirmatively, return true;
- otherwise, return false. Uses "/dev/tty", not stdin. */
+ * if user responds affirmatively, return true;
+ * otherwise, return false. Uses "/dev/tty", not stdin.
+ */
static int xargs_ask_confirmation(void)
{
FILE *tty_stream;
@@ -360,6 +433,9 @@ static int xargs_ask_confirmation(void)
//usage: "\n -e[STR] STR stops input processing"
//usage: "\n -n N Pass no more than N args to PROG"
//usage: "\n -s N Pass command line of no more than N bytes"
+//usage: IF_FEATURE_XARGS_SUPPORT_REPL_STR(
+//usage: "\n -I STR Replace STR within PROG ARGS with input line"
+//usage: )
//usage: IF_FEATURE_XARGS_SUPPORT_TERMOPT(
//usage: "\n -x Exit if size is exceeded"
//usage: )
@@ -378,6 +454,8 @@ enum {
IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
IF_FEATURE_XARGS_SUPPORT_TERMOPT( OPTBIT_TERMINATE ,)
IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( OPTBIT_ZEROTERM ,)
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR ,)
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR( OPTBIT_REPLSTR1 ,)
OPT_VERBOSE = 1 << OPTBIT_VERBOSE ,
OPT_NO_EMPTY = 1 << OPTBIT_NO_EMPTY ,
@@ -388,11 +466,14 @@ enum {
OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
OPT_TERMINATE = IF_FEATURE_XARGS_SUPPORT_TERMOPT( (1 << OPTBIT_TERMINATE )) + 0,
OPT_ZEROTERM = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( (1 << OPTBIT_ZEROTERM )) + 0,
+ OPT_REPLSTR = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR )) + 0,
+ OPT_REPLSTR1 = IF_FEATURE_XARGS_SUPPORT_REPL_STR( (1 << OPTBIT_REPLSTR1 )) + 0,
};
#define OPTION_STR "+trn:s:e::E:" \
IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
IF_FEATURE_XARGS_SUPPORT_TERMOPT( "x") \
- IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0")
+ IF_FEATURE_XARGS_SUPPORT_ZERO_TERM( "0") \
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR( "I:i::")
int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int xargs_main(int argc, char **argv)
@@ -405,7 +486,8 @@ int xargs_main(int argc, char **argv)
unsigned opt;
int n_max_chars;
int n_max_arg;
-#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
+#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM \
+ || ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
#else
#define read_args process_stdin
@@ -419,7 +501,10 @@ int xargs_main(int argc, char **argv)
"no-run-if-empty\0" No_argument "r"
;
#endif
- opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str);
+ opt = getopt32(argv, OPTION_STR,
+ &max_args, &max_chars, &G.eof_str, &G.eof_str
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR(, &G.repl_str, &G.repl_str)
+ );
/* -E ""? You may wonder why not just omit -E?
* This is used for portability:
@@ -427,8 +512,10 @@ int xargs_main(int argc, char **argv)
if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
G.eof_str = NULL;
- if (opt & OPT_ZEROTERM)
- IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
+ if (opt & OPT_ZEROTERM) {
+ IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin;)
+ IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\0';)
+ }
argv += optind;
argc -= optind;
@@ -438,12 +525,7 @@ int xargs_main(int argc, char **argv)
argc++;
}
- /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate
- * to use such a big value - first need to change code to use
- * growable buffer instead of fixed one.
- */
- n_max_chars = 32 * 1024;
- /* Make smaller if system does not allow our default value.
+ /*
* The Open Group Base Specifications Issue 6:
* "The xargs utility shall limit the command line length such that
* when the command line is invoked, the combined argument
@@ -451,16 +533,15 @@ int xargs_main(int argc, char **argv)
* in the System Interfaces volume of IEEE Std 1003.1-2001)
* shall not exceed {ARG_MAX}-2048 bytes".
*/
- {
- long arg_max = 0;
-#if defined _SC_ARG_MAX
- arg_max = sysconf(_SC_ARG_MAX) - 2048;
-#elif defined ARG_MAX
- arg_max = ARG_MAX - 2048;
-#endif
- if (arg_max > 0 && n_max_chars > arg_max)
- n_max_chars = arg_max;
- }
+ n_max_chars = bb_arg_max();
+ if (n_max_chars > 32 * 1024)
+ n_max_chars = 32 * 1024;
+ /*
+ * POSIX suggests substracting 2048 bytes from sysconf(_SC_ARG_MAX)
+ * so that the process may safely modify its environment.
+ */
+ n_max_chars -= 2048;
+
if (opt & OPT_UPTO_SIZE) {
n_max_chars = xatou_range(max_chars, 1, INT_MAX);
}
@@ -486,20 +567,39 @@ int xargs_main(int argc, char **argv)
/* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
}
- /* Allocate pointers for execvp */
- /* We can statically allocate (argc + n_max_arg + 1) elements
- * and do not bother with resizing args[], but on 64-bit machines
- * this results in args[] vector which is ~8 times bigger
- * than n_max_chars! That is, with n_max_chars == 20k,
- * args[] will take 160k (!), which will most likely be
- * almost entirely unused.
- */
- /* See store_param() for matching 256-step growth logic */
- G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
-
- /* Store the command to be executed, part 1 */
- for (i = 0; argv[i]; i++)
- G.args[i] = argv[i];
+#if ENABLE_FEATURE_XARGS_SUPPORT_REPL_STR
+ if (opt & (OPT_REPLSTR | OPT_REPLSTR1)) {
+ /*
+ * -I<str>:
+ * Unmodified args are kept in G.argv[i],
+ * G.args[i] receives malloced G.argv[i] with <str> replaced
+ * with input line. Setting this up:
+ */
+ G.args = NULL;
+ G.argv = argv;
+ argc = 0;
+ read_args = process_stdin_with_replace;
+ /* Make -I imply -r. GNU findutils seems to do the same: */
+ /* (otherwise "echo -n | xargs -I% echo %" would SEGV) */
+ opt |= OPT_NO_EMPTY;
+ } else
+#endif
+ {
+ /* Allocate pointers for execvp.
+ * We can statically allocate (argc + n_max_arg + 1) elements
+ * and do not bother with resizing args[], but on 64-bit machines
+ * this results in args[] vector which is ~8 times bigger
+ * than n_max_chars! That is, with n_max_chars == 20k,
+ * args[] will take 160k (!), which will most likely be
+ * almost entirely unused.
+ *
+ * See store_param() for matching 256-step growth logic
+ */
+ G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
+ /* Store the command to be executed, part 1 */
+ for (i = 0; argv[i]; i++)
+ G.args[i] = argv[i];
+ }
while (1) {
char *rem;
diff --git a/include/applets.h.sh b/include/applets.h.sh
new file mode 100755
index 0000000..be117cf
--- a/dev/null
+++ b/include/applets.h.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This script allows to check whether every applet has a separate option
+# enabling it. Run it after applets.h is generated.
+
+# CONFIG_applet names
+grep ^IF_ applets.h | grep -v ^IF_FEATURE_ | sed 's/IF_\([A-Z0-9._-]*\)(.*/\1/' \
+| sort | uniq \
+>applets_APP1
+
+# command line applet names
+grep ^IF_ applets.h | sed -e's/ //g' -e's/.*(\([a-z[][^,]*\),.*/\1/' \
+| grep -v '^bash$' \
+| grep -v '^sh$' \
+| tr a-z A-Z \
+| sed 's/^SYSCTL$/BB_SYSCTL/' \
+| sed 's/^\[\[$/TEST1/' \
+| sed 's/^\[$/TEST2/' \
+| sort | uniq \
+>applets_APP2
+
+diff -u applets_APP1 applets_APP2 >applets_APP.diff
+#rm applets_APP1 applets_APP2
diff --git a/include/applets.src.h b/include/applets.src.h
index 7dbd4c7..2ddf120 100644
--- a/include/applets.src.h
+++ b/include/applets.src.h
@@ -8,7 +8,7 @@
/*
name - applet name as it is typed on command line
-name2 - applet name, converted to C (ether-wake: name2 = ether_wake)
+help - applet name, converted to C (ether-wake: help = ether_wake)
main - corresponding <applet>_main to call (bzcat: main = bunzip2)
l - location to install link to: [/usr]/[s]bin
s - suid type:
@@ -24,46 +24,46 @@ s - suid type:
#if defined(PROTOTYPES)
# define APPLET(name,l,s) int name##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-# define APPLET_ODDNAME(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-# define APPLET_NOEXEC(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-# define APPLET_NOFORK(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_ODDNAME(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_NOEXEC(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_NOFORK(name,main,l,s,help) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-#elif defined(NAME_MAIN_CNAME)
-# define APPLET(name,l,s) name name##_main name
-# define APPLET_ODDNAME(name,main,l,s,name2) name main##_main name2
-# define APPLET_NOEXEC(name,main,l,s,name2) name main##_main name2
-# define APPLET_NOFORK(name,main,l,s,name2) name main##_main name2
+#elif defined(NAME_MAIN)
+# define APPLET(name,l,s) name name##_main
+# define APPLET_ODDNAME(name,main,l,s,help) name main##_main
+# define APPLET_NOEXEC(name,main,l,s,help) name main##_main
+# define APPLET_NOFORK(name,main,l,s,help) name main##_main
#elif defined(MAKE_USAGE) && ENABLE_FEATURE_VERBOSE_USAGE
# define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage name##_full_usage)
-# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
-# define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
-# define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
+# define APPLET_ODDNAME(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage)
+# define APPLET_NOEXEC(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage)
+# define APPLET_NOFORK(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage help##_full_usage)
#elif defined(MAKE_USAGE) && !ENABLE_FEATURE_VERBOSE_USAGE
# define APPLET(name,l,s) MAKE_USAGE(#name, name##_trivial_usage)
-# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage)
-# define APPLET_NOEXEC(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage)
-# define APPLET_NOFORK(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage)
+# define APPLET_ODDNAME(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage)
+# define APPLET_NOEXEC(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage)
+# define APPLET_NOFORK(name,main,l,s,help) MAKE_USAGE(#name, help##_trivial_usage)
#elif defined(MAKE_LINKS)
# define APPLET(name,l,c) LINK l name
-# define APPLET_ODDNAME(name,main,l,s,name2) LINK l name
-# define APPLET_NOEXEC(name,main,l,s,name2) LINK l name
-# define APPLET_NOFORK(name,main,l,s,name2) LINK l name
+# define APPLET_ODDNAME(name,main,l,s,help) LINK l name
+# define APPLET_NOEXEC(name,main,l,s,help) LINK l name
+# define APPLET_NOFORK(name,main,l,s,help) LINK l name
#elif defined(MAKE_SUID)
# define APPLET(name,l,s) SUID s l name
-# define APPLET_ODDNAME(name,main,l,s,name2) SUID s l name
-# define APPLET_NOEXEC(name,main,l,s,name2) SUID s l name
-# define APPLET_NOFORK(name,main,l,s,name2) SUID s l name
+# define APPLET_ODDNAME(name,main,l,s,help) SUID s l name
+# define APPLET_NOEXEC(name,main,l,s,help) SUID s l name
+# define APPLET_NOFORK(name,main,l,s,help) SUID s l name
#else
static struct bb_applet applets[] = { /* name, main, location, need_suid */
# define APPLET(name,l,s) { #name, #name, l, s },
-# define APPLET_ODDNAME(name,main,l,s,name2) { #name, #main, l, s },
-# define APPLET_NOEXEC(name,main,l,s,name2) { #name, #main, l, s, 1 },
-# define APPLET_NOFORK(name,main,l,s,name2) { #name, #main, l, s, 1, 1 },
+# define APPLET_ODDNAME(name,main,l,s,help) { #name, #main, l, s },
+# define APPLET_NOEXEC(name,main,l,s,help) { #name, #main, l, s, 1 },
+# define APPLET_NOFORK(name,main,l,s,help) { #name, #main, l, s, 1, 1 },
#endif
#if ENABLE_INSTALL_NO_USR
@@ -73,325 +73,9 @@ s - suid type:
INSERT
-IF_TEST(APPLET_NOFORK([, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
-IF_TEST(APPLET_NOFORK([[, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
-IF_ACPID(APPLET(acpid, BB_DIR_SBIN, BB_SUID_DROP))
-IF_ADDGROUP(APPLET(addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_ADJTIMEX(APPLET(adjtimex, BB_DIR_SBIN, BB_SUID_DROP))
-IF_ARP(APPLET(arp, BB_DIR_SBIN, BB_SUID_DROP))
-IF_ARPING(APPLET(arping, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_BASENAME(APPLET_NOFORK(basename, basename, BB_DIR_USR_BIN, BB_SUID_DROP, basename))
-IF_BBCONFIG(APPLET(bbconfig, BB_DIR_BIN, BB_SUID_DROP))
-IF_BEEP(APPLET(beep, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_BLKID(APPLET(blkid, BB_DIR_SBIN, BB_SUID_DROP))
-IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_CAL(APPLET(cal, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CAT(APPLET_NOFORK(cat, cat, BB_DIR_BIN, BB_SUID_DROP, cat))
-IF_CATV(APPLET(catv, BB_DIR_BIN, BB_SUID_DROP))
-IF_CHAT(APPLET(chat, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_CHATTR(APPLET(chattr, BB_DIR_BIN, BB_SUID_DROP))
-IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CHGRP(APPLET_NOEXEC(chgrp, chgrp, BB_DIR_BIN, BB_SUID_DROP, chgrp))
-IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod))
-IF_CHOWN(APPLET_NOEXEC(chown, chown, BB_DIR_BIN, BB_SUID_DROP, chown))
-IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_CHPST(APPLET(chpst, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CHROOT(APPLET(chroot, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CHVT(APPLET(chvt, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CKSUM(APPLET_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
-IF_CLEAR(APPLET(clear, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_COMM(APPLET(comm, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CP(APPLET_NOEXEC(cp, cp, BB_DIR_BIN, BB_SUID_DROP, cp))
-IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP))
-/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */
-IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
-IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_CUT(APPLET_NOEXEC(cut, cut, BB_DIR_USR_BIN, BB_SUID_DROP, cut))
-IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_DD(APPLET_NOEXEC(dd, dd, BB_DIR_BIN, BB_SUID_DROP, dd))
-IF_DEALLOCVT(APPLET(deallocvt, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup))
-IF_DELUSER(APPLET(deluser, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_DEVFSD(APPLET(devfsd, BB_DIR_SBIN, BB_SUID_DROP))
-IF_DEVMEM(APPLET(devmem, BB_DIR_SBIN, BB_SUID_DROP))
-IF_DF(APPLET(df, BB_DIR_BIN, BB_SUID_DROP))
-IF_DHCPRELAY(APPLET(dhcprelay, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_DIRNAME(APPLET_NOFORK(dirname, dirname, BB_DIR_USR_BIN, BB_SUID_DROP, dirname))
-IF_DMESG(APPLET(dmesg, BB_DIR_BIN, BB_SUID_DROP))
-IF_DNSD(APPLET(dnsd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_HOSTNAME(APPLET_ODDNAME(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
-IF_DOS2UNIX(APPLET_NOEXEC(dos2unix, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, dos2unix))
-IF_DU(APPLET(du, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_DUMPKMAP(APPLET(dumpkmap, BB_DIR_BIN, BB_SUID_DROP))
-IF_DUMPLEASES(APPLET(dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP))
-//IF_E2FSCK(APPLET(e2fsck, BB_DIR_SBIN, BB_SUID_DROP))
-//IF_E2LABEL(APPLET_ODDNAME(e2label, tune2fs, BB_DIR_SBIN, BB_SUID_DROP, e2label))
-IF_ECHO(APPLET_NOFORK(echo, echo, BB_DIR_BIN, BB_SUID_DROP, echo))
-IF_EJECT(APPLET(eject, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_ENV(APPLET_NOEXEC(env, env, BB_DIR_USR_BIN, BB_SUID_DROP, env))
-IF_ENVDIR(APPLET_ODDNAME(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir))
-IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid))
-IF_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, BB_DIR_USR_SBIN, BB_SUID_DROP, ether_wake))
-IF_EXPAND(APPLET(expand, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_EXPR(APPLET(expr, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_FAKEIDENTD(APPLET(fakeidentd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FALSE(APPLET_NOFORK(false, false, BB_DIR_BIN, BB_SUID_DROP, false))
-IF_FBSET(APPLET(fbset, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FBSPLASH(APPLET(fbsplash, BB_DIR_SBIN, BB_SUID_DROP))
-IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush))
-IF_FDFORMAT(APPLET(fdformat, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FDISK(APPLET(fdisk, BB_DIR_SBIN, BB_SUID_DROP))
-IF_FGCONSOLE(APPLET(fgconsole, BB_DIR_USR_BIN, BB_SUID_DROP))
-/* Benefits from suid root: better access to /dev/BLOCKDEVs: */
-IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE))
-IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FLASH_LOCK(APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock))
-IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock))
-IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FLOCK(APPLET(flock, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_FOLD(APPLET_NOEXEC(fold, fold, BB_DIR_USR_BIN, BB_SUID_DROP, fold))
-IF_FREE(APPLET(free, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_FREERAMDISK(APPLET(freeramdisk, BB_DIR_SBIN, BB_SUID_DROP))
-IF_FSCK(APPLET(fsck, BB_DIR_SBIN, BB_SUID_DROP))
-//IF_E2FSCK(APPLET_ODDNAME(fsck.ext2, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext2))
-//IF_E2FSCK(APPLET_ODDNAME(fsck.ext3, e2fsck, BB_DIR_SBIN, BB_SUID_DROP, fsck_ext3))
-IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix))
-IF_FSYNC(APPLET_NOFORK(fsync, fsync, BB_DIR_BIN, BB_SUID_DROP, fsync))
-IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpget))
-IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpput))
-IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_GETENFORCE(APPLET(getenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP))
-IF_GETSEBOOL(APPLET(getsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_GETTY(APPLET(getty, BB_DIR_SBIN, BB_SUID_DROP))
-IF_HD(APPLET_NOEXEC(hd, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hd))
-IF_HDPARM(APPLET(hdparm, BB_DIR_SBIN, BB_SUID_DROP))
-IF_HEAD(APPLET_NOEXEC(head, head, BB_DIR_USR_BIN, BB_SUID_DROP, head))
-IF_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hexdump))
-IF_HOSTNAME(APPLET(hostname, BB_DIR_BIN, BB_SUID_DROP))
-IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_HWCLOCK(APPLET(hwclock, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IFCONFIG(APPLET(ifconfig, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IFUPDOWN(APPLET_ODDNAME(ifdown, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifdown))
-IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_IFUPDOWN(APPLET_ODDNAME(ifup, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifup))
-IF_INETD(APPLET(inetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_INOTIFYD(APPLET(inotifyd, BB_DIR_SBIN, BB_SUID_DROP))
-IF_INSTALL(APPLET(install, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP))
-#if ENABLE_FEATURE_IP_ADDRESS \
- || ENABLE_FEATURE_IP_ROUTE \
- || ENABLE_FEATURE_IP_LINK \
- || ENABLE_FEATURE_IP_TUNNEL \
- || ENABLE_FEATURE_IP_RULE
-IF_IP(APPLET(ip, BB_DIR_SBIN, BB_SUID_DROP))
-#endif
-IF_IPADDR(APPLET(ipaddr, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP))
-IF_IPCRM(APPLET(ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_IPLINK(APPLET(iplink, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IPROUTE(APPLET(iproute, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IPRULE(APPLET(iprule, BB_DIR_SBIN, BB_SUID_DROP))
-IF_IPTUNNEL(APPLET(iptunnel, BB_DIR_SBIN, BB_SUID_DROP))
-IF_KBD_MODE(APPLET(kbd_mode, BB_DIR_BIN, BB_SUID_DROP))
-IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP))
-IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
-IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5))
-IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP))
-IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP))
-//IF_LENGTH(APPLET_NOFORK(length, length, BB_DIR_USR_BIN, BB_SUID_DROP, length))
-IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SETARCH(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32))
-IF_SETARCH(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64))
-IF_LN(APPLET_NOEXEC(ln, ln, BB_DIR_BIN, BB_SUID_DROP, ln))
-IF_LOAD_POLICY(APPLET(load_policy, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_LOADFONT(APPLET(loadfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_LOADKMAP(APPLET(loadkmap, BB_DIR_SBIN, BB_SUID_DROP))
-IF_LOGGER(APPLET(logger, BB_DIR_USR_BIN, BB_SUID_DROP))
-/* Needs to be run by root or be suid root - needs to change uid and gid: */
-IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE))
-IF_LOGNAME(APPLET_NOFORK(logname, logname, BB_DIR_USR_BIN, BB_SUID_DROP, logname))
-IF_LOGREAD(APPLET(logread, BB_DIR_SBIN, BB_SUID_DROP))
-IF_LOSETUP(APPLET(losetup, BB_DIR_SBIN, BB_SUID_DROP))
-IF_LPD(APPLET(lpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_LPQ(APPLET_ODDNAME(lpq, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpq))
-IF_LPR(APPLET_ODDNAME(lpr, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpr))
-IF_LS(APPLET_NOEXEC(ls, ls, BB_DIR_BIN, BB_SUID_DROP, ls))
-IF_LSATTR(APPLET(lsattr, BB_DIR_BIN, BB_SUID_DROP))
-IF_LSPCI(APPLET(lspci, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_LSUSB(APPLET(lsusb, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
-IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP))
-IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_MD5SUM(APPLET_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
-IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_MKDIR(APPLET_NOFORK(mkdir, mkdir, BB_DIR_BIN, BB_SUID_DROP, mkdir))
-IF_MKFS_VFAT(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
-IF_MKFS_EXT2(APPLET_ODDNAME(mke2fs, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
-IF_MKFIFO(APPLET_NOEXEC(mkfifo, mkfifo, BB_DIR_USR_BIN, BB_SUID_DROP, mkfifo))
-IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
-//IF_MKE2FS(APPLET_ODDNAME(mkfs.ext3, mke2fs, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext3))
-IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, BB_DIR_SBIN, BB_SUID_DROP, mkfs_minix))
-IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, BB_DIR_SBIN, BB_SUID_DROP, mkfs_reiser))
-IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
-IF_MKNOD(APPLET_NOEXEC(mknod, mknod, BB_DIR_BIN, BB_SUID_DROP, mknod))
-IF_CRYPTPW(APPLET_ODDNAME(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, mkpasswd))
-IF_MKSWAP(APPLET(mkswap, BB_DIR_SBIN, BB_SUID_DROP))
-IF_MKTEMP(APPLET(mktemp, BB_DIR_BIN, BB_SUID_DROP))
-IF_MORE(APPLET(more, BB_DIR_BIN, BB_SUID_DROP))
-/* On full-blown systems, requires suid for user mounts.
- * But it's not unthinkable to have it available in non-suid flavor on some systems,
- * for viewing mount table.
- * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
-IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
-IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP))
-IF_MT(APPLET(mt, BB_DIR_BIN, BB_SUID_DROP))
-IF_MV(APPLET(mv, BB_DIR_BIN, BB_SUID_DROP))
-IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP))
-IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_NETSTAT(APPLET(netstat, BB_DIR_BIN, BB_SUID_DROP))
-IF_NICE(APPLET(nice, BB_DIR_BIN, BB_SUID_DROP))
-IF_NOHUP(APPLET(nohup, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_OD(APPLET(od, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_OPENVT(APPLET(openvt, BB_DIR_USR_BIN, BB_SUID_DROP))
-//IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP))
-/* Needs to be run by root or be suid root - needs to change /etc/{passwd,shadow}: */
-IF_PASSWD(APPLET(passwd, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
-IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP))
-IF_PIPE_PROGRESS(APPLET(pipe_progress, BB_DIR_BIN, BB_SUID_DROP))
-IF_PIVOT_ROOT(APPLET(pivot_root, BB_DIR_SBIN, BB_SUID_DROP))
-IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
-IF_POPMAILDIR(APPLET(popmaildir, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_PRINTENV(APPLET_NOFORK(printenv, printenv, BB_DIR_BIN, BB_SUID_DROP, printenv))
-IF_PRINTF(APPLET_NOFORK(printf, printf, BB_DIR_USR_BIN, BB_SUID_DROP, printf))
-IF_PS(APPLET(ps, BB_DIR_BIN, BB_SUID_DROP))
-IF_PSCAN(APPLET(pscan, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_PWD(APPLET_NOFORK(pwd, pwd, BB_DIR_BIN, BB_SUID_DROP, pwd))
-IF_RAIDAUTORUN(APPLET(raidautorun, BB_DIR_SBIN, BB_SUID_DROP))
-IF_RDATE(APPLET(rdate, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_RDEV(APPLET(rdev, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_READAHEAD(APPLET(readahead, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_READLINK(APPLET(readlink, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_READPROFILE(APPLET(readprofile, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_REALPATH(APPLET(realpath, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_REFORMIME(APPLET(reformime, BB_DIR_BIN, BB_SUID_DROP))
-IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RESET(APPLET(reset, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RESIZE(APPLET(resize, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, BB_DIR_SBIN, BB_SUID_DROP, restorecon))
-IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
-IF_RMDIR(APPLET_NOFORK(rmdir, rmdir, BB_DIR_BIN, BB_SUID_DROP, rmdir))
-IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP))
-IF_RTCWAKE(APPLET(rtcwake, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_RUN_PARTS(APPLET_ODDNAME(run-parts, run_parts, BB_DIR_BIN, BB_SUID_DROP, run_parts))
-IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RUNLEVEL(APPLET(runlevel, BB_DIR_SBIN, BB_SUID_DROP))
-IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RUNSVDIR(APPLET(runsvdir, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SCRIPT(APPLET(script, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SCRIPTREPLAY(APPLET(scriptreplay, BB_DIR_BIN, BB_SUID_DROP))
-IF_SELINUXENABLED(APPLET(selinuxenabled, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SENDMAIL(APPLET(sendmail, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
-IF_SESTATUS(APPLET(sestatus, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SETARCH(APPLET(setarch, BB_DIR_BIN, BB_SUID_DROP))
-IF_SETCONSOLE(APPLET(setconsole, BB_DIR_SBIN, BB_SUID_DROP))
-IF_SETENFORCE(APPLET(setenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SETFILES(APPLET(setfiles, BB_DIR_SBIN, BB_SUID_DROP))
-IF_SETFONT(APPLET(setfont, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SETKEYCODES(APPLET(setkeycodes, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SETLOGCONS(APPLET(setlogcons, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid))
-IF_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
-IF_SHA3SUM(APPLET_NOEXEC(sha3sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha3sum))
-IF_SHA256SUM(APPLET_NOEXEC(sha256sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha256sum))
-IF_SHA512SUM(APPLET_NOEXEC(sha512sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha512sum))
-IF_SHOWKEY(APPLET(showkey, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
-/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells: */
-IF_SLEEP(APPLET(sleep, BB_DIR_BIN, BB_SUID_DROP))
-IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit))
-IF_SORT(APPLET_NOEXEC(sort, sort, BB_DIR_USR_BIN, BB_SUID_DROP, sort))
-IF_SPLIT(APPLET(split, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
-IF_STAT(APPLET(stat, BB_DIR_BIN, BB_SUID_DROP))
-IF_STRINGS(APPLET(strings, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_STTY(APPLET(stty, BB_DIR_BIN, BB_SUID_DROP))
-/* Needs to be run by root or be suid root - needs to change uid and gid: */
-IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE))
-IF_SULOGIN(APPLET(sulogin, BB_DIR_SBIN, BB_SUID_DROP))
-IF_SUM(APPLET(sum, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_SWAPONOFF(APPLET_ODDNAME(swapoff, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapoff))
-IF_SWAPONOFF(APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon))
-IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP))
-IF_SYNC(APPLET_NOFORK(sync, sync, BB_DIR_BIN, BB_SUID_DROP, sync))
-IF_BB_SYSCTL(APPLET(sysctl, BB_DIR_SBIN, BB_SUID_DROP))
-IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP))
-IF_TAC(APPLET_NOEXEC(tac, tac, BB_DIR_USR_BIN, BB_SUID_DROP, tac))
-IF_TAIL(APPLET(tail, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP))
-/* IF_TC(APPLET(tc, BB_DIR_SBIN, BB_SUID_DROP)) */
-IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, tcpsvd))
-IF_TEE(APPLET(tee, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TELNET(APPLET(telnet, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TELNETD(APPLET(telnetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_TEST(APPLET_NOFORK(test, test, BB_DIR_USR_BIN, BB_SUID_DROP, test))
-#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
-IF_TFTP(APPLET(tftp, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TFTPD(APPLET(tftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-#endif
-IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TR(APPLET(tr, BB_DIR_USR_BIN, BB_SUID_DROP))
-/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
-IF_TRACEROUTE(APPLET(traceroute, BB_DIR_USR_BIN, BB_SUID_MAYBE))
-IF_TRACEROUTE6(APPLET(traceroute6, BB_DIR_USR_BIN, BB_SUID_MAYBE))
-IF_TRUE(APPLET_NOFORK(true, true, BB_DIR_BIN, BB_SUID_DROP, true))
-IF_TTY(APPLET(tty, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TTYSIZE(APPLET(ttysize, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_TUNCTL(APPLET(tunctl, BB_DIR_SBIN, BB_SUID_DROP))
-IF_TUNE2FS(APPLET(tune2fs, BB_DIR_SBIN, BB_SUID_DROP))
-IF_UDHCPC(APPLET(udhcpc, BB_DIR_SBIN, BB_SUID_DROP))
-IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
-IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, udpsvd))
-IF_UMOUNT(APPLET(umount, BB_DIR_BIN, BB_SUID_DROP))
-IF_UNAME(APPLET(uname, BB_DIR_BIN, BB_SUID_DROP))
-IF_UNEXPAND(APPLET_ODDNAME(unexpand, expand, BB_DIR_USR_BIN, BB_SUID_DROP, unexpand))
-IF_UNIQ(APPLET(uniq, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_UNIX2DOS(APPLET_NOEXEC(unix2dos, dos2unix, BB_DIR_USR_BIN, BB_SUID_DROP, unix2dos))
-IF_UPTIME(APPLET(uptime, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_USLEEP(APPLET_NOFORK(usleep, usleep, BB_DIR_BIN, BB_SUID_DROP, usleep))
-IF_UUDECODE(APPLET(uudecode, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_UUENCODE(APPLET(uuencode, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_VCONFIG(APPLET(vconfig, BB_DIR_SBIN, BB_SUID_DROP))
-/* Needs to be run by root or be suid root - needs to change uid and gid: */
-IF_VLOCK(APPLET(vlock, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
-IF_VOLNAME(APPLET(volname, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_WATCH(APPLET(watch, BB_DIR_BIN, BB_SUID_DROP))
-IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
-IF_WC(APPLET(wc, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_WHICH(APPLET(which, BB_DIR_USR_BIN, BB_SUID_DROP))
-IF_WHOAMI(APPLET_NOFORK(whoami, whoami, BB_DIR_USR_BIN, BB_SUID_DROP, whoami))
-IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
-IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP))
-#if !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE) \
+
+#if !defined(PROTOTYPES) && !defined(NAME_MAIN) && !defined(MAKE_USAGE) \
&& !defined(MAKE_LINKS) && !defined(MAKE_SUID)
};
#endif
diff --git a/include/bb_archive.h b/include/bb_archive.h
index fc976f2..7b43a29 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -77,6 +77,9 @@ typedef struct archive_handle_t {
off_t offset;
/* Archiver specific. Can make it a union if it ever gets big */
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ unsigned tar__strip_components;
+#endif
#define PAX_NEXT_FILE 0
#define PAX_GLOBAL 1
#if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB
@@ -95,6 +98,7 @@ typedef struct archive_handle_t {
#endif
#if ENABLE_CPIO || ENABLE_RPM2CPIO || ENABLE_RPM
uoff_t cpio__blocks;
+ struct bb_uidgid_t cpio__owner;
struct hardlinks_t *cpio__hardlinks_to_create;
struct hardlinks_t *cpio__created_hardlinks;
#endif
@@ -162,6 +166,8 @@ struct BUG_tar_header {
};
+extern const char cpio_TRAILER[];
+
archive_handle_t *init_handle(void) FAST_FUNC;
@@ -187,6 +193,7 @@ char get_header_tar(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
+char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
void seek_by_jump(int fd, off_t amount) FAST_FUNC;
void seek_by_read(int fd, off_t amount) FAST_FUNC;
@@ -206,43 +213,57 @@ int read_bunzip(bunzip_data *bd, char *outbuf, int len) FAST_FUNC;
void dealloc_bunzip(bunzip_data *bd) FAST_FUNC;
/* Meaning and direction (input/output) of the fields are transformer-specific */
-typedef struct transformer_aux_data_t {
- smallint check_signature; /* most often referenced member */
+typedef struct transformer_state_t {
+ smallint signature_skipped; /* most often referenced member */
+
+ IF_DESKTOP(long long) int FAST_FUNC (*xformer)(struct transformer_state_t *xstate);
+ USE_FOR_NOMMU(const char *xformer_prog;)
+
+ /* Source */
+ int src_fd;
+ /* Output */
+ int dst_fd;
+ size_t mem_output_size_max; /* if non-zero, decompress to RAM instead of fd */
+ size_t mem_output_size;
+ char *mem_output_buf;
+
off_t bytes_out;
off_t bytes_in; /* used in unzip code only: needs to know packed size */
uint32_t crc32;
time_t mtime; /* gunzip code may set this on exit */
-} transformer_aux_data_t;
+} transformer_state_t;
-void init_transformer_aux_data(transformer_aux_data_t *aux) FAST_FUNC;
-int FAST_FUNC check_signature16(transformer_aux_data_t *aux, int src_fd, unsigned magic16) FAST_FUNC;
+void init_transformer_state(transformer_state_t *xstate) FAST_FUNC;
+ssize_t transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize) FAST_FUNC;
+ssize_t xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize) FAST_FUNC;
+int check_signature16(transformer_state_t *xstate, unsigned magic16) FAST_FUNC;
-IF_DESKTOP(long long) int inflate_unzip(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_Z_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_gz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_bz2_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_lzma_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
-IF_DESKTOP(long long) int unpack_xz_stream(transformer_aux_data_t *aux, int src_fd, int dst_fd) FAST_FUNC;
+IF_DESKTOP(long long) int inflate_unzip(transformer_state_t *xstate) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_Z_stream(transformer_state_t *xstate) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_gz_stream(transformer_state_t *xstate) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_bz2_stream(transformer_state_t *xstate) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_lzma_stream(transformer_state_t *xstate) FAST_FUNC;
+IF_DESKTOP(long long) int unpack_xz_stream(transformer_state_t *xstate) FAST_FUNC;
char* append_ext(char *filename, const char *expected_ext) FAST_FUNC;
int bbunpack(char **argv,
- IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
+ IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_state_t *xstate),
char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
const char *expected_ext
) FAST_FUNC;
void check_errors_in_children(int signo);
#if BB_MMU
-void open_transformer(int fd,
- int check_signature,
- IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_aux_data_t *aux, int src_fd, int dst_fd)
+void fork_transformer(int fd,
+ int signature_skipped,
+ IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate)
) FAST_FUNC;
-#define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), 1, (transformer))
-#define open_transformer_with_no_sig(fd, transformer) open_transformer((fd), 0, (transformer))
+#define fork_transformer_with_sig(fd, transformer, transform_prog) fork_transformer((fd), 0, (transformer))
+#define fork_transformer_with_no_sig(fd, transformer) fork_transformer((fd), 1, (transformer))
#else
-void open_transformer(int fd, const char *transform_prog) FAST_FUNC;
-#define open_transformer_with_sig(fd, transformer, transform_prog) open_transformer((fd), (transform_prog))
-/* open_transformer_with_no_sig() does not exist on NOMMU */
+void fork_transformer(int fd, const char *transform_prog) FAST_FUNC;
+#define fork_transformer_with_sig(fd, transformer, transform_prog) fork_transformer((fd), (transform_prog))
+/* fork_transformer_with_no_sig() does not exist on NOMMU */
#endif
diff --git a/include/busybox.h b/include/busybox.h
index b1e31e5..6a003d5 100644
--- a/include/busybox.h
+++ b/include/busybox.h
@@ -15,25 +15,22 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
/* Keep in sync with applets/applet_tables.c! */
extern const char applet_names[] ALIGN1;
extern int (*const applet_main[])(int argc, char **argv);
-extern const uint16_t applet_nameofs[];
+extern const uint8_t applet_flags[] ALIGN1;
+extern const uint8_t applet_suid[] ALIGN1;
extern const uint8_t applet_install_loc[] ALIGN1;
-#if ENABLE_FEATURE_SUID || ENABLE_FEATURE_PREFER_APPLETS
-# define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff))
-#else
-# define APPLET_NAME(i) (applet_names + applet_nameofs[i])
-#endif
-
-#if ENABLE_FEATURE_PREFER_APPLETS
-# define APPLET_IS_NOFORK(i) (applet_nameofs[i] & (1 << 12))
-# define APPLET_IS_NOEXEC(i) (applet_nameofs[i] & (1 << 13))
+#if ENABLE_FEATURE_PREFER_APPLETS \
+ || ENABLE_FEATURE_SH_STANDALONE \
+ || ENABLE_FEATURE_SH_NOFORK
+# define APPLET_IS_NOFORK(i) (applet_flags[(i)/4] & (1 << (2 * ((i)%4))))
+# define APPLET_IS_NOEXEC(i) (applet_flags[(i)/4] & (1 << ((2 * ((i)%4))+1)))
#else
# define APPLET_IS_NOFORK(i) 0
# define APPLET_IS_NOEXEC(i) 0
#endif
#if ENABLE_FEATURE_SUID
-# define APPLET_SUID(i) ((applet_nameofs[i] >> 14) & 0x3)
+# define APPLET_SUID(i) ((applet_suid[(i)/4] >> (2 * ((i)%4)) & 3))
#endif
#if ENABLE_FEATURE_INSTALLER
diff --git a/include/grp_.h b/include/grp_.h
index e5075e5..db13ce3 100644
--- a/include/grp_.h
+++ b/include/grp_.h
@@ -30,89 +30,36 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
* so that function calls are directed to bb_internal_XXX replacements
*/
#undef endgrent
-#define setgrent bb_internal_setgrent
#define endgrent bb_internal_endgrent
-#define getgrent bb_internal_getgrent
-#define fgetgrent bb_internal_fgetgrent
-#define putgrent bb_internal_putgrent
#define getgrgid bb_internal_getgrgid
#define getgrnam bb_internal_getgrnam
-#define getgrent_r bb_internal_getgrent_r
-#define getgrgid_r bb_internal_getgrgid_r
-#define getgrnam_r bb_internal_getgrnam_r
-#define fgetgrent_r bb_internal_fgetgrent_r
#define getgrouplist bb_internal_getgrouplist
#define initgroups bb_internal_initgroups
-
/* All function names below should be remapped by #defines above
* in order to not collide with libc names. */
-
-/* Rewind the group-file stream. */
-extern void setgrent(void);
-
/* Close the group-file stream. */
-extern void endgrent(void);
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-/* Read an entry from the group-file stream, opening it if necessary. */
-extern struct group *getgrent(void);
-
-/* Read a group entry from STREAM. */
-extern struct group *fgetgrent(FILE *__stream);
-
-/* Write the given entry onto the given stream. */
-extern int putgrent(const struct group *__restrict __p,
- FILE *__restrict __f);
-#endif
-
-/* Search for an entry with a matching group ID. */
-extern struct group *getgrgid(gid_t __gid);
-
-/* Search for an entry with a matching group name. */
-extern struct group *getgrnam(const char *__name);
-
-/* Reentrant versions of some of the functions above.
-
- PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
- The interface may change in later versions of this library. But
- the interface is designed following the principals used for the
- other reentrant functions so the chances are good this is what the
- POSIX people would choose. */
-
-extern int getgrent_r(struct group *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct group **__restrict __result);
+void FAST_FUNC endgrent(void);
/* Search for an entry with a matching group ID. */
-extern int getgrgid_r(gid_t __gid, struct group *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct group **__restrict __result);
+struct group* FAST_FUNC getgrgid(gid_t __gid);
/* Search for an entry with a matching group name. */
-extern int getgrnam_r(const char *__restrict __name,
- struct group *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct group **__restrict __result);
+struct group* FAST_FUNC getgrnam(const char *__name);
-/* Read a group entry from STREAM. This function is not standardized
- an probably never will. */
-extern int fgetgrent_r(FILE *__restrict __stream,
- struct group *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct group **__restrict __result);
+/* Reentrant versions of some of the functions above. */
/* Store at most *NGROUPS members of the group set for USER into
*GROUPS. Also include GROUP. The actual number of groups found is
returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */
-extern int getgrouplist(const char *__user, gid_t __group,
+int FAST_FUNC getgrouplist(const char *__user, gid_t __group,
gid_t *__groups, int *__ngroups);
/* Initialize the group set for the current user
by reading the group database and using all groups
of which USER is a member. Also include GROUP. */
-extern int initgroups(const char *__user, gid_t __group);
+int FAST_FUNC initgroups(const char *__user, gid_t __group);
POP_SAVED_FUNCTION_VISIBILITY
diff --git a/include/libbb.h b/include/libbb.h
index 85fc485..0fdddfc 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -33,6 +33,7 @@
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
+#include <paths.h>
#if defined __UCLIBC__ /* TODO: and glibc? */
/* use inlined versions of these: */
# define sigfillset(s) __sigfillset(s)
@@ -57,7 +58,7 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
-#ifndef major
+#if !defined(major) || defined(__GLIBC__)
# include <sys/sysmacros.h>
#endif
#include <sys/wait.h>
@@ -97,9 +98,36 @@
# include <selinux/av_permissions.h>
#endif
#endif
-#if ENABLE_FEATURE_UTMP
-# include <utmp.h>
-#endif
+/*#if ENABLE_FEATURE_UTMP
+# if defined __UCLIBC__ && ( \
+ (UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 32) \
+ && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 34) \
+ && defined __UCLIBC_HAS_UTMPX__ \
+ ) || ( \
+ UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 34) \
+ ) \
+ )
+# include <utmpx.h>
+# elif defined __UCLIBC__*/
+# include <utmp.h>
+# define utmpx utmp
+# define setutxent setutent
+# define endutxent endutent
+# define getutxent getutent
+# define getutxid getutid
+# define getutxline getutline
+# define pututxline pututline
+# define utmpxname utmpname
+# define updwtmpx updwtmp
+# define _PATH_UTMPX _PATH_UTMP
+/*# else
+# include <utmp.h>
+//# include <utmpx.h>
+# if defined _PATH_UTMP && !defined _PATH_UTMPX
+# define _PATH_UTMPX _PATH_UTMP
+# endif
+# endif
+#endif*/
#if ENABLE_LOCALE_SUPPORT
# include <locale.h>
#else
@@ -128,14 +156,18 @@
# include <netinet/in.h>
#else
# include <arpa/inet.h>
-# if !defined(__socklen_t_defined) && !defined(_SOCKLEN_T_DECLARED)
-/* We #define socklen_t *after* includes, otherwise we get
- * typedef redefinition errors from system headers
- * (in case "is it defined already" detection above failed)
- */
-# define socklen_t bb_socklen_t
- typedef unsigned socklen_t;
-# endif
+//This breaks on bionic:
+//# if !defined(__socklen_t_defined) && !defined(_SOCKLEN_T_DECLARED)
+///* We #define socklen_t *after* includes, otherwise we get
+// * typedef redefinition errors from system headers
+// * (in case "is it defined already" detection above failed)
+// */
+//# define socklen_t bb_socklen_t
+// typedef unsigned socklen_t;
+//# endif
+//if this is still needed, add a fix along the lines of
+// ifdef SPECIFIC_BROKEN_LIBC_CHECK / typedef socklen_t / endif
+//in platform.h instead!
#endif
#ifndef HAVE_CLEARENV
# define clearenv() do { if (environ) environ[0] = NULL; } while (0)
@@ -167,25 +199,34 @@ int klogctl(int type, char *b, int len);
/* Busybox does not use threads, we can speed up stdio. */
#ifdef HAVE_UNLOCKED_STDIO
# undef getc
-# define getc(stream) getc_unlocked(stream)
+# define getc(stream) getc_unlocked(stream)
# undef getchar
-# define getchar() getchar_unlocked()
+# define getchar() getchar_unlocked()
# undef putc
-# define putc(c, stream) putc_unlocked(c, stream)
+# define putc(c,stream) putc_unlocked(c,stream)
# undef putchar
-# define putchar(c) putchar_unlocked(c)
+# define putchar(c) putchar_unlocked(c)
# undef fgetc
-# define fgetc(stream) getc_unlocked(stream)
+# define fgetc(stream) getc_unlocked(stream)
# undef fputc
-# define fputc(c, stream) putc_unlocked(c, stream)
+# define fputc(c,stream) putc_unlocked(c,stream)
#endif
/* Above functions are required by POSIX.1-2008, below ones are extensions */
#ifdef HAVE_UNLOCKED_LINE_OPS
# undef fgets
-# define fgets(s, n, stream) fgets_unlocked(s, n, stream)
+# define fgets(s,n,stream) fgets_unlocked(s,n,stream)
# undef fputs
-# define fputs(s, stream) fputs_unlocked(s, stream)
+# define fputs(s,stream) fputs_unlocked(s,stream)
+/* musl <= 1.1.15 does not support fflush_unlocked(NULL) */
+//# undef fflush
+//# define fflush(stream) fflush_unlocked(stream)
+# undef feof
+# define feof(stream) feof_unlocked(stream)
+# undef ferror
+# define ferror(stream) ferror_unlocked(stream)
+# undef fileno
+# define fileno(stream) fileno_unlocked(stream)
#endif
@@ -350,7 +391,7 @@ extern char *strrstr(const char *haystack, const char *needle) FAST_FUNC;
//TODO: supply a pointer to char[11] buffer (avoid statics)?
extern const char *bb_mode_string(mode_t mode) FAST_FUNC;
extern int is_directory(const char *name, int followLinks) FAST_FUNC;
-enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */
+enum { /* cp.c, mv.c, install.c depend on these values. CAREFUL when changing them! */
FILEUTILS_PRESERVE_STATUS = 1 << 0, /* -p */
FILEUTILS_DEREFERENCE = 1 << 1, /* !-d */
FILEUTILS_RECUR = 1 << 2, /* -R */
@@ -360,13 +401,25 @@ enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */
FILEUTILS_MAKE_SOFTLINK = 1 << 6, /* -s */
FILEUTILS_DEREF_SOFTLINK = 1 << 7, /* -L */
FILEUTILS_DEREFERENCE_L0 = 1 << 8, /* -H */
+ /* -a = -pdR (mapped in cp.c) */
+ /* -r = -dR (mapped in cp.c) */
+ /* -P = -d (mapped in cp.c) */
+ FILEUTILS_VERBOSE = (1 << 12) * ENABLE_FEATURE_VERBOSE, /* -v */
+ FILEUTILS_UPDATE = 1 << 13, /* -u */
+#if ENABLE_SELINUX
+ FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 14, /* -c */
+#endif
+ FILEUTILS_RMDEST = 1 << (15 - !ENABLE_SELINUX), /* --remove-destination */
+ /*
+ * Hole. cp may have some bits set here,
+ * they should not affect remove_file()/copy_file()
+ */
#if ENABLE_SELINUX
- FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 9, /* -c */
- FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10,
+ FILEUTILS_SET_SECURITY_CONTEXT = 1 << 30,
#endif
- FILEUTILS_IGNORE_CHMOD_ERR = 1 << 11,
+ FILEUTILS_IGNORE_CHMOD_ERR = 1 << 31,
};
-#define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c")
+#define FILEUTILS_CP_OPTSTR "pdRfilsLHarPvu" IF_SELINUX("c")
extern int remove_file(const char *path, int flags) FAST_FUNC;
/* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
* the source, not copy (unless "source" is a directory).
@@ -418,9 +471,11 @@ const char *bb_basename(const char *name) FAST_FUNC;
/* NB: can violate const-ness (similarly to strchr) */
char *last_char_is(const char *s, int c) FAST_FUNC;
const char* endofname(const char *name) FAST_FUNC;
+char *is_prefixed_with(const char *string, const char *key) FAST_FUNC;
+char *is_suffixed_with(const char *string, const char *key) FAST_FUNC;
-void ndelay_on(int fd) FAST_FUNC;
-void ndelay_off(int fd) FAST_FUNC;
+int ndelay_on(int fd) FAST_FUNC;
+int ndelay_off(int fd) FAST_FUNC;
void close_on_exec_on(int fd) FAST_FUNC;
void xdup2(int, int) FAST_FUNC;
void xmove_fd(int, int) FAST_FUNC;
@@ -495,6 +550,7 @@ void xsetuid(uid_t uid) FAST_FUNC;
void xsetegid(gid_t egid) FAST_FUNC;
void xseteuid(uid_t euid) FAST_FUNC;
void xchdir(const char *path) FAST_FUNC;
+void xfchdir(int fd) FAST_FUNC;
void xchroot(const char *path) FAST_FUNC;
void xsetenv(const char *key, const char *value) FAST_FUNC;
void bb_unsetenv(const char *key) FAST_FUNC;
@@ -566,6 +622,11 @@ void xlisten(int s, int backlog) FAST_FUNC;
void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC;
ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
socklen_t tolen) FAST_FUNC;
+
+int setsockopt_int(int fd, int level, int optname, int optval) FAST_FUNC;
+int setsockopt_1(int fd, int level, int optname) FAST_FUNC;
+int setsockopt_SOL_SOCKET_int(int fd, int optname, int optval) FAST_FUNC;
+int setsockopt_SOL_SOCKET_1(int fd, int optname) FAST_FUNC;
/* SO_REUSEADDR allows a server to rebind to an address that is already
* "in use" by old connections to e.g. previous server instance which is
* killed or crashed. Without it bind will fail until all such connections
@@ -573,6 +634,7 @@ ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
* regardless of SO_REUSEADDR (unlike some other flavors of Unix).
* Turn it on before you call bind(). */
void setsockopt_reuseaddr(int fd) FAST_FUNC; /* On Linux this never fails. */
+int setsockopt_keepalive(int fd) FAST_FUNC;
int setsockopt_broadcast(int fd) FAST_FUNC;
int setsockopt_bindtodevice(int fd, const char *iface) FAST_FUNC;
/* NB: returns port in host byte order */
@@ -678,15 +740,19 @@ uint16_t inet_cksum(uint16_t *addr, int len) FAST_FUNC;
char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC;
char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC;
+void *xmemdup(const void *s, int n) FAST_FUNC RETURNS_MALLOC;
void overlapping_strcpy(char *dst, const char *src) FAST_FUNC;
char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC;
char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC;
+unsigned count_strstr(const char *str, const char *sub) FAST_FUNC;
+char *xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl) FAST_FUNC;
/* Guaranteed to NOT be a macro (smallest code). Saves nearly 2k on uclibc.
* But potentially slow, don't use in one-billion-times loops */
int bb_putchar(int ch) FAST_FUNC;
/* Note: does not use stdio, writes to fd 2 directly */
int bb_putchar_stderr(char ch) FAST_FUNC;
char *xasprintf(const char *format, ...) __attribute__ ((format(printf, 1, 2))) FAST_FUNC RETURNS_MALLOC;
+char *auto_string(char *str) FAST_FUNC;
// gcc-4.1.1 still isn't good enough at optimizing it
// (+200 bytes compared to macro)
//static ALWAYS_INLINE
@@ -739,7 +805,7 @@ void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx) F
extern ssize_t safe_read(int fd, void *buf, size_t count) FAST_FUNC;
-extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR) FAST_FUNC;
+extern ssize_t nonblock_immune_read(int fd, void *buf, size_t count) FAST_FUNC;
// NB: will return short read on error, not -1,
// if some data was read before error occurred
extern ssize_t full_read(int fd, void *buf, size_t count) FAST_FUNC;
@@ -758,6 +824,18 @@ extern void *xmalloc_open_read_close(const char *filename, size_t *maxsz_p) FAST
/* Never returns NULL */
extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
+#if defined(ARG_MAX) && (ARG_MAX >= 60*1024 || !defined(_SC_ARG_MAX))
+/* Use _constant_ maximum if: defined && (big enough || no variable one exists) */
+# define bb_arg_max() ((unsigned)ARG_MAX)
+#elif defined(_SC_ARG_MAX)
+/* Else use variable one (a bit more expensive) */
+unsigned bb_arg_max(void) FAST_FUNC;
+#else
+/* If all else fails */
+# define bb_arg_max() ((unsigned)(32 * 1024))
+#endif
+unsigned bb_clk_tck(void) FAST_FUNC;
+
#define SEAMLESS_COMPRESSION (0 \
|| ENABLE_FEATURE_SEAMLESS_XZ \
|| ENABLE_FEATURE_SEAMLESS_LZMA \
@@ -770,11 +848,12 @@ extern void *xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p) FAS
extern int setup_unzip_on_fd(int fd, int fail_if_not_compressed) FAST_FUNC;
/* Autodetects .gz etc */
extern int open_zipped(const char *fname, int fail_if_not_compressed) FAST_FUNC;
+extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
#else
# define setup_unzip_on_fd(...) (0)
# define open_zipped(fname, fail_if_not_compressed) open((fname), O_RDONLY);
+# define xmalloc_open_zipped_read_close(fname, maxsz_p) xmalloc_open_read_close((fname), (maxsz_p))
#endif
-extern void *xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
extern ssize_t safe_write(int fd, const void *buf, size_t count) FAST_FUNC;
// NB: will return short write on error, not -1,
@@ -875,6 +954,9 @@ struct suffix_mult {
};
extern const struct suffix_mult bkm_suffixes[];
#define km_suffixes (bkm_suffixes + 1)
+extern const struct suffix_mult cwbkMG_suffixes[];
+#define kMG_suffixes (cwbkMG_suffixes + 3)
+extern const struct suffix_mult kmg_i_suffixes[];
#include "xatonum.h"
/* Specialized: */
@@ -899,14 +981,13 @@ long xuname2uid(const char *name) FAST_FUNC;
long xgroup2gid(const char *name) FAST_FUNC;
/* wrapper: allows string to contain numeric uid or gid */
unsigned long get_ug_id(const char *s, long FAST_FUNC (*xname2id)(const char *)) FAST_FUNC;
-/* from chpst. Does not die, returns 0 on failure */
struct bb_uidgid_t {
uid_t uid;
gid_t gid;
};
-/* always sets uid and gid */
-int get_uidgid(struct bb_uidgid_t*, const char*, int numeric_ok) FAST_FUNC;
-/* always sets uid and gid, allows numeric; exits on failure */
+/* always sets uid and gid; returns 0 on failure */
+int get_uidgid(struct bb_uidgid_t*, const char*) FAST_FUNC;
+/* always sets uid and gid; exits on failure */
void xget_uidgid(struct bb_uidgid_t*, const char*) FAST_FUNC;
/* chown-like handling of "user[:[group]" */
void parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group) FAST_FUNC;
@@ -937,15 +1018,17 @@ void die_if_bad_username(const char* name) FAST_FUNC;
#if ENABLE_FEATURE_UTMP
void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
+void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid);
#else
# define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
# define update_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
+# define update_utmp_DEAD_PROCESS(pid) ((void)0)
#endif
-int execable_file(const char *name) FAST_FUNC;
-char *find_execable(const char *filename, char **PATHp) FAST_FUNC;
-int exists_execable(const char *filename) FAST_FUNC;
+int file_is_executable(const char *name) FAST_FUNC;
+char *find_executable(const char *filename, char **PATHp) FAST_FUNC;
+int executable_exists(const char *filename) FAST_FUNC;
/* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff),
* but it may exec busybox and call applet instead of searching PATH.
@@ -962,9 +1045,10 @@ int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC;
#define BB_EXECVP(prog,cmd) execvp(prog,cmd)
#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__)
#endif
-int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+void exec_prog_or_SHELL(char **argv) NORETURN FAST_FUNC;
-/* xvfork() can't be a _function_, return after vfork mangles stack
+/* xvfork() can't be a _function_, return after vfork in child mangles stack
* in the parent. It must be a macro. */
#define xvfork() \
({ \
@@ -976,6 +1060,7 @@ int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
#if BB_MMU
pid_t xfork(void) FAST_FUNC;
#endif
+void xvfork_parent_waits_and_exits(void) FAST_FUNC;
/* NOMMU friendy fork+exec: */
pid_t spawn(char **argv) FAST_FUNC;
@@ -992,6 +1077,7 @@ pid_t wait_any_nohang(int *wstat) FAST_FUNC;
* if (rc > 0) bb_error_msg("exit code: %d", rc & 0xff);
*/
int wait4pid(pid_t pid) FAST_FUNC;
+int wait_for_exitstatus(pid_t pid) FAST_FUNC;
/* Same as wait4pid(spawn(argv)), but with NOFORK/NOEXEC if configured: */
int spawn_and_wait(char **argv) FAST_FUNC;
/* Does NOT check that applet is NOFORK, just blindly runs it */
@@ -1105,10 +1191,10 @@ enum {
LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
};
extern const char *msg_eol;
+extern smallint syslog_level;
extern smallint logmode;
-extern int die_sleep;
extern uint8_t xfunc_error_retval;
-extern jmp_buf die_jmp;
+extern void (*die_func)(void);
extern void xfunc_die(void) NORETURN FAST_FUNC;
extern void bb_show_usage(void) NORETURN FAST_FUNC;
extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
@@ -1121,8 +1207,8 @@ extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1,
extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC;
extern void bb_perror_nomsg_and_die(void) NORETURN FAST_FUNC;
extern void bb_perror_nomsg(void) FAST_FUNC;
-extern void bb_info_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
extern void bb_verror_msg(const char *s, va_list p, const char *strerr) FAST_FUNC;
+extern void bb_logenv_override(void) FAST_FUNC;
/* We need to export XXX_main from libbusybox
* only if we build "individual" binaries
@@ -1139,8 +1225,16 @@ int bb_cat(char** argv);
/* If shell needs them, they exist even if not enabled as applets */
int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE);
int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE);
-int test_main(int argc, char **argv) IF_TEST(MAIN_EXTERNALLY_VISIBLE);
-int kill_main(int argc, char **argv) IF_KILL(MAIN_EXTERNALLY_VISIBLE);
+int test_main(int argc, char **argv)
+#if ENABLE_TEST || ENABLE_TEST1 || ENABLE_TEST2
+ MAIN_EXTERNALLY_VISIBLE
+#endif
+;
+int kill_main(int argc, char **argv)
+#if ENABLE_KILL || ENABLE_KILLALL || ENABLE_KILLALL5
+ MAIN_EXTERNALLY_VISIBLE
+#endif
+;
/* Similar, but used by chgrp, not shell */
int chown_main(int argc, char **argv) IF_CHOWN(MAIN_EXTERNALLY_VISIBLE);
/* Used by ftpd */
@@ -1196,8 +1290,6 @@ const struct hwtype *get_hwntype(int type) FAST_FUNC;
#ifndef BUILD_INDIVIDUAL
extern int find_applet_by_name(const char *name) FAST_FUNC;
-/* Returns only if applet is not found. */
-extern void run_applet_and_exit(const char *name, char **argv) FAST_FUNC;
extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC;
#endif
@@ -1229,7 +1321,8 @@ char *bb_ask_stdin(const char * prompt) FAST_FUNC;
char *bb_ask(const int fd, int timeout, const char * prompt) FAST_FUNC;
int bb_ask_confirmation(void) FAST_FUNC;
-int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
+/* Returns -1 if input is invalid. current_mode is a base for e.g. "u+rw" */
+int bb_parse_mode(const char* s, unsigned cur_mode) FAST_FUNC;
/*
* Config file parser
@@ -1285,10 +1378,12 @@ char *bb_simplify_path(const char *path) FAST_FUNC;
/* Returns ptr to NUL */
char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC;
+#ifndef LOGIN_FAIL_DELAY
#define LOGIN_FAIL_DELAY 3
+#endif
extern void bb_do_delay(int seconds) FAST_FUNC;
extern void change_identity(const struct passwd *pw) FAST_FUNC;
-extern void run_shell(const char *shell, int loginshell, const char *command, const char **additional_args) NORETURN FAST_FUNC;
+extern void run_shell(const char *shell, int loginshell, const char **args) NORETURN FAST_FUNC;
/* Returns $SHELL, getpwuid(getuid())->pw_shell, or DEFAULT_SHELL.
* Note that getpwuid result might need xstrdup'ing
@@ -1309,11 +1404,6 @@ extern void selinux_preserve_fcontext(int fdesc) FAST_FUNC;
extern void selinux_or_die(void) FAST_FUNC;
-/* systemd support */
-#define SD_LISTEN_FDS_START 3
-int sd_listen_fds(void);
-
-
/* setup_environment:
* if chdir pw->pw_dir: ok: else if to_tmp == 1: goto /tmp else: goto / or die
* if clear_env = 1: cd(pw->pw_dir), clear environment, then set
@@ -1335,6 +1425,7 @@ int sd_listen_fds(void);
#define SETUP_ENV_NO_CHDIR (1 << 4)
void setup_environment(const char *shell, int flags, const struct passwd *pw) FAST_FUNC;
void nuke_str(char *str) FAST_FUNC;
+int check_password(const struct passwd *pw, const char *plaintext) FAST_FUNC;
int ask_and_check_password_extended(const struct passwd *pw, int timeout, const char *prompt) FAST_FUNC;
int ask_and_check_password(const struct passwd *pw) FAST_FUNC;
/* Returns a malloced string */
@@ -1379,6 +1470,7 @@ extern void print_login_prompt(void) FAST_FUNC;
char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC;
/* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */
int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC;
+int get_terminal_width(int fd) FAST_FUNC;
int tcsetattr_stdin_TCSANOW(const struct termios *tp) FAST_FUNC;
@@ -1413,46 +1505,46 @@ unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC;
* yet doesn't represent any valid Unicode character.
* Also, -1 is reserved for error indication and we don't use it. */
enum {
- KEYCODE_UP = -2,
- KEYCODE_DOWN = -3,
- KEYCODE_RIGHT = -4,
- KEYCODE_LEFT = -5,
- KEYCODE_HOME = -6,
- KEYCODE_END = -7,
- KEYCODE_INSERT = -8,
- KEYCODE_DELETE = -9,
- KEYCODE_PAGEUP = -10,
- KEYCODE_PAGEDOWN = -11,
- // -12 is reserved for Alt/Ctrl/Shift-TAB
+ KEYCODE_UP = -2,
+ KEYCODE_DOWN = -3,
+ KEYCODE_RIGHT = -4,
+ KEYCODE_LEFT = -5,
+ KEYCODE_HOME = -6,
+ KEYCODE_END = -7,
+ KEYCODE_INSERT = -8,
+ KEYCODE_DELETE = -9,
+ KEYCODE_PAGEUP = -10,
+ KEYCODE_PAGEDOWN = -11,
+ KEYCODE_BACKSPACE = -12, /* Used only if Alt/Ctrl/Shifted */
+ KEYCODE_D = -13, /* Used only if Alted */
#if 0
- KEYCODE_FUN1 = -13,
- KEYCODE_FUN2 = -14,
- KEYCODE_FUN3 = -15,
- KEYCODE_FUN4 = -16,
- KEYCODE_FUN5 = -17,
- KEYCODE_FUN6 = -18,
- KEYCODE_FUN7 = -19,
- KEYCODE_FUN8 = -20,
- KEYCODE_FUN9 = -21,
- KEYCODE_FUN10 = -22,
- KEYCODE_FUN11 = -23,
- KEYCODE_FUN12 = -24,
-#endif
- /* Be sure that last defined value is small enough
- * to not interfere with Alt/Ctrl/Shift bits.
- * So far we do not exceed -31 (0xfff..fffe1),
- * which gives us three upper bits in LSB to play with.
+ KEYCODE_FUN1 = ,
+ KEYCODE_FUN2 = ,
+ KEYCODE_FUN3 = ,
+ KEYCODE_FUN4 = ,
+ KEYCODE_FUN5 = ,
+ KEYCODE_FUN6 = ,
+ KEYCODE_FUN7 = ,
+ KEYCODE_FUN8 = ,
+ KEYCODE_FUN9 = ,
+ KEYCODE_FUN10 = ,
+ KEYCODE_FUN11 = ,
+ KEYCODE_FUN12 = ,
+#endif
+ /* ^^^^^ Be sure that last defined value is small enough.
+ * Current read_key() code allows going up to -32 (0xfff..fffe0).
+ * This gives three upper bits in LSB to play with:
+ * KEYCODE_foo values are 0xfff..fffXX, lowest XX bits are: scavvvvv,
+ * s=0 if SHIFT, c=0 if CTRL, a=0 if ALT,
+ * vvvvv bits are the same for same key regardless of "shift bits".
*/
- //KEYCODE_SHIFT_TAB = (-12) & ~0x80,
- //KEYCODE_SHIFT_... = KEYCODE_... & ~0x80,
- //KEYCODE_CTRL_UP = KEYCODE_UP & ~0x40,
- //KEYCODE_CTRL_DOWN = KEYCODE_DOWN & ~0x40,
- KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40,
- KEYCODE_CTRL_LEFT = KEYCODE_LEFT & ~0x40,
- //KEYCODE_ALT_UP = KEYCODE_UP & ~0x20,
- //KEYCODE_ALT_DOWN = KEYCODE_DOWN & ~0x20,
- KEYCODE_ALT_RIGHT = KEYCODE_RIGHT & ~0x20,
- KEYCODE_ALT_LEFT = KEYCODE_LEFT & ~0x20,
+ //KEYCODE_SHIFT_... = KEYCODE_... & ~0x80,
+ KEYCODE_CTRL_RIGHT = KEYCODE_RIGHT & ~0x40,
+ KEYCODE_CTRL_LEFT = KEYCODE_LEFT & ~0x40,
+ KEYCODE_ALT_RIGHT = KEYCODE_RIGHT & ~0x20,
+ KEYCODE_ALT_LEFT = KEYCODE_LEFT & ~0x20,
+ KEYCODE_ALT_BACKSPACE = KEYCODE_BACKSPACE & ~0x20,
+ KEYCODE_ALT_D = KEYCODE_D & ~0x20,
KEYCODE_CURSOR_POS = -0x100, /* 0xfff..fff00 */
/* How long is the longest ESC sequence we know?
@@ -1688,6 +1780,7 @@ typedef struct sha512_ctx_t {
typedef struct sha3_ctx_t {
uint64_t state[25];
unsigned bytes_queued;
+ unsigned input_block_bytes;
} sha3_ctx_t;
void md5_begin(md5_ctx_t *ctx) FAST_FUNC;
void md5_hash(md5_ctx_t *ctx, const void *buffer, size_t len) FAST_FUNC;
@@ -1737,6 +1830,9 @@ void bb_progress_update(bb_progress_t *p,
uoff_t transferred,
uoff_t totalsize) FAST_FUNC;
+unsigned ubi_devnum_from_devname(const char *str) FAST_FUNC;
+int ubi_get_volid_by_name(unsigned ubi_devnum, const char *vol_name) FAST_FUNC;
+
extern const char *applet_name;
@@ -1760,7 +1856,7 @@ extern const char bb_msg_can_not_create_raw_socket[] ALIGN1;
extern const char bb_msg_perm_denied_are_you_root[] ALIGN1;
extern const char bb_msg_you_must_be_root[] ALIGN1;
extern const char bb_msg_requires_arg[] ALIGN1;
-extern const char bb_msg_invalid_arg[] ALIGN1;
+extern const char bb_msg_invalid_arg_to[] ALIGN1;
extern const char bb_msg_standard_input[] ALIGN1;
extern const char bb_msg_standard_output[] ALIGN1;
@@ -1793,12 +1889,8 @@ extern const char bb_PATH_root_path[] ALIGN1; /* "PATH=/sbin:/usr/sbin:/bin:/usr
#endif
extern const int const_int_0;
-extern const int const_int_1;
+//extern const int const_int_1;
-
-/* Providing hard guarantee on minimum size (think of BUFSIZ == 128) */
-enum { COMMON_BUFSIZE = (BUFSIZ >= 256*sizeof(void*) ? BUFSIZ+1 : 256*sizeof(void*)) };
-extern char bb_common_bufsiz1[COMMON_BUFSIZE];
/* This struct is deliberately not defined. */
/* See docs/keep_data_small.txt */
struct globals;
@@ -1905,6 +1997,7 @@ extern const char bb_default_login_shell[] ALIGN1;
#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
+#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
/* We redefine ctype macros. Unicode-correct handling of char types
@@ -1990,6 +2083,140 @@ static ALWAYS_INLINE unsigned char bb_ascii_tolower(unsigned char a)
#define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20)
+/* Simple unit-testing framework */
+
+typedef void (*bbunit_testfunc)(void);
+
+struct bbunit_listelem {
+ const char* name;
+ bbunit_testfunc testfunc;
+};
+
+void bbunit_registertest(struct bbunit_listelem* test);
+void bbunit_settestfailed(void);
+
+#define BBUNIT_DEFINE_TEST(NAME) \
+ static void bbunit_##NAME##_test(void); \
+ static struct bbunit_listelem bbunit_##NAME##_elem = { \
+ .name = #NAME, \
+ .testfunc = bbunit_##NAME##_test, \
+ }; \
+ static void INIT_FUNC bbunit_##NAME##_register(void) \
+ { \
+ bbunit_registertest(&bbunit_##NAME##_elem); \
+ } \
+ static void bbunit_##NAME##_test(void)
+
+/*
+ * Both 'goto bbunit_end' and 'break' are here only to get rid
+ * of compiler warnings.
+ */
+#define BBUNIT_ENDTEST \
+ do { \
+ goto bbunit_end; \
+ bbunit_end: \
+ break; \
+ } while (0)
+
+#define BBUNIT_PRINTASSERTFAIL \
+ do { \
+ bb_error_msg( \
+ "[ERROR] Assertion failed in file %s, line %d", \
+ __FILE__, __LINE__); \
+ } while (0)
+
+#define BBUNIT_ASSERTION_FAILED \
+ do { \
+ bbunit_settestfailed(); \
+ goto bbunit_end; \
+ } while (0)
+
+/*
+ * Assertions.
+ * For now we only offer assertions which cause tests to fail
+ * immediately. In the future 'expects' might be added too -
+ * similar to those offered by the gtest framework.
+ */
+#define BBUNIT_ASSERT_EQ(EXPECTED, ACTUAL) \
+ do { \
+ if ((EXPECTED) != (ACTUAL)) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] '%s' isn't equal to '%s'", \
+ #EXPECTED, #ACTUAL); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_NOTEQ(EXPECTED, ACTUAL) \
+ do { \
+ if ((EXPECTED) == (ACTUAL)) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] '%s' is equal to '%s'", \
+ #EXPECTED, #ACTUAL); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_NOTNULL(PTR) \
+ do { \
+ if ((PTR) == NULL) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] '%s' is NULL!", #PTR); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_NULL(PTR) \
+ do { \
+ if ((PTR) != NULL) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] '%s' is not NULL!", #PTR); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_FALSE(STATEMENT) \
+ do { \
+ if ((STATEMENT)) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] Statement '%s' evaluated to true!", \
+ #STATEMENT); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_TRUE(STATEMENT) \
+ do { \
+ if (!(STATEMENT)) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] Statement '%s' evaluated to false!", \
+ #STATEMENT); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_STREQ(STR1, STR2) \
+ do { \
+ if (strcmp(STR1, STR2) != 0) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] Strings '%s' and '%s' " \
+ "are not the same", STR1, STR2); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+#define BBUNIT_ASSERT_STRNOTEQ(STR1, STR2) \
+ do { \
+ if (strcmp(STR1, STR2) == 0) { \
+ BBUNIT_PRINTASSERTFAIL; \
+ bb_error_msg("[ERROR] Strings '%s' and '%s' " \
+ "are the same, but were " \
+ "expected to differ", STR1, STR2); \
+ BBUNIT_ASSERTION_FAILED; \
+ } \
+ } while (0)
+
+
POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/include/platform.h b/include/platform.h
index b5aa683..9d357a0 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -76,6 +76,9 @@
# define UNUSED_PARAM_RESULT
#endif
+/* used by unit test machinery to run registration functions before calling main() */
+#define INIT_FUNC __attribute__ ((constructor))
+
/* -fwhole-program makes all symbols local. The attribute externally_visible
* forces a symbol global. */
#if __GNUC_PREREQ(4,1)
@@ -172,13 +175,7 @@
# define BB_BIG_ENDIAN 0
# define BB_LITTLE_ENDIAN 1
#else
-# error
-# error
-# error "Can't determine endianness !"
-# error "DO NOT USE 'MAKE' TO BUILD BIONIC BUSYBOX"
-# error
-# error
- exit 1
+# error "Can't determine endianness"
#endif
#if ULONG_MAX > 0xffffffff
@@ -220,6 +217,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
* a lvalue. This makes it more likely to not swap them by mistake
*/
#if defined(i386) || defined(__x86_64__) || defined(__powerpc__)
+# define BB_UNALIGNED_MEMACCESS_OK 1
# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp))
# define move_from_unaligned_long(v, longp) ((v) = *(bb__aliased_long*)(longp))
# define move_from_unaligned16(v, u16p) ((v) = *(bb__aliased_uint16_t*)(u16p))
@@ -228,6 +226,7 @@ typedef uint64_t bb__aliased_uint64_t FIX_ALIASING;
# define move_to_unaligned32(u32p, v) (*(bb__aliased_uint32_t*)(u32p) = (v))
/* #elif ... - add your favorite arch today! */
#else
+# define BB_UNALIGNED_MEMACCESS_OK 0
/* performs reasonably well (gcc usually inlines memcpy here) */
# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int)))
# define move_from_unaligned_long(v, longp) (memcpy(&(v), (longp), sizeof(long)))
@@ -353,7 +352,7 @@ typedef unsigned smalluint;
#endif
#if defined(ANDROID) || defined(__ANDROID__)
-# define BB_ADDITIONAL_PATH ":/vendor/bin:/system/sbin:/system/bin:/system/xbin"
+# define BB_ADDITIONAL_PATH ":/system/sbin:/system/bin:/system/xbin"
# define SYS_ioprio_set __NR_ioprio_set
# define SYS_ioprio_get __NR_ioprio_get
#endif
@@ -369,16 +368,19 @@ typedef unsigned smalluint;
#define HAVE_DPRINTF 1
#define HAVE_MEMRCHR 1
#define HAVE_MKDTEMP 1
+#define HAVE_TTYNAME_R 1
#define HAVE_PTSNAME_R 1
#define HAVE_SETBIT 1
#define HAVE_SIGHANDLER_T 1
#define HAVE_STPCPY 1
+#define HAVE_MEMPCPY 1
#define HAVE_STRCASESTR 1
#define HAVE_STRCHRNUL 1
#define HAVE_STRSEP 1
#define HAVE_STRSIGNAL 1
#define HAVE_STRVERSCMP 1
#define HAVE_VASPRINTF 1
+#define HAVE_USLEEP 1
#define HAVE_UNLOCKED_STDIO 1
#define HAVE_UNLOCKED_LINE_OPS 1
#define HAVE_GETLINE 1
@@ -387,12 +389,15 @@ typedef unsigned smalluint;
#define HAVE_NET_ETHERNET_H 1
#define HAVE_SYS_STATFS_H 1
-#if defined(__UCLIBC__) && UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
-# undef HAVE_STRVERSCMP
-#endif
-
-#if defined(__dietlibc__)
-# undef HAVE_STRCHRNUL
+#if defined(__UCLIBC__)
+# if UCLIBC_VERSION < KERNEL_VERSION(0, 9, 32)
+# undef HAVE_STRVERSCMP
+# endif
+# if UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 30)
+# ifndef __UCLIBC_SUSV3_LEGACY__
+# undef HAVE_USLEEP
+# endif
+# endif
#endif
#if defined(__WATCOMC__)
@@ -425,7 +430,7 @@ typedef unsigned smalluint;
/* These BSD-derived OSes share many similarities */
#if (defined __digital__ && defined __unix__) \
|| defined __APPLE__ \
- || defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__
+ || defined __OpenBSD__ || defined __NetBSD__
# undef HAVE_CLEARENV
# undef HAVE_FDATASYNC
# undef HAVE_GETLINE
@@ -440,10 +445,32 @@ typedef unsigned smalluint;
# undef HAVE_UNLOCKED_LINE_OPS
#endif
-#if defined(__FreeBSD__) || defined(__APPLE__)
+#if defined(__dietlibc__)
+# undef HAVE_STRCHRNUL
+#endif
+
+#if defined(__APPLE__)
# undef HAVE_STRCHRNUL
#endif
+#if defined(__FreeBSD__)
+/* users say mempcpy is not present in FreeBSD 9.x */
+# undef HAVE_MEMPCPY
+# undef HAVE_CLEARENV
+# undef HAVE_FDATASYNC
+# undef HAVE_MNTENT_H
+# undef HAVE_PTSNAME_R
+# undef HAVE_SYS_STATFS_H
+# undef HAVE_SIGHANDLER_T
+# undef HAVE_STRVERSCMP
+# undef HAVE_XTABS
+# undef HAVE_UNLOCKED_LINE_OPS
+# include <osreldate.h>
+# if __FreeBSD_version < 1000029
+# undef HAVE_STRCHRNUL /* FreeBSD added strchrnul() between 1000028 and 1000029 */
+# endif
+#endif
+
#if defined(__NetBSD__)
# define HAVE_GETLINE 1 /* Recent NetBSD versions have getline() */
#endif
@@ -453,14 +480,25 @@ typedef unsigned smalluint;
#endif
#if defined(ANDROID) || defined(__ANDROID__)
-# undef HAVE_DPRINTF
+# if __ANDROID_API__ < 8
+ /* ANDROID < 8 has no [f]dprintf at all */
+# undef HAVE_DPRINTF
+# elif __ANDROID_API__ < 21
+ /* ANDROID < 21 has fdprintf */
+# define dprintf fdprintf
+# else
+ /* ANDROID >= 21 has standard dprintf */
+# endif
+# if __ANDROID_API__ < 21
+# undef HAVE_TTYNAME_R
+# undef HAVE_GETLINE
+# undef HAVE_STPCPY
+# endif
+# undef HAVE_MEMPCPY
# undef HAVE_STRCHRNUL
# undef HAVE_STRVERSCMP
# undef HAVE_UNLOCKED_LINE_OPS
# undef HAVE_NET_ETHERNET_H
-# ifndef BIONIC_L
-# undef HAVE_STPCPY
-# endif
#endif
/*
@@ -480,6 +518,11 @@ extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC;
extern char *mkdtemp(char *template) FAST_FUNC;
#endif
+#ifndef HAVE_TTYNAME_R
+#define ttyname_r bb_ttyname_r
+extern int ttyname_r(int fd, char *buf, size_t buflen);
+#endif
+
#ifndef HAVE_SETBIT
# define setbit(a, b) ((a)[(b) >> 3] |= 1 << ((b) & 7))
# define clrbit(a, b) ((a)[(b) >> 3] &= ~(1 << ((b) & 7)))
@@ -493,6 +536,18 @@ typedef void (*sighandler_t)(int);
extern char *stpcpy(char *p, const char *to_add) FAST_FUNC;
#endif
+#ifndef HAVE_MEMPCPY
+#include <string.h>
+/* In case we are wrong about !HAVE_MEMPCPY, and toolchain _does_ have
+ * mempcpy(), avoid colliding with it:
+ */
+#define mempcpy bb__mempcpy
+static ALWAYS_INLINE void *mempcpy(void *dest, const void *src, size_t len)
+{
+ return (char*)((uintptr_t)memcpy(dest, src, len) + len);
+}
+#endif
+
#ifndef HAVE_STRCASESTR
extern char *strcasestr(const char *s, const char *pattern) FAST_FUNC;
#endif
@@ -510,6 +565,10 @@ extern char *strsep(char **stringp, const char *delim) FAST_FUNC;
# define strsignal(sig) get_signame(sig)
#endif
+#ifndef HAVE_USLEEP
+extern int usleep(unsigned) FAST_FUNC;
+#endif
+
#ifndef HAVE_VASPRINTF
extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC;
#endif
@@ -520,9 +579,4 @@ extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC
extern ssize_t getline(char **lineptr, size_t *n, FILE *stream) FAST_FUNC;
#endif
-#if defined(__BIONIC__)
-#include "android.h"
#endif
-
-
-#endif /* BB_PLATFORM_H */
diff --git a/include/pwd_.h b/include/pwd_.h
index 625b6f5..1734829 100644
--- a/include/pwd_.h
+++ b/include/pwd_.h
@@ -34,69 +34,30 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
#define setpwent bb_internal_setpwent
#define endpwent bb_internal_endpwent
#define getpwent bb_internal_getpwent
-#define fgetpwent bb_internal_fgetpwent
-#define putpwent bb_internal_putpwent
#define getpwuid bb_internal_getpwuid
#define getpwnam bb_internal_getpwnam
-#define getpwent_r bb_internal_getpwent_r
-#define getpwuid_r bb_internal_getpwuid_r
#define getpwnam_r bb_internal_getpwnam_r
-#define fgetpwent_r bb_internal_fgetpwent_r
-
/* All function names below should be remapped by #defines above
* in order to not collide with libc names. */
-
/* Rewind the password-file stream. */
-extern void setpwent(void);
+void FAST_FUNC setpwent(void);
/* Close the password-file stream. */
-extern void endpwent(void);
+void FAST_FUNC endpwent(void);
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
/* Read an entry from the password-file stream, opening it if necessary. */
-extern struct passwd *getpwent(void);
-
-/* Read an entry from STREAM. */
-extern struct passwd *fgetpwent(FILE *__stream);
-
-/* Write the given entry onto the given stream. */
-extern int putpwent(const struct passwd *__restrict __p,
- FILE *__restrict __f);
-#endif
+struct passwd* FAST_FUNC getpwent(void);
/* Search for an entry with a matching user ID. */
-extern struct passwd *getpwuid(uid_t __uid);
+struct passwd* FAST_FUNC getpwuid(uid_t __uid);
/* Search for an entry with a matching username. */
-extern struct passwd *getpwnam(const char *__name);
-
-/* Reentrant versions of some of the functions above.
-
- PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
- The interface may change in later versions of this library. But
- the interface is designed following the principals used for the
- other reentrant functions so the chances are good this is what the
- POSIX people would choose. */
-
-extern int getpwent_r(struct passwd *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct passwd **__restrict __result);
-
-extern int getpwuid_r(uid_t __uid,
- struct passwd *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct passwd **__restrict __result);
-
-extern int getpwnam_r(const char *__restrict __name,
- struct passwd *__restrict __resultbuf,
- char *__restrict __buffer, size_t __buflen,
- struct passwd **__restrict __result);
+struct passwd* FAST_FUNC getpwnam(const char *__name);
-/* Read an entry from STREAM. This function is not standardized and
- probably never will. */
-extern int fgetpwent_r(FILE *__restrict __stream,
+/* Reentrant versions of some of the functions above. */
+int FAST_FUNC getpwnam_r(const char *__restrict __name,
struct passwd *__restrict __resultbuf,
char *__restrict __buffer, size_t __buflen,
struct passwd **__restrict __result);
diff --git a/include/shadow_.h b/include/shadow_.h
index 7babe4f..8e2581e 100644
--- a/include/shadow_.h
+++ b/include/shadow_.h
@@ -57,48 +57,48 @@ struct spwd {
#ifdef UNUSED_FOR_NOW
/* Open database for reading */
-extern void setspent(void);
+void FAST_FUNC setspent(void);
/* Close database */
-extern void endspent(void);
+void FAST_FUNC endspent(void);
/* Get next entry from database, perhaps after opening the file */
-extern struct spwd *getspent(void);
+struct spwd* FAST_FUNC getspent(void);
/* Get shadow entry matching NAME */
-extern struct spwd *getspnam(const char *__name);
+struct spwd* FAST_FUNC getspnam(const char *__name);
/* Read shadow entry from STRING */
-extern struct spwd *sgetspent(const char *__string);
+struct spwd* FAST_FUNC sgetspent(const char *__string);
/* Read next shadow entry from STREAM */
-extern struct spwd *fgetspent(FILE *__stream);
+struct spwd* FAST_FUNC fgetspent(FILE *__stream);
/* Write line containing shadow password entry to stream */
-extern int putspent(const struct spwd *__p, FILE *__stream);
+int FAST_FUNC putspent(const struct spwd *__p, FILE *__stream);
/* Reentrant versions of some of the functions above */
-extern int getspent_r(struct spwd *__result_buf, char *__buffer,
+int FAST_FUNC getspent_r(struct spwd *__result_buf, char *__buffer,
size_t __buflen, struct spwd **__result);
#endif
-extern int getspnam_r(const char *__name, struct spwd *__result_buf,
+int FAST_FUNC getspnam_r(const char *__name, struct spwd *__result_buf,
char *__buffer, size_t __buflen,
struct spwd **__result);
#ifdef UNUSED_FOR_NOW
-extern int sgetspent_r(const char *__string, struct spwd *__result_buf,
+int FAST_FUNC sgetspent_r(const char *__string, struct spwd *__result_buf,
char *__buffer, size_t __buflen,
struct spwd **__result);
-extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf,
+int FAST_FUNC fgetspent_r(FILE *__stream, struct spwd *__result_buf,
char *__buffer, size_t __buflen,
struct spwd **__result);
/* Protect password file against multi writers */
-extern int lckpwdf(void);
+int FAST_FUNC lckpwdf(void);
/* Unlock password file */
-extern int ulckpwdf(void);
+int FAST_FUNC ulckpwdf(void);
#endif
POP_SAVED_FUNCTION_VISIBILITY
diff --git a/init/bootchartd.c b/init/bootchartd.c
index c7388c9..92aaade 100644
--- a/init/bootchartd.c
+++ b/init/bootchartd.c
@@ -47,6 +47,7 @@
//config: and /etc/bootchartd.conf files.
#include "libbb.h"
+#include "common_bufsiz.h"
/* After libbb.h, since it needs sys/types.h on some systems */
#include <sys/utsname.h>
@@ -115,8 +116,8 @@
struct globals {
char jiffy_line[COMMON_BUFSIZE];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void dump_file(FILE *fp, const char *filename)
{
@@ -193,7 +194,7 @@ static char *make_tempdir(void)
* Since we unmount it at once, we can mount it anywhere.
* Try a few locations which are likely ti exist.
*/
- static const char dirs[] = "/mnt\0""/tmp\0""/boot\0""/proc\0";
+ static const char dirs[] ALIGN1 = "/mnt\0""/tmp\0""/boot\0""/proc\0";
const char *try_dir = dirs;
while (mount("none", try_dir, "tmpfs", MS_SILENT, "size=16m") != 0) {
try_dir += strlen(try_dir) + 1;
diff --git a/init/halt.c b/init/halt.c
index 7f1bebb..8073103 100644
--- a/init/halt.c
+++ b/init/halt.c
@@ -7,22 +7,28 @@
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
-//applet:IF_HALT(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
-//applet:IF_HALT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
-
-//kbuild:lib-$(CONFIG_HALT) += halt.o
-
//config:config HALT
-//config: bool "poweroff, halt, and reboot"
+//config: bool "halt"
+//config: default y
+//config: help
+//config: Stop all processes and halt the system.
+//config:
+//config:config POWEROFF
+//config: bool "poweroff"
//config: default y
//config: help
-//config: Stop all processes and either halt, reboot, or power off the system.
+//config: Stop all processes and power off the system.
+//config:
+//config:config REBOOT
+//config: bool "reboot"
+//config: default y
+//config: help
+//config: Stop all processes and reboot the system.
//config:
//config:config FEATURE_CALL_TELINIT
//config: bool "Call telinit on shutdown and reboot"
//config: default y
-//config: depends on HALT && !INIT
+//config: depends on (HALT || POWEROFF || REBOOT) && !INIT
//config: help
//config: Call an external program (normally telinit) to facilitate
//config: a switch to a proper runlevel.
@@ -39,6 +45,14 @@
//config: to facilitate proper shutdown, this path is to be used when
//config: locating telinit executable.
+//applet:IF_HALT(APPLET(halt, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_POWEROFF(APPLET_ODDNAME(poweroff, halt, BB_DIR_SBIN, BB_SUID_DROP, poweroff))
+//applet:IF_REBOOT(APPLET_ODDNAME(reboot, halt, BB_DIR_SBIN, BB_SUID_DROP, reboot))
+
+//kbuild:lib-$(CONFIG_HALT) += halt.o
+//kbuild:lib-$(CONFIG_POWEROFF) += halt.o
+//kbuild:lib-$(CONFIG_REBOOT) += halt.o
+
//usage:#define halt_trivial_usage
//usage: "[-d DELAY] [-n] [-f]" IF_FEATURE_WTMP(" [-w]")
//usage:#define halt_full_usage "\n\n"
@@ -78,7 +92,7 @@
static void write_wtmp(void)
{
- struct utmp utmp;
+ struct utmpx utmp;
struct utsname uts;
/* "man utmp" says wtmp file should *not* be created automagically */
/*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
@@ -92,7 +106,7 @@ static void write_wtmp(void)
utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */
uname(&uts);
safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host));
- updwtmp(bb_path_wtmp_file, &utmp);
+ updwtmpx(bb_path_wtmp_file, &utmp);
}
#else
#define write_wtmp() ((void)0)
@@ -113,16 +127,24 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
int which, flags, rc;
/* Figure out which applet we're running */
+ if (ENABLE_HALT && !ENABLE_POWEROFF && !ENABLE_REBOOT)
+ which = 0;
+ else
+ if (!ENABLE_HALT && ENABLE_POWEROFF && !ENABLE_REBOOT)
+ which = 1;
+ else
+ if (!ENABLE_HALT && !ENABLE_POWEROFF && ENABLE_REBOOT)
+ which = 2;
+ else
for (which = 0; "hpr"[which] != applet_name[0]; which++)
continue;
/* Parse and handle arguments */
- opt_complementary = "d+"; /* -d N */
/* We support -w even if !ENABLE_FEATURE_WTMP,
* in order to not break scripts.
* -i (shut down network interfaces) is ignored.
*/
- flags = getopt32(argv, "d:nfwi", &delay);
+ flags = getopt32(argv, "d:+nfwi", &delay);
sleep(delay);
@@ -164,7 +186,7 @@ int halt_main(int argc UNUSED_PARAM, char **argv)
if (!(flags & 4)) { /* no -f */
//TODO: I tend to think that signalling linuxrc is wrong
// pity original author didn't comment on it...
- if (ENABLE_FEATURE_INITRD) {
+ if (ENABLE_LINUXRC) {
/* talk to linuxrc */
/* bbox init/linuxrc assumed */
pid_t *pidlist = find_pid_by_name("linuxrc");
diff --git a/init/init.c b/init/init.c
index d29328c..055f2b4 100644
--- a/init/init.c
+++ b/init/init.c
@@ -16,10 +16,21 @@
//config: help
//config: init is the first program run when the system boots.
//config:
+//config:config LINUXRC
+//config: bool "Support running init from within an initrd (not initramfs)"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: Legacy support for running init under the old-style initrd. Allows
+//config: the name linuxrc to act as init, and it doesn't assume init is PID 1.
+//config:
+//config: This does not apply to initramfs, which runs /init as PID 1 and
+//config: requires no special support.
+//config:
//config:config FEATURE_USE_INITTAB
//config: bool "Support reading an inittab file"
//config: default y
-//config: depends on INIT
+//config: depends on INIT || LINUXRC
//config: help
//config: Allow init to read an inittab file when the system boot.
//config:
@@ -46,7 +57,7 @@
//config:config FEATURE_INIT_SCTTY
//config: bool "Run commands with leading dash with controlling tty"
//config: default y
-//config: depends on INIT
+//config: depends on INIT || LINUXRC
//config: help
//config: If this option is enabled, init will try to give a controlling
//config: tty to any command which has leading hyphen (often it's "-/bin/sh").
@@ -61,40 +72,29 @@
//config:config FEATURE_INIT_SYSLOG
//config: bool "Enable init to write to syslog"
//config: default y
-//config: depends on INIT
+//config: depends on INIT || LINUXRC
//config:
//config:config FEATURE_EXTRA_QUIET
//config: bool "Be _extra_ quiet on boot"
//config: default y
-//config: depends on INIT
+//config: depends on INIT || LINUXRC
//config: help
//config: Prevent init from logging some messages to the console during boot.
//config:
//config:config FEATURE_INIT_COREDUMPS
//config: bool "Support dumping core for child processes (debugging only)"
-//config: default y
-//config: depends on INIT
+//config: default n # not Y because this is a debug option
+//config: depends on INIT || LINUXRC
//config: help
//config: If this option is enabled and the file /.init_enable_core
//config: exists, then init will call setrlimit() to allow unlimited
//config: core file sizes. If this option is disabled, processes
//config: will not generate any core files.
//config:
-//config:config FEATURE_INITRD
-//config: bool "Support running init from within an initrd (not initramfs)"
-//config: default y
-//config: depends on INIT
-//config: help
-//config: Legacy support for running init under the old-style initrd. Allows
-//config: the name linuxrc to act as init, and it doesn't assume init is PID 1.
-//config:
-//config: This does not apply to initramfs, which runs /init as PID 1 and
-//config: requires no special support.
-//config:
//config:config INIT_TERMINAL_TYPE
//config: string "Initial terminal type"
//config: default "linux"
-//config: depends on INIT
+//config: depends on INIT || LINUXRC
//config: help
//config: This is the initial value set by init for the TERM environment
//config: variable. This variable is used by programs which make use of
@@ -102,17 +102,32 @@
//config:
//config: Note that on Linux, init attempts to detect serial terminal and
//config: sets TERM to "vt102" if one is found.
+//config:
+//config:config FEATURE_INIT_MODIFY_CMDLINE
+//config: bool "Modify the command-line to \"init\""
+//config: default y
+//config: depends on INIT || LINUXRC
+//config: help
+//config: When launched as PID 1 and after parsing its arguments, init
+//config: wipes all the arguments but argv[0] and rewrites argv[0] to
+//config: contain only "init", so that its command-line appears solely as
+//config: "init" in tools such as ps.
+//config: If this option is set to Y, init will keep its original behavior,
+//config: otherwise, all the arguments including argv[0] will be preserved,
+//config: be they parsed or ignored by init.
+//config: The original command-line used to launch init can then be
+//config: retrieved in /proc/1/cmdline on Linux, for example.
//applet:IF_INIT(APPLET(init, BB_DIR_SBIN, BB_SUID_DROP))
-//applet:IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc))
+//applet:IF_LINUXRC(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc))
//kbuild:lib-$(CONFIG_INIT) += init.o
+//kbuild:lib-$(CONFIG_LINUXRC) += init.o
#define DEBUG_SEGV_HANDLER 0
#include "libbb.h"
#include <syslog.h>
-#include <paths.h>
#include <sys/resource.h>
#ifdef __linux__
# include <linux/vt.h>
@@ -282,6 +297,11 @@ static void console_init(void)
s = getenv("CONSOLE");
if (!s)
s = getenv("console");
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /* BSD people say their kernels do not open fd 0,1,2; they need this: */
+ if (!s)
+ s = (char*)"/dev/console";
+#endif
if (s) {
int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (fd >= 0) {
@@ -538,11 +558,7 @@ static struct init_action *mark_terminated(pid_t pid)
struct init_action *a;
if (pid > 0) {
- update_utmp(pid, DEAD_PROCESS,
- /*tty_name:*/ NULL,
- /*username:*/ NULL,
- /*hostname:*/ NULL
- );
+ update_utmp_DEAD_PROCESS(pid);
for (a = init_action_list; a; a = a->next) {
if (a->pid == pid) {
a->pid = 0;
@@ -822,7 +838,7 @@ static void halt_reboot_pwoff(int sig)
/* Handler for QUIT - exec "restart" action,
* else (no such action defined) do nothing */
-static void restart_handler(int sig UNUSED_PARAM)
+static void exec_restart_action(void)
{
struct init_action *a;
@@ -975,6 +991,20 @@ static int check_delayed_sigs(void)
#endif
if (sig == SIGINT)
run_actions(CTRLALTDEL);
+ if (sig == SIGQUIT) {
+ exec_restart_action();
+ /* returns only if no restart action defined */
+ }
+ if ((1 << sig) & (0
+#ifdef SIGPWR
+ + (1 << SIGPWR)
+#endif
+ + (1 << SIGUSR1)
+ + (1 << SIGUSR2)
+ + (1 << SIGTERM)
+ )) {
+ halt_reboot_pwoff(sig);
+ }
}
}
@@ -1005,6 +1035,11 @@ void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
}
#endif
+static void sleep_much(void)
+{
+ sleep(30 * 24*60*60);
+}
+
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int init_main(int argc UNUSED_PARAM, char **argv)
{
@@ -1028,7 +1063,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
if (!DEBUG_INIT) {
/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
if (getpid() != 1
- && (!ENABLE_FEATURE_INITRD || applet_name[0] != 'l') /* not linuxrc? */
+ && (!ENABLE_LINUXRC || applet_name[0] != 'l') /* not linuxrc? */
) {
bb_error_msg_and_die("must be run as PID 1");
}
@@ -1041,12 +1076,12 @@ int init_main(int argc UNUSED_PARAM, char **argv)
/* If, say, xmalloc would ever die, we don't want to oops kernel
* by exiting.
- * NB: we set die_sleep *after* PID 1 check and bb_show_usage.
+ * NB: we set die_func *after* PID 1 check and bb_show_usage.
* Otherwise, for example, "init u" ("please rexec yourself"
* command for sysvinit) will show help text (which isn't too bad),
* *and sleep forever* (which is bad!)
*/
- die_sleep = 30 * 24*60*60;
+ die_func = sleep_much;
/* Figure out where the default console should be */
console_init();
@@ -1070,7 +1105,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
#if 0
/* It's 2013, does anyone really still depend on this? */
-/* If you do, consider adding swapon to sysinot actions then! */
+/* If you do, consider adding swapon to sysinit actions then! */
/* struct sysinfo is linux-specific */
# ifdef __linux__
/* Make sure there is enough memory to do something useful. */
@@ -1108,7 +1143,7 @@ int init_main(int argc UNUSED_PARAM, char **argv)
parse_inittab();
}
-#if ENABLE_SELINUX
+/*#if ENABLE_SELINUX
if (getenv("SELINUX_INIT") == NULL) {
int enforce = 0;
@@ -1116,32 +1151,25 @@ int init_main(int argc UNUSED_PARAM, char **argv)
if (selinux_init_load_policy(&enforce) == 0) {
BB_EXECVP(argv[0], argv);
} else if (enforce > 0) {
- /* SELinux in enforcing mode but load_policy failed */
message(L_CONSOLE, "can't load SELinux Policy. "
"Machine is in enforcing mode. Halting now.");
return EXIT_FAILURE;
}
}
-#endif
-
- /* Make the command line just say "init" - thats all, nothing else */
- strncpy(argv[0], "init", strlen(argv[0]));
- /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
- while (*++argv)
- nuke_str(*argv);
+#endif*/
+
+ if (ENABLE_FEATURE_INIT_MODIFY_CMDLINE) {
+ /* Make the command line just say "init" - that's all, nothing else */
+ strncpy(argv[0], "init", strlen(argv[0]));
+ /* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
+ while (*++argv)
+ nuke_str(*argv);
+ }
/* Set up signal handlers */
if (!DEBUG_INIT) {
struct sigaction sa;
- bb_signals(0
- + (1 << SIGPWR) /* halt */
- + (1 << SIGUSR1) /* halt */
- + (1 << SIGTERM) /* reboot */
- + (1 << SIGUSR2) /* poweroff */
- , halt_reboot_pwoff);
- signal(SIGQUIT, restart_handler); /* re-exec another init */
-
/* Stop handler must allow only SIGCONT inside itself */
memset(&sa, 0, sizeof(sa));
sigfillset(&sa.sa_mask);
@@ -1156,18 +1184,24 @@ int init_main(int argc UNUSED_PARAM, char **argv)
*/
sigaction_set(SIGSTOP, &sa); /* pause */
- /* SIGINT (Ctrl-Alt-Del) must interrupt wait(),
+ /* These signals must interrupt wait(),
* setting handler without SA_RESTART flag.
*/
- bb_signals_recursive_norestart((1 << SIGINT), record_signo);
+ bb_signals_recursive_norestart(0
+ + (1 << SIGINT) /* Ctrl-Alt-Del */
+ + (1 << SIGQUIT) /* re-exec another init */
+#ifdef SIGPWR
+ + (1 << SIGPWR) /* halt */
+#endif
+ + (1 << SIGUSR1) /* halt */
+ + (1 << SIGTERM) /* reboot */
+ + (1 << SIGUSR2) /* poweroff */
+#if ENABLE_FEATURE_USE_INITTAB
+ + (1 << SIGHUP) /* reread /etc/inittab */
+#endif
+ , record_signo);
}
- /* Set up "reread /etc/inittab" handler.
- * Handler is set up without SA_RESTART, it will interrupt syscalls.
- */
- if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB)
- bb_signals_recursive_norestart((1 << SIGHUP), record_signo);
-
/* Now run everything that needs to be run */
/* First run the sysinit command */
run_actions(SYSINIT);
diff --git a/libbb/Config.src b/libbb/Config.src
index 19021fe..172fbcc 100644
--- a/libbb/Config.src
+++ b/libbb/Config.src
@@ -7,6 +7,30 @@ menu "Busybox Library Tuning"
INSERT
+choice
+ prompt "Buffer allocation policy"
+ default FEATURE_BUFFERS_USE_MALLOC
+ help
+ There are 3 ways BusyBox can handle buffer allocations:
+ - Use malloc. This costs code size for the call to xmalloc.
+ - Put them on stack. For some very small machines with limited stack
+ space, this can be deadly. For most folks, this works just fine.
+ - Put them in BSS. This works beautifully for computers with a real
+ MMU (and OS support), but wastes runtime RAM for uCLinux. This
+ behavior was the only one available for BusyBox versions 0.48 and
+ earlier.
+
+config FEATURE_BUFFERS_USE_MALLOC
+ bool "Allocate with Malloc"
+
+config FEATURE_BUFFERS_GO_ON_STACK
+ bool "Allocate on the Stack"
+
+config FEATURE_BUFFERS_GO_IN_BSS
+ bool "Allocate in the .bss section"
+
+endchoice
+
config PASSWORD_MINLEN
int "Minimum password length"
default 6
@@ -16,7 +40,7 @@ config PASSWORD_MINLEN
config MD5_SMALL
int "MD5: Trade bytes for speed (0:fast, 3:slow)"
- default 1
+ default 1 # all "fast or small" options default to small
range 0 3
help
Trade binary size versus speed for the md5sum algorithm.
@@ -30,7 +54,7 @@ config MD5_SMALL
config SHA3_SMALL
int "SHA3: Trade bytes for speed (0:fast, 1:slow)"
- default 1
+ default 1 # all "fast or small" options default to small
range 0 1
help
Trade binary size versus speed for the sha3sum algorithm.
@@ -40,7 +64,7 @@ config SHA3_SMALL
config FEATURE_FAST_TOP
bool "Faster /proc scanning code (+100 bytes)"
- default y
+ default n # all "fast or small" options default to small
help
This option makes top (and ps) ~20% faster (or 20% less CPU hungry),
but code size is slightly bigger.
@@ -114,7 +138,7 @@ config FEATURE_EDITING_SAVE_ON_EXIT
config FEATURE_REVERSE_SEARCH
bool "Reverse history search"
default y
- depends on FEATURE_EDITING_SAVEHISTORY
+ depends on FEATURE_EDITING
help
Enable readline-like Ctrl-R combination for reverse history search.
Increases code by about 0.5k.
@@ -128,7 +152,7 @@ config FEATURE_TAB_COMPLETION
config FEATURE_USERNAME_COMPLETION
bool "Username completion"
- default n
+ default y
depends on FEATURE_TAB_COMPLETION
help
Enable username completion.
@@ -153,6 +177,131 @@ config FEATURE_EDITING_ASK_TERMINAL
correctly, or want to save on code size (about 400 bytes),
then do not turn this option on.
+config LOCALE_SUPPORT
+ bool "Enable locale support (system needs locale for this to work)"
+ default n
+ help
+ Enable this if your system has locale support and you would like
+ busybox to support locale settings.
+
+config UNICODE_SUPPORT
+ bool "Support Unicode"
+ default y
+ help
+ This makes various applets aware that one byte is not
+ one character on screen.
+
+ Busybox aims to eventually work correctly with Unicode displays.
+ Any older encodings are not guaranteed to work.
+ Probably by the time when busybox will be fully Unicode-clean,
+ other encodings will be mainly of historic interest.
+
+config UNICODE_USING_LOCALE
+ bool "Use libc routines for Unicode (else uses internal ones)"
+ default n
+ depends on UNICODE_SUPPORT && LOCALE_SUPPORT
+ help
+ With this option on, Unicode support is implemented using libc
+ routines. Otherwise, internal implementation is used.
+ Internal implementation is smaller.
+
+config FEATURE_CHECK_UNICODE_IN_ENV
+ bool "Check $LC_ALL, $LC_CTYPE and $LANG environment variables"
+ default n
+ depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
+ help
+ With this option on, Unicode support is activated
+ only if locale-related variables have the value of the form
+ "xxxx.utf8"
+
+ Otherwise, Unicode support will be always enabled and active.
+
+config SUBST_WCHAR
+ int "Character code to substitute unprintable characters with"
+ depends on UNICODE_SUPPORT
+ default 63
+ help
+ Typical values are 63 for '?' (works with any output device),
+ 30 for ASCII substitute control code,
+ 65533 (0xfffd) for Unicode replacement character.
+
+config LAST_SUPPORTED_WCHAR
+ int "Range of supported Unicode characters"
+ depends on UNICODE_SUPPORT
+ default 767
+ help
+ Any character with Unicode value bigger than this is assumed
+ to be non-printable on output device. Many applets replace
+ such characters with substitution character.
+
+ The idea is that many valid printable Unicode chars
+ nevertheless are not displayed correctly. Think about
+ combining charachers, double-wide hieroglyphs, obscure
+ characters in dozens of ancient scripts...
+ Many terminals, terminal emulators, xterms etc will fail
+ to handle them correctly. Choose the smallest value
+ which suits your needs.
+
+ Typical values are:
+ 126 - ASCII only
+ 767 (0x2ff) - there are no combining chars in [0..767] range
+ (the range includes Latin 1, Latin Ext. A and B),
+ code is ~700 bytes smaller for this case.
+ 4351 (0x10ff) - there are no double-wide chars in [0..4351] range,
+ code is ~300 bytes smaller for this case.
+ 12799 (0x31ff) - nearly all non-ideographic characters are
+ available in [0..12799] range, including
+ East Asian scripts like katakana, hiragana, hangul,
+ bopomofo...
+ 0 - off, any valid printable Unicode character will be printed.
+
+config UNICODE_COMBINING_WCHARS
+ bool "Allow zero-width Unicode characters on output"
+ default n
+ depends on UNICODE_SUPPORT
+ help
+ With this option off, any Unicode char with width of 0
+ is substituted on output.
+
+config UNICODE_WIDE_WCHARS
+ bool "Allow wide Unicode characters on output"
+ default n
+ depends on UNICODE_SUPPORT
+ help
+ With this option off, any Unicode char with width > 1
+ is substituted on output.
+
+config UNICODE_BIDI_SUPPORT
+ bool "Bidirectional character-aware line input"
+ default n
+ depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
+ help
+ With this option on, right-to-left Unicode characters
+ are treated differently on input (e.g. cursor movement).
+
+config UNICODE_NEUTRAL_TABLE
+ bool "In bidi input, support non-ASCII neutral chars too"
+ default n
+ depends on UNICODE_BIDI_SUPPORT
+ help
+ In most cases it's enough to treat only ASCII non-letters
+ (i.e. punctuation, numbers and space) as characters
+ with neutral directionality.
+ With this option on, more extensive (and bigger) table
+ of neutral chars will be used.
+
+config UNICODE_PRESERVE_BROKEN
+ bool "Make it possible to enter sequences of chars which are not Unicode"
+ default n
+ depends on UNICODE_SUPPORT
+ help
+ With this option on, on line-editing input (such as used by shells)
+ invalid UTF-8 bytes are not substituted with the selected
+ substitution character.
+ For example, this means that entering 'l', 's', ' ', 0xff, [Enter]
+ at shell prompt will list file named 0xff (single char name
+ with char value 255), not file named '?'.
+
config FEATURE_NON_POSIX_CP
bool "Non-POSIX, but safer, copying to special nodes"
default y
@@ -177,6 +326,19 @@ config FEATURE_VERBOSE_CP_MESSAGE
cp: cannot stat '/vmlinuz/file': Not a directory
This will cost you ~60 bytes.
+config FEATURE_USE_SENDFILE
+ bool "Use sendfile system call"
+ default y
+ select PLATFORM_LINUX
+ help
+ When enabled, busybox will use the kernel sendfile() function
+ instead of read/write loops to copy data between file descriptors
+ (for example, cp command does this a lot).
+ If sendfile() doesn't work, copying code falls back to read/write
+ loop. sendfile() was originally implemented for faster I/O
+ from files to sockets, but since Linux 2.6.33 it was extended
+ to work for many more file types.
+
config FEATURE_COPYBUF_KB
int "Copy buffer size, in kilobytes"
range 1 1024
@@ -208,7 +370,7 @@ config FEATURE_SKIP_ROOTFS
config MONOTONIC_SYSCALL
bool "Use clock_gettime(CLOCK_MONOTONIC) syscall"
- default n
+ default y
select PLATFORM_LINUX
help
Use clock_gettime(CLOCK_MONOTONIC) syscall for measuring
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src
index a6468f1..898a51a 100644
--- a/libbb/Kbuild.src
+++ b/libbb/Kbuild.src
@@ -30,7 +30,7 @@ lib-y += crc32.o
lib-y += default_error_retval.o
lib-y += device_open.o
lib-y += dump.o
-lib-y += execable.o
+lib-y += executable.o
lib-y += fclose_nonstdin.o
lib-y += fflush_stdout_and_exit.o
lib-y += fgets_str.o
@@ -46,7 +46,6 @@ lib-y += get_volsize.o
lib-y += herror_msg.o
lib-y += human_readable.o
lib-y += inet_common.o
-lib-y += info_msg.o
lib-y += inode_hash.o
lib-y += isdirectory.o
lib-y += kernel_version.o
@@ -92,6 +91,7 @@ lib-y += skip_whitespace.o
lib-y += speed_table.o
lib-y += str_tolower.o
lib-y += strrstr.o
+lib-y += sysconf.o
lib-y += time.o
lib-y += trim.o
lib-y += u_signal_names.o
@@ -132,6 +132,7 @@ lib-$(CONFIG_TFTPD) += udp_io.o
lib-$(CONFIG_TCPSVD) += udp_io.o
lib-$(CONFIG_UDPSVD) += udp_io.o
lib-$(CONFIG_TRACEROUTE) += udp_io.o
+lib-$(CONFIG_TRACEROUTE6) += udp_io.o
lib-$(CONFIG_LOSETUP) += loop.o
lib-$(CONFIG_FEATURE_MOUNT_LOOP) += loop.o
@@ -141,18 +142,22 @@ lib-$(CONFIG_ADDUSER) += update_passwd.o
lib-$(CONFIG_DELGROUP) += update_passwd.o
lib-$(CONFIG_DELUSER) += update_passwd.o
+lib-$(CONFIG_FTPD) += pw_encrypt.o correct_password.o
lib-$(CONFIG_PASSWD) += pw_encrypt.o update_passwd.o obscure.o
lib-$(CONFIG_CHPASSWD) += pw_encrypt.o update_passwd.o
lib-$(CONFIG_CRYPTPW) += pw_encrypt.o
+lib-$(CONFIG_MKPASSWD) += pw_encrypt.o
lib-$(CONFIG_SULOGIN) += pw_encrypt.o correct_password.o
lib-$(CONFIG_VLOCK) += pw_encrypt.o correct_password.o
lib-$(CONFIG_SU) += pw_encrypt.o correct_password.o
lib-$(CONFIG_LOGIN) += pw_encrypt.o correct_password.o
lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o
+lib-$(CONFIG_FEATURE_FTP_AUTHENTICATION) += pw_encrypt.o
lib-$(CONFIG_DF) += find_mount_point.o
lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o
lib-$(CONFIG_MKFS_EXT2) += find_mount_point.o
+lib-$(CONFIG_MKE2FS) += find_mount_point.o
lib-$(CONFIG_MKFS_REISER) += find_mount_point.o
lib-$(CONFIG_FSCK_MINIX) += find_mount_point.o
lib-$(CONFIG_MOUNT) += find_mount_point.o
@@ -165,11 +170,13 @@ lib-$(CONFIG_MPSTAT) += get_cpu_count.o
lib-$(CONFIG_POWERTOP) += get_cpu_count.o
lib-$(CONFIG_PING) += inet_cksum.o
+lib-$(CONFIG_PING6) += inet_cksum.o
lib-$(CONFIG_TRACEROUTE) += inet_cksum.o
lib-$(CONFIG_TRACEROUTE6) += inet_cksum.o
lib-$(CONFIG_UDHCPC) += inet_cksum.o
lib-$(CONFIG_UDHCPC6) += inet_cksum.o
lib-$(CONFIG_UDHCPD) += inet_cksum.o
+lib-$(CONFIG_DHCPRELAY) += inet_cksum.o
# We shouldn't build xregcomp.c if we don't need it - this ensures we don't
# require regex.h to be in the include dir even if we don't need it thereby
@@ -178,6 +185,8 @@ lib-$(CONFIG_UDHCPD) += inet_cksum.o
lib-$(CONFIG_AWK) += xregcomp.o
lib-$(CONFIG_SED) += xregcomp.o
lib-$(CONFIG_GREP) += xregcomp.o
+lib-$(CONFIG_EGREP) += xregcomp.o
+lib-$(CONFIG_FGREP) += xregcomp.o
lib-$(CONFIG_EXPR) += xregcomp.o
lib-$(CONFIG_MDEV) += xregcomp.o
lib-$(CONFIG_LESS) += xregcomp.o
@@ -185,3 +194,6 @@ lib-$(CONFIG_PGREP) += xregcomp.o
lib-$(CONFIG_PKILL) += xregcomp.o
lib-$(CONFIG_DEVFSD) += xregcomp.o
lib-$(CONFIG_FEATURE_FIND_REGEX) += xregcomp.o
+
+# Add the experimental logging functionality, only used by zcip
+lib-$(CONFIG_ZCIP) += logenv.o
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 5e4f4a9..966d3a8 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -52,7 +52,6 @@
#include "usage_compressed.h"
-
#if ENABLE_SHOW_USAGE && !ENABLE_FEATURE_COMPRESS_USAGE
static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
#else
@@ -130,7 +129,7 @@ void FAST_FUNC bb_show_usage(void)
full_write2_str(applet_name);
full_write2_str(" ");
full_write2_str(p);
- full_write2_str("\n\n");
+ full_write2_str("\n");
}
if (ENABLE_FEATURE_CLEAN_UP)
dealloc_usage_messages((char*)usage_string);
@@ -139,32 +138,127 @@ void FAST_FUNC bb_show_usage(void)
xfunc_die();
}
-#if NUM_APPLETS > 8
-static int applet_name_compare(const void *name, const void *idx)
-{
- int i = (int)(ptrdiff_t)idx - 1;
- return strcmp(name, APPLET_NAME(i));
-}
-#endif
int FAST_FUNC find_applet_by_name(const char *name)
{
-#if NUM_APPLETS > 8
- /* Do a binary search to find the applet entry given the name. */
+ unsigned i, max;
+ int j;
const char *p;
- p = bsearch(name, (void*)(ptrdiff_t)1, ARRAY_SIZE(applet_main), 1, applet_name_compare);
- /*
- * if (!p) return -1;
- * ^^^^^^^^^^^^^^^^^^ the code below will do this if p == NULL :)
- */
- return (int)(ptrdiff_t)p - 1;
+
+/* The commented-out word-at-a-time code is ~40% faster, but +160 bytes.
+ * "Faster" here saves ~0.5 microsecond of real time - not worth it.
+ */
+#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
+ uint32_t n32;
+
+ /* Handle all names < 2 chars long early */
+ if (name[0] == '\0')
+ return -1; /* "" is not a valid applet name */
+ if (name[1] == '\0') {
+ if (!ENABLE_TEST)
+ return -1; /* 1-char name is not valid */
+ if (name[0] != ']')
+ return -1; /* 1-char name which isn't "[" is not valid */
+ /* applet "[" is always applet #0: */
+ return 0;
+ }
+#endif
+
+ p = applet_names;
+ i = 0;
+#if KNOWN_APPNAME_OFFSETS <= 0
+ max = NUM_APPLETS;
#else
- /* A version which does not pull in bsearch */
- int i = 0;
- const char *p = applet_names;
- while (i < NUM_APPLETS) {
- if (strcmp(name, p) == 0)
+ max = NUM_APPLETS * KNOWN_APPNAME_OFFSETS;
+ for (j = ARRAY_SIZE(applet_nameofs)-1; j >= 0; j--) {
+ const char *pp = applet_names + applet_nameofs[j];
+ if (strcmp(name, pp) >= 0) {
+ //bb_error_msg("name:'%s' >= pp:'%s'", name, pp);
+ p = pp;
+ i = max - NUM_APPLETS;
+ break;
+ }
+ max -= NUM_APPLETS;
+ }
+ max /= (unsigned)KNOWN_APPNAME_OFFSETS;
+ i /= (unsigned)KNOWN_APPNAME_OFFSETS;
+ //bb_error_msg("name:'%s' starting from:'%s' i:%u max:%u", name, p, i, max);
+#endif
+
+ /* Open-coded linear search without strcmp/strlen calls for speed */
+
+#if 0 /*BB_UNALIGNED_MEMACCESS_OK && BB_LITTLE_ENDIAN*/
+ /* skip "[\0" name, it's surely not it */
+ if (ENABLE_TEST && LONE_CHAR(p, '['))
+ i++, p += 2;
+ /* All remaining applet names in p[] are at least 2 chars long */
+ /* name[] is also at least 2 chars long */
+
+ n32 = (name[0] << 0) | (name[1] << 8) | (name[2] << 16);
+ while (i < max) {
+ uint32_t p32;
+ char ch;
+
+ /* Quickly check match of the first 3 bytes */
+ move_from_unaligned32(p32, p);
+ p += 3;
+ if ((p32 & 0x00ffffff) != n32) {
+ /* Most likely case: 3 first bytes do not match */
+ i++;
+ if ((p32 & 0x00ff0000) == '\0')
+ continue; // p[2] was NUL
+ p++;
+ if ((p32 & 0xff000000) == '\0')
+ continue; // p[3] was NUL
+ /* p[0..3] aren't matching and none is NUL, check the rest */
+ while (*p++ != '\0')
+ continue;
+ continue;
+ }
+
+ /* Unlikely branch: first 3 bytes ([0..2]) match */
+ if ((p32 & 0x00ff0000) == '\0') {
+ /* name is 2-byte long, it is full match */
+ //bb_error_msg("found:'%s' i:%u", name, i);
return i;
- p += strlen(p) + 1;
+ }
+ /* Check remaining bytes [3..NUL] */
+ ch = (p32 >> 24);
+ j = 3;
+ while (ch == name[j]) {
+ if (ch == '\0') {
+ //bb_error_msg("found:'%s' i:%u", name, i);
+ return i;
+ }
+ ch = *++p;
+ j++;
+ }
+ /* Not a match. Skip it, including NUL */
+ while (ch != '\0')
+ ch = *++p;
+ p++;
+ i++;
+ }
+ return -1;
+#else
+ while (i < max) {
+ char ch;
+ j = 0;
+ /* Do we see "name\0" in applet_names[p] position? */
+ while ((ch = *p) == name[j]) {
+ if (ch == '\0') {
+ //bb_error_msg("found:'%s' i:%u", name, i);
+ return i; /* yes */
+ }
+ p++;
+ j++;
+ }
+ /* No.
+ * p => 1st non-matching char in applet_names[],
+ * skip to and including NUL.
+ */
+ while (ch != '\0')
+ ch = *++p;
+ p++;
i++;
}
return -1;
@@ -184,8 +278,7 @@ void lbb_prepare(const char *applet
#endif
applet_name = applet;
- /* Set locale for everybody except 'init' */
- if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
+ if (ENABLE_LOCALE_SUPPORT)
setlocale(LC_ALL, "");
#if ENABLE_FEATURE_INDIVIDUAL
@@ -194,7 +287,7 @@ void lbb_prepare(const char *applet
if (argv[1]
&& !argv[2]
&& strcmp(argv[1], "--help") == 0
- && strncmp(applet, "busybox", 7) != 0
+ && !is_prefixed_with(applet, "busybox")
) {
/* Special case. POSIX says "test --help"
* should be no different from e.g. "test --foo". */
@@ -236,21 +329,6 @@ static struct suid_config_t {
static bool suid_cfg_readable;
-/* check if u is member of group g */
-static int ingroup(uid_t u, gid_t g)
-{
- struct group *grp = getgrgid(g);
- if (grp) {
- char **mem;
- for (mem = grp->gr_mem; *mem; mem++) {
- struct passwd *pwd = getpwnam(*mem);
- if (pwd && (pwd->pw_uid == u))
- return 1;
- }
- }
- return 0;
-}
-
/* libbb candidate */
static char *get_trimmed_slice(char *s, char *e)
{
@@ -438,7 +516,7 @@ static void parse_config_file(void)
goto pe_label;
}
*e = ':'; /* get_uidgid needs USER:GROUP syntax */
- if (get_uidgid(&sct->m_ugid, s, /*allow_numeric:*/ 1) == 0) {
+ if (get_uidgid(&sct->m_ugid, s) == 0) {
errmsg = "unknown user/group";
goto pe_label;
}
@@ -458,7 +536,6 @@ static void parse_config_file(void)
errmsg = "keyword outside section";
goto pe_label;
}
-
} /* while (1) */
pe_label:
@@ -476,7 +553,22 @@ static inline void parse_config_file(void)
# endif /* FEATURE_SUID_CONFIG */
-# if ENABLE_FEATURE_SUID
+# if ENABLE_FEATURE_SUID && NUM_APPLETS > 0
+/* check if u is member of group g */
+static int ingroup(uid_t u, gid_t g)
+{
+ struct group *grp = getgrgid(g);
+ if (grp) {
+ char **mem;
+ for (mem = grp->gr_mem; *mem; mem++) {
+ struct passwd *pwd = getpwnam(*mem);
+ if (pwd && (pwd->pw_uid == u))
+ return 1;
+ }
+ }
+ return 0;
+}
+
static void check_suid(int applet_no)
{
gid_t rgid; /* real gid */
@@ -585,6 +677,7 @@ static void install_links(const char *busybox, int use_symbolic_links,
* busybox.h::bb_install_loc_t, or else... */
int (*lf)(const char *, const char *);
char *fpc;
+ const char *appname = applet_names;
unsigned i;
int rc;
@@ -595,7 +688,7 @@ static void install_links(const char *busybox, int use_symbolic_links,
for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
fpc = concat_path_file(
custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
- APPLET_NAME(i));
+ appname);
// debug: bb_error_msg("%slinking %s to busybox",
// use_symbolic_links ? "sym" : "", fpc);
rc = lf(busybox, fpc);
@@ -603,12 +696,21 @@ static void install_links(const char *busybox, int use_symbolic_links,
bb_simple_perror_msg(fpc);
}
free(fpc);
+ while (*appname++ != '\0')
+ continue;
}
}
-# else
-# define install_links(x,y,z) ((void)0)
+# elif ENABLE_BUSYBOX
+static void install_links(const char *busybox UNUSED_PARAM,
+ int use_symbolic_links UNUSED_PARAM,
+ char *custom_install_dir UNUSED_PARAM)
+{
+}
# endif
+# if ENABLE_BUSYBOX
+static void run_applet_and_exit(const char *name, char **argv) NORETURN;
+
/* If we were called as "busybox..." */
static int busybox_main(char **argv)
{
@@ -621,14 +723,14 @@ static int busybox_main(char **argv)
output_width = 80;
if (ENABLE_FEATURE_AUTOWIDTH) {
/* Obtain the terminal width */
- get_terminal_width_height(0, &output_width, NULL);
+ output_width = get_terminal_width(2);
}
dup2(1, 2);
full_write2_str(bb_banner); /* reuse const string */
full_write2_str(" multi-call binary.\n"); /* reuse */
full_write2_str(
- "BusyBox is copyrighted by many authors between 1998-2012.\n"
+ "BusyBox is copyrighted by many authors between 1998-2015.\n"
"Licensed under GPLv2. See source distribution for detailed\n"
"copyright notices. Merged for bionic by tpruvot@github\n"
"\n"
@@ -639,10 +741,19 @@ static int busybox_main(char **argv)
)
" or: function [arguments]...\n"
"\n"
+ IF_NOT_FEATURE_SH_STANDALONE(
"\tBusyBox is a multi-call binary that combines many common Unix\n"
"\tutilities into a single executable. Most people will create a\n"
"\tlink to busybox for each function they wish to use and BusyBox\n"
"\twill act like whatever it was invoked as.\n"
+ )
+ IF_FEATURE_SH_STANDALONE(
+ "\tBusyBox is a multi-call binary that combines many common Unix\n"
+ "\tutilities into a single executable. The shell in this build\n"
+ "\tis configured to run built-in utilities without $PATH search.\n"
+ "\tYou don't need to install a link to busybox for each utility.\n"
+ "\tTo run external program, use full path (/sbin/ip instead of ip).\n"
+ )
"\n"
"Currently defined functions:\n"
);
@@ -666,23 +777,24 @@ static int busybox_main(char **argv)
col += len2;
a += len2 - 1;
}
- full_write2_str("\n\n");
+ full_write2_str("\n");
return 0;
}
- if (strncmp(argv[1], "--list", 6) == 0) {
+ if (is_prefixed_with(argv[1], "--list")) {
unsigned i = 0;
const char *a = applet_names;
dup2(1, 2);
while (*a) {
-# if ENABLE_FEATURE_INSTALLER
+# if ENABLE_FEATURE_INSTALLER
if (argv[1][6]) /* --list-full? */
full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
-# endif
+# endif
full_write2_str(a);
full_write2_str("\n");
i++;
- a += strlen(a) + 1;
+ while (*a++ != '\0')
+ continue;
}
return 0;
}
@@ -726,13 +838,10 @@ static int busybox_main(char **argv)
* "#!/bin/busybox"-style wrappers */
applet_name = bb_get_last_path_component_nostrip(argv[0]);
run_applet_and_exit(applet_name, argv);
-
- /*bb_error_msg_and_die("applet not found"); - sucks in printf */
- full_write2_str(applet_name);
- full_write2_str(": applet not found\n");
- xfunc_die();
}
+# endif
+# if NUM_APPLETS > 0
void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
{
int argc = 1;
@@ -742,42 +851,84 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
/* Reinit some shared global data */
xfunc_error_retval = EXIT_FAILURE;
+ applet_name = bb_get_last_path_component_nostrip(argv[0]);
- applet_name = APPLET_NAME(applet_no);
- if (argc == 2 && strcmp(argv[1], "--help") == 0) {
- /* Special case. POSIX says "test --help"
- * should be no different from e.g. "test --foo". */
-//TODO: just compare applet_no with APPLET_NO_test
- if (!ENABLE_TEST || strcmp(applet_name, "test") != 0) {
- /* If you want "foo --help" to return 0: */
+ /* Special case. POSIX says "test --help"
+ * should be no different from e.g. "test --foo".
+ * Thus for "test", we skip --help check.
+ * "true" and "false" are also special.
+ */
+ if (1
+# if defined APPLET_NO_test
+ && applet_no != APPLET_NO_test
+# endif
+# if defined APPLET_NO_true
+ && applet_no != APPLET_NO_true
+# endif
+# if defined APPLET_NO_false
+ && applet_no != APPLET_NO_false
+# endif
+ ) {
+ if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+ /* Make "foo --help" exit with 0: */
xfunc_error_retval = 0;
bb_show_usage();
}
}
if (ENABLE_FEATURE_SUID)
check_suid(applet_no);
- exit(applet_main[applet_no](argc, argv));
+ xfunc_error_retval = applet_main[applet_no](argc, argv);
+ /* Note: applet_main() may also not return (die on a xfunc or such) */
+ xfunc_die();
}
+# endif /* NUM_APPLETS > 0 */
-void FAST_FUNC run_applet_and_exit(const char *name, char **argv)
+# if ENABLE_BUSYBOX || NUM_APPLETS > 0
+static NORETURN void run_applet_and_exit(const char *name, char **argv)
{
- int applet = find_applet_by_name(name);
- if (applet >= 0)
- run_applet_no_and_exit(applet, argv);
- if (strncmp(name, "busybox", 7) == 0)
+# if ENABLE_BUSYBOX
+ if (is_prefixed_with(name, "busybox"))
exit(busybox_main(argv));
+# endif
+# if NUM_APPLETS > 0
+ /* find_applet_by_name() search is more expensive, so goes second */
+ {
+ int applet = find_applet_by_name(name);
+ if (applet >= 0)
+ run_applet_no_and_exit(applet, argv);
+ }
+# endif
+
+ /*bb_error_msg_and_die("applet not found"); - links in printf */
+ full_write2_str(applet_name);
+ full_write2_str(": applet not found\n");
+ /* POSIX: "If a command is not found, the exit status shall be 127" */
+ exit(127);
}
+# endif
#endif /* !defined(SINGLE_APPLET_MAIN) */
-
#if ENABLE_BUILD_LIBBUSYBOX
int lbb_main(char **argv)
#else
int main(int argc UNUSED_PARAM, char **argv)
#endif
{
+#if 0
+ /* TODO: find a use for a block of memory between end of .bss
+ * and end of page. For example, I'm getting "_end:0x812e698 2408 bytes"
+ * - more than 2k of wasted memory (in this particular build)
+ * *per each running process*!
+ * (If your linker does not generate "_end" name, weak attribute
+ * makes &_end == NULL, end_len == 0 here.)
+ */
+ extern char _end[] __attribute__((weak));
+ unsigned end_len = (-(int)_end) & 0xfff;
+ printf("_end:%p %u bytes\n", &_end, end_len);
+#endif
+
/* Tweak malloc for reduced memory consumption */
#ifdef M_TRIM_THRESHOLD
/* M_TRIM_THRESHOLD is the maximum amount of freed top-most memory
@@ -792,6 +943,14 @@ int main(int argc UNUSED_PARAM, char **argv)
*/
mallopt(M_MMAP_THRESHOLD, 32 * 1024 - 256);
#endif
+#if 0 /*def M_TOP_PAD*/
+ /* When the program break is increased, then M_TOP_PAD bytes are added
+ * to the sbrk(2) request. When the heap is trimmed because of free(3),
+ * this much free space is preserved at the top of the heap.
+ * glibc default seems to be way too big: 128k, but need to verify.
+ */
+ mallopt(M_TOP_PAD, 8 * 1024);
+#endif
#if !BB_MMU
/* NOMMU re-exec trick sets high-order bit in first byte of name */
@@ -802,29 +961,35 @@ int main(int argc UNUSED_PARAM, char **argv)
#endif
#if defined(SINGLE_APPLET_MAIN)
+
/* Only one applet is selected in .config */
- if (argv[1] && strncmp(argv[0], "busybox", 7) == 0) {
+ if (argv[1] && is_prefixed_with(argv[0], "busybox")) {
/* "busybox <applet> <params>" should still work as expected */
argv++;
}
/* applet_names in this case is just "applet\0\0" */
lbb_prepare(applet_names IF_FEATURE_INDIVIDUAL(, argv));
return SINGLE_APPLET_MAIN(argc, argv);
+
+#elif !ENABLE_BUSYBOX && NUM_APPLETS == 0
+
+ full_write2_str(bb_basename(argv[0]));
+ full_write2_str(": no applets enabled\n");
+ exit(127);
+
#else
- lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
+ lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
+# if !ENABLE_BUSYBOX
+ if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox"))
+ argv++;
+# endif
applet_name = argv[0];
if (applet_name[0] == '-')
applet_name++;
applet_name = bb_basename(applet_name);
-
parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
-
run_applet_and_exit(applet_name, argv);
- /*bb_error_msg_and_die("applet not found"); - sucks in printf */
- full_write2_str(applet_name);
- full_write2_str(": applet not found\n");
- xfunc_die();
#endif
}
diff --git a/libbb/auto_string.c b/libbb/auto_string.c
new file mode 100644
index 0000000..ae94006
--- a/dev/null
+++ b/libbb/auto_string.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2015 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//kbuild:lib-y += auto_string.o
+
+#include "libbb.h"
+
+char* FAST_FUNC auto_string(char *str)
+{
+ static char *saved[4];
+ static uint8_t cur_saved; /* = 0 */
+
+ free(saved[cur_saved]);
+ saved[cur_saved] = str;
+ cur_saved = (cur_saved + 1) & (ARRAY_SIZE(saved)-1);
+
+ return str;
+}
diff --git a/libbb/bb_askpass.c b/libbb/bb_askpass.c
index 77c1bcd..c2580b9 100644
--- a/libbb/bb_askpass.c
+++ b/libbb/bb_askpass.c
@@ -1,7 +1,6 @@
/* vi: set sw=4 ts=4: */
/*
* Ask for a password
- * I use a static buffer in this function. Plan accordingly.
*
* Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
*
@@ -23,16 +22,19 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
{
/* Was static char[BIGNUM] */
enum { sizeof_passwd = 128 };
- static char *passwd;
+ char *passwd;
char *ret;
int i;
struct sigaction sa, oldsa;
struct termios tio, oldtio;
+ tcflush(fd, TCIFLUSH);
+ /* Was buggy: was printing prompt *before* flushing input,
+ * which was upsetting "expect" based scripts of some users.
+ */
fputs(prompt, stdout);
fflush_all();
- tcflush(fd, TCIFLUSH);
tcgetattr(fd, &oldtio);
tio = oldtio;
@@ -59,8 +61,7 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
alarm(timeout);
}
- if (!passwd)
- passwd = xmalloc(sizeof_passwd);
+ passwd = auto_string(xmalloc(sizeof_passwd));
ret = passwd;
i = 0;
while (1) {
diff --git a/libbb/bb_pwd.c b/libbb/bb_pwd.c
index d5e651c..f51bfe9 100644
--- a/libbb/bb_pwd.c
+++ b/libbb/bb_pwd.c
@@ -131,51 +131,3 @@ unsigned long FAST_FUNC get_ug_id(const char *s,
return xname2id(s);
return r;
}
-
-/* Experimental "mallocing" API.
- * The goal is nice: "we want to support a case when "guests" group is very large"
- * but the code is butt-ugly.
- */
-#if 0
-static char *find_latest(char last, char *cp)
-{
- if (!cp)
- return last;
- cp += strlen(cp) + 1;
- if (last < cp)
- last = cp;
- return last;
-}
-
-struct group* FAST_FUNC xmalloc_getgrnam(const char *name)
-{
- struct {
- struct group gr;
- // May still be not enough!
- char buf[64*1024 - sizeof(struct group) - 16];
- } *s;
- struct group *grp;
- int r;
- char *last;
- char **gr_mem;
-
- s = xmalloc(sizeof(*s));
- r = getgrnam_r(name, &s->gr, s->buf, sizeof(s->buf), &grp);
- if (!grp) {
- free(s);
- return grp;
- }
- last = find_latest(s->buf, grp->gr_name);
- last = find_latest(last, grp->gr_passwd);
- gr_mem = grp->gr_mem;
- while (*gr_mem)
- last = find_latest(last, *gr_mem++);
- gr_mem++; /* points past NULL */
- if (last < (char*)gr_mem)
- last = (char*)gr_mem;
-//FIXME: what if we get not only truncated, but also moved here?
-// grp->gr_name pointer and friends are invalid now!!!
- s = xrealloc(s, last - (char*)s);
- return grp;
-}
-#endif
diff --git a/libbb/bbunit.c b/libbb/bbunit.c
new file mode 100644
index 0000000..db67b10
--- a/dev/null
+++ b/libbb/bbunit.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bbunit: Simple unit-testing framework for Busybox.
+ *
+ * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_UNIT_TEST) += bbunit.o
+//applet:IF_UNIT_TEST(APPLET(unit, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//usage:#define unit_trivial_usage
+//usage: ""
+//usage:#define unit_full_usage "\n\n"
+//usage: "Run the unit-test suite"
+
+#include "libbb.h"
+
+static llist_t *tests = NULL;
+static unsigned tests_registered = 0;
+static int test_retval;
+
+void bbunit_registertest(struct bbunit_listelem *test)
+{
+ llist_add_to_end(&tests, test);
+ tests_registered++;
+}
+
+void bbunit_settestfailed(void)
+{
+ test_retval = -1;
+}
+
+int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) MAIN_EXTERNALLY_VISIBLE;
+int unit_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ unsigned tests_run = 0;
+ unsigned tests_failed = 0;
+
+ bb_error_msg("Running %d test(s)...", tests_registered);
+ for (;;) {
+ struct bbunit_listelem* el = llist_pop(&tests);
+ if (!el)
+ break;
+
+ bb_error_msg("Case: [%s]", el->name);
+ test_retval = 0;
+ el->testfunc();
+
+ if (test_retval < 0) {
+ bb_error_msg("[ERROR] [%s]: TEST FAILED", el->name);
+ tests_failed++;
+ }
+ tests_run++;
+ }
+
+ if (tests_failed > 0) {
+ bb_error_msg("[ERROR] %u test(s) FAILED", tests_failed);
+ return EXIT_FAILURE;
+ }
+
+ bb_error_msg("All tests passed");
+ return EXIT_SUCCESS;
+}
diff --git a/libbb/change_identity.c b/libbb/change_identity.c
index 619db09..d48d863 100644
--- a/libbb/change_identity.c
+++ b/libbb/change_identity.c
@@ -33,9 +33,28 @@
/* Become the user and group(s) specified by PW. */
void FAST_FUNC change_identity(const struct passwd *pw)
{
- if (initgroups(pw->pw_name, pw->pw_gid) == -1)
- bb_perror_msg_and_die("can't set groups");
+ int res;
+
+ res = initgroups(pw->pw_name, pw->pw_gid);
endgrent(); /* helps to close a fd used internally by libc */
+
+ if (res != 0) {
+ /*
+ * If initgroups() fails because a system call is unimplemented
+ * then we are running on a Linux kernel compiled without multiuser
+ * support (CONFIG_MULTIUSER is not defined).
+ *
+ * If we are running without multiuser support *and* the target uid
+ * already matches the current uid then we can skip the change of
+ * identity.
+ */
+ if (errno == ENOSYS && pw->pw_uid == getuid()) {
+ return;
+ }
+
+ bb_perror_msg_and_die("can't set groups");
+ }
+
xsetgid(pw->pw_gid);
xsetuid(pw->pw_uid);
}
diff --git a/libbb/common_bufsiz.c b/libbb/common_bufsiz.c
new file mode 100644
index 0000000..2847eb5
--- a/dev/null
+++ b/libbb/common_bufsiz.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2016 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//config:config FEATURE_USE_BSS_TAIL
+//config: bool "Use the end of BSS page"
+//config: default n
+//config: help
+//config: Attempt to reclaim a small unused part of BSS.
+//config:
+//config: Executables have the following parts:
+//config: = read-only executable code and constants, also known as "text"
+//config: = read-write data
+//config: = non-initialized (zeroed on demand) data, also known as "bss"
+//config:
+//config: At link time, "text" is padded to a full page. At runtime, all "text"
+//config: pages are mapped RO and executable.
+//config: "Data" starts on the next page boundary, but is not padded
+//config: to a full page at the end. "Bss" starts wherever "data" ends.
+//config: At runtime, "data" pages are mapped RW and they are file-backed
+//config: (this includes a small portion of "bss" which may live in the last
+//config: partial page of "data").
+//config: Pages which are fully in "bss" are mapped to anonymous memory.
+//config:
+//config: "Bss" end is usually not page-aligned. There is an unused space
+//config: in the last page. Linker marks its start with the "_end" symbol.
+//config:
+//config: This option will attempt to use that space for bb_common_bufsiz1[]
+//config: array. If it fits after _end, it will be used, and COMMON_BUFSIZE
+//config: will be enlarged from its guaranteed minimum size of 1 kbyte.
+//config: This may require recompilation a second time, since value of _end
+//config: is known only after final link.
+//config:
+//config: If you are getting a build error like this:
+//config: appletlib.c:(.text.main+0xd): undefined reference to '_end'
+//config: disable this option.
+
+//kbuild:lib-y += common_bufsiz.o
+
+#include "libbb.h"
+#include "common_bufsiz.h"
+
+#if !ENABLE_FEATURE_USE_BSS_TAIL
+
+/* We use it for "global" data via *(struct global*)bb_common_bufsiz1.
+ * Since gcc insists on aligning struct global's members, it would be a pity
+ * (and an alignment fault on some CPUs) to mess it up. */
+char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long));
+
+#else
+
+# ifndef setup_common_bufsiz
+/* For now, this is never used:
+ * scripts/generate_BUFSIZ.sh never generates "malloced" bufsiz1:
+ * enum { COMMON_BUFSIZE = 1024 };
+ * extern char *const bb_common_bufsiz1;
+ * void setup_common_bufsiz(void);
+ * This has proved to be worse than the approach of defining
+ * larger bb_common_bufsiz1[] array.
+ */
+
+/*
+ * It is not defined as a dummy macro.
+ * It means we have to provide this function.
+ */
+char *const bb_common_bufsiz1 __attribute__ ((section (".data")));
+void setup_common_bufsiz(void)
+{
+ if (!bb_common_bufsiz1)
+ *(char**)&bb_common_bufsiz1 = xzalloc(COMMON_BUFSIZE);
+}
+# else
+# ifndef bb_common_bufsiz1
+ /* bb_common_bufsiz1[] is not aliased to _end[] */
+char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long));
+# endif
+# endif
+
+#endif
diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c
index 4b10cc1..2f51237 100644
--- a/libbb/compare_string_array.c
+++ b/libbb/compare_string_array.c
@@ -5,6 +5,49 @@
#include "libbb.h"
+/*
+ * Return NULL if string is not prefixed with key. Return pointer to the
+ * first character in string after the prefix key. If key is an empty string,
+ * return pointer to the beginning of string.
+ */
+char* FAST_FUNC is_prefixed_with(const char *string, const char *key)
+{
+#if 0 /* Two passes over key - probably slower */
+ int len = strlen(key);
+ if (strncmp(string, key, len) == 0)
+ return string + len;
+ return NULL;
+#else /* Open-coded */
+ while (*key != '\0') {
+ if (*key != *string)
+ return NULL;
+ key++;
+ string++;
+ }
+ return (char*)string;
+#endif
+}
+
+/*
+ * Return NULL if string is not suffixed with key. Return pointer to the
+ * beginning of prefix key in string. If key is an empty string return pointer
+ * to the end of string.
+ */
+char* FAST_FUNC is_suffixed_with(const char *string, const char *key)
+{
+ size_t key_len = strlen(key);
+ ssize_t len_diff = strlen(string) - key_len;
+
+ if (len_diff >= 0) {
+ string += len_diff;
+ if (strcmp(string, key) == 0) {
+ return (char*)string;
+ }
+ }
+
+ return NULL;
+}
+
/* returns the array index of the string */
/* (index of first match is returned, or -1) */
int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key)
@@ -39,10 +82,9 @@ int FAST_FUNC index_in_strings(const char *strings, const char *key)
int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key)
{
int i;
- int len = strlen(key);
- if (len) {
+ if (key[0]) {
for (i = 0; string_array[i] != 0; i++) {
- if (strncmp(string_array[i], key, len) == 0) {
+ if (is_prefixed_with(string_array[i], key)) {
return i;
}
}
@@ -93,3 +135,37 @@ smallint FAST_FUNC yesno(const char *str)
return ret / 3;
}
#endif
+
+#if ENABLE_UNIT_TEST
+
+BBUNIT_DEFINE_TEST(is_prefixed_with)
+{
+ BBUNIT_ASSERT_STREQ(" bar", is_prefixed_with("foo bar", "foo"));
+ BBUNIT_ASSERT_STREQ("bar", is_prefixed_with("foo bar", "foo "));
+ BBUNIT_ASSERT_STREQ("", is_prefixed_with("foo", "foo"));
+ BBUNIT_ASSERT_STREQ("foo", is_prefixed_with("foo", ""));
+ BBUNIT_ASSERT_STREQ("", is_prefixed_with("", ""));
+
+ BBUNIT_ASSERT_NULL(is_prefixed_with("foo", "bar foo"));
+ BBUNIT_ASSERT_NULL(is_prefixed_with("foo foo", "bar"));
+ BBUNIT_ASSERT_NULL(is_prefixed_with("", "foo"));
+
+ BBUNIT_ENDTEST;
+}
+
+BBUNIT_DEFINE_TEST(is_suffixed_with)
+{
+ BBUNIT_ASSERT_STREQ("bar", is_suffixed_with("foo bar", "bar"));
+ BBUNIT_ASSERT_STREQ("foo", is_suffixed_with("foo", "foo"));
+ BBUNIT_ASSERT_STREQ("", is_suffixed_with("foo", ""));
+ BBUNIT_ASSERT_STREQ("", is_suffixed_with("", ""));
+ BBUNIT_ASSERT_STREQ("foo", is_suffixed_with("barfoofoo", "foo"));
+
+ BBUNIT_ASSERT_NULL(is_suffixed_with("foo", "bar foo"));
+ BBUNIT_ASSERT_NULL(is_suffixed_with("foo foo", "bar"));
+ BBUNIT_ASSERT_NULL(is_suffixed_with("", "foo"));
+
+ BBUNIT_ENDTEST;
+}
+
+#endif /* ENABLE_UNIT_TEST */
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 9333a8d..23c0f83 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -64,6 +64,11 @@ static int ask_and_unlink(const char *dest, int flags)
bb_perror_msg("can't create '%s'", dest);
return -1; /* error */
}
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+ if (flags & FILEUTILS_RMDEST)
+ if (flags & FILEUTILS_VERBOSE)
+ printf("removed '%s'\n", dest);
+#endif
return 1; /* ok (to try again) */
}
@@ -210,6 +215,22 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
goto preserve_mode_ugid_time;
}
+ if (dest_exists) {
+ if (flags & FILEUTILS_UPDATE) {
+ if (source_stat.st_mtime <= dest_stat.st_mtime) {
+ return 0; /* source file must be newer */
+ }
+ }
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+ if (flags & FILEUTILS_RMDEST) {
+ ovr = ask_and_unlink(dest, flags);
+ if (ovr <= 0)
+ return ovr;
+ dest_exists = 0;
+ }
+#endif
+ }
+
if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
int (*lf)(const char *oldpath, const char *newpath);
make_links:
@@ -275,11 +296,16 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
if (!S_ISREG(source_stat.st_mode))
new_mode = 0666;
- // POSIX way is a security problem versus (sym)link attacks
- if (!ENABLE_FEATURE_NON_POSIX_CP) {
- dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
- } else { /* safe way: */
+ if (ENABLE_FEATURE_NON_POSIX_CP || (flags & FILEUTILS_INTERACTIVE)) {
+ /*
+ * O_CREAT|O_EXCL: require that file did not exist before creation
+ */
dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+ } else { /* POSIX, and not "cp -i" */
+ /*
+ * O_CREAT|O_TRUNC: create, or truncate (security problem versus (sym)link attacks)
+ */
+ dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
}
if (dst_fd == -1) {
ovr = ask_and_unlink(dest, flags);
@@ -354,7 +380,7 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
}
/* _Not_ jumping to preserve_mode_ugid_time:
* symlinks don't have those */
- return 0;
+ goto verb_and_exit;
}
if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
|| S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
@@ -389,5 +415,10 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
}
+ verb_and_exit:
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("'%s' -> '%s'\n", source, dest);
+ }
+
return retval;
}
diff --git a/libbb/copyfd.c b/libbb/copyfd.c
index eda2747..7e35319 100644
--- a/libbb/copyfd.c
+++ b/libbb/copyfd.c
@@ -8,6 +8,20 @@
*/
#include "libbb.h"
+#if ENABLE_FEATURE_USE_SENDFILE
+# include <sys/sendfile.h>
+#else
+# define sendfile(a,b,c,d) (-1)
+#endif
+
+/*
+ * We were using 0x7fff0000 as sendfile chunk size, but it
+ * was seen to cause largish delays when user tries to ^C a file copy.
+ * Let's use a saner size.
+ * Note: needs to be >= max(CONFIG_FEATURE_COPYBUF_KB),
+ * or else "copy to eof" code will use neddlesly short reads.
+ */
+#define SENDFILE_BIGBUF (16*1024*1024)
/* Used by NOFORK applets (e.g. cat) - must not use xmalloc.
* size < 0 means "ignore write errors", used by tar --to-command
@@ -18,12 +32,13 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
int status = -1;
off_t total = 0;
bool continue_on_write_error = 0;
-#if CONFIG_FEATURE_COPYBUF_KB <= 4
+ ssize_t sendfile_sz;
+#if CONFIG_FEATURE_COPYBUF_KB > 4
+ char *buffer = buffer; /* for compiler */
+ int buffer_size = 0;
+#else
char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
enum { buffer_size = sizeof(buffer) };
-#else
- char *buffer;
- int buffer_size;
#endif
if (size < 0) {
@@ -31,46 +46,58 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
continue_on_write_error = 1;
}
-#if CONFIG_FEATURE_COPYBUF_KB > 4
- if (size > 0 && size <= 4 * 1024)
- goto use_small_buf;
- /* We want page-aligned buffer, just in case kernel is clever
- * and can do page-aligned io more efficiently */
- buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON,
- /* ignored: */ -1, 0);
- buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
- if (buffer == MAP_FAILED) {
- use_small_buf:
- buffer = alloca(4 * 1024);
- buffer_size = 4 * 1024;
- }
-#endif
-
if (src_fd < 0)
goto out;
+ sendfile_sz = !ENABLE_FEATURE_USE_SENDFILE
+ ? 0
+ : SENDFILE_BIGBUF;
if (!size) {
- size = buffer_size;
+ size = SENDFILE_BIGBUF;
status = 1; /* copy until eof */
}
while (1) {
ssize_t rd;
- rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
-
- if (!rd) { /* eof - all done */
- status = 0;
- break;
+ if (sendfile_sz) {
+ rd = sendfile(dst_fd, src_fd, NULL,
+ size > sendfile_sz ? sendfile_sz : size);
+ if (rd >= 0)
+ goto read_ok;
+ sendfile_sz = 0; /* do not try sendfile anymore */
+ }
+#if CONFIG_FEATURE_COPYBUF_KB > 4
+ if (buffer_size == 0) {
+ if (size > 0 && size <= 4 * 1024)
+ goto use_small_buf;
+ /* We want page-aligned buffer, just in case kernel is clever
+ * and can do page-aligned io more efficiently */
+ buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ /* ignored: */ -1, 0);
+ buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
+ if (buffer == MAP_FAILED) {
+ use_small_buf:
+ buffer = alloca(4 * 1024);
+ buffer_size = 4 * 1024;
+ }
}
+#endif
+ rd = safe_read(src_fd, buffer,
+ size > buffer_size ? buffer_size : size);
if (rd < 0) {
bb_perror_msg(bb_msg_read_error);
break;
}
+ read_ok:
+ if (!rd) { /* eof - all done */
+ status = 0;
+ break;
+ }
/* dst_fd == -1 is a fake, else... */
- if (dst_fd >= 0) {
+ if (dst_fd >= 0 && !sendfile_sz) {
ssize_t wr = full_write(dst_fd, buffer, rd);
if (wr < rd) {
if (!continue_on_write_error) {
@@ -92,10 +119,8 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
}
out:
-#if CONFIG_FEATURE_COPYBUF_KB > 4
- if (buffer_size != 4 * 1024)
+ if (buffer_size > 4 * 1024)
munmap(buffer, buffer_size);
-#endif
return status ? -1 : total;
}
diff --git a/libbb/correct_password.c b/libbb/correct_password.c
index acadf39..513c930 100644
--- a/libbb/correct_password.c
+++ b/libbb/correct_password.c
@@ -30,6 +30,63 @@
#include "libbb.h"
+#define SHADOW_BUFSIZE 256
+
+/* Retrieve encrypted password string for pw.
+ * If pw == NULL, return a string which fails password check against any
+ * password.
+ */
+#if !ENABLE_FEATURE_SHADOWPASSWDS
+#define get_passwd(pw, buffer) get_passwd(pw)
+#endif
+static const char *get_passwd(const struct passwd *pw, char buffer[SHADOW_BUFSIZE])
+{
+ const char *pass;
+
+ if (!pw)
+ return "aa"; /* "aa" will never match */
+
+ pass = pw->pw_passwd;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ if ((pass[0] == 'x' || pass[0] == '*') && !pass[1]) {
+ struct spwd spw;
+ int r;
+ /* getspnam_r may return 0 yet set result to NULL.
+ * At least glibc 2.4 does this. Be extra paranoid here. */
+ struct spwd *result = NULL;
+ r = getspnam_r(pw->pw_name, &spw, buffer, SHADOW_BUFSIZE, &result);
+ pass = (r || !result) ? "aa" : result->sp_pwdp;
+ }
+#endif
+ return pass;
+}
+
+/*
+ * Return 1 if PW has an empty password.
+ * Return 1 if the user gives the correct password for entry PW,
+ * 0 if not.
+ * NULL pw means "just fake it for login with bad username"
+ */
+int FAST_FUNC check_password(const struct passwd *pw, const char *plaintext)
+{
+ IF_FEATURE_SHADOWPASSWDS(char buffer[SHADOW_BUFSIZE];)
+ char *encrypted;
+ const char *pw_pass;
+ int r;
+
+ pw_pass = get_passwd(pw, buffer);
+ if (!pw_pass[0]) { /* empty password field? */
+ return 1;
+ }
+
+ encrypted = pw_encrypt(plaintext, /*salt:*/ pw_pass, 1);
+ r = (strcmp(encrypted, pw_pass) == 0);
+ free(encrypted);
+ return r;
+}
+
+
/* Ask the user for a password.
* Return 1 without asking if PW has an empty password.
* Return -1 on EOF, error while reading input, or timeout.
@@ -41,42 +98,23 @@
int FAST_FUNC ask_and_check_password_extended(const struct passwd *pw,
int timeout, const char *prompt)
{
- char *unencrypted, *encrypted;
- const char *correct;
+ IF_FEATURE_SHADOWPASSWDS(char buffer[SHADOW_BUFSIZE];)
+ char *plaintext;
+ const char *pw_pass;
int r;
- /* fake salt. crypt() can choke otherwise. */
- correct = "aa";
- if (!pw) {
- /* "aa" will never match */
- goto fake_it;
- }
- correct = pw->pw_passwd;
-#if ENABLE_FEATURE_SHADOWPASSWDS
- /* Using _r function to avoid pulling in static buffers */
- if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) {
- struct spwd spw;
- char buffer[256];
- /* getspnam_r may return 0 yet set result to NULL.
- * At least glibc 2.4 does this. Be extra paranoid here. */
- struct spwd *result = NULL;
- r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result);
- correct = (r || !result) ? "aa" : result->sp_pwdp;
- }
-#endif
- if (!correct[0]) /* empty password field? */
+ pw_pass = get_passwd(pw, buffer);
+ if (!pw_pass[0]) /* empty password field? */
return 1;
- fake_it:
- unencrypted = bb_ask(STDIN_FILENO, timeout, prompt);
- if (!unencrypted) {
+ plaintext = bb_ask(STDIN_FILENO, timeout, prompt);
+ if (!plaintext) {
/* EOF (such as ^D) or error (such as ^C) or timeout */
return -1;
}
- encrypted = pw_encrypt(unencrypted, correct, 1);
- r = (strcmp(encrypted, correct) == 0);
- free(encrypted);
- nuke_str(unencrypted);
+
+ r = check_password(pw, plaintext);
+ nuke_str(plaintext);
return r;
}
diff --git a/libbb/dump.c b/libbb/dump.c
index b465c5d..9bb7bb2 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -157,7 +157,7 @@ static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
/*
* figure out the byte count for each conversion;
* rewrite the format as necessary, set up blank-
- * pbb_dump_adding for end of data.
+ * padding for end of data.
*/
if (*p1 == 'c') {
pr->flags = F_CHAR;
@@ -466,14 +466,14 @@ static void bpad(PR *pr)
}
static const char conv_str[] ALIGN1 =
- "\0\\0\0"
- "\007\\a\0" /* \a */
- "\b\\b\0"
- "\f\\b\0"
- "\n\\n\0"
- "\r\\r\0"
- "\t\\t\0"
- "\v\\v\0"
+ "\0" "\\""0""\0"
+ "\007""\\""a""\0" /* \a */
+ "\b" "\\""b""\0"
+ "\f" "\\""f""\0"
+ "\n" "\\""n""\0"
+ "\r" "\\""r""\0"
+ "\t" "\\""t""\0"
+ "\v" "\\""v""\0"
;
@@ -485,7 +485,7 @@ static void conv_c(PR *pr, unsigned char *p)
do {
if (*p == *str) {
++str;
- goto strpr;
+ goto strpr; /* map e.g. '\n' to "\\n" */
}
str += 4;
} while (*str);
@@ -702,8 +702,6 @@ int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
{
const char *p;
- char *p1;
- char *p2;
FS *tfs;
FU *tfu, **nextfupp;
const char *savep;
@@ -779,29 +777,42 @@ void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
}
}
tfu->fmt = xstrndup(savep, p - savep);
-/* escape(tfu->fmt); */
-
- p1 = tfu->fmt;
/* alphabetic escape sequences have to be done in place */
+ strcpy_and_process_escape_sequences(tfu->fmt, tfu->fmt);
+ /* unknown mappings are not changed: "\z" -> '\\' 'z' */
+ /* trailing backslash, if any, is preserved */
+#if 0
+ char *p1;
+ char *p2;
+ p1 = tfu->fmt;
for (p2 = p1;; ++p1, ++p2) {
- if (*p1 == '\0') {
- *p2 = *p1;
+ *p2 = *p1;
+ if (*p1 == '\0')
break;
- }
+
if (*p1 == '\\') {
- const char *cs = conv_str + 4;
- ++p1;
+ const char *cs;
+
+ p1++;
*p2 = *p1;
+ if (*p1 == '\0') {
+ /* "...\" trailing backslash. Eaten. */
+ break;
+ }
+ cs = conv_str + 4; /* skip NUL element */
do {
+ /* map e.g. "\n" -> '\n' */
if (*p1 == cs[2]) {
*p2 = cs[0];
break;
}
cs += 4;
} while (*cs);
+ /* unknown mappings remove bkslash: "\z" -> 'z' */
}
}
+#endif
p++;
}
diff --git a/libbb/execable.c b/libbb/execable.c
deleted file mode 100644
index 178a00a..0000000
--- a/libbb/execable.c
+++ b/dev/null
@@ -1,86 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
-
-#include "libbb.h"
-
-/* check if path points to an executable file;
- * return 1 if found;
- * return 0 otherwise;
- */
-int FAST_FUNC execable_file(const char *name)
-{
- struct stat s;
- return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
-}
-
-/* search (*PATHp) for an executable file;
- * return allocated string containing full path if found;
- * PATHp points to the component after the one where it was found
- * (or NULL),
- * you may call find_execable again with this PATHp to continue
- * (if it's not NULL).
- * return NULL otherwise; (PATHp is undefined)
- * in all cases (*PATHp) contents will be trashed (s/:/NUL/).
- */
-char* FAST_FUNC find_execable(const char *filename, char **PATHp)
-{
- char *p, *n;
-
- p = *PATHp;
- while (p) {
- n = strchr(p, ':');
- if (n)
- *n++ = '\0';
- if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
- p = concat_path_file(p, filename);
- if (execable_file(p)) {
- *PATHp = n;
- return p;
- }
- free(p);
- }
- p = n;
- } /* on loop exit p == NULL */
- return p;
-}
-
-/* search $PATH for an executable file;
- * return 1 if found;
- * return 0 otherwise;
- */
-int FAST_FUNC exists_execable(const char *filename)
-{
- char *path = xstrdup(getenv("PATH"));
- char *tmp = path;
- char *ret = find_execable(filename, &tmp);
- free(path);
- if (ret) {
- free(ret);
- return 1;
- }
- return 0;
-}
-
-#if ENABLE_FEATURE_PREFER_APPLETS
-/* just like the real execvp, but try to launch an applet named 'file' first */
-int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
-{
- if (find_applet_by_name(file) >= 0)
- execvp(bb_busybox_exec_path, argv);
- return execvp(file, argv);
-}
-#endif
-
-int FAST_FUNC BB_EXECVP_or_die(char **argv)
-{
- BB_EXECVP(argv[0], argv);
- /* SUSv3-mandated exit codes */
- xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
- bb_perror_msg_and_die("can't execute '%s'", argv[0]);
-}
diff --git a/libbb/executable.c b/libbb/executable.c
new file mode 100644
index 0000000..3a1d4ff
--- a/dev/null
+++ b/libbb/executable.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* check if path points to an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int FAST_FUNC file_is_executable(const char *name)
+{
+ struct stat s;
+ return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
+}
+
+/* search (*PATHp) for an executable file;
+ * return allocated string containing full path if found;
+ * PATHp points to the component after the one where it was found
+ * (or NULL),
+ * you may call find_executable again with this PATHp to continue
+ * (if it's not NULL).
+ * return NULL otherwise; (PATHp is undefined)
+ * in all cases (*PATHp) contents will be trashed (s/:/NUL/).
+ */
+char* FAST_FUNC find_executable(const char *filename, char **PATHp)
+{
+ /* About empty components in $PATH:
+ * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
+ * 8.3 Other Environment Variables - PATH
+ * A zero-length prefix is a legacy feature that indicates the current
+ * working directory. It appears as two adjacent colons ( "::" ), as an
+ * initial colon preceding the rest of the list, or as a trailing colon
+ * following the rest of the list.
+ */
+ char *p, *n;
+
+ p = *PATHp;
+ while (p) {
+ n = strchr(p, ':');
+ if (n)
+ *n++ = '\0';
+ p = concat_path_file(
+ p[0] ? p : ".", /* handle "::" case */
+ filename
+ );
+ if (file_is_executable(p)) {
+ *PATHp = n;
+ return p;
+ }
+ free(p);
+ p = n;
+ } /* on loop exit p == NULL */
+ return p;
+}
+
+/* search $PATH for an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int FAST_FUNC executable_exists(const char *filename)
+{
+ char *path = xstrdup(getenv("PATH"));
+ char *tmp = path;
+ char *ret = find_executable(filename, &tmp);
+ free(path);
+ free(ret);
+ return ret != NULL;
+}
+
+#if ENABLE_FEATURE_PREFER_APPLETS
+/* just like the real execvp, but try to launch an applet named 'file' first */
+int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
+{
+ if (find_applet_by_name(file) >= 0)
+ execvp(bb_busybox_exec_path, argv);
+ return execvp(file, argv);
+}
+#endif
+
+void FAST_FUNC BB_EXECVP_or_die(char **argv)
+{
+ BB_EXECVP(argv[0], argv);
+ /* SUSv3-mandated exit codes */
+ xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
+ bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+}
+
+/* Typical idiom for applets which exec *optional* PROG [ARGS] */
+void FAST_FUNC exec_prog_or_SHELL(char **argv)
+{
+ if (argv[0]) {
+ BB_EXECVP_or_die(argv);
+ }
+ run_shell(getenv("SHELL"), /*login:*/ 1, NULL);
+}
diff --git a/libbb/fflush_stdout_and_exit.c b/libbb/fflush_stdout_and_exit.c
index 41a14d3..1d4d6e9 100644
--- a/libbb/fflush_stdout_and_exit.c
+++ b/libbb/fflush_stdout_and_exit.c
@@ -12,18 +12,15 @@
*/
#include "libbb.h"
+#include <strings.h>
+
void FAST_FUNC fflush_stdout_and_exit(int retval)
{
+ xfunc_error_retval = retval;
if (fflush(stdout))
bb_perror_msg_and_die("%s", bb_msg_standard_output);
-
- if (ENABLE_FEATURE_PREFER_APPLETS && die_sleep < 0) {
- /* We are in NOFORK applet. Do not exit() directly,
- * but use xfunc_die() */
- xfunc_error_retval = retval;
- xfunc_die();
- }
-
- exit(retval);
+ /* In case we are in NOFORK applet. Do not exit() directly,
+ * but use xfunc_die() */
+ xfunc_die();
}
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index d0e83d8..15b6efc 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -17,23 +17,20 @@
uint32_t
getopt32(char **argv, const char *applet_opts, ...)
- The command line options must be declared in const char
- *applet_opts as a string of chars, for example:
-
- flags = getopt32(argv, "rnug");
+ The command line options are passed as the applet_opts string.
If one of the given options is found, a flag value is added to
- the return value (an unsigned long).
+ the return value.
The flag value is determined by the position of the char in
- applet_opts string. For example, in the above case:
+ applet_opts string. For example:
flags = getopt32(argv, "rnug");
- "r" will add 1 (bit 0)
- "n" will add 2 (bit 1)
- "u" will add 4 (bit 2)
- "g" will add 8 (bit 3)
+ "r" will set 1 (bit 0)
+ "n" will set 2 (bit 1)
+ "u" will set 4 (bit 2)
+ "g" will set 8 (bit 3)
and so on. You can also look at the return value as a bit
field and each option sets one bit.
@@ -45,7 +42,7 @@ getopt32(char **argv, const char *applet_opts, ...)
(options and their parameters will be moved into argv[]
positions prior to argv[optind]).
- ":" If one of the options requires an argument, then add a ":"
+ "o:" If one of the options requires an argument, then add a ":"
after the char in applet_opts and provide a pointer to store
the argument. For example:
@@ -58,15 +55,39 @@ getopt32(char **argv, const char *applet_opts, ...)
&pointer_to_arg_for_a, &pointer_to_arg_for_b,
&pointer_to_arg_for_c, &pointer_to_arg_for_d);
- The type of the pointer (char* or llist_t*) may be controlled
- by the "::" special separator that is set in the external string
- opt_complementary (see below for more info).
+ The type of the pointer may be controlled by "o::" or "o+" in
+ the external string opt_complementary (see below for more info).
- "::" If option can have an *optional* argument, then add a "::"
+ "o::" If option can have an *optional* argument, then add a "::"
after its char in applet_opts and provide a pointer to store
the argument. Note that optional arguments _must_
immediately follow the option: -oparam, not -o param.
+ "o:+" This means that the parameter for this option is a nonnegative integer.
+ It will be processed with xatoi_positive() - allowed range
+ is 0..INT_MAX.
+
+ int param; // "unsigned param;" will also work
+ getopt32(argv, "p:+", &param);
+
+ "o:*" This means that the option can occur multiple times. Each occurrence
+ will be saved as a llist_t element instead of char*.
+
+ For example:
+ The grep applet can have one or more "-e pattern" arguments.
+ In this case you should use getopt32() as follows:
+
+ llist_t *patterns = NULL;
+
+ (this pointer must be initializated to NULL if the list is empty
+ as required by llist_add_to_end(llist_t **old_head, char *new_item).)
+
+ getopt32(argv, "e:*", &patterns);
+
+ $ grep -e user -e root /etc/passwd
+ root:x:0:0:root:/root:/bin/bash
+ user:x:500:500::/home/user:/bin/bash
+
"+" If the first character in the applet_opts string is a plus,
then option processing will stop as soon as a non-option is
encountered in the argv array. Useful for applets like env
@@ -82,7 +103,7 @@ const char *applet_long_options
This struct allows you to define long options:
static const char applet_longopts[] ALIGN1 =
- //"name\0" has_arg val
+ //"name\0" has_arg val
"verbose\0" No_argument "v"
;
applet_long_options = applet_longopts;
@@ -90,7 +111,7 @@ const char *applet_long_options
The last member of struct option (val) typically is set to
matching short option from applet_opts. If there is no matching
char in applet_opts, then:
- - return bit have next position after short options
+ - return bit has next position after short options
- if has_arg is not "No_argument", use ptr for arg also
- opt_complementary affects it too
@@ -139,8 +160,8 @@ const char *opt_complementary
llist_t *my_b = NULL;
int verbose_level = 0;
- opt_complementary = "vv:b::b-c:c-b";
- f = getopt32(argv, "vb:c", &my_b, &verbose_level);
+ opt_complementary = "vv:b-c:c-b";
+ f = getopt32(argv, "vb:*c", &my_b, &verbose_level);
if (f & 2) // -c after -b unsets -b flag
while (my_b) dosomething_with(llist_pop(&my_b));
if (my_b) // but llist is stored if -b is specified
@@ -233,7 +254,7 @@ Special characters:
"x--x" Variation of the above, it means that -x option should occur
at most once.
- "a+" A plus after a char in opt_complementary means that the parameter
+ "o+" A plus after a char in opt_complementary means that the parameter
for this option is a nonnegative integer. It will be processed
with xatoi_positive() - allowed range is 0..INT_MAX.
@@ -241,7 +262,7 @@ Special characters:
opt_complementary = "p+";
getopt32(argv, "p:", &param);
- "a::" A double colon after a char in opt_complementary means that the
+ "o::" A double colon after a char in opt_complementary means that the
option can occur multiple times. Each occurrence will be saved as
a llist_t element instead of char*.
@@ -255,12 +276,17 @@ Special characters:
as required by llist_add_to_end(llist_t **old_head, char *new_item).)
opt_complementary = "e::";
-
getopt32(argv, "e:", &patterns);
+
$ grep -e user -e root /etc/passwd
root:x:0:0:root:/root:/bin/bash
user:x:500:500::/home/user:/bin/bash
+ "o+" and "o::" can be handled by "o:+" and "o:*" specifiers
+ in option string (and it is preferred), but this does not work
+ for "long options only" cases, such as tar --exclude=PATTERN,
+ wget --header=HDR cases.
+
"a?b" A "?" between an option and a group of options means that
at least one of them is required to occur if the first option
occurs in preceding command line arguments.
@@ -359,10 +385,11 @@ getopt32(char **argv, const char *applet_opts, ...)
va_start(p, applet_opts);
- c = 0;
on_off = complementary;
memset(on_off, 0, sizeof(complementary));
+ applet_opts = strcpy(alloca(strlen(applet_opts) + 1), applet_opts);
+
/* skip bbox extension */
first_char = applet_opts[0];
if (first_char == '!')
@@ -372,6 +399,7 @@ getopt32(char **argv, const char *applet_opts, ...)
s = (const unsigned char *)applet_opts;
if (*s == '+' || *s == '-')
s++;
+ c = 0;
while (*s) {
if (c >= 32)
break;
@@ -379,6 +407,13 @@ getopt32(char **argv, const char *applet_opts, ...)
on_off->switch_on = (1 << c);
if (*++s == ':') {
on_off->optarg = va_arg(p, void **);
+ if (s[1] == '+' || s[1] == '*') {
+ /* 'o:+' or 'o:*' */
+ on_off->param_type = (s[1] == '+') ?
+ PARAM_INT : PARAM_LIST;
+ overlapping_strcpy((char*)s + 1, (char*)s + 2);
+ }
+ /* skip possible 'o::' (or 'o:+:' !) */
while (*++s == ':')
continue;
}
@@ -431,6 +466,7 @@ getopt32(char **argv, const char *applet_opts, ...)
applet_long_options = NULL;
}
#endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */
+
for (s = (const unsigned char *)opt_complementary; s && *s; s++) {
t_complementary *pair;
unsigned *pair_switch;
diff --git a/libbb/getpty.c b/libbb/getpty.c
index 435e4d0..391d729 100644
--- a/libbb/getpty.c
+++ b/libbb/getpty.c
@@ -16,7 +16,7 @@ int FAST_FUNC xgetpty(char *line)
#if ENABLE_FEATURE_DEVPTS
p = open("/dev/ptmx", O_RDWR);
- if (p > 0) {
+ if (p >= 0) {
grantpt(p); /* chmod+chown corresponding slave pty */
unlockpt(p); /* (what does this do?) */
# ifndef HAVE_PTSNAME_R
diff --git a/libbb/hash_md5_sha.c b/libbb/hash_md5_sha.c
index 3f743ac..7e7d8da 100644
--- a/libbb/hash_md5_sha.c
+++ b/libbb/hash_md5_sha.c
@@ -137,7 +137,7 @@ static void FAST_FUNC md5_process_block64(md5_ctx_t *ctx)
#if MD5_SMALL > 0
/* Before we start, one word to the strange constants.
They are defined in RFC 1321 as
- T[i] = (int)(4294967296.0 * fabs(sin(i))), i=1..64
+ T[i] = (int)(2^32 * fabs(sin(i))), i=1..64
*/
static const uint32_t C_array[] = {
/* round 1 */
@@ -213,7 +213,7 @@ static void FAST_FUNC md5_process_block64(md5_ctx_t *ctx)
case 2:
temp += FH(B, C, D);
break;
- case 3:
+ default: /* case 3 */
temp += FI(B, C, D);
}
temp += words[(int) (*pp++)] + *pc++;
@@ -277,10 +277,6 @@ static void FAST_FUNC md5_process_block64(md5_ctx_t *ctx)
#else /* MD5_SMALL == 0 or 1 */
- uint32_t A_save = A;
- uint32_t B_save = B;
- uint32_t C_save = C;
- uint32_t D_save = D;
# if MD5_SMALL == 1
const uint32_t *pc;
const char *pp;
@@ -425,10 +421,10 @@ static void FAST_FUNC md5_process_block64(md5_ctx_t *ctx)
# undef OP
# endif
/* Add checksum to the starting values */
- ctx->hash[0] = A_save + A;
- ctx->hash[1] = B_save + B;
- ctx->hash[2] = C_save + C;
- ctx->hash[3] = D_save + D;
+ ctx->hash[0] += A;
+ ctx->hash[1] += B;
+ ctx->hash[2] += C;
+ ctx->hash[3] += D;
#endif
}
#undef FF
@@ -926,9 +922,76 @@ void FAST_FUNC sha512_end(sha512_ctx_t *ctx, void *resbuf)
# define SHA3_SMALL CONFIG_SHA3_SMALL
#endif
-enum {
- SHA3_IBLK_BYTES = 72, /* 576 bits / 8 */
-};
+#define OPTIMIZE_SHA3_FOR_32 0
+/*
+ * SHA3 can be optimized for 32-bit CPUs with bit-slicing:
+ * every 64-bit word of state[] can be split into two 32-bit words
+ * by even/odd bits. In this form, all rotations of sha3 round
+ * are 32-bit - and there are lots of them.
+ * However, it requires either splitting/combining state words
+ * before/after sha3 round (code does this now)
+ * or shuffling bits before xor'ing them into state and in sha3_end.
+ * Without shuffling, bit-slicing results in -130 bytes of code
+ * and marginal speedup (but of course it gives wrong result).
+ * With shuffling it works, but +260 code bytes, and slower.
+ * Disabled for now:
+ */
+#if 0 /* LONG_MAX == 0x7fffffff */
+# undef OPTIMIZE_SHA3_FOR_32
+# define OPTIMIZE_SHA3_FOR_32 1
+#endif
+
+#if OPTIMIZE_SHA3_FOR_32
+/* This splits every 64-bit word into a pair of 32-bit words,
+ * even bits go into first word, odd bits go to second one.
+ * The conversion is done in-place.
+ */
+static void split_halves(uint64_t *state)
+{
+ /* Credit: Henry S. Warren, Hacker's Delight, Addison-Wesley, 2002 */
+ uint32_t *s32 = (uint32_t*)state;
+ uint32_t t, x0, x1;
+ int i;
+ for (i = 24; i >= 0; --i) {
+ x0 = s32[0];
+ t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1);
+ t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2);
+ t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4);
+ t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8);
+ x1 = s32[1];
+ t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1);
+ t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2);
+ t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4);
+ t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8);
+ *s32++ = (x0 & 0x0000FFFF) | (x1 << 16);
+ *s32++ = (x0 >> 16) | (x1 & 0xFFFF0000);
+ }
+}
+/* The reverse operation */
+static void combine_halves(uint64_t *state)
+{
+ uint32_t *s32 = (uint32_t*)state;
+ uint32_t t, x0, x1;
+ int i;
+ for (i = 24; i >= 0; --i) {
+ x0 = s32[0];
+ x1 = s32[1];
+ t = (x0 & 0x0000FFFF) | (x1 << 16);
+ x1 = (x0 >> 16) | (x1 & 0xFFFF0000);
+ x0 = t;
+ t = (x0 ^ (x0 >> 8)) & 0x0000FF00; x0 = x0 ^ t ^ (t << 8);
+ t = (x0 ^ (x0 >> 4)) & 0x00F000F0; x0 = x0 ^ t ^ (t << 4);
+ t = (x0 ^ (x0 >> 2)) & 0x0C0C0C0C; x0 = x0 ^ t ^ (t << 2);
+ t = (x0 ^ (x0 >> 1)) & 0x22222222; x0 = x0 ^ t ^ (t << 1);
+ *s32++ = x0;
+ t = (x1 ^ (x1 >> 8)) & 0x0000FF00; x1 = x1 ^ t ^ (t << 8);
+ t = (x1 ^ (x1 >> 4)) & 0x00F000F0; x1 = x1 ^ t ^ (t << 4);
+ t = (x1 ^ (x1 >> 2)) & 0x0C0C0C0C; x1 = x1 ^ t ^ (t << 2);
+ t = (x1 ^ (x1 >> 1)) & 0x22222222; x1 = x1 ^ t ^ (t << 1);
+ *s32++ = x1;
+ }
+}
+#endif
/*
* In the crypto literature this function is usually called Keccak-f().
@@ -937,12 +1000,179 @@ static void sha3_process_block72(uint64_t *state)
{
enum { NROUNDS = 24 };
- /* Elements should be 64-bit, but top half is always zero or 0x80000000.
- * We encode 63rd bits in a separate word below.
- * Same is true for 31th bits, which lets us use 16-bit table instead of 64-bit.
- * The speed penalty is lost in the noise.
- */
+#if OPTIMIZE_SHA3_FOR_32
+ /*
+ static const uint32_t IOTA_CONST_0[NROUNDS] = {
+ 0x00000001UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000001UL,
+ 0x00000000UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000001UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000000UL,
+ 0x00000001UL,
+ 0x00000000UL,
+ 0x00000001UL,
+ 0x00000000UL,
+ };
+ ** bits are in lsb: 0101 0000 1111 0100 1111 0001
+ */
+ uint32_t IOTA_CONST_0bits = (uint32_t)(0x0050f4f1);
+ static const uint32_t IOTA_CONST_1[NROUNDS] = {
+ 0x00000000UL,
+ 0x00000089UL,
+ 0x8000008bUL,
+ 0x80008080UL,
+ 0x0000008bUL,
+ 0x00008000UL,
+ 0x80008088UL,
+ 0x80000082UL,
+ 0x0000000bUL,
+ 0x0000000aUL,
+ 0x00008082UL,
+ 0x00008003UL,
+ 0x0000808bUL,
+ 0x8000000bUL,
+ 0x8000008aUL,
+ 0x80000081UL,
+ 0x80000081UL,
+ 0x80000008UL,
+ 0x00000083UL,
+ 0x80008003UL,
+ 0x80008088UL,
+ 0x80000088UL,
+ 0x00008000UL,
+ 0x80008082UL,
+ };
+
+ uint32_t *const s32 = (uint32_t*)state;
+ unsigned round;
+
+ split_halves(state);
+
+ for (round = 0; round < NROUNDS; round++) {
+ unsigned x;
+
+ /* Theta */
+ {
+ uint32_t BC[20];
+ for (x = 0; x < 10; ++x) {
+ BC[x+10] = BC[x] = s32[x]^s32[x+10]^s32[x+20]^s32[x+30]^s32[x+40];
+ }
+ for (x = 0; x < 10; x += 2) {
+ uint32_t ta, tb;
+ ta = BC[x+8] ^ rotl32(BC[x+3], 1);
+ tb = BC[x+9] ^ BC[x+2];
+ s32[x+0] ^= ta;
+ s32[x+1] ^= tb;
+ s32[x+10] ^= ta;
+ s32[x+11] ^= tb;
+ s32[x+20] ^= ta;
+ s32[x+21] ^= tb;
+ s32[x+30] ^= ta;
+ s32[x+31] ^= tb;
+ s32[x+40] ^= ta;
+ s32[x+41] ^= tb;
+ }
+ }
+ /* RhoPi */
+ {
+ uint32_t t0a,t0b, t1a,t1b;
+ t1a = s32[1*2+0];
+ t1b = s32[1*2+1];
+
+#define RhoPi(PI_LANE, ROT_CONST) \
+ t0a = s32[PI_LANE*2+0];\
+ t0b = s32[PI_LANE*2+1];\
+ if (ROT_CONST & 1) {\
+ s32[PI_LANE*2+0] = rotl32(t1b, ROT_CONST/2+1);\
+ s32[PI_LANE*2+1] = ROT_CONST == 1 ? t1a : rotl32(t1a, ROT_CONST/2+0);\
+ } else {\
+ s32[PI_LANE*2+0] = rotl32(t1a, ROT_CONST/2);\
+ s32[PI_LANE*2+1] = rotl32(t1b, ROT_CONST/2);\
+ }\
+ t1a = t0a; t1b = t0b;
+
+ RhoPi(10, 1)
+ RhoPi( 7, 3)
+ RhoPi(11, 6)
+ RhoPi(17,10)
+ RhoPi(18,15)
+ RhoPi( 3,21)
+ RhoPi( 5,28)
+ RhoPi(16,36)
+ RhoPi( 8,45)
+ RhoPi(21,55)
+ RhoPi(24, 2)
+ RhoPi( 4,14)
+ RhoPi(15,27)
+ RhoPi(23,41)
+ RhoPi(19,56)
+ RhoPi(13, 8)
+ RhoPi(12,25)
+ RhoPi( 2,43)
+ RhoPi(20,62)
+ RhoPi(14,18)
+ RhoPi(22,39)
+ RhoPi( 9,61)
+ RhoPi( 6,20)
+ RhoPi( 1,44)
+#undef RhoPi
+ }
+ /* Chi */
+ for (x = 0; x <= 40;) {
+ uint32_t BC0, BC1, BC2, BC3, BC4;
+ BC0 = s32[x + 0*2];
+ BC1 = s32[x + 1*2];
+ BC2 = s32[x + 2*2];
+ s32[x + 0*2] = BC0 ^ ((~BC1) & BC2);
+ BC3 = s32[x + 3*2];
+ s32[x + 1*2] = BC1 ^ ((~BC2) & BC3);
+ BC4 = s32[x + 4*2];
+ s32[x + 2*2] = BC2 ^ ((~BC3) & BC4);
+ s32[x + 3*2] = BC3 ^ ((~BC4) & BC0);
+ s32[x + 4*2] = BC4 ^ ((~BC0) & BC1);
+ x++;
+ BC0 = s32[x + 0*2];
+ BC1 = s32[x + 1*2];
+ BC2 = s32[x + 2*2];
+ s32[x + 0*2] = BC0 ^ ((~BC1) & BC2);
+ BC3 = s32[x + 3*2];
+ s32[x + 1*2] = BC1 ^ ((~BC2) & BC3);
+ BC4 = s32[x + 4*2];
+ s32[x + 2*2] = BC2 ^ ((~BC3) & BC4);
+ s32[x + 3*2] = BC3 ^ ((~BC4) & BC0);
+ s32[x + 4*2] = BC4 ^ ((~BC0) & BC1);
+ x += 9;
+ }
+ /* Iota */
+ s32[0] ^= IOTA_CONST_0bits & 1;
+ IOTA_CONST_0bits >>= 1;
+ s32[1] ^= IOTA_CONST_1[round];
+ }
+
+ combine_halves(state);
+#else
+ /* Native 64-bit algorithm */
static const uint16_t IOTA_CONST[NROUNDS] = {
+ /* Elements should be 64-bit, but top half is always zero
+ * or 0x80000000. We encode 63rd bits in a separate word below.
+ * Same is true for 31th bits, which lets us use 16-bit table
+ * instead of 64-bit. The speed penalty is lost in the noise.
+ */
0x0001,
0x8082,
0x808a,
@@ -983,7 +1213,7 @@ static void sha3_process_block72(uint64_t *state)
};
/*static const uint8_t MOD5[10] = { 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, };*/
- unsigned x, y;
+ unsigned x;
unsigned round;
if (BB_BIG_ENDIAN) {
@@ -1045,22 +1275,62 @@ static void sha3_process_block72(uint64_t *state)
RhoPi_twice(20); RhoPi_twice(22);
#undef RhoPi_twice
}
-
/* Chi */
- for (y = 0; y <= 20; y += 5) {
+# if LONG_MAX > 0x7fffffff
+ for (x = 0; x <= 20; x += 5) {
uint64_t BC0, BC1, BC2, BC3, BC4;
- BC0 = state[y + 0];
- BC1 = state[y + 1];
- BC2 = state[y + 2];
- state[y + 0] = BC0 ^ ((~BC1) & BC2);
- BC3 = state[y + 3];
- state[y + 1] = BC1 ^ ((~BC2) & BC3);
- BC4 = state[y + 4];
- state[y + 2] = BC2 ^ ((~BC3) & BC4);
- state[y + 3] = BC3 ^ ((~BC4) & BC0);
- state[y + 4] = BC4 ^ ((~BC0) & BC1);
+ BC0 = state[x + 0];
+ BC1 = state[x + 1];
+ BC2 = state[x + 2];
+ state[x + 0] = BC0 ^ ((~BC1) & BC2);
+ BC3 = state[x + 3];
+ state[x + 1] = BC1 ^ ((~BC2) & BC3);
+ BC4 = state[x + 4];
+ state[x + 2] = BC2 ^ ((~BC3) & BC4);
+ state[x + 3] = BC3 ^ ((~BC4) & BC0);
+ state[x + 4] = BC4 ^ ((~BC0) & BC1);
}
-
+# else
+ /* Reduced register pressure version
+ * for register-starved 32-bit arches
+ * (i386: -95 bytes, and it is _faster_)
+ */
+ for (x = 0; x <= 40;) {
+ uint32_t BC0, BC1, BC2, BC3, BC4;
+ uint32_t *const s32 = (uint32_t*)state;
+# if SHA3_SMALL
+ do_half:
+# endif
+ BC0 = s32[x + 0*2];
+ BC1 = s32[x + 1*2];
+ BC2 = s32[x + 2*2];
+ s32[x + 0*2] = BC0 ^ ((~BC1) & BC2);
+ BC3 = s32[x + 3*2];
+ s32[x + 1*2] = BC1 ^ ((~BC2) & BC3);
+ BC4 = s32[x + 4*2];
+ s32[x + 2*2] = BC2 ^ ((~BC3) & BC4);
+ s32[x + 3*2] = BC3 ^ ((~BC4) & BC0);
+ s32[x + 4*2] = BC4 ^ ((~BC0) & BC1);
+ x++;
+# if SHA3_SMALL
+ if (x & 1)
+ goto do_half;
+ x += 8;
+# else
+ BC0 = s32[x + 0*2];
+ BC1 = s32[x + 1*2];
+ BC2 = s32[x + 2*2];
+ s32[x + 0*2] = BC0 ^ ((~BC1) & BC2);
+ BC3 = s32[x + 3*2];
+ s32[x + 1*2] = BC1 ^ ((~BC2) & BC3);
+ BC4 = s32[x + 4*2];
+ s32[x + 2*2] = BC2 ^ ((~BC3) & BC4);
+ s32[x + 3*2] = BC3 ^ ((~BC4) & BC0);
+ s32[x + 4*2] = BC4 ^ ((~BC0) & BC1);
+ x += 9;
+# endif
+ }
+# endif /* long is 32-bit */
/* Iota */
state[0] ^= IOTA_CONST[round]
| (uint32_t)((IOTA_CONST_bit31 << round) & 0x80000000)
@@ -1072,11 +1342,14 @@ static void sha3_process_block72(uint64_t *state)
state[x] = SWAP_LE64(state[x]);
}
}
+#endif
}
void FAST_FUNC sha3_begin(sha3_ctx_t *ctx)
{
memset(ctx, 0, sizeof(*ctx));
+ /* SHA3-512, user can override */
+ ctx->input_block_bytes = (1600 - 512*2) / 8; /* 72 bytes */
}
void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
@@ -1086,7 +1359,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
unsigned bufpos = ctx->bytes_queued;
while (1) {
- unsigned remaining = SHA3_IBLK_BYTES - bufpos;
+ unsigned remaining = ctx->input_block_bytes - bufpos;
if (remaining > len)
remaining = len;
len -= remaining;
@@ -1098,38 +1371,41 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
remaining--;
}
/* Clever way to do "if (bufpos != N) break; ... ; bufpos = 0;" */
- bufpos -= SHA3_IBLK_BYTES;
+ bufpos -= ctx->input_block_bytes;
if (bufpos != 0)
break;
/* Buffer is filled up, process it */
sha3_process_block72(ctx->state);
/*bufpos = 0; - already is */
}
- ctx->bytes_queued = bufpos + SHA3_IBLK_BYTES;
+ ctx->bytes_queued = bufpos + ctx->input_block_bytes;
#else
/* +50 bytes code size, but a bit faster because of long-sized XORs */
const uint8_t *data = buffer;
unsigned bufpos = ctx->bytes_queued;
+ unsigned iblk_bytes = ctx->input_block_bytes;
/* If already data in queue, continue queuing first */
- while (len != 0 && bufpos != 0) {
- uint8_t *buf = (uint8_t*)ctx->state;
- buf[bufpos] ^= *data++;
- len--;
- bufpos++;
- if (bufpos == SHA3_IBLK_BYTES) {
- bufpos = 0;
- goto do_block;
+ if (bufpos != 0) {
+ while (len != 0) {
+ uint8_t *buf = (uint8_t*)ctx->state;
+ buf[bufpos] ^= *data++;
+ len--;
+ bufpos++;
+ if (bufpos == iblk_bytes) {
+ bufpos = 0;
+ goto do_block;
+ }
}
}
/* Absorb complete blocks */
- while (len >= SHA3_IBLK_BYTES) {
+ while (len >= iblk_bytes) {
/* XOR data onto beginning of state[].
* We try to be efficient - operate one word at a time, not byte.
* Careful wrt unaligned access: can't just use "*(long*)data"!
*/
- unsigned count = SHA3_IBLK_BYTES / sizeof(long);
+ unsigned count = iblk_bytes / sizeof(long);
long *buf = (long*)ctx->state;
do {
long v;
@@ -1137,7 +1413,7 @@ void FAST_FUNC sha3_hash(sha3_ctx_t *ctx, const void *buffer, size_t len)
*buf++ ^= v;
data += sizeof(long);
} while (--count);
- len -= SHA3_IBLK_BYTES;
+ len -= iblk_bytes;
do_block:
sha3_process_block72(ctx->state);
}
@@ -1158,8 +1434,22 @@ void FAST_FUNC sha3_end(sha3_ctx_t *ctx, void *resbuf)
{
/* Padding */
uint8_t *buf = (uint8_t*)ctx->state;
- buf[ctx->bytes_queued] ^= 1;
- buf[SHA3_IBLK_BYTES - 1] ^= 0x80;
+ /*
+ * Keccak block padding is: add 1 bit after last bit of input,
+ * then add zero bits until the end of block, and add the last 1 bit
+ * (the last bit in the block) - the "10*1" pattern.
+ * SHA3 standard appends additional two bits, 01, before that padding:
+ *
+ * SHA3-224(M) = KECCAK[448](M||01, 224)
+ * SHA3-256(M) = KECCAK[512](M||01, 256)
+ * SHA3-384(M) = KECCAK[768](M||01, 384)
+ * SHA3-512(M) = KECCAK[1024](M||01, 512)
+ * (M is the input, || is bit concatenation)
+ *
+ * The 6 below contains 01 "SHA3" bits and the first 1 "Keccak" bit:
+ */
+ buf[ctx->bytes_queued] ^= 6; /* bit pattern 00000110 */
+ buf[ctx->input_block_bytes - 1] ^= 0x80;
sha3_process_block72(ctx->state);
diff --git a/libbb/human_readable.c b/libbb/human_readable.c
index 0b2eb77..b4e0ef1 100644
--- a/libbb/human_readable.c
+++ b/libbb/human_readable.c
@@ -14,16 +14,11 @@
* representations (say, powers of 1024) and manipulating coefficients.
* The base ten "bytes" output could be handled similarly.
*
- * 2) This routine always outputs a decimal point and a tenths digit when
- * display_unit != 0. Hence, it isn't uncommon for the returned string
+ * 2) This routine outputs a decimal point and a tenths digit when
+ * display_unit == 0. Hence, it isn't uncommon for the returned string
* to have a length of 5 or 6.
*
- * It might be nice to add a flag to indicate no decimal digits in
- * that case. This could be either an additional parameter, or a
- * special value of display_unit. Such a flag would also be nice for du.
- *
- * Some code to omit the decimal point and tenths digit is sketched out
- * and "#if 0"'d below.
+ * If block_size is also 0, no decimal digits are printed.
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
@@ -37,8 +32,6 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
};
- static char *str;
-
unsigned frac; /* 0..9 - the fractional digit */
const char *u;
const char *fmt;
@@ -81,12 +74,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
#endif
}
- if (!str) {
- /* sufficient for any width of val */
- str = xmalloc(sizeof(val)*3 + 2 + 3);
- }
- sprintf(str, fmt, val, frac, *u);
- return str;
+ return auto_string(xasprintf(fmt, val, frac, *u));
}
diff --git a/libbb/in_ether.c b/libbb/in_ether.c
index 1de383b..161c8ea 100644
--- a/libbb/in_ether.c
+++ b/libbb/in_ether.c
@@ -51,7 +51,6 @@ int FAST_FUNC in_ether(const char *bufp, struct sockaddr *sap)
} while (j);
*ptr++ = val;
-
} while (--i);
/* Error if we aren't at end of string */
diff --git a/libbb/inet_common.c b/libbb/inet_common.c
index b3e0802..5b4a4a1 100644
--- a/libbb/inet_common.c
+++ b/libbb/inet_common.c
@@ -32,14 +32,12 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
return 0;
}
/* If we expect this to be a hostname, try hostname database first */
-#ifdef DEBUG
if (hostfirst) {
+#ifdef DEBUG
bb_error_msg("gethostbyname(%s)", name);
- }
#endif
- if (hostfirst) {
hp = gethostbyname(name);
- if (hp != NULL) {
+ if (hp) {
memcpy(&s_in->sin_addr, hp->h_addr_list[0],
sizeof(struct in_addr));
return 0;
@@ -51,7 +49,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
bb_error_msg("getnetbyname(%s)", name);
#endif
np = getnetbyname(name);
- if (np != NULL) {
+ if (np) {
s_in->sin_addr.s_addr = htonl(np->n_net);
return 1;
}
@@ -66,7 +64,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
bb_error_msg("gethostbyname(%s)", name);
#endif
hp = gethostbyname(name);
- if (hp == NULL) {
+ if (!hp) {
return -1;
}
memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
@@ -74,7 +72,7 @@ int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostf
}
-/* numeric: & 0x8000: default instead of *,
+/* numeric: & 0x8000: "default" instead of "*",
* & 0x4000: host instead of net,
* & 0x0fff: don't resolve
*/
@@ -83,16 +81,16 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
/* addr-to-name cache */
struct addr {
struct addr *next;
- struct sockaddr_in addr;
- int host;
+ uint32_t nip;
+ smallint is_host;
char name[1];
};
static struct addr *cache = NULL;
struct addr *pn;
char *name;
- uint32_t ad, host_ad;
- int host = 0;
+ uint32_t nip;
+ smallint is_host;
if (s_in->sin_family != AF_INET) {
#ifdef DEBUG
@@ -102,61 +100,57 @@ char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t ne
errno = EAFNOSUPPORT;
return NULL;
}
- ad = s_in->sin_addr.s_addr;
+ nip = s_in->sin_addr.s_addr;
#ifdef DEBUG
- bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric);
+ bb_error_msg("rresolve: %08x mask:%08x num:%08x", (unsigned)nip, netmask, numeric);
#endif
- if (ad == INADDR_ANY) {
- if ((numeric & 0x0FFF) == 0) {
- if (numeric & 0x8000)
- return xstrdup("default");
- return xstrdup("*");
- }
- }
if (numeric & 0x0FFF)
- return xstrdup(inet_ntoa(s_in->sin_addr));
+ return xmalloc_sockaddr2dotted_noport((void*)s_in);
+ if (nip == INADDR_ANY) {
+ if (numeric & 0x8000)
+ return xstrdup("default");
+ return xstrdup("*");
+ }
+
+ is_host = ((nip & (~netmask)) != 0 || (numeric & 0x4000));
- if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
- host = 1;
pn = cache;
while (pn) {
- if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
+ if (pn->nip == nip && pn->is_host == is_host) {
#ifdef DEBUG
bb_error_msg("rresolve: found %s %08x in cache",
- (host ? "host" : "net"), (unsigned)ad);
+ (is_host ? "host" : "net"), (unsigned)nip);
#endif
return xstrdup(pn->name);
}
pn = pn->next;
}
- host_ad = ntohl(ad);
name = NULL;
- if (host) {
- struct hostent *ent;
+ if (is_host) {
#ifdef DEBUG
- bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad);
+ bb_error_msg("sockaddr2host_noport(%08x)", (unsigned)nip);
#endif
- ent = gethostbyaddr((char *) &ad, 4, AF_INET);
- if (ent)
- name = xstrdup(ent->h_name);
+ name = xmalloc_sockaddr2host_noport((void*)s_in);
} else if (ENABLE_FEATURE_ETC_NETWORKS) {
struct netent *np;
#ifdef DEBUG
- bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad);
+ bb_error_msg("getnetbyaddr(%08x)", (unsigned)ntohl(nip));
#endif
- np = getnetbyaddr(host_ad, AF_INET);
+ np = getnetbyaddr(ntohl(nip), AF_INET);
if (np)
name = xstrdup(np->n_name);
}
if (!name)
- name = xstrdup(inet_ntoa(s_in->sin_addr));
+ name = xmalloc_sockaddr2dotted_noport((void*)s_in);
+
pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */
pn->next = cache;
- pn->addr = *s_in;
- pn->host = host;
+ pn->nip = nip;
+ pn->is_host = is_host;
strcpy(pn->name, name);
cache = pn;
+
return name;
}
@@ -188,9 +182,6 @@ int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
{
- char name[128];
- int s;
-
if (sin6->sin6_family != AF_INET6) {
#ifdef DEBUG
bb_error_msg("rresolve: unsupported address family %d!",
@@ -200,8 +191,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
return NULL;
}
if (numeric & 0x7FFF) {
- inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name));
- return xstrdup(name);
+ return xmalloc_sockaddr2dotted_noport((void*)sin6);
}
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
if (numeric & 0x8000)
@@ -209,15 +199,7 @@ char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
return xstrdup("*");
}
- s = getnameinfo((struct sockaddr *) sin6, sizeof(*sin6),
- name, sizeof(name),
- /*serv,servlen:*/ NULL, 0,
- 0);
- if (s != 0) {
- bb_error_msg("getnameinfo failed");
- return NULL;
- }
- return xstrdup(name);
+ return xmalloc_sockaddr2host_noport((void*)sin6);
}
#endif /* CONFIG_FEATURE_IPV6 */
diff --git a/libbb/info_msg.c b/libbb/info_msg.c
deleted file mode 100644
index 56ca2ef..0000000
--- a/libbb/info_msg.c
+++ b/dev/null
@@ -1,62 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Utility routines.
- *
- * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
-
-#include "libbb.h"
-#if ENABLE_FEATURE_SYSLOG
-# include <syslog.h>
-#endif
-
-void FAST_FUNC bb_info_msg(const char *s, ...)
-{
-#ifdef THIS_ONE_DOESNT_DO_SINGLE_WRITE
- va_list p;
- /* va_copy is used because it is not portable
- * to use va_list p twice */
- va_list p2;
-
- va_start(p, s);
- va_copy(p2, p);
- if (logmode & LOGMODE_STDIO) {
- vprintf(s, p);
- fputs(msg_eol, stdout);
- }
-# if ENABLE_FEATURE_SYSLOG
- if (logmode & LOGMODE_SYSLOG)
- vsyslog(LOG_INFO, s, p2);
-# endif
- va_end(p2);
- va_end(p);
-#else
- int used;
- char *msg;
- va_list p;
-
- if (logmode == 0)
- return;
-
- va_start(p, s);
- used = vasprintf(&msg, s, p);
- va_end(p);
- if (used < 0)
- return;
-
-# if ENABLE_FEATURE_SYSLOG
- if (logmode & LOGMODE_SYSLOG)
- syslog(LOG_INFO, "%s", msg);
-# endif
- if (logmode & LOGMODE_STDIO) {
- fflush_all();
- /* used = strlen(msg); - must be true already */
- msg[used++] = '\n';
- full_write(STDOUT_FILENO, msg, used);
- }
-
- free(msg);
-#endif
-}
diff --git a/libbb/inode_hash.c b/libbb/inode_hash.c
index 377ecbc..f11c2af 100644
--- a/libbb/inode_hash.c
+++ b/libbb/inode_hash.c
@@ -11,14 +11,23 @@
#include "libbb.h"
typedef struct ino_dev_hash_bucket_struct {
- struct ino_dev_hash_bucket_struct *next;
ino_t ino;
dev_t dev;
+ /*
+ * Above fields can be 64-bit, while pointer may be 32-bit.
+ * Putting "next" field here may reduce size of this struct:
+ */
+ struct ino_dev_hash_bucket_struct *next;
+ /*
+ * Reportedly, on cramfs a file and a dir can have same ino.
+ * Need to also remember "file/dir" bit:
+ */
+ char isdir; /* bool */
char name[1];
} ino_dev_hashtable_bucket_t;
-#define HASH_SIZE 311 /* Should be prime */
-#define hash_inode(i) ((i) % HASH_SIZE)
+#define HASH_SIZE 311u /* Should be prime */
+#define hash_inode(i) ((unsigned)(i) % HASH_SIZE)
/* array of [HASH_SIZE] elements */
static ino_dev_hashtable_bucket_t **ino_dev_hashtable;
@@ -38,6 +47,7 @@ char* FAST_FUNC is_in_ino_dev_hashtable(const struct stat *statbuf)
while (bucket != NULL) {
if ((bucket->ino == statbuf->st_ino)
&& (bucket->dev == statbuf->st_dev)
+ && (bucket->isdir == !!S_ISDIR(statbuf->st_mode))
) {
return bucket->name;
}
@@ -52,17 +62,18 @@ void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *
int i;
ino_dev_hashtable_bucket_t *bucket;
- i = hash_inode(statbuf->st_ino);
if (!name)
name = "";
bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name));
bucket->ino = statbuf->st_ino;
bucket->dev = statbuf->st_dev;
+ bucket->isdir = !!S_ISDIR(statbuf->st_mode);
strcpy(bucket->name, name);
if (!ino_dev_hashtable)
ino_dev_hashtable = xzalloc(HASH_SIZE * sizeof(*ino_dev_hashtable));
+ i = hash_inode(statbuf->st_ino);
bucket->next = ino_dev_hashtable[i];
ino_dev_hashtable[i] = bucket;
}
@@ -71,14 +82,19 @@ void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *
/* Clear statbuf hash table */
void FAST_FUNC reset_ino_dev_hashtable(void)
{
- unsigned i;
- ino_dev_hashtable_bucket_t *bucket;
+ int i;
+ ino_dev_hashtable_bucket_t *bucket, *next;
+
+ if (!ino_dev_hashtable)
+ return;
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ bucket = ino_dev_hashtable[i];
- for (i = 0; ino_dev_hashtable && i < HASH_SIZE; i++) {
- while (ino_dev_hashtable[i] != NULL) {
- bucket = ino_dev_hashtable[i]->next;
- free(ino_dev_hashtable[i]);
- ino_dev_hashtable[i] = bucket;
+ while (bucket != NULL) {
+ next = bucket->next;
+ free(bucket);
+ bucket = next;
}
}
free(ino_dev_hashtable);
diff --git a/libbb/kernel_version.c b/libbb/kernel_version.c
index 738ed02..9b6c622 100644
--- a/libbb/kernel_version.c
+++ b/libbb/kernel_version.c
@@ -20,16 +20,16 @@
int FAST_FUNC get_linux_version_code(void)
{
struct utsname name;
- char *s, *t;
+ char *t;
int i, r;
uname(&name); /* never fails */
- s = name.release;
+ t = name.release;
r = 0;
for (i = 0; i < 3; i++) {
- t = strtok(s, ".");
+ t = strtok(t, ".");
r = r * 256 + (t ? atoi(t) : 0);
- s = NULL;
+ t = NULL;
}
return r;
}
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index f897e96..9908658 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -13,7 +13,6 @@
*
* This code is 'as is' with no warranty.
*/
-
/*
* Usage and known bugs:
* Terminal key codes are not extensive, more needs to be added.
@@ -23,9 +22,6 @@
* Ctrl-E also works as End.
*
* The following readline-like commands are not implemented:
- * ESC-b -- Move back one word
- * ESC-f -- Move forward one word
- * ESC-d -- Delete forward one word
* CTL-t -- Transpose two characters
*
* lineedit does not know that the terminal escape sequences do not
@@ -47,18 +43,20 @@
* It stems from simplistic "cmdedit_y = cmdedit_prmt_len / cmdedit_termw"
* calculation of how many lines the prompt takes.
*/
-#include "libbb.h"
+#include "busybox.h"
+#include "NUM_APPLETS.h"
#include "unicode.h"
+#include "pwd_.h"
#ifndef _POSIX_VDISABLE
# define _POSIX_VDISABLE '\0'
#endif
-#ifdef TEST
-# define ENABLE_FEATURE_EDITING 0
-# define ENABLE_FEATURE_TAB_COMPLETION 0
+
+//# define ENABLE_FEATURE_EDITING 0
+//# define ENABLE_FEATURE_TAB_COMPLETION 0
# define ENABLE_FEATURE_USERNAME_COMPLETION 0
-#endif
+
/* Entire file (except TESTing part) sits inside this #if */
@@ -80,7 +78,9 @@
# define CHAR_T wchar_t
static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
# if ENABLE_FEATURE_EDITING_VI
-static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
+static bool BB_isalnum_or_underscore(CHAR_T c) {
+ return ((unsigned)c < 256 && isalnum(c)) || c == '_';
+}
# endif
static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
# undef isspace
@@ -95,7 +95,11 @@ static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
# define BB_NUL '\0'
# define CHAR_T char
# define BB_isspace(c) isspace(c)
-# define BB_isalnum(c) isalnum(c)
+# if ENABLE_FEATURE_EDITING_VI
+static bool BB_isalnum_or_underscore(CHAR_T c) {
+ return ((unsigned)c < 256 && isalnum(c)) || c == '_';
+}
+# endif
# define BB_ispunct(c) ispunct(c)
#endif
#if ENABLE_UNICODE_PRESERVE_BROKEN
@@ -126,14 +130,13 @@ static const char null_str[] ALIGN1 = "";
struct lineedit_statics {
line_input_t *state;
- volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
- sighandler_t previous_SIGWINCH_handler;
+ unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
unsigned cmdedit_x; /* real x (col) terminal position */
unsigned cmdedit_y; /* pseudoreal y (row) terminal position */
unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
- int cursor;
+ unsigned cursor;
int command_len; /* must be signed */
/* signed maxsize: we want x in "if (x > S.maxsize)"
* to _not_ be promoted to unsigned */
@@ -152,15 +155,22 @@ struct lineedit_statics {
unsigned num_matches;
#endif
+ unsigned SIGWINCH_saved;
+ volatile unsigned SIGWINCH_count;
+ volatile smallint ok_to_redraw;
+
#if ENABLE_FEATURE_EDITING_VI
# define DELBUFSIZ 128
- CHAR_T *delptr;
smallint newdelflag; /* whether delbuf should be reused yet */
+ CHAR_T *delptr;
CHAR_T delbuf[DELBUFSIZ]; /* a place to store deleted characters */
#endif
#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
smallint sent_ESC_br6n;
#endif
+
+ /* Largish struct, keeping it last results in smaller code */
+ struct sigaction SIGWINCH_handler;
};
/* See lineedit_ptr_hack.c */
@@ -169,7 +179,6 @@ extern struct lineedit_statics *const lineedit_ptr_to_statics;
#define S (*lineedit_ptr_to_statics)
#define state (S.state )
#define cmdedit_termw (S.cmdedit_termw )
-#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
#define cmdedit_x (S.cmdedit_x )
#define cmdedit_y (S.cmdedit_y )
#define cmdedit_prmt_len (S.cmdedit_prmt_len)
@@ -221,7 +230,7 @@ static size_t load_string(const char *src)
return len;
} else {
unsigned i = 0;
- while (src[i] && i < (unsigned) S.maxsize - 1) {
+ while (src[i] && i < S.maxsize - 1) {
command_ps[i] = src[i];
i++;
}
@@ -431,21 +440,17 @@ static void beep(void)
static void put_prompt(void)
{
- unsigned w;
-
fputs(cmdedit_prompt, stdout);
- fflush_all();
cursor = 0;
- w = cmdedit_termw; /* read volatile var once */
- cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
- cmdedit_x = cmdedit_prmt_len % w;
+ cmdedit_y = cmdedit_prmt_len / cmdedit_termw; /* new quasireal y */
+ cmdedit_x = cmdedit_prmt_len % cmdedit_termw;
}
/* Move back one character */
/* (optimized for slow terminals) */
static void input_backward(unsigned num)
{
- if (num > (unsigned) cursor)
+ if (num > cursor)
num = cursor;
if (num == 0)
return;
@@ -500,7 +505,7 @@ static void input_backward(unsigned num)
* A simpler thing to do is to redraw everything from the start
* up to new cursor position (which is already known):
*/
- int sv_cursor;
+ unsigned sv_cursor;
/* go to 1st column; go up to first line */
printf("\r" ESC"[%uA", cmdedit_y);
cmdedit_y = 0;
@@ -510,13 +515,11 @@ static void input_backward(unsigned num)
put_cur_glyph_and_inc_cursor();
} else {
int lines_up;
- unsigned width;
/* num = chars to go back from the beginning of current line: */
num -= cmdedit_x;
- width = cmdedit_termw; /* read volatile var once */
/* num=1...w: one line up, w+1...2w: two, etc: */
- lines_up = 1 + (num - 1) / width;
- cmdedit_x = (width * cmdedit_y - num) % width;
+ lines_up = 1 + (num - 1) / cmdedit_termw;
+ cmdedit_x = (cmdedit_termw * cmdedit_y - num) % cmdedit_termw;
cmdedit_y -= lines_up;
/* go to 1st column; go up */
printf("\r" ESC"[%uA", lines_up);
@@ -672,23 +675,20 @@ static char *username_path_completion(char *ud)
*/
static NOINLINE unsigned complete_username(const char *ud)
{
- /* Using _r function to avoid pulling in static buffers */
- char line_buff[256];
- struct passwd pwd;
- struct passwd *result;
+ struct passwd *pw;
unsigned userlen;
ud++; /* skip ~ */
userlen = strlen(ud);
setpwent();
- while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
+ while ((pw = getpwent()) != NULL) {
/* Null usernames should result in all users as possible completions. */
- if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
- add_match(xasprintf("~%s/", pwd.pw_name));
+ if (/* !ud[0] || */ is_prefixed_with(pw->pw_name, ud)) {
+ add_match(xasprintf("~%s/", pw->pw_name));
}
}
- endpwent();
+ endpwent(); /* don't keep password file open */
return 1 + userlen;
}
@@ -777,6 +777,19 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
}
pf_len = strlen(pfind);
+#if ENABLE_FEATURE_SH_STANDALONE && NUM_APPLETS != 1
+ if (type == FIND_EXE_ONLY && !dirbuf) {
+ const char *p = applet_names;
+
+ while (*p) {
+ if (strncmp(pfind, p, pf_len) == 0)
+ add_match(xstrdup(p));
+ while (*p++ != '\0')
+ continue;
+ }
+ }
+#endif
+
for (i = 0; i < npaths; i++) {
DIR *dir;
struct dirent *next;
@@ -795,7 +808,7 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
if (!pfind[0] && DOT_OR_DOTDOT(name_found))
continue;
/* match? */
- if (strncmp(name_found, pfind, pf_len) != 0)
+ if (!is_prefixed_with(name_found, pfind))
continue; /* no */
found = concat_path_file(paths[i], name_found);
@@ -1076,7 +1089,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
char *match_buf;
size_t len_found;
/* Length of string used for matching */
- unsigned match_pfx_len = 0;
+ unsigned match_pfx_len = match_pfx_len;
int find_type;
# if ENABLE_UNICODE_SUPPORT
/* cursor pos in command converted to multibyte form */
@@ -1322,7 +1335,7 @@ static int get_next_history(void)
/* Lists command history. Used by shell 'history' builtins */
void FAST_FUNC show_history(const line_input_t *st)
{
- unsigned i;
+ int i;
if (!st)
return;
@@ -1424,8 +1437,7 @@ void save_history(line_input_t *st)
fp = fopen(st->hist_file, "a");
if (fp) {
- int fd;
- unsigned i;
+ int i, fd;
char *new_name;
line_input_t *st_temp;
@@ -1494,7 +1506,7 @@ static void save_history(char *str)
fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fd >= 0) {
FILE *fp;
- unsigned i;
+ int i;
fp = xfdopen_for_write(fd);
for (i = 0; i < st_temp->cnt_history; i++)
@@ -1515,7 +1527,7 @@ static void save_history(char *str)
static void remember_in_history(char *str)
{
- unsigned i;
+ int i;
if (!(state->flags & DO_HISTORY))
return;
@@ -1576,9 +1588,9 @@ vi_word_motion(int eat)
{
CHAR_T *command = command_ps;
- if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+ if (BB_isalnum_or_underscore(command[cursor])) {
while (cursor < command_len
- && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
+ && (BB_isalnum_or_underscore(command[cursor+1]))
) {
input_forward();
}
@@ -1620,9 +1632,9 @@ vi_end_motion(void)
input_forward();
if (cursor >= command_len-1)
return;
- if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+ if (BB_isalnum_or_underscore(command[cursor])) {
while (cursor < command_len-1
- && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
+ && (BB_isalnum_or_underscore(command[cursor+1]))
) {
input_forward();
}
@@ -1655,9 +1667,9 @@ vi_back_motion(void)
input_backward(1);
if (cursor <= 0)
return;
- if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+ if (BB_isalnum_or_underscore(command[cursor])) {
while (cursor > 0
- && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
+ && (BB_isalnum_or_underscore(command[cursor-1]))
) {
input_backward(1);
}
@@ -1883,15 +1895,16 @@ static void parse_and_put_prompt(const char *prmt_ptr)
cwd_buf = xrealloc_getcwd_or_warn(NULL);
if (!cwd_buf)
cwd_buf = (char *)bb_msg_unknown;
- else {
+ else if (home_pwd_buf[0]) {
+ char *after_home_user;
+
/* /home/user[/something] -> ~[/something] */
- l = strlen(home_pwd_buf);
- if (l != 0
- && strncmp(home_pwd_buf, cwd_buf, l) == 0
- && (cwd_buf[l] == '/' || cwd_buf[l] == '\0')
+ after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf);
+ if (after_home_user
+ && (*after_home_user == '/' || *after_home_user == '\0')
) {
cwd_buf[0] = '~';
- overlapping_strcpy(cwd_buf + 1, cwd_buf + l);
+ overlapping_strcpy(cwd_buf + 1, after_home_user);
}
}
}
@@ -1965,28 +1978,29 @@ static void parse_and_put_prompt(const char *prmt_ptr)
}
#endif
-static void cmdedit_setwidth(unsigned w, int redraw_flg)
+static void cmdedit_setwidth(void)
{
- cmdedit_termw = w;
- if (redraw_flg) {
- /* new y for current cursor */
- unsigned new_y = (cursor + cmdedit_prmt_len) / w;
- /* redraw */
- redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
- fflush_all();
- }
+ int new_y;
+
+ cmdedit_termw = get_terminal_width(STDIN_FILENO);
+ /* new y for current cursor */
+ new_y = (cursor + cmdedit_prmt_len) / cmdedit_termw;
+ /* redraw */
+ redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
}
-static void win_changed(int nsig)
+static void win_changed(int nsig UNUSED_PARAM)
{
- int sv_errno = errno;
- unsigned width;
-
- get_terminal_width_height(0, &width, NULL);
-//FIXME: cmdedit_setwidth() -> redraw() -> printf() -> KABOOM! (we are in signal handler!)
- cmdedit_setwidth(width, /*redraw_flg:*/ nsig);
-
- errno = sv_errno;
+ if (S.ok_to_redraw) {
+ /* We are in read_key(), safe to redraw immediately */
+ int sv_errno = errno;
+ cmdedit_setwidth();
+ fflush_all();
+ errno = sv_errno;
+ } else {
+ /* Signal main loop that redraw is necessary */
+ S.SIGWINCH_count++;
+ }
}
static int lineedit_read_key(char *read_key_buffer, int timeout)
@@ -1997,6 +2011,7 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
int unicode_idx = 0;
#endif
+ fflush_all();
while (1) {
/* Wait for input. TIMEOUT = -1 makes read_key wait even
* on nonblocking stdin, TIMEOUT = 50 makes sure we won't
@@ -2005,7 +2020,9 @@ static int lineedit_read_key(char *read_key_buffer, int timeout)
*
* Note: read_key sets errno to 0 on success.
*/
+ S.ok_to_redraw = 1;
ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
+ S.ok_to_redraw = 0;
if (errno) {
#if ENABLE_UNICODE_SUPPORT
if (errno == EAGAIN && unicode_idx != 0)
@@ -2136,7 +2153,6 @@ static int32_t reverse_i_search(void)
int h;
unsigned match_buf_len = strlen(match_buf);
- fflush_all();
//FIXME: correct timeout?
ic = lineedit_read_key(read_key_buffer, -1);
@@ -2238,6 +2254,7 @@ static int32_t reverse_i_search(void)
* Returns:
* -1 on read errors or EOF, or on bare Ctrl-D,
* 0 on ctrl-C (the line entered is still returned in 'command'),
+ * (in both cases the cursor remains on the input line, '\n' is not printed)
* >0 length of input string, including terminating '\n'
*/
int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
@@ -2257,11 +2274,15 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
INIT_S();
if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
- || !(initial_settings.c_lflag & ECHO)
+ || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON
) {
- /* Happens when e.g. stty -echo was run before */
+ /* Happens when e.g. stty -echo was run before.
+ * But if ICANON is not set, we don't come here.
+ * (example: interactive python ^Z-backgrounded,
+ * tty is still in "raw mode").
+ */
parse_and_put_prompt(prompt);
- /* fflush_all(); - done by parse_and_put_prompt */
+ fflush_all();
if (fgets(command, maxsize, stdin) == NULL)
len = -1; /* EOF or error */
else
@@ -2314,7 +2335,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
/* new_settings.c_cc[VINTR] = _POSIX_VDISABLE; */
tcsetattr_stdin_TCSANOW(&new_settings);
-#if ENABLE_USERNAME_OR_HOMEDIR
+/*#if ENABLE_USERNAME_OR_HOMEDIR
{
struct passwd *entry;
@@ -2324,7 +2345,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
home_pwd_buf = xstrdup(entry->pw_dir);
}
}
-#endif
+#endif*/
#if 0
for (i = 0; i <= state->max_history; i++)
@@ -2337,9 +2358,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
ask_terminal();
/* Install window resize handler (NB: after *all* init is complete) */
-//FIXME: save entire sigaction!
- previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
- win_changed(0); /* get initial window size */
+ S.SIGWINCH_handler.sa_handler = win_changed;
+ S.SIGWINCH_handler.sa_flags = SA_RESTART;
+ sigaction(SIGWINCH, &S.SIGWINCH_handler, &S.SIGWINCH_handler);
+
+ cmdedit_termw = get_terminal_width(STDIN_FILENO);
read_key_buffer[0] = 0;
while (1) {
@@ -2352,8 +2375,14 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
* in one place.
*/
int32_t ic, ic_raw;
+ unsigned count;
+
+ count = S.SIGWINCH_count;
+ if (S.SIGWINCH_saved != count) {
+ S.SIGWINCH_saved = count;
+ cmdedit_setwidth();
+ }
- fflush_all();
ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
#if ENABLE_FEATURE_REVERSE_SEARCH
@@ -2462,6 +2491,24 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
while (cursor > 0 && !BB_isspace(command_ps[cursor-1]))
input_backspace();
break;
+ case KEYCODE_ALT_D: {
+ /* Delete word forward */
+ int nc, sc = cursor;
+ ctrl_right();
+ nc = cursor - sc;
+ input_backward(nc);
+ while (--nc >= 0)
+ input_delete(1);
+ break;
+ }
+ case KEYCODE_ALT_BACKSPACE: {
+ /* Delete word backward */
+ int sc = cursor;
+ ctrl_left();
+ while (sc-- > cursor)
+ input_delete(1);
+ break;
+ }
#if ENABLE_FEATURE_REVERSE_SEARCH
case CTRL('R'):
ic = ic_raw = reverse_i_search();
@@ -2604,44 +2651,6 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
vi_cmdmode = 1;
input_backward(1);
}
- /* Handle a few ESC-<key> combinations the same way
- * standard readline bindings (IOW: bash) do.
- * Often, Alt-<key> generates ESC-<key>.
- */
- ic = lineedit_read_key(read_key_buffer, timeout);
- switch (ic) {
- //case KEYCODE_LEFT: - bash doesn't do this
- case 'b':
- ctrl_left();
- break;
- //case KEYCODE_RIGHT: - bash doesn't do this
- case 'f':
- ctrl_right();
- break;
- //case KEYCODE_DELETE: - bash doesn't do this
- case 'd': /* Alt-D */
- {
- /* Delete word forward */
- int nc, sc = cursor;
- ctrl_right();
- nc = cursor - sc;
- input_backward(nc);
- while (--nc >= 0)
- input_delete(1);
- break;
- }
- case '\b': /* Alt-Backspace(?) */
- case '\x7f': /* Alt-Backspace(?) */
- //case 'w': - bash doesn't do this
- {
- /* Delete word backward */
- int sc = cursor;
- ctrl_left();
- while (sc-- > cursor)
- input_delete(1);
- break;
- }
- }
break;
#endif /* FEATURE_COMMAND_EDITING_VI */
@@ -2689,7 +2698,6 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
&& ic_raw == initial_settings.c_cc[VINTR]
) {
/* Ctrl-C (usually) - stop gathering input */
- goto_new_line();
command_len = 0;
break_out = -1; /* "do not append '\n'" */
break;
@@ -2811,7 +2819,7 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
/* restore initial_settings */
tcsetattr_stdin_TCSANOW(&initial_settings);
/* restore SIGWINCH handler */
- signal(SIGWINCH, previous_SIGWINCH_handler);
+ sigaction_set(SIGWINCH, &S.SIGWINCH_handler);
fflush_all();
len = command_len;
diff --git a/libbb/logenv.c b/libbb/logenv.c
new file mode 100644
index 0000000..66c60bd
--- a/dev/null
+++ b/libbb/logenv.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2014 by Fugro Intersite B.V. <m.stam@fugro.nl>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+void FAST_FUNC bb_logenv_override(void)
+{
+ const char* mode = getenv("LOGGING");
+
+ if (!mode)
+ return;
+
+ if (strcmp(mode, "none") == 0)
+ logmode = LOGMODE_NONE;
+#if ENABLE_FEATURE_SYSLOG
+ else if (strcmp(mode, "syslog") == 0)
+ logmode = LOGMODE_SYSLOG;
+#endif
+}
diff --git a/libbb/login.c b/libbb/login.c
index 8f080b7..5a7acfc 100644
--- a/libbb/login.c
+++ b/libbb/login.c
@@ -45,6 +45,45 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
if (c == '\\' || c == '%') {
c = fgetc(fp);
switch (c) {
+//From getty manpage (* - supported by us)
+//========================================
+//4 or 4{interface}
+// Insert the IPv4 address of the network interface (example: \4{eth0}).
+// If the interface argument is not specified, then select the first
+// fully configured (UP, non-LOOPBACK, RUNNING) interface.
+//6 or 6{interface} -- The same as \4 but for IPv6.
+//b -- Insert the baudrate of the current line.
+//*d -- Insert the current date.
+//*t -- Insert the current time.
+//e or e{name}
+// Translate the human-readable name to an escape sequence and insert it
+// (for example: \e{red}Alert text.\e{reset}). If the name argument
+// is not specified, then insert \033. The currently supported names are:
+// black, blink, blue, bold, brown, cyan, darkgray, gray, green, halfbright,
+// lightblue, lightcyan, lightgray, lightgreen, lightmagenta, lightred,
+// magenta, red, reset, reverse, and yellow. Unknown names are ignored.
+//*s
+// Insert the system name (the name of the operating system - `uname -s`)
+//*S or S{VARIABLE}
+// Insert the VARIABLE data from /etc/os-release.
+// If the VARIABLE argument is not specified, use PRETTY_NAME.
+// If PRETTY_NAME is not in /etc/os-release, \S is the same as \s.
+//*l -- Insert the name of the current tty line.
+//*m -- Insert the architecture identifier of the machine: `uname -m`.
+//*n -- Insert the nodename of the machine: `uname -n`.
+//*o -- Insert the NIS domainname of the machine. Same as `hostname -d'.
+//*O -- Insert the DNS domainname of the machine.
+//*r -- Insert the release number of the OS: `uname -r`.
+//u -- Insert the number of current users logged in.
+//U -- Insert the string "1 user" or "N users" (current users logged in).
+//*v -- Insert the version of the OS, e.g. the build-date etc: `uname -v`.
+//We also implement:
+//*D -- same as \O "DNS domainname"
+//*h -- same as \n "nodename"
+
+ case 'S':
+ /* minimal implementation, not reading /etc/os-release */
+ /*FALLTHROUGH*/
case 's':
outbuf = uts.sysname;
break;
@@ -65,6 +104,7 @@ void FAST_FUNC print_login_issue(const char *issue_file, const char *tty)
#if defined(__linux__)
case 'D':
case 'o':
+ case 'O':
outbuf = uts.domainname;
break;
#endif
diff --git a/libbb/loop.c b/libbb/loop.c
index 823fba0..d30b378 100644
--- a/libbb/loop.c
+++ b/libbb/loop.c
@@ -94,19 +94,19 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
/* Open the file. Barf if this doesn't work. */
mode = ro ? O_RDONLY : O_RDWR;
+ open_ffd:
ffd = open(file, mode);
if (ffd < 0) {
if (mode != O_RDONLY) {
mode = O_RDONLY;
- ffd = open(file, mode);
+ goto open_ffd;
}
- if (ffd < 0)
- return -errno;
+ return -errno;
}
/* Find a loop device. */
try = *device ? *device : dev;
- /* 1048575 is a max possible minor number in Linux circa 2010 */
+ /* 1048575 (0xfffff) is a max possible minor number in Linux circa 2010 */
for (i = 0; rc && i < 1048576; i++) {
sprintf(dev, LOOP_FORMAT, i);
@@ -121,7 +121,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
goto try_to_open;
}
/* Ran out of block devices, return failure. */
- rc = -ENOENT;
+ rc = -1;
break;
}
try_to_open:
@@ -131,8 +131,14 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
mode = O_RDONLY;
dfd = open(try, mode);
}
- if (dfd < 0)
+ if (dfd < 0) {
+ if (errno == ENXIO) {
+ /* Happens if loop module is not loaded */
+ rc = -1;
+ break;
+ }
goto try_again;
+ }
rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo);
@@ -148,16 +154,7 @@ int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offse
else
ioctl(dfd, LOOP_CLR_FD, 0);
}
-
- /* If this block device already set up right, re-use it.
- * (Yes this is racy, but associating two loop devices with the same
- * file isn't pretty either. In general, mounting the same file twice
- * without using losetup manually is problematic.)
- */
- } else
- if (strcmp(file, (char *)loopinfo.lo_file_name) != 0
- || offset != loopinfo.lo_offset
- ) {
+ } else {
rc = -1;
}
close(dfd);
diff --git a/libbb/make_directory.c b/libbb/make_directory.c
index 7826b90..a6b7c28 100644
--- a/libbb/make_directory.c
+++ b/libbb/make_directory.c
@@ -35,9 +35,20 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
char c;
struct stat st;
- /* Happens on bb_make_directory(dirname("no_slashes"),...) */
- if (LONE_CHAR(path, '.'))
+ /* "path" can be a result of dirname().
+ * dirname("no_slashes") returns ".", possibly read-only.
+ * musl dirname() can return read-only "/" too.
+ * We need writable string. And for "/", "." (and ".."?)
+ * nothing needs to be created anyway.
+ */
+ if (LONE_CHAR(path, '/'))
return 0;
+ if (path[0] == '.') {
+ if (path[1] == '\0')
+ return 0; /* "." */
+// if (path[1] == '.' && path[2] == '\0')
+// return 0; /* ".." */
+ }
org_mask = cur_mask = (mode_t)-1L;
s = path;
@@ -99,6 +110,10 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
if (!c) {
goto ret0;
}
+ } else {
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("created directory: '%s'\n", path);
+ }
}
if (!c) {
diff --git a/libbb/match_fstype.c b/libbb/match_fstype.c
index 32c3d7f..b066b42 100644
--- a/libbb/match_fstype.c
+++ b/libbb/match_fstype.c
@@ -17,7 +17,6 @@
int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
{
int match = 1;
- int len;
if (!t_fstype)
return match;
@@ -27,10 +26,10 @@ int FAST_FUNC match_fstype(const struct mntent *mt, const char *t_fstype)
t_fstype += 2;
}
- len = strlen(mt->mnt_type);
while (1) {
- if (strncmp(mt->mnt_type, t_fstype, len) == 0
- && (t_fstype[len] == '\0' || t_fstype[len] == ',')
+ char *after_mnt_type = is_prefixed_with(t_fstype, mt->mnt_type);
+ if (after_mnt_type
+ && (*after_mnt_type == '\0' || *after_mnt_type == ',')
) {
return match;
}
diff --git a/libbb/messages.c b/libbb/messages.c
index 1ce84cf..00d8b96 100644
--- a/libbb/messages.c
+++ b/libbb/messages.c
@@ -29,7 +29,7 @@ const char bb_msg_can_not_create_raw_socket[] ALIGN1 = "can't create raw socket"
const char bb_msg_perm_denied_are_you_root[] ALIGN1 = "permission denied (are you root?)";
const char bb_msg_you_must_be_root[] ALIGN1 = "you must be root";
const char bb_msg_requires_arg[] ALIGN1 = "%s requires an argument";
-const char bb_msg_invalid_arg[] ALIGN1 = "invalid argument '%s' to '%s'";
+const char bb_msg_invalid_arg_to[] ALIGN1 = "invalid argument '%s' to '%s'";
const char bb_msg_standard_input[] ALIGN1 = "standard input";
const char bb_msg_standard_output[] ALIGN1 = "standard output";
@@ -47,7 +47,7 @@ const char bb_PATH_root_path[] ALIGN1 =
#endif
-const int const_int_1 = 1;
+//const int const_int_1 = 1;
/* explicitly = 0, otherwise gcc may make it a common variable
* and it will end up in bss */
const int const_int_0 = 0;
@@ -63,8 +63,3 @@ const char bb_path_wtmp_file[] ALIGN1 =
# error unknown path to wtmp file
# endif
#endif
-
-/* We use it for "global" data via *(struct global*)&bb_common_bufsiz1.
- * Since gcc insists on aligning struct global's members, it would be a pity
- * (and an alignment fault on some CPUs) to mess it up. */
-char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long));
diff --git a/libbb/missing_syscalls.c b/libbb/missing_syscalls.c
index dd430e3..0934128 100644
--- a/libbb/missing_syscalls.c
+++ b/libbb/missing_syscalls.c
@@ -39,4 +39,11 @@ int pivot_root(const char *new_root, const char *put_old)
{
return syscall(__NR_pivot_root, new_root, put_old);
}
+
+# if __ANDROID_API__ < 21
+int tcdrain(int fd)
+{
+ return ioctl(fd, TCSBRK, 1);
+}
+# endif
#endif
diff --git a/libbb/mode_string.c b/libbb/mode_string.c
index f1afe7d..934eb6d 100644
--- a/libbb/mode_string.c
+++ b/libbb/mode_string.c
@@ -87,9 +87,9 @@ const char* FAST_FUNC bb_mode_string(mode_t mode)
/* The previous version used "0pcCd?bB-?l?s???". However, the '0', 'C',
* and 'B' types don't appear to be available on linux. So I removed them. */
-static const char type_chars[16] = "?pc?d?b?-?l?s???";
+static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???";
/********************************** 0123456789abcdef */
-static const char mode_chars[7] = "rwxSTst";
+static const char mode_chars[7] ALIGN1 = "rwxSTst";
const char* FAST_FUNC bb_mode_string(mode_t mode)
{
diff --git a/libbb/obscure.c b/libbb/obscure.c
index 9ecc1f6..ad17d1f 100644
--- a/libbb/obscure.c
+++ b/libbb/obscure.c
@@ -76,7 +76,7 @@ static int string_checker(const char *p1, const char *p2)
ret |= string_checker_helper(p, p2);
/* clean up */
- memset(p, 0, size);
+ nuke_str(p);
free(p);
return ret;
@@ -182,3 +182,41 @@ int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *
}
return 0;
}
+
+#if ENABLE_UNIT_TEST
+
+/* Test obscure_msg() instead of obscure() in order not to print anything. */
+
+static const struct passwd pw = {
+ .pw_name = (char *)"johndoe",
+ .pw_gecos = (char *)"John Doe",
+};
+
+BBUNIT_DEFINE_TEST(obscure_weak_pass)
+{
+ /* Empty password */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "", &pw));
+ /* Pure numbers */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "23577315", &pw));
+ /* Similar to pw_name */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "johndoe123%", &pw));
+ /* Similar to pw_gecos, reversed */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "eoD nhoJ^44@", &pw));
+ /* Similar to the old password */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "d4#21?'S", &pw));
+ /* adjacent letters */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "qwerty123", &pw));
+ /* Many similar chars */
+ BBUNIT_ASSERT_NOTNULL(obscure_msg("Ad4#21?'S|", "^33Daaaaaa1", &pw));
+
+ BBUNIT_ENDTEST;
+}
+
+BBUNIT_DEFINE_TEST(obscure_strong_pass)
+{
+ BBUNIT_ASSERT_NULL(obscure_msg("Rt4##2&:'|", "}(^#rrSX3S*22", &pw));
+
+ BBUNIT_ENDTEST;
+}
+
+#endif /* ENABLE_UNIT_TEST */
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index e3840dc..e18e2dc 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -42,8 +42,8 @@ int parse_main(int argc UNUSED_PARAM, char **argv)
int mintokens = 0, ntokens = 128;
unsigned noout;
- opt_complementary = "-1:n+:m+:f+";
- noout = 1 & getopt32(argv, "xn:m:d:f:", &ntokens, &mintokens, &delims, &flags);
+ opt_complementary = "-1";
+ noout = 1 & getopt32(argv, "xn:+m:+d:f:+", &ntokens, &mintokens, &delims, &flags);
//argc -= optind;
argv += optind;
diff --git a/libbb/parse_mode.c b/libbb/parse_mode.c
index 5a4e1c5..bddd39b 100644
--- a/libbb/parse_mode.c
+++ b/libbb/parse_mode.c
@@ -15,7 +15,7 @@
#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
-int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
+int FAST_FUNC bb_parse_mode(const char *s, unsigned current_mode)
{
static const mode_t who_mask[] = {
S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
@@ -46,13 +46,12 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
tmp = strtoul(s, &e, 8);
if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
- return 0;
+ return -1;
}
- *current_mode = tmp;
- return 1;
+ return tmp;
}
- new_mode = *current_mode;
+ new_mode = current_mode;
/* Note: we allow empty clauses, and hence empty modes.
* We treat an empty mode as no change to perms. */
@@ -71,7 +70,7 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
if (*p == *s) {
wholist |= who_mask[(int)(p-who_chars)];
if (!*++s) {
- return 0;
+ return -1;
}
goto WHO_LIST;
}
@@ -80,7 +79,7 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
do { /* Process action list. */
if ((*s != '+') && (*s != '-')) {
if (*s != '=') {
- return 0;
+ return -1;
}
/* Since op is '=', clear all bits corresponding to the
* wholist, or all file bits if wholist is empty. */
@@ -145,6 +144,5 @@ int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
} while (*s && (*s != ','));
}
- *current_mode = new_mode;
- return 1;
+ return new_mode;
}
diff --git a/libbb/platform.c b/libbb/platform.c
index 5fcd448..04ef246 100644
--- a/libbb/platform.c
+++ b/libbb/platform.c
@@ -17,6 +17,24 @@ char* FAST_FUNC strchrnul(const char *s, int c)
}
#endif
+#ifndef HAVE_USLEEP
+int FAST_FUNC usleep(unsigned usec)
+{
+ struct timespec ts;
+ ts.tv_sec = usec / 1000000u;
+ ts.tv_nsec = (usec % 1000000u) * 1000u;
+ /*
+ * If a signal has non-default handler, nanosleep returns early.
+ * Our version of usleep doesn't return early
+ * if interrupted by such signals:
+ *
+ */
+ while (nanosleep(&ts, &ts) != 0)
+ continue;
+ return 0;
+}
+#endif
+
#ifndef HAVE_VASPRINTF
int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p)
{
@@ -179,3 +197,22 @@ ssize_t FAST_FUNC getline(char **lineptr, size_t *n, FILE *stream)
return len;
}
#endif
+
+#ifndef HAVE_TTYNAME_R
+int ttyname_r(int fd, char *buf, size_t buflen)
+{
+ int r;
+ char path[sizeof("/proc/self/fd/%d") + sizeof(int)*3];
+
+ if (!isatty(fd))
+ return errno == EINVAL ? ENOTTY : errno;
+ sprintf(path, "/proc/self/fd/%d", fd);
+ r = readlink(path, buf, buflen);
+ if (r < 0)
+ return errno;
+ if (r >= buflen)
+ return ERANGE;
+ buf[r] = '\0';
+ return 0;
+}
+#endif
diff --git a/libbb/printable_string.c b/libbb/printable_string.c
index a316f60..077d58d 100644
--- a/libbb/printable_string.c
+++ b/libbb/printable_string.c
@@ -11,9 +11,6 @@
const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
{
- static char *saved[4];
- static unsigned cur_saved; /* = 0 */
-
char *dst;
const char *s;
@@ -56,10 +53,5 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
}
}
#endif
-
- free(saved[cur_saved]);
- saved[cur_saved] = dst;
- cur_saved = (cur_saved + 1) & (ARRAY_SIZE(saved)-1);
-
- return dst;
+ return auto_string(dst);
}
diff --git a/libbb/procps.c b/libbb/procps.c
index 5b68d34..4edc54d 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -205,11 +205,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
// Rss: nnn kB
// .....
- char *tp = buf, *p;
+ char *tp, *p;
#define SCAN(S, X) \
- if (strncmp(tp, S, sizeof(S)-1) == 0) { \
- tp = skip_whitespace(tp + sizeof(S)-1); \
+ if ((tp = is_prefixed_with(buf, S)) != NULL) { \
+ tp = skip_whitespace(tp); \
total->X += currec.X = fast_strtoul_10(&tp); \
continue; \
}
@@ -247,7 +247,7 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
// skipping "rw-s FILEOFS M:m INODE "
tp = skip_whitespace(skip_fields(tp, 4));
// filter out /dev/something (something != zero)
- if (strncmp(tp, "/dev/", 5) != 0 || strcmp(tp, "/dev/zero\n") == 0) {
+ if (!is_prefixed_with(tp, "/dev/") || strcmp(tp, "/dev/zero\n") == 0) {
if (currec.smap_mode[1] == 'w') {
currec.mapped_rw = currec.smap_size;
total->mapped_rw += currec.smap_size;
@@ -283,7 +283,6 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
}
#endif
-void BUG_comm_size(void);
procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
{
if (!sp)
@@ -385,8 +384,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
/*if (!cp || cp[1] != ' ')
continue;*/
cp[0] = '\0';
- if (sizeof(sp->comm) < 16)
- BUG_comm_size();
+ BUILD_BUG_ON(sizeof(sp->comm) < 16);
comm1 = strchr(buf, '(');
/*if (comm1)*/
safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
@@ -497,8 +495,8 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
while (fgets(buf, sizeof(buf), file)) {
char *tp;
#define SCAN_TWO(str, name, statement) \
- if (strncmp(buf, str, sizeof(str)-1) == 0) { \
- tp = skip_whitespace(buf + sizeof(str)-1); \
+ if ((tp = is_prefixed_with(buf, str)) != NULL) { \
+ tp = skip_whitespace(tp); \
sscanf(tp, "%u", &sp->name); \
statement; \
}
@@ -554,8 +552,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
break;
if (flags & PSSCAN_ARGVN) {
sp->argv_len = n;
- sp->argv0 = xmalloc(n + 1);
- memcpy(sp->argv0, buf, n + 1);
+ sp->argv0 = xmemdup(buf, n + 1);
/* sp->argv0[n] = '\0'; - buf has it */
} else {
sp->argv_len = 0;
@@ -591,12 +588,14 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
buf[sz] = ' ';
sz--;
}
+ if (base[0] == '-') /* "-sh" (login shell)? */
+ base++;
/* If comm differs from argv0, prepend "{comm} ".
* It allows to see thread names set by prctl(PR_SET_NAME).
*/
- if (base[0] == '-') /* "-sh" (login shell)? */
- base++;
+ if (!comm)
+ return;
comm_len = strlen(comm);
/* Why compare up to comm_len, not COMM_LEN-1?
* Well, some processes rewrite argv, and use _spaces_ there
@@ -614,9 +613,8 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
buf[comm_len - 1] = ' ';
buf[col - 1] = '\0';
}
-
} else {
- snprintf(buf, col, "[%s]", comm);
+ snprintf(buf, col, "[%s]", comm ? comm : "?");
}
}
diff --git a/libbb/progress.c b/libbb/progress.c
index 372feb0..3c2f016 100644
--- a/libbb/progress.c
+++ b/libbb/progress.c
@@ -45,13 +45,6 @@ enum {
STALLTIME = 5
};
-static unsigned int get_tty2_width(void)
-{
- unsigned width;
- get_terminal_width_height(2, &width, NULL);
- return width;
-}
-
void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile)
{
#if ENABLE_UNICODE_SUPPORT
@@ -80,7 +73,7 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p,
{
uoff_t beg_and_transferred;
unsigned since_last_update, elapsed;
- int barlength;
+ int notty;
int kiloscale;
//transferred = 1234; /* use for stall detection testing */
@@ -137,18 +130,21 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p,
}
}
+ notty = !isatty(STDERR_FILENO);
+
if (ENABLE_UNICODE_SUPPORT)
- fprintf(stderr, "\r%s", p->curfile);
+ fprintf(stderr, "\r%s" + notty, p->curfile);
else
- fprintf(stderr, "\r%-20.20s", p->curfile);
+ fprintf(stderr, "\r%-20.20s" + notty, p->curfile);
beg_and_transferred = beg_size + transferred;
if (totalsize != 0) {
+ int barlength;
unsigned ratio = 100 * beg_and_transferred / totalsize;
fprintf(stderr, "%4u%%", ratio);
- barlength = get_tty2_width() - 49;
+ barlength = get_terminal_width(2) - 49;
if (barlength > 0) {
/* god bless gcc for variable arrays :) */
char buf[barlength + 1];
@@ -204,4 +200,6 @@ void FAST_FUNC bb_progress_update(bb_progress_t *p,
hours = eta / 3600;
fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60);
}
+ if (notty)
+ fputc('\n', stderr);
}
diff --git a/libbb/pw_encrypt.c b/libbb/pw_encrypt.c
index 39ffa08..4cdc2de 100644
--- a/libbb/pw_encrypt.c
+++ b/libbb/pw_encrypt.c
@@ -9,7 +9,7 @@
#include "libbb.h"
-/* static const uint8_t ascii64[] =
+/* static const uint8_t ascii64[] ALIGN1 =
* "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
*/
@@ -52,14 +52,18 @@ char* FAST_FUNC crypt_make_pw_salt(char salt[MAX_PW_SALT_LEN], const char *algo)
{
int len = 2/2;
char *salt_ptr = salt;
- if (algo[0] != 'd') { /* not des */
+
+ /* Standard chpasswd uses uppercase algos ("MD5", not "md5").
+ * Need to be case-insensitive in the code below.
+ */
+ if ((algo[0]|0x20) != 'd') { /* not des */
len = 8/2; /* so far assuming md5 */
*salt_ptr++ = '$';
*salt_ptr++ = '1';
*salt_ptr++ = '$';
#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
- if (algo[0] == 's') { /* sha */
- salt[1] = '5' + (strcmp(algo, "sha512") == 0);
+ if ((algo[0]|0x20) == 's') { /* sha */
+ salt[1] = '5' + (strcasecmp(algo, "sha512") == 0);
len = 16/2;
}
#endif
@@ -142,7 +146,14 @@ char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup)
char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup)
{
- return xstrdup(crypt(clear, salt));
+ char *s;
+
+ s = crypt(clear, salt);
+ /*
+ * glibc used to return "" on malformed salts (for example, ""),
+ * but since 2.17 it returns NULL.
+ */
+ return xstrdup(s ? s : "");
}
#endif
diff --git a/libbb/read_key.c b/libbb/read_key.c
index ace23de..9517868 100644
--- a/libbb/read_key.c
+++ b/libbb/read_key.c
@@ -18,8 +18,20 @@ int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
/* Known escape sequences for cursor and function keys.
* See "Xterm Control Sequences"
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * Array should be sorted from shortest to longest.
*/
static const char esccmds[] ALIGN1 = {
+ '\x7f' |0x80,KEYCODE_ALT_BACKSPACE,
+ '\b' |0x80,KEYCODE_ALT_BACKSPACE,
+ 'd' |0x80,KEYCODE_ALT_D ,
+ /* lineedit mimics bash: Alt-f and Alt-b are forward/backward
+ * word jumps. We cheat here and make them return ALT_LEFT/RIGHT
+ * keycodes. This way, lineedit need no special code to handle them.
+ * If we'll need to distinguish them, introduce new ALT_F/B keycodes,
+ * and update lineedit to react to them.
+ */
+ 'f' |0x80,KEYCODE_ALT_RIGHT,
+ 'b' |0x80,KEYCODE_ALT_LEFT,
'O','A' |0x80,KEYCODE_UP ,
'O','B' |0x80,KEYCODE_DOWN ,
'O','C' |0x80,KEYCODE_RIGHT ,
diff --git a/libbb/read_printf.c b/libbb/read_printf.c
index 5ed6e36..b6a17cc 100644
--- a/libbb/read_printf.c
+++ b/libbb/read_printf.c
@@ -45,20 +45,20 @@
* which detects EAGAIN and uses poll() to wait on the fd.
* Thankfully, poll() doesn't care about O_NONBLOCK flag.
*/
-ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR)
+ssize_t FAST_FUNC nonblock_immune_read(int fd, void *buf, size_t count)
{
struct pollfd pfd[1];
ssize_t n;
while (1) {
- n = loop_on_EINTR ? safe_read(fd, buf, count) : read(fd, buf, count);
+ n = safe_read(fd, buf, count);
if (n >= 0 || errno != EAGAIN)
return n;
/* fd is in O_NONBLOCK mode. Wait using poll and repeat */
pfd[0].fd = fd;
pfd[0].events = POLLIN;
/* note: safe_poll pulls in printf */
- loop_on_EINTR ? safe_poll(pfd, 1, -1) : poll(pfd, 1, -1);
+ safe_poll(pfd, 1, -1);
}
}
@@ -81,7 +81,7 @@ char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p)
p = buf + sz;
sz += 128;
}
- if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) {
+ if (nonblock_immune_read(fd, p, 1) != 1) {
/* EOF/error */
if (p == buf) { /* we read nothing */
free(buf);
diff --git a/libbb/remove_file.c b/libbb/remove_file.c
index 5b75f7f..eaca293 100644
--- a/libbb/remove_file.c
+++ b/libbb/remove_file.c
@@ -78,6 +78,10 @@ int FAST_FUNC remove_file(const char *path, int flags)
return -1;
}
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("removed directory: '%s'\n", path);
+ }
+
return status;
}
@@ -98,5 +102,9 @@ int FAST_FUNC remove_file(const char *path, int flags)
return -1;
}
+ if (flags & FILEUTILS_VERBOSE) {
+ printf("removed '%s'\n", path);
+ }
+
return 0;
}
diff --git a/libbb/replace.c b/libbb/replace.c
new file mode 100644
index 0000000..8711f95
--- a/dev/null
+++ b/libbb/replace.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-y += replace.o
+
+#include "libbb.h"
+
+unsigned FAST_FUNC count_strstr(const char *str, const char *sub)
+{
+ size_t sub_len = strlen(sub);
+ unsigned count = 0;
+
+ while ((str = strstr(str, sub)) != NULL) {
+ count++;
+ str += sub_len;
+ }
+ return count;
+}
+
+char* FAST_FUNC xmalloc_substitute_string(const char *src, int count, const char *sub, const char *repl)
+{
+ char *buf, *dst, *end;
+ size_t sub_len = strlen(sub);
+ size_t repl_len = strlen(repl);
+
+ //dbg_msg("subst(s:'%s',count:%d,sub:'%s',repl:'%s'", src, count, sub, repl);
+
+ buf = dst = xmalloc(strlen(src) + count * ((int)repl_len - (int)sub_len) + 1);
+ /* we replace each sub with repl */
+ while ((end = strstr(src, sub)) != NULL) {
+ dst = mempcpy(dst, src, end - src);
+ dst = mempcpy(dst, repl, repl_len);
+ /*src = end + 1; - GNU findutils 4.5.10 doesn't do this... */
+ src = end + sub_len; /* but this. Try "xargs -Iaa echo aaa" */
+ }
+ strcpy(dst, src);
+ //dbg_msg("subst9:'%s'", buf);
+ return buf;
+}
diff --git a/libbb/rtc.c b/libbb/rtc.c
index c31f848..488149d 100644
--- a/libbb/rtc.c
+++ b/libbb/rtc.c
@@ -22,7 +22,7 @@ int FAST_FUNC rtc_adjtime_is_utc(void)
char buffer[128];
while (fgets(buffer, sizeof(buffer), f)) {
- if (strncmp(buffer, "UTC", 3) == 0) {
+ if (is_prefixed_with(buffer, "UTC")) {
utc = 1;
break;
}
@@ -33,23 +33,55 @@ int FAST_FUNC rtc_adjtime_is_utc(void)
return utc;
}
+/* rtc opens are exclusive.
+ * Try to run two "hwclock -w" at the same time to see it.
+ * Users wouldn't expect that to fail merely because /dev/rtc
+ * was momentarily busy, let's try a bit harder on errno == EBUSY.
+ */
+static int open_loop_on_busy(const char *name, int flags)
+{
+ int rtc;
+ /*
+ * Tested with two parallel "hwclock -w" loops.
+ * With try = 10, no failures with 2x1000000 loop iterations.
+ */
+ int try = 1000 / 20;
+ again:
+ errno = 0;
+ rtc = open(name, flags);
+ if (errno == EBUSY) {
+ usleep(20 * 1000);
+ if (--try != 0)
+ goto again;
+ /* EBUSY. Last try, exit on error instead of returning -1 */
+ return xopen(name, flags);
+ }
+ return rtc;
+}
+
+/* Never fails */
int FAST_FUNC rtc_xopen(const char **default_rtc, int flags)
{
int rtc;
+ const char *name =
+ "/dev/rtc""\0"
+ "/dev/rtc0""\0"
+ "/dev/misc/rtc""\0";
- if (!*default_rtc) {
- *default_rtc = "/dev/rtc";
- rtc = open(*default_rtc, flags);
- if (rtc >= 0)
- return rtc;
- *default_rtc = "/dev/rtc0";
- rtc = open(*default_rtc, flags);
+ if (!*default_rtc)
+ goto try_name;
+ name = ""; /*else: we have rtc name, don't try other names */
+
+ for (;;) {
+ rtc = open_loop_on_busy(*default_rtc, flags);
if (rtc >= 0)
return rtc;
- *default_rtc = "/dev/misc/rtc";
+ if (!name[0])
+ return xopen(*default_rtc, flags);
+ try_name:
+ *default_rtc = name;
+ name += strlen(name) + 1;
}
-
- return xopen(*default_rtc, flags);
}
void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd)
diff --git a/libbb/run_shell.c b/libbb/run_shell.c
index 9494f27..93ab816 100644
--- a/libbb/run_shell.c
+++ b/libbb/run_shell.c
@@ -52,19 +52,17 @@ void FAST_FUNC set_current_security_context(security_context_t sid)
#endif
/* Run SHELL, or DEFAULT_SHELL if SHELL is "" or NULL.
- * If COMMAND is nonzero, pass it to the shell with the -c option.
- * If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
- * arguments. */
-void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, const char **additional_args)
+ * If ADDITIONAL_ARGS is not NULL, pass them to the shell.
+ */
+void FAST_FUNC run_shell(const char *shell, int loginshell, const char **additional_args)
{
const char **args;
- int argno;
- int additional_args_cnt = 0;
- for (args = additional_args; args && *args; args++)
- additional_args_cnt++;
+ args = additional_args;
+ while (args && *args)
+ args++;
- args = xmalloc(sizeof(char*) * (4 + additional_args_cnt));
+ args = xmalloc(sizeof(char*) * (2 + (args - additional_args)));
if (!shell || !shell[0])
shell = DEFAULT_SHELL;
@@ -72,16 +70,13 @@ void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command,
args[0] = bb_get_last_path_component_nostrip(shell);
if (loginshell)
args[0] = xasprintf("-%s", args[0]);
- argno = 1;
- if (command) {
- args[argno++] = "-c";
- args[argno++] = command;
- }
+ args[1] = NULL;
if (additional_args) {
- for (; *additional_args; ++additional_args)
- args[argno++] = *additional_args;
+ int cnt = 1;
+ for (;;)
+ if ((args[cnt++] = *additional_args++) == NULL)
+ break;
}
- args[argno] = NULL;
#if ENABLE_SELINUX
renew_current_security_context();
diff --git a/libbb/skip_whitespace.c b/libbb/skip_whitespace.c
index 8c7b674..b6cfbba 100644
--- a/libbb/skip_whitespace.c
+++ b/libbb/skip_whitespace.c
@@ -33,7 +33,7 @@ char* FAST_FUNC skip_non_whitespace(const char *s)
char* FAST_FUNC skip_dev_pfx(const char *tty_name)
{
- if (strncmp(tty_name, "/dev/", 5) == 0)
+ if (is_prefixed_with(tty_name, "/dev/"))
tty_name += 5;
return (char*)tty_name;
}
diff --git a/libbb/speed_table.c b/libbb/speed_table.c
index 45159f1..11ced01 100644
--- a/libbb/speed_table.c
+++ b/libbb/speed_table.c
@@ -10,10 +10,25 @@
#include "libbb.h"
struct speed_map {
+#if defined __FreeBSD__ \
+ || (defined B115200 && B115200 > 0xffff) \
+ || (defined B230400 && B230400 > 0xffff) \
+ || (defined B460800 && B460800 > 0xffff) \
+ || (defined B921600 && B921600 > 0xffff) \
+ || (defined B1152000 && B1152000 > 0xffff) \
+ || (defined B1000000 && B1000000 > 0xffff) \
+ || (defined B2000000 && B2000000 > 0xffff) \
+ || (defined B3000000 && B3000000 > 0xffff) \
+ || (defined B4000000 && B4000000 > 0xffff)
+ /* On FreeBSD, B<num> constants don't fit into a short */
+ unsigned speed;
+#else
unsigned short speed;
+#endif
unsigned short value;
};
+/* On Linux, Bxx constants are 0..15 (up to B38400) and 0x1001..0x100f */
static const struct speed_map speeds[] = {
{B0, 0},
{B50, 50},
@@ -32,30 +47,79 @@ static const struct speed_map speeds[] = {
#ifdef B19200
{B19200, 19200},
#elif defined(EXTA)
- {EXTA, 19200},
+ {EXTA, 19200},
#endif
+/* 19200 = 0x4b00 */
+/* 38400 = 0x9600, this value would use bit#15 if not "/200" encoded: */
#ifdef B38400
- {B38400, 38400/256 + 0x8000U},
+ {B38400, 38400/200 + 0x8000u},
#elif defined(EXTB)
- {EXTB, 38400/256 + 0x8000U},
+ {EXTB, 38400/200 + 0x8000u},
#endif
#ifdef B57600
- {B57600, 57600/256 + 0x8000U},
+ {B57600, 57600/200 + 0x8000u},
#endif
#ifdef B115200
- {B115200, 115200/256 + 0x8000U},
+ {B115200, 115200/200 + 0x8000u},
#endif
#ifdef B230400
- {B230400, 230400/256 + 0x8000U},
+ {B230400, 230400/200 + 0x8000u},
#endif
#ifdef B460800
- {B460800, 460800/256 + 0x8000U},
+ {B460800, 460800/200 + 0x8000u},
+#endif
+#ifdef B576000
+ {B576000, 576000/200 + 0x8000u},
#endif
#ifdef B921600
- {B921600, 921600/256 + 0x8000U},
+ {B921600, 921600/200 + 0x8000u},
+#endif
+#ifdef B1152000
+ {B1152000, 1152000/200 + 0x8000u},
+#endif
+
+#ifdef B500000
+ {B500000, 500000/200 + 0x8000u},
+#endif
+#ifdef B1000000
+ {B1000000, 1000000/200 + 0x8000u},
+#endif
+#ifdef B1500000
+ {B1500000, 1500000/200 + 0x8000u},
+#endif
+#ifdef B2000000
+ {B2000000, 2000000/200 + 0x8000u},
#endif
+#ifdef B2500000
+ {B2500000, 2500000/200 + 0x8000u},
+#endif
+#ifdef B3000000
+ {B3000000, 3000000/200 + 0x8000u},
+#endif
+#ifdef B3500000
+ {B3500000, 3500000/200 + 0x8000u},
+#endif
+#ifdef B4000000
+ {B4000000, 4000000/200 + 0x8000u},
+#endif
+/* 4000000/200 = 0x4e20, bit#15 still does not interfere with the value */
+/* (can use /800 if higher speeds would appear, /1600 won't work for B500000) */
};
+/*
+ * TODO: maybe we can just bite the bullet, ditch the table and use termios2
+ * Linux API (supports arbitrary baud rates, no Bxxxx mess needed)? Example:
+ *
+ * #include <asm/termios.h>
+ * #include <asm/ioctls.h>
+ * struct termios2 t;
+ * ioctl(fd, TCGETS2, &t);
+ * t.c_ospeed = t.c_ispeed = 543210;
+ * t.c_cflag &= ~CBAUD;
+ * t.c_cflag |= BOTHER;
+ * ioctl(fd, TCSETS2, &t);
+ */
+
enum { NUM_SPEEDS = ARRAY_SIZE(speeds) };
unsigned FAST_FUNC tty_baud_to_value(speed_t speed)
@@ -64,8 +128,8 @@ unsigned FAST_FUNC tty_baud_to_value(speed_t speed)
do {
if (speed == speeds[i].speed) {
- if (speeds[i].value & 0x8000U) {
- return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256;
+ if (speeds[i].value & 0x8000u) {
+ return ((unsigned)(speeds[i].value) & 0x7fffU) * 200;
}
return speeds[i].value;
}
diff --git a/libbb/strrstr.c b/libbb/strrstr.c
index d8823fc..93d970a 100644
--- a/libbb/strrstr.c
+++ b/libbb/strrstr.c
@@ -7,13 +7,7 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-#ifdef __DO_STRRSTR_TEST
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#else
#include "libbb.h"
-#endif
/*
* The strrstr() function finds the last occurrence of the substring needle
@@ -34,8 +28,9 @@ char* FAST_FUNC strrstr(const char *haystack, const char *needle)
}
}
-#ifdef __DO_STRRSTR_TEST
-int main(int argc, char **argv)
+#if ENABLE_UNIT_TEST
+
+BBUNIT_DEFINE_TEST(strrstr)
{
static const struct {
const char *h, *n;
@@ -59,13 +54,13 @@ int main(int argc, char **argv)
i = 0;
while (i < sizeof(test_array) / sizeof(test_array[0])) {
const char *r = strrstr(test_array[i].h, test_array[i].n);
- printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r);
if (r == NULL)
r = test_array[i].h - 1;
- printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED");
+ BBUNIT_ASSERT_EQ(r, test_array[i].h + test_array[i].pos);
i++;
}
- return 0;
+ BBUNIT_ENDTEST;
}
-#endif
+
+#endif /* ENABLE_UNIT_TEST */
diff --git a/libbb/sysconf.c b/libbb/sysconf.c
new file mode 100644
index 0000000..8c1caef
--- a/dev/null
+++ b/libbb/sysconf.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Various system configuration helpers.
+ *
+ * Copyright (C) 2014 Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+#if !defined(bb_arg_max)
+unsigned FAST_FUNC bb_arg_max(void)
+{
+ long r = sysconf(_SC_ARG_MAX);
+
+ /* I've seen a version of uclibc which returned -1.
+ * Guard about it, and also avoid insanely large values
+ */
+ if ((unsigned long)r > 64*1024*1024)
+ r = 64*1024*1024;
+
+ return r;
+}
+#endif
+
+/* Return the number of clock ticks per second. */
+unsigned FAST_FUNC bb_clk_tck(void)
+{
+ return sysconf(_SC_CLK_TCK);
+}
diff --git a/libbb/systemd_support.c b/libbb/systemd_support.c
deleted file mode 100644
index 542a3ef..0000000
--- a/libbb/systemd_support.c
+++ b/dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 Davide Cavalca <davide@geexbox.org>
- *
- * Based on http://cgit.freedesktop.org/systemd/tree/src/sd-daemon.c
- * Copyright 2010 Lennart Poettering
- *
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-#include "libbb.h"
-
-//config:config FEATURE_SYSTEMD
-//config: bool "Enable systemd support"
-//config: default y
-//config: help
-//config: If you plan to use busybox daemons on a system where daemons
-//config: are controlled by systemd, enable this option.
-//config: If you don't use systemd, it is still safe to enable it,
-//config: but the downside is increased code size.
-
-//kbuild:lib-$(CONFIG_FEATURE_SYSTEMD) += systemd_support.o
-
-int sd_listen_fds(void)
-{
- const char *e;
- int n;
- int fd;
-
- e = getenv("LISTEN_PID");
- if (!e)
- return 0;
- n = xatoi_positive(e);
- /* Is this for us? */
- if (getpid() != (pid_t) n)
- return 0;
-
- e = getenv("LISTEN_FDS");
- if (!e)
- return 0;
- n = xatoi_positive(e);
- for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++)
- close_on_exec_on(fd);
-
- return n;
-}
diff --git a/libbb/time.c b/libbb/time.c
index 5a64bcb..fa5ab43 100644
--- a/libbb/time.c
+++ b/libbb/time.c
@@ -186,6 +186,7 @@ void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
} else {
bb_error_msg_and_die(bb_msg_invalid_date, date_str);
}
+ ptm->tm_sec = 0; /* assume zero if [.SS] is not given */
if (end == '.') {
/* xxx.SS */
if (sscanf(strchr(date_str, '.') + 1, "%u%c",
diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c
index 8c78f5e..b49714f 100644
--- a/libbb/u_signal_names.c
+++ b/libbb/u_signal_names.c
@@ -19,7 +19,7 @@
/* Believe it or not, but some arches have more than 32 SIGs!
* HPPA: SIGSTKFLT == 36. */
-static const char signals[][7] = {
+static const char signals[][7] ALIGN1 = {
// SUSv3 says kill must support these, and specifies the numerical values,
// http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html
// {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"},
diff --git a/libbb/ubi.c b/libbb/ubi.c
new file mode 100644
index 0000000..34595d7
--- a/dev/null
+++ b/libbb/ubi.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2016 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//kbuild:lib-y += ubi.o
+
+#include "libbb.h"
+
+// from ubi-media.h
+#define UBI_MAX_VOLUME_NAME 127
+#define UBI_MAX_VOLUMES 128
+
+unsigned FAST_FUNC ubi_devnum_from_devname(const char *str)
+{
+ unsigned ubi_devnum;
+
+ if (sscanf(str, "/dev/ubi%u", &ubi_devnum) != 1)
+ bb_error_msg_and_die("not an UBI device: '%s'", str);
+ return ubi_devnum;
+}
+
+int FAST_FUNC ubi_get_volid_by_name(unsigned ubi_devnum, const char *vol_name)
+{
+ unsigned i;
+
+ for (i = 0; i < UBI_MAX_VOLUMES; i++) {
+ char buf[UBI_MAX_VOLUME_NAME + 1];
+ char fname[sizeof("/sys/class/ubi/ubi%u_%u/name") + 2 * sizeof(int)*3];
+
+ sprintf(fname, "/sys/class/ubi/ubi%u_%u/name", ubi_devnum, i);
+ if (open_read_close(fname, buf, sizeof(buf)) <= 0)
+ continue;
+
+ strchrnul(buf, '\n')[0] = '\0';
+ if (strcmp(vol_name, buf) == 0)
+ return i;
+ }
+ bb_error_msg_and_die("volume '%s' not found", vol_name);
+}
diff --git a/libbb/udp_io.c b/libbb/udp_io.c
index 702ca65..d37a73b 100644
--- a/libbb/udp_io.c
+++ b/libbb/udp_io.c
@@ -24,10 +24,10 @@ void FAST_FUNC
socket_want_pktinfo(int fd UNUSED_PARAM)
{
#ifdef IP_PKTINFO
- setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
+ setsockopt_1(fd, IPPROTO_IP, IP_PKTINFO);
#endif
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
- setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int));
+ setsockopt_1(fd, IPPROTO_IPV6, IPV6_PKTINFO);
#endif
}
@@ -78,7 +78,13 @@ send_to_from(int fd, void *buf, size_t len, int flags,
msg.msg_flags = flags;
cmsgptr = CMSG_FIRSTHDR(&msg);
- if (to->sa_family == AF_INET && from->sa_family == AF_INET) {
+ /*
+ * Users report that to->sa_family can be AF_INET6 too,
+ * if "to" was acquired by recv_from_to(). IOW: recv_from_to()
+ * was seen showing IPv6 "from" even when the destination
+ * of received packet (our local address) was IPv4.
+ */
+ if (/* to->sa_family == AF_INET && */ from->sa_family == AF_INET) {
struct in_pktinfo *pktptr;
cmsgptr->cmsg_level = IPPROTO_IP;
cmsgptr->cmsg_type = IP_PKTINFO;
@@ -94,7 +100,7 @@ send_to_from(int fd, void *buf, size_t len, int flags,
pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
}
# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
- else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
+ else if (/* to->sa_family == AF_INET6 && */ from->sa_family == AF_INET6) {
struct in6_pktinfo *pktptr;
cmsgptr->cmsg_level = IPPROTO_IPV6;
cmsgptr->cmsg_type = IPV6_PKTINFO;
diff --git a/libbb/update_passwd.c b/libbb/update_passwd.c
index b2d0464..95423d1 100644
--- a/libbb/update_passwd.c
+++ b/libbb/update_passwd.c
@@ -30,7 +30,18 @@ static void check_selinux_update_passwd(const char *username)
if (!seuser)
bb_error_msg_and_die("invalid context '%s'", context);
if (strcmp(seuser, username) != 0) {
- if (selinux_check_passwd_access(PASSWD__PASSWD) != 0)
+ security_class_t tclass;
+ access_vector_t av;
+
+ tclass = string_to_security_class("passwd");
+ if (tclass == 0)
+ goto die;
+ av = string_to_av_perm(tclass, "passwd");
+ if (av == 0)
+ goto die;
+
+ if (selinux_check_passwd_access(av) != 0)
+ die:
bb_error_msg_and_die("SELinux: access denied");
}
if (ENABLE_FEATURE_CLEAN_UP)
@@ -62,6 +73,8 @@ static void check_selinux_update_passwd(const char *username)
only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd
or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd
+ 8) delete a user from all groups: update_passwd(FILE, NULL, NULL, MEMBER)
+
This function does not validate the arguments fed to it
so the calling program should take care of that.
@@ -82,7 +95,6 @@ int FAST_FUNC update_passwd(const char *filename,
char *fnamesfx;
char *sfx_char;
char *name_colon;
- unsigned user_len;
int old_fd;
int new_fd;
int i;
@@ -99,13 +111,13 @@ int FAST_FUNC update_passwd(const char *filename,
if (filename == NULL)
return ret;
- check_selinux_update_passwd(name);
+ if (name)
+ check_selinux_update_passwd(name);
/* New passwd file, "/etc/passwd+" for now */
fnamesfx = xasprintf("%s+", filename);
sfx_char = &fnamesfx[strlen(fnamesfx)-1];
- name_colon = xasprintf("%s:", name);
- user_len = strlen(name_colon);
+ name_colon = xasprintf("%s:", name ? name : "");
if (shadow)
old_fp = fopen(filename, "r+");
@@ -167,13 +179,47 @@ int FAST_FUNC update_passwd(const char *filename,
line = xmalloc_fgetline(old_fp);
if (!line) /* EOF/error */
break;
- if (strncmp(name_colon, line, user_len) != 0) {
+
+#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
+ if (!name && member) {
+ /* Delete member from all groups */
+ /* line is "GROUP:PASSWD:[member1[,member2]...]" */
+ unsigned member_len = strlen(member);
+ char *list = strrchr(line, ':');
+ while (list) {
+ list++;
+ next_list_element:
+ if (is_prefixed_with(list, member)) {
+ char c;
+ changed_lines++;
+ c = list[member_len];
+ if (c == '\0') {
+ if (list[-1] == ',')
+ list--;
+ *list = '\0';
+ break;
+ }
+ if (c == ',') {
+ overlapping_strcpy(list, list + member_len + 1);
+ goto next_list_element;
+ }
+ changed_lines--;
+ }
+ list = strchr(list, ',');
+ }
+ fprintf(new_fp, "%s\n", line);
+ goto next;
+ }
+#endif
+
+ cp = is_prefixed_with(line, name_colon);
+ if (!cp) {
fprintf(new_fp, "%s\n", line);
goto next;
}
/* We have a match with "name:"... */
- cp = line + user_len; /* move past name: */
+ /* cp points past "name:" */
#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
if (member) {
diff --git a/libbb/utmp.c b/libbb/utmp.c
index 09443fb..bd07670 100644
--- a/libbb/utmp.c
+++ b/libbb/utmp.c
@@ -16,7 +16,7 @@ static void touch(const char *filename)
void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
{
- struct utmp utent;
+ struct utmpx utent;
char *id;
unsigned width;
@@ -45,17 +45,17 @@ void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, con
tty_name += 3;
strncpy(id, tty_name, width);
- touch(_PATH_UTMP);
- //utmpname(_PATH_UTMP);
- setutent();
+ touch(_PATH_UTMPX);
+ //utmpxname(_PATH_UTMPX);
+ setutxent();
/* Append new one (hopefully, unless we collide on ut_id) */
- pututline(&utent);
- endutent();
+ pututxline(&utent);
+ endutxent();
#if ENABLE_FEATURE_WTMP
/* "man utmp" says wtmp file should *not* be created automagically */
/*touch(bb_path_wtmp_file);*/
- updwtmp(bb_path_wtmp_file, &utent);
+ updwtmpx(bb_path_wtmp_file, &utent);
#endif
}
@@ -64,17 +64,17 @@ void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, con
*/
void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
{
- struct utmp utent;
- struct utmp *utp;
+ struct utmpx utent;
+ struct utmpx *utp;
- touch(_PATH_UTMP);
- //utmpname(_PATH_UTMP);
- setutent();
+ touch(_PATH_UTMPX);
+ //utmpxname(_PATH_UTMPX);
+ setutxent();
/* Did init/getty/telnetd/sshd/... create an entry for us?
* It should be (new_type-1), but we'd also reuse
* any other potentially stale xxx_PROCESS entry */
- while ((utp = getutent()) != NULL) {
+ while ((utp = getutxent()) != NULL) {
if (utp->ut_pid == pid
// && ut->ut_line[0]
&& utp->ut_id[0] /* must have nonzero id */
@@ -88,25 +88,25 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const
/* Stale record. Nuke hostname */
memset(utp->ut_host, 0, sizeof(utp->ut_host));
}
- /* NB: pututline (see later) searches for matching utent
- * using getutid(utent) - we must not change ut_id
+ /* NB: pututxline (see later) searches for matching utxent
+ * using getutxid(utent) - we must not change ut_id
* if we want *exactly this* record to be overwritten!
*/
break;
}
}
- //endutent(); - no need, pututline can deal with (and actually likes)
+ //endutxent(); - no need, pututxline can deal with (and actually likes)
//the situation when utmp file is positioned on found record
if (!utp) {
if (new_type != DEAD_PROCESS)
write_new_utmp(pid, new_type, tty_name, username, hostname);
else
- endutent();
+ endutxent();
return;
}
- /* Make a copy. We can't use *utp, pututline's internal getutid
+ /* Make a copy. We can't use *utp, pututxline's internal getutxid
* will overwrite it before it is used! */
utent = *utp;
@@ -120,13 +120,27 @@ void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const
utent.ut_tv.tv_sec = time(NULL);
/* Update, or append new one */
- //setutent();
- pututline(&utent);
- endutent();
+ //setutxent();
+ pututxline(&utent);
+ endutxent();
#if ENABLE_FEATURE_WTMP
/* "man utmp" says wtmp file should *not* be created automagically */
/*touch(bb_path_wtmp_file);*/
- updwtmp(bb_path_wtmp_file, &utent);
+ updwtmpx(bb_path_wtmp_file, &utent);
#endif
}
+
+/* man utmp:
+ * When init(8) finds that a process has exited, it locates its utmp entry
+ * by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
+ * and ut_time with null bytes.
+ * [same applies to other processes which maintain utmp entries, like telnetd]
+ *
+ * We do not bother actually clearing fields:
+ * it might be interesting to know who was logged in and from where
+ */
+void FAST_FUNC update_utmp_DEAD_PROCESS(pid_t pid)
+{
+ update_utmp(pid, DEAD_PROCESS, NULL, NULL, NULL);
+}
diff --git a/libbb/verror_msg.c b/libbb/verror_msg.c
index ee95be3..22c3035 100644
--- a/libbb/verror_msg.c
+++ b/libbb/verror_msg.c
@@ -11,12 +11,16 @@
# include <syslog.h>
#endif
+#if ENABLE_FEATURE_SYSLOG
+smallint syslog_level = LOG_ERR;
+#endif
smallint logmode = LOGMODE_STDIO;
const char *msg_eol = "\n";
void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
{
char *msg, *msg1;
+ char stack_msg[80];
int applet_len, strerr_len, msgeol_len, used;
if (!logmode)
@@ -25,6 +29,27 @@ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
if (!s) /* nomsg[_and_die] uses NULL fmt */
s = ""; /* some libc don't like printf(NULL) */
+ applet_len = strlen(applet_name) + 2; /* "applet: " */
+ strerr_len = strerr ? strlen(strerr) : 0;
+ msgeol_len = strlen(msg_eol);
+
+ /* This costs ~90 bytes of code, but avoids costly
+ * malloc()[in vasprintf]+realloc()+memmove()+free() in 99% of cases.
+ * ~40% speedup.
+ */
+ if ((int)sizeof(stack_msg) - applet_len > 0) {
+ va_list p2;
+
+ /* It is not portable to use va_list twice, need to va_copy it */
+ va_copy(p2, p);
+ used = vsnprintf(stack_msg + applet_len, (int)sizeof(stack_msg) - applet_len, s, p2);
+ va_end(p2);
+ msg = stack_msg;
+ used += applet_len;
+ if (used < (int)sizeof(stack_msg) - 3 - msgeol_len - strerr_len)
+ goto add_pfx_and_sfx;
+ }
+
used = vasprintf(&msg, s, p);
if (used < 0)
return;
@@ -34,9 +59,6 @@ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
* This is needed for e.g. httpd logging, when multiple
* children can produce log messages simultaneously. */
- applet_len = strlen(applet_name) + 2; /* "applet: " */
- strerr_len = strerr ? strlen(strerr) : 0;
- msgeol_len = strlen(msg_eol);
/* can't use xrealloc: it calls error_msg on failure,
* that may result in a recursion */
/* +3 is for ": " before strerr and for terminating NUL */
@@ -49,6 +71,7 @@ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
/* TODO: maybe use writev instead of memmoving? Need full_writev? */
memmove(msg + applet_len, msg, used);
used += applet_len;
+ add_pfx_and_sfx:
strcpy(msg, applet_name);
msg[applet_len - 2] = ':';
msg[applet_len - 1] = ' ';
@@ -70,10 +93,11 @@ void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
}
#if ENABLE_FEATURE_SYSLOG
if (logmode & LOGMODE_SYSLOG) {
- syslog(LOG_ERR, "%s", msg + applet_len);
+ syslog(syslog_level, "%s", msg + applet_len);
}
#endif
- free(msg);
+ if (msg != stack_msg)
+ free(msg);
}
#ifdef VERSION_WITH_WRITEV
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index ed1f86f..c192829 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -68,29 +68,46 @@ pid_t FAST_FUNC xspawn(char **argv)
return pid;
}
-#if ENABLE_FEATURE_PREFER_APPLETS
+#if ENABLE_FEATURE_PREFER_APPLETS \
+ || ENABLE_FEATURE_SH_NOFORK
+static jmp_buf die_jmp;
+static void jump(void)
+{
+ /* Special case. We arrive here if NOFORK applet
+ * calls xfunc, which then decides to die.
+ * We don't die, but jump instead back to caller.
+ * NOFORK applets still cannot carelessly call xfuncs:
+ * p = xmalloc(10);
+ * q = xmalloc(10); // BUG! if this dies, we leak p!
+ */
+ /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0).
+ * This works because exitcodes are bytes,
+ * run_nofork_applet() ensures that by "& 0xff" */
+ longjmp(die_jmp, xfunc_error_retval | 0x100);
+}
+
struct nofork_save_area {
jmp_buf die_jmp;
+ void (*die_func)(void);
const char *applet_name;
uint32_t option_mask32;
- int die_sleep;
uint8_t xfunc_error_retval;
};
static void save_nofork_data(struct nofork_save_area *save)
{
memcpy(&save->die_jmp, &die_jmp, sizeof(die_jmp));
+ save->die_func = die_func;
save->applet_name = applet_name;
- save->xfunc_error_retval = xfunc_error_retval;
save->option_mask32 = option_mask32;
- save->die_sleep = die_sleep;
+ save->xfunc_error_retval = xfunc_error_retval;
}
static void restore_nofork_data(struct nofork_save_area *save)
{
memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp));
+ die_func = save->die_func;
applet_name = save->applet_name;
- xfunc_error_retval = save->xfunc_error_retval;
option_mask32 = save->option_mask32;
- die_sleep = save->die_sleep;
+ xfunc_error_retval = save->xfunc_error_retval;
}
int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
@@ -100,8 +117,6 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
save_nofork_data(&old);
- applet_name = APPLET_NAME(applet_no);
-
xfunc_error_retval = EXIT_FAILURE;
/* In case getopt() or getopt32() was already called:
@@ -133,23 +148,19 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
while (argv[argc])
argc++;
- /* Special flag for xfunc_die(). If xfunc will "die"
- * in NOFORK applet, xfunc_die() sees negative
- * die_sleep and longjmp here instead. */
- die_sleep = -1;
-
+ /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */
+ die_func = jump;
rc = setjmp(die_jmp);
if (!rc) {
/* Some callers (xargs)
* need argv untouched because they free argv[i]! */
char *tmp_argv[argc+1];
memcpy(tmp_argv, argv, (argc+1) * sizeof(tmp_argv[0]));
+ applet_name = tmp_argv[0];
/* Finally we can call NOFORK applet's main() */
rc = applet_main[applet_no](argc, tmp_argv);
- } else { /* xfunc died in NOFORK applet */
- /* in case they meant to return 0... */
- if (rc == -2222)
- rc = 0;
+ } else {
+ /* xfunc died in NOFORK applet */
}
/* Restoring some globals */
@@ -164,7 +175,7 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
}
-#endif /* FEATURE_PREFER_APPLETS */
+#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */
int FAST_FUNC spawn_and_wait(char **argv)
{
diff --git a/libbb/xatonum.c b/libbb/xatonum.c
index 6f4e023..b63b7f5 100644
--- a/libbb/xatonum.c
+++ b/libbb/xatonum.c
@@ -75,3 +75,43 @@ const struct suffix_mult bkm_suffixes[] = {
{ "m", 1024*1024 },
{ "", 0 }
};
+
+const struct suffix_mult cwbkMG_suffixes[] = {
+ { "c", 1 },
+ { "w", 2 },
+ { "b", 512 },
+ { "kB", 1000 },
+ { "kD", 1000 },
+ { "k", 1024 },
+ { "KB", 1000 }, /* compat with coreutils dd */
+ { "KD", 1000 }, /* compat with coreutils dd */
+ { "K", 1024 }, /* compat with coreutils dd */
+ { "MB", 1000000 },
+ { "MD", 1000000 },
+ { "M", 1024*1024 },
+ { "GB", 1000000000 },
+ { "GD", 1000000000 },
+ { "G", 1024*1024*1024 },
+ /* "D" suffix for decimal is not in coreutils manpage, looks like it's deprecated */
+ /* coreutils also understands TPEZY suffixes for tera- and so on, with B suffix for decimal */
+ { "", 0 }
+};
+
+const struct suffix_mult kmg_i_suffixes[] = {
+ { "KiB", 1024 },
+ { "kiB", 1024 },
+ { "K", 1024 },
+ { "k", 1024 },
+ { "MiB", 1048576 },
+ { "miB", 1048576 },
+ { "M", 1048576 },
+ { "m", 1048576 },
+ { "GiB", 1073741824 },
+ { "giB", 1073741824 },
+ { "G", 1073741824 },
+ { "g", 1073741824 },
+ { "KB", 1000 },
+ { "MB", 1000000 },
+ { "GB", 1000000000 },
+ { "", 0 }
+};
diff --git a/libbb/xconnect.c b/libbb/xconnect.c
index 1c8bb2b..3a0dc26 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -14,13 +14,34 @@
#include <sys/un.h>
#include "libbb.h"
+int FAST_FUNC setsockopt_int(int fd, int level, int optname, int optval)
+{
+ return setsockopt(fd, level, optname, &optval, sizeof(int));
+}
+int FAST_FUNC setsockopt_1(int fd, int level, int optname)
+{
+ return setsockopt_int(fd, level, optname, 1);
+}
+int FAST_FUNC setsockopt_SOL_SOCKET_int(int fd, int optname, int optval)
+{
+ return setsockopt_int(fd, SOL_SOCKET, optname, optval);
+}
+int FAST_FUNC setsockopt_SOL_SOCKET_1(int fd, int optname)
+{
+ return setsockopt_SOL_SOCKET_int(fd, optname, 1);
+}
+
void FAST_FUNC setsockopt_reuseaddr(int fd)
{
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1));
+ setsockopt_SOL_SOCKET_1(fd, SO_REUSEADDR);
}
int FAST_FUNC setsockopt_broadcast(int fd)
{
- return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+ return setsockopt_SOL_SOCKET_1(fd, SO_BROADCAST);
+}
+int FAST_FUNC setsockopt_keepalive(int fd)
+{
+ return setsockopt_SOL_SOCKET_1(fd, SO_KEEPALIVE);
}
#ifdef SO_BINDTODEVICE
@@ -171,7 +192,7 @@ IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
const char *cp;
struct addrinfo hint;
- if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) {
+ if (ENABLE_FEATURE_UNIX_LOCAL && is_prefixed_with(host, "local:")) {
struct sockaddr_un *sun;
r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
@@ -475,12 +496,15 @@ char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
{
return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
}
+#ifndef NI_NUMERICSCOPE
+# define NI_NUMERICSCOPE 0
+#endif
char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
{
- return sockaddr2str(sa, NI_NUMERICHOST);
+ return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE);
}
char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
{
- return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
+ return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE | IGNORE_PORT);
}
diff --git a/libbb/xfunc_die.c b/libbb/xfunc_die.c
index 204e5e4..73f7998 100644
--- a/libbb/xfunc_die.c
+++ b/libbb/xfunc_die.c
@@ -7,34 +7,16 @@
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-/* Keeping it separate allows to NOT suck in stdio for VERY small applets.
+/* Keeping it separate allows to NOT pull in stdio for VERY small applets.
* Try building busybox with only "true" enabled... */
#include "libbb.h"
-int die_sleep;
-#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH
-jmp_buf die_jmp;
-#endif
+void (*die_func)(void);
void FAST_FUNC xfunc_die(void)
{
- if (die_sleep) {
- if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH)
- && die_sleep < 0
- ) {
- /* Special case. We arrive here if NOFORK applet
- * calls xfunc, which then decides to die.
- * We don't die, but jump instead back to caller.
- * NOFORK applets still cannot carelessly call xfuncs:
- * p = xmalloc(10);
- * q = xmalloc(10); // BUG! if this dies, we leak p!
- */
- /* -2222 means "zero" (longjmp can't pass 0)
- * run_nofork_applet() catches -2222. */
- longjmp(die_jmp, xfunc_error_retval ? xfunc_error_retval : -2222);
- }
- sleep(die_sleep);
- }
+ if (die_func)
+ die_func();
exit(xfunc_error_retval);
}
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 23f2751..45650ed 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -25,20 +25,22 @@
#include "libbb.h"
/* Turn on nonblocking I/O on a fd */
-void FAST_FUNC ndelay_on(int fd)
+int FAST_FUNC ndelay_on(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (flags & O_NONBLOCK)
- return;
+ return flags;
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ return flags;
}
-void FAST_FUNC ndelay_off(int fd)
+int FAST_FUNC ndelay_off(int fd)
{
int flags = fcntl(fd, F_GETFL);
if (!(flags & O_NONBLOCK))
- return;
+ return flags;
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ return flags;
}
void FAST_FUNC close_on_exec_on(int fd)
@@ -205,7 +207,6 @@ off_t FAST_FUNC fdlength(int fd)
else bottom = pos;
// If we can't, it's smaller.
-
} else {
if (bottom == top) {
if (!top) return 0;
@@ -236,16 +237,27 @@ ssize_t FAST_FUNC full_write2_str(const char *str)
static int wh_helper(int value, int def_val, const char *env_name, int *err)
{
- if (value == 0) {
- char *s = getenv(env_name);
- if (s) {
- value = atoi(s);
- /* If LINES/COLUMNS are set, pretend that there is
- * no error getting w/h, this prevents some ugly
- * cursor tricks by our callers */
- *err = 0;
- }
+ /* Envvars override even if "value" from ioctl is valid (>0).
+ * Rationale: it's impossible to guess what user wants.
+ * For example: "man CMD | ...": should "man" format output
+ * to stdout's width? stdin's width? /dev/tty's width? 80 chars?
+ * We _cant_ know it. If "..." saves text for e.g. email,
+ * then it's probably 80 chars.
+ * If "..." is, say, "grep -v DISCARD | $PAGER", then user
+ * would prefer his tty's width to be used!
+ *
+ * Since we don't know, at least allow user to do this:
+ * "COLUMNS=80 man CMD | ..."
+ */
+ char *s = getenv(env_name);
+ if (s) {
+ value = atoi(s);
+ /* If LINES/COLUMNS are set, pretend that there is
+ * no error getting w/h, this prevents some ugly
+ * cursor tricks by our callers */
+ *err = 0;
}
+
if (value <= 1 || value >= 30000)
value = def_val;
return value;
@@ -257,6 +269,20 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
{
struct winsize win;
int err;
+ int close_me = -1;
+
+ if (fd == -1) {
+ if (isatty(STDOUT_FILENO))
+ fd = STDOUT_FILENO;
+ else
+ if (isatty(STDERR_FILENO))
+ fd = STDERR_FILENO;
+ else
+ if (isatty(STDIN_FILENO))
+ fd = STDIN_FILENO;
+ else
+ close_me = fd = open("/dev/tty", O_RDONLY);
+ }
win.ws_row = 0;
win.ws_col = 0;
@@ -267,8 +293,18 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
*height = wh_helper(win.ws_row, 24, "LINES", &err);
if (width)
*width = wh_helper(win.ws_col, 80, "COLUMNS", &err);
+
+ if (close_me >= 0)
+ close(close_me);
+
return err;
}
+int FAST_FUNC get_terminal_width(int fd)
+{
+ unsigned width;
+ get_terminal_width_height(fd, &width, NULL);
+ return width;
+}
int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
{
@@ -308,3 +344,15 @@ int FAST_FUNC wait4pid(pid_t pid)
return WTERMSIG(status) + 0x180;
return 0;
}
+
+// Useful when we do know that pid is valid, and we just want to wait
+// for it to exit. Not existing pid is fatal. waitpid() status is not returned.
+int FAST_FUNC wait_for_exitstatus(pid_t pid)
+{
+ int exit_status, n;
+
+ n = safe_waitpid(pid, &exit_status, 0);
+ if (n < 0)
+ bb_perror_msg_and_die("waitpid");
+ return exit_status;
+}
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 1e9d11d..2a20d53 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -112,6 +112,11 @@ char* FAST_FUNC xstrndup(const char *s, int n)
return memcpy(t, s, n);
}
+void* FAST_FUNC xmemdup(const void *s, int n)
+{
+ return memcpy(xmalloc(n), s, n);
+}
+
// Die if we can't open a file and return a FILE* to it.
// Notice we haven't got xfread(), This is for use with fscanf() and friends.
FILE* FAST_FUNC xfopen(const char *path, const char *mode)
@@ -230,8 +235,16 @@ void FAST_FUNC xwrite(int fd, const void *buf, size_t count)
{
if (count) {
ssize_t size = full_write(fd, buf, count);
- if ((size_t)size != count)
- bb_error_msg_and_die("short write");
+ if ((size_t)size != count) {
+ /*
+ * Two cases: write error immediately;
+ * or some writes succeeded, then we hit an error.
+ * In either case, errno is set.
+ */
+ bb_perror_msg_and_die(
+ size >= 0 ? "short write" : "write error"
+ );
+ }
}
}
void FAST_FUNC xwrite_str(int fd, const char *str)
@@ -390,6 +403,12 @@ void FAST_FUNC xchdir(const char *path)
bb_perror_msg_and_die("can't change directory to '%s'", path);
}
+void FAST_FUNC xfchdir(int fd)
+{
+ if (fchdir(fd))
+ bb_perror_msg_and_die("fchdir");
+}
+
void FAST_FUNC xchroot(const char *path)
{
if (chroot(path))
@@ -653,3 +672,19 @@ pid_t FAST_FUNC xfork(void)
return pid;
}
#endif
+
+void FAST_FUNC xvfork_parent_waits_and_exits(void)
+{
+ pid_t pid;
+
+ fflush_all();
+ pid = xvfork();
+ if (pid > 0) {
+ /* Parent */
+ int exit_status = wait_for_exitstatus(pid);
+ if (WIFSIGNALED(exit_status))
+ kill_myself_with_sig(WTERMSIG(exit_status));
+ _exit(WEXITSTATUS(exit_status));
+ }
+ /* Child continues */
+}
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index bb63da0..7d4cb60 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -1,14 +1,14 @@
/* vi: set sw=4 ts=4: */
/*
* xreadlink.c - safe implementation of readlink.
- * Returns a NULL on failure...
+ * Returns a NULL on failure.
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
#include "libbb.h"
-/* some systems (eg Hurd) does not have MAXSYMLINKS definition,
+/* Some systems (eg Hurd) do not have MAXSYMLINKS definition,
* set it to some reasonable value if it isn't defined */
#ifndef MAXSYMLINKS
# define MAXSYMLINKS 20
@@ -108,8 +108,11 @@ char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
char* FAST_FUNC xmalloc_realpath(const char *path)
{
-#if defined(__GLIBC__) || \
- (defined(__UCLIBC__) && UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
+/* NB: uclibc also defines __GLIBC__
+ * Therefore the test "if glibc, or uclibc >= 0.9.31" looks a bit weird:
+ */
+#if defined(__GLIBC__) && \
+ (!defined(__UCLIBC__) || UCLIBC_VERSION >= KERNEL_VERSION(0, 9, 31))
/* glibc provides a non-standard extension */
/* new: POSIX.1-2008 specifies this behavior as well */
return realpath(path, NULL);
diff --git a/libpwdgrp/pwd_grp.c b/libpwdgrp/pwd_grp.c
index c806a12..cefbc8a 100644
--- a/libpwdgrp/pwd_grp.c
+++ b/libpwdgrp/pwd_grp.c
@@ -1,613 +1,540 @@
/* vi: set sw=4 ts=4: */
-/* Copyright (C) 2003 Manuel Novoa III
+/* Copyright (C) 2014 Tito Ragusa <farmatito@tiscali.it>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-/* Nov 6, 2003 Initial version.
+/* This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY!!
*
- * NOTE: This implementation is quite strict about requiring all
- * field seperators. It also does not allow leading whitespace
- * except when processing the numeric fields. glibc is more
- * lenient. See the various glibc difference comments below.
+ * Rewrite of some parts. Main differences are:
*
- * TODO:
- * Move to dynamic allocation of (currently statically allocated)
- * buffers; especially for the group-related functions since
- * large group member lists will cause error returns.
+ * 1) the buffer for getpwuid, getgrgid, getpwnam, getgrnam is dynamically
+ * allocated.
+ * If ENABLE_FEATURE_CLEAN_UP is set the buffers are freed at program
+ * exit using the atexit function to make valgrind happy.
+ * 2) the passwd/group files:
+ * a) must contain the expected number of fields (as per count of field
+ * delimeters ":") or we will complain with a error message.
+ * b) leading and trailing whitespace in fields is stripped.
+ * c) some fields are not allowed to be empty (e.g. username, uid/gid),
+ * and in this case NULL is returned and errno is set to EINVAL.
+ * This behaviour could be easily changed by modifying PW_DEF, GR_DEF,
+ * SP_DEF strings (uppercase makes a field mandatory).
+ * d) the string representing uid/gid must be convertible by strtoXX
+ * functions, or errno is set to EINVAL.
+ * e) leading and trailing whitespace in group member names is stripped.
+ * 3) the internal function for getgrouplist uses dynamically allocated buffer.
+ * 4) at the moment only the functions really used by busybox code are
+ * implemented, if you need a particular missing function it should be
+ * easy to write it by using the internal common code.
*/
#include "libbb.h"
-#include <assert.h>
-
-/**********************************************************************/
-/* Sizes for statically allocated buffers. */
-
-#define PWD_BUFFER_SIZE 256
-#define GRP_BUFFER_SIZE 256
-/**********************************************************************/
-/* Prototypes for internal functions. */
-
-static int bb__pgsreader(
- int FAST_FUNC (*parserfunc)(void *d, char *line),
- void *data,
- char *__restrict line_buff,
- size_t buflen,
- FILE *f);
+struct const_passdb {
+ const char *filename;
+ char def[7 + 2*ENABLE_USE_BB_SHADOW];
+ uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW];
+ uint8_t numfields;
+ uint8_t size_of;
+};
+struct passdb {
+ const char *filename;
+ char def[7 + 2*ENABLE_USE_BB_SHADOW];
+ uint8_t off[7 + 2*ENABLE_USE_BB_SHADOW];
+ uint8_t numfields;
+ uint8_t size_of;
+ FILE *fp;
+ char *malloced;
+};
+/* Note: for shadow db, def[] will not contain terminating NUL,
+ * but convert_to_struct() logic detects def[] end by "less than SP?",
+ * not by "is it NUL?" condition; and off[0] happens to be zero
+ * for every db anyway, so there _is_ in fact a terminating NUL there.
+ */
-static int FAST_FUNC bb__parsepwent(void *pw, char *line);
-static int FAST_FUNC bb__parsegrent(void *gr, char *line);
+/* S = string not empty, s = string maybe empty,
+ * I = uid,gid, l = long maybe empty, m = members,
+ * r = reserved
+ */
+#define PW_DEF "SsIIsss"
+#define GR_DEF "SsIm"
+#define SP_DEF "Ssllllllr"
+
+static const struct const_passdb const_pw_db = {
+ _PATH_PASSWD, PW_DEF,
+ {
+ offsetof(struct passwd, pw_name), /* 0 S */
+ offsetof(struct passwd, pw_passwd), /* 1 s */
+ offsetof(struct passwd, pw_uid), /* 2 I */
+ offsetof(struct passwd, pw_gid), /* 3 I */
+ offsetof(struct passwd, pw_gecos), /* 4 s */
+ offsetof(struct passwd, pw_dir), /* 5 s */
+ offsetof(struct passwd, pw_shell) /* 6 s */
+ },
+ sizeof(PW_DEF)-1, sizeof(struct passwd)
+};
+static const struct const_passdb const_gr_db = {
+ _PATH_GROUP, GR_DEF,
+ {
+ offsetof(struct group, gr_name), /* 0 S */
+ offsetof(struct group, gr_passwd), /* 1 s */
+ offsetof(struct group, gr_gid), /* 2 I */
+ offsetof(struct group, gr_mem) /* 3 m (char **) */
+ },
+ sizeof(GR_DEF)-1, sizeof(struct group)
+};
#if ENABLE_USE_BB_SHADOW
-static int FAST_FUNC bb__parsespent(void *sp, char *line);
+static const struct const_passdb const_sp_db = {
+ _PATH_SHADOW, SP_DEF,
+ {
+ offsetof(struct spwd, sp_namp), /* 0 S Login name */
+ offsetof(struct spwd, sp_pwdp), /* 1 s Encrypted password */
+ offsetof(struct spwd, sp_lstchg), /* 2 l */
+ offsetof(struct spwd, sp_min), /* 3 l */
+ offsetof(struct spwd, sp_max), /* 4 l */
+ offsetof(struct spwd, sp_warn), /* 5 l */
+ offsetof(struct spwd, sp_inact), /* 6 l */
+ offsetof(struct spwd, sp_expire), /* 7 l */
+ offsetof(struct spwd, sp_flag) /* 8 r Reserved */
+ },
+ sizeof(SP_DEF)-1, sizeof(struct spwd)
+};
#endif
-/**********************************************************************/
/* We avoid having big global data. */
-
struct statics {
- /* Smaller things first */
- /* It's ok to use one buffer for getpwuid and getpwnam. Manpage says:
+ /* We use same buffer (db[0].malloced) for getpwuid and getpwnam.
+ * Manpage says:
* "The return value may point to a static area, and may be overwritten
* by subsequent calls to getpwent(), getpwnam(), or getpwuid()."
*/
- struct passwd getpw_resultbuf;
- struct group getgr_resultbuf;
-
- char getpw_buffer[PWD_BUFFER_SIZE];
- char getgr_buffer[GRP_BUFFER_SIZE];
-#if 0 //ENABLE_USE_BB_SHADOW
- struct spwd getsp_resultbuf;
- char getsp_buffer[PWD_BUFFER_SIZE];
-#endif
-// Not converted - too small to bother
-//pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
-//FILE *pwf /*= NULL*/;
-//FILE *grf /*= NULL*/;
-//FILE *spf /*= NULL*/;
+ struct passdb db[2 + ENABLE_USE_BB_SHADOW];
+ char *tokenize_end;
+ unsigned string_size;
};
static struct statics *ptr_to_statics;
+#define S (*ptr_to_statics)
+#define has_S (ptr_to_statics)
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void free_static(void)
+{
+ free(S.db[0].malloced);
+ free(S.db[1].malloced);
+# if ENABLE_USE_BB_SHADOW
+ free(S.db[2].malloced);
+# endif
+ free(ptr_to_statics);
+}
+#endif
static struct statics *get_S(void)
{
- if (!ptr_to_statics)
- ptr_to_statics = xzalloc(sizeof(*ptr_to_statics));
+ if (!ptr_to_statics) {
+ ptr_to_statics = xzalloc(sizeof(S));
+ memcpy(&S.db[0], &const_pw_db, sizeof(const_pw_db));
+ memcpy(&S.db[1], &const_gr_db, sizeof(const_gr_db));
+#if ENABLE_USE_BB_SHADOW
+ memcpy(&S.db[2], &const_sp_db, sizeof(const_sp_db));
+#endif
+#if ENABLE_FEATURE_CLEAN_UP
+ atexit(free_static);
+#endif
+ }
return ptr_to_statics;
}
-/* Always use in this order, get_S() must be called first */
-#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
-#define BUFFER(name) (S->name##_buffer)
+/* Internal functions */
-/**********************************************************************/
-/* For the various fget??ent_r funcs, return
- *
- * 0: success
- * ENOENT: end-of-file encountered
- * ERANGE: buflen too small
- * other error values possible. See bb__pgsreader.
- *
- * Also, *result == resultbuf on success and NULL on failure.
- *
- * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
- * We do not, as it really isn't an error if we reach the end-of-file.
- * Doing so is analogous to having fgetc() set errno on EOF.
+/* Divide the passwd/group/shadow record in fields
+ * by substituting the given delimeter
+ * e.g. ':' or ',' with '\0'.
+ * Returns the number of fields found.
+ * Strips leading and trailing whitespace in fields.
*/
-/**********************************************************************/
-
-int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct passwd **__restrict result)
+static int tokenize(char *buffer, int ch)
{
- int rv;
-
- *result = NULL;
+ char *p = buffer;
+ char *s = p;
+ int num_fields = 0;
- rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
- if (!rv) {
- *result = resultbuf;
+ for (;;) {
+ if (isblank(*s)) {
+ overlapping_strcpy(s, skip_whitespace(s));
+ }
+ if (*p == ch || *p == '\0') {
+ char *end = p;
+ while (p != s && isblank(p[-1]))
+ p--;
+ if (p != end)
+ overlapping_strcpy(p, end);
+ num_fields++;
+ if (*end == '\0') {
+ S.tokenize_end = p + 1;
+ return num_fields;
+ }
+ *p = '\0';
+ s = p + 1;
+ }
+ p++;
}
-
- return rv;
}
-int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct group **__restrict result)
-{
- int rv;
+/* Returns !NULL on success and matching line broken up in fields by '\0' in buf.
+ * We require the expected number of fields to be found.
+ */
+static char *parse_common(FILE *fp, struct passdb *db,
+ const char *key, int field_pos)
+{
+ char *buf;
+
+ while ((buf = xmalloc_fgetline(fp)) != NULL) {
+ /* Skip empty lines, comment lines */
+ if (buf[0] == '\0' || buf[0] == '#')
+ goto free_and_next;
+ if (tokenize(buf, ':') != db->numfields) {
+ /* number of fields is wrong */
+ bb_error_msg("%s: bad record", db->filename);
+ goto free_and_next;
+ }
- *result = NULL;
+ if (field_pos == -1) {
+ /* no key specified: sequential read, return a record */
+ break;
+ }
+ if (strcmp(key, nth_string(buf, field_pos)) == 0) {
+ /* record found */
+ break;
+ }
+ free_and_next:
+ free(buf);
+ }
- rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
- if (!rv) {
- *result = resultbuf;
+ S.string_size = S.tokenize_end - buf;
+/*
+ * Ugly hack: group db requires additional buffer space
+ * for members[] array. If there is only one group, we need space
+ * for 3 pointers: alignment padding, group name, NULL.
+ * +1 for every additional group.
+ */
+ if (buf && db->numfields == sizeof(GR_DEF)-1) { /* if we read group file... */
+ int cnt = 3;
+ char *p = buf;
+ while (p < S.tokenize_end)
+ if (*p++ == ',')
+ cnt++;
+ S.string_size += cnt * sizeof(char*);
+//bb_error_msg("+%d words = %u key:%s buf:'%s'", cnt, S.string_size, key, buf);
+ buf = xrealloc(buf, S.string_size);
}
- return rv;
+ return buf;
}
-#if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_FOR_NOW
-int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct spwd **__restrict result)
+static char *parse_file(struct passdb *db,
+ const char *key, int field_pos)
{
- int rv;
-
- *result = NULL;
+ char *buf = NULL;
+ FILE *fp = fopen_for_read(db->filename);
- rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
- if (!rv) {
- *result = resultbuf;
+ if (fp) {
+ buf = parse_common(fp, db, key, field_pos);
+ fclose(fp);
}
-
- return rv;
+ return buf;
}
-#endif
-#endif
-
-/**********************************************************************/
-/* For the various fget??ent funcs, return NULL on failure and a
- * pointer to the appropriate struct (statically allocated) on success.
- * TODO: audit & stop using these in bbox, they pull in static buffers */
-/**********************************************************************/
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct passwd *fgetpwent(FILE *stream)
+/* Convert passwd/group/shadow file record in buffer to a struct */
+static void *convert_to_struct(struct passdb *db,
+ char *buffer, void *result)
{
- struct statics *S;
- struct passwd *resultbuf = RESULTBUF(getpw);
- char *buffer = BUFFER(getpw);
- struct passwd *result;
+ const char *def = db->def;
+ const uint8_t *off = db->off;
- fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
- return result;
-}
+ /* For consistency, zero out all fields */
+ memset(result, 0, db->size_of);
-struct group *fgetgrent(FILE *stream)
-{
- struct statics *S;
- struct group *resultbuf = RESULTBUF(getgr);
- char *buffer = BUFFER(getgr);
- struct group *result;
-
- fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
- return result;
-}
-#endif
+ for (;;) {
+ void *member = (char*)result + (*off++);
+ if ((*def | 0x20) == 's') { /* s or S */
+ *(char **)member = (char*)buffer;
+ if (!buffer[0] && (*def == 'S')) {
+ errno = EINVAL;
+ }
+ }
+ if (*def == 'I') {
+ *(int *)member = bb_strtou(buffer, NULL, 10);
+ }
#if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct spwd *fgetspent(FILE *stream)
-{
- struct statics *S;
- struct spwd *resultbuf = RESULTBUF(getsp);
- char *buffer = BUFFER(getsp);
- struct spwd *result;
-
- fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
- return result;
-}
+ if (*def == 'l') {
+ long n = -1;
+ if (buffer[0])
+ n = bb_strtol(buffer, NULL, 10);
+ *(long *)member = n;
+ }
#endif
-
-#ifdef UNUSED_FOR_NOW
-int sgetspent_r(const char *string, struct spwd *result_buf,
- char *buffer, size_t buflen, struct spwd **result)
-{
- int rv = ERANGE;
-
- *result = NULL;
-
- if (buflen < PWD_BUFFER_SIZE) {
- DO_ERANGE:
- errno = rv;
- goto DONE;
- }
-
- if (string != buffer) {
- if (strlen(string) >= buflen) {
- goto DO_ERANGE;
+ if (*def == 'm') {
+ char **members;
+ int i = tokenize(buffer, ',');
+
+ /* Store members[] after buffer's end.
+ * This is safe ONLY because there is a hack
+ * in parse_common() which allocates additional space
+ * at the end of malloced buffer!
+ */
+ members = (char **)
+ ( ((intptr_t)S.tokenize_end + sizeof(members[0]))
+ & -(intptr_t)sizeof(members[0])
+ );
+ ((struct group *)result)->gr_mem = members;
+ while (--i >= 0) {
+ if (buffer[0]) {
+ *members++ = buffer;
+ // bb_error_msg("member[]='%s'", buffer);
+ }
+ buffer += strlen(buffer) + 1;
+ }
+ *members = NULL;
}
- strcpy(buffer, string);
- }
+ /* def "r" does nothing */
- rv = bb__parsespent(result_buf, buffer);
- if (!rv) {
- *result = result_buf;
+ def++;
+ if ((unsigned char)*def <= (unsigned char)' ')
+ break;
+ buffer += strlen(buffer) + 1;
}
- DONE:
- return rv;
-}
-#endif
-#endif /* ENABLE_USE_BB_SHADOW */
-
-/**********************************************************************/
-
-#define GETXXKEY_R_FUNC getpwnam_r
-#define GETXXKEY_R_PARSER bb__parsepwent
-#define GETXXKEY_R_ENTTYPE struct passwd
-#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->pw_name, key))
-#define GETXXKEY_R_KEYTYPE const char *__restrict
-#define GETXXKEY_R_PATHNAME _PATH_PASSWD
-#include "pwd_grp_internal.c"
-
-#define GETXXKEY_R_FUNC getgrnam_r
-#define GETXXKEY_R_PARSER bb__parsegrent
-#define GETXXKEY_R_ENTTYPE struct group
-#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->gr_name, key))
-#define GETXXKEY_R_KEYTYPE const char *__restrict
-#define GETXXKEY_R_PATHNAME _PATH_GROUP
-#include "pwd_grp_internal.c"
-
-#if ENABLE_USE_BB_SHADOW
-#define GETXXKEY_R_FUNC getspnam_r
-#define GETXXKEY_R_PARSER bb__parsespent
-#define GETXXKEY_R_ENTTYPE struct spwd
-#define GETXXKEY_R_TEST(ENT) (!strcmp((ENT)->sp_namp, key))
-#define GETXXKEY_R_KEYTYPE const char *__restrict
-#define GETXXKEY_R_PATHNAME _PATH_SHADOW
-#include "pwd_grp_internal.c"
-#endif
-
-#define GETXXKEY_R_FUNC getpwuid_r
-#define GETXXKEY_R_PARSER bb__parsepwent
-#define GETXXKEY_R_ENTTYPE struct passwd
-#define GETXXKEY_R_TEST(ENT) ((ENT)->pw_uid == key)
-#define GETXXKEY_R_KEYTYPE uid_t
-#define GETXXKEY_R_PATHNAME _PATH_PASSWD
-#include "pwd_grp_internal.c"
-
-#define GETXXKEY_R_FUNC getgrgid_r
-#define GETXXKEY_R_PARSER bb__parsegrent
-#define GETXXKEY_R_ENTTYPE struct group
-#define GETXXKEY_R_TEST(ENT) ((ENT)->gr_gid == key)
-#define GETXXKEY_R_KEYTYPE gid_t
-#define GETXXKEY_R_PATHNAME _PATH_GROUP
-#include "pwd_grp_internal.c"
-
-/**********************************************************************/
-/* TODO: audit & stop using these in bbox, they pull in static buffers */
-
-/* This one has many users */
-struct passwd *getpwuid(uid_t uid)
-{
- struct statics *S;
- struct passwd *resultbuf = RESULTBUF(getpw);
- char *buffer = BUFFER(getpw);
- struct passwd *result;
-
- getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
+ if (errno)
+ result = NULL;
return result;
}
-/* This one has many users */
-struct group *getgrgid(gid_t gid)
+static int massage_data_for_r_func(struct passdb *db,
+ char *buffer, size_t buflen,
+ void **result,
+ char *buf)
{
- struct statics *S;
- struct group *resultbuf = RESULTBUF(getgr);
- char *buffer = BUFFER(getgr);
- struct group *result;
-
- getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
- return result;
-}
-
-#if 0 //ENABLE_USE_BB_SHADOW
-/* This function is non-standard and is currently not built. It seems
- * to have been created as a reentrant version of the non-standard
- * functions getspuid. Why getspuid was added, I do not know. */
-int getspuid_r(uid_t uid, struct spwd *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct spwd **__restrict result)
-{
- int rv;
- struct passwd *pp;
- struct passwd password;
- char pwd_buff[PWD_BUFFER_SIZE];
-
+ void *result_buf = *result;
*result = NULL;
- rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
- if (!rv) {
- rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
+ if (buf) {
+ if (S.string_size > buflen) {
+ errno = ERANGE;
+ } else {
+ memcpy(buffer, buf, S.string_size);
+ *result = convert_to_struct(db, buffer, result_buf);
+ }
+ free(buf);
}
-
- return rv;
+ /* "The reentrant functions return zero on success.
+ * In case of error, an error number is returned."
+ * NB: not finding the record is also a "success" here:
+ */
+ return errno;
}
-/* This function is non-standard and is currently not built.
- * Why it was added, I do not know. */
-struct spwd *getspuid(uid_t uid)
+static void* massage_data_for_non_r_func(struct passdb *db, char *buf)
{
- struct statics *S;
- struct spwd *resultbuf = RESULTBUF(getsp);
- char *buffer = BUFFER(getsp);
- struct spwd *result;
+ if (!buf)
+ return NULL;
- getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
- return result;
+ free(db->malloced);
+ /* We enlarge buf and move string data up, freeing space
+ * for struct passwd/group/spwd at the beginning. This way,
+ * entire result of getXXnam is in a single malloced block.
+ * This enables easy creation of xmalloc_getpwnam() API.
+ */
+ db->malloced = buf = xrealloc(buf, db->size_of + S.string_size);
+ memmove(buf + db->size_of, buf, S.string_size);
+ return convert_to_struct(db, buf + db->size_of, buf);
}
-#endif
-/* This one has many users */
-struct passwd *getpwnam(const char *name)
+/****** getXXnam/id_r */
+
+static int FAST_FUNC getXXnam_r(const char *name, uintptr_t db_and_field_pos,
+ char *buffer, size_t buflen,
+ void *result)
{
- struct statics *S;
- struct passwd *resultbuf = RESULTBUF(getpw);
- char *buffer = BUFFER(getpw);
- struct passwd *result;
+ char *buf;
+ struct passdb *db = &get_S()->db[db_and_field_pos >> 2];
- getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpw)), &result);
- return result;
+ buf = parse_file(db, name, 0 /*db_and_field_pos & 3*/);
+ /* "db_and_field_pos & 3" is commented out since so far we don't implement
+ * getXXXid_r() functions which would use that to pass 2 here */
+
+ return massage_data_for_r_func(db, buffer, buflen, result, buf);
}
-/* This one has many users */
-struct group *getgrnam(const char *name)
+int FAST_FUNC getpwnam_r(const char *name, struct passwd *struct_buf,
+ char *buffer, size_t buflen,
+ struct passwd **result)
{
- struct statics *S;
- struct group *resultbuf = RESULTBUF(getgr);
- char *buffer = BUFFER(getgr);
- struct group *result;
-
- getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgr)), &result);
- return result;
+ /* Why the "store buffer address in result" trick?
+ * This way, getXXnam_r has the same ABI signature as getpwnam_r,
+ * hopefully compiler can optimize tail call better in this case.
+ */
+ *result = struct_buf;
+ return getXXnam_r(name, (0 << 2) + 0, buffer, buflen, result);
}
-
-#if 0 //ENABLE_USE_BB_SHADOW
-struct spwd *getspnam(const char *name)
+#if ENABLE_USE_BB_SHADOW
+int FAST_FUNC getspnam_r(const char *name, struct spwd *struct_buf, char *buffer, size_t buflen,
+ struct spwd **result)
{
- struct statics *S;
- struct spwd *resultbuf = RESULTBUF(getsp);
- char *buffer = BUFFER(getsp);
- struct spwd *result;
-
- getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getsp)), &result);
- return result;
+ *result = struct_buf;
+ return getXXnam_r(name, (2 << 2) + 0, buffer, buflen, result);
}
#endif
-/**********************************************************************/
-
-/* FIXME: we don't have such CONFIG_xx - ?! */
-
-#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
-static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
-# define LOCK pthread_mutex_lock(&mylock)
-# define UNLOCK pthread_mutex_unlock(&mylock);
-#else
-# define LOCK ((void) 0)
-# define UNLOCK ((void) 0)
-#endif
+#ifdef UNUSED
+/****** getXXent_r */
-static FILE *pwf /*= NULL*/;
-void setpwent(void)
+static int FAST_FUNC getXXent_r(uintptr_t db_idx, char *buffer, size_t buflen,
+ void *result)
{
- LOCK;
- if (pwf) {
- rewind(pwf);
+ char *buf;
+ struct passdb *db = &get_S()->db[db_idx];
+
+ if (!db->fp) {
+ db->fp = fopen_for_read(db->filename);
+ if (!db->fp) {
+ return errno;
+ }
+ close_on_exec_on(fileno(db->fp));
}
- UNLOCK;
+
+ buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1);
+ if (!buf && !errno)
+ errno = ENOENT;
+ return massage_data_for_r_func(db, buffer, buflen, result, buf);
}
-void endpwent(void)
+int FAST_FUNC getpwent_r(struct passwd *struct_buf, char *buffer, size_t buflen,
+ struct passwd **result)
{
- LOCK;
- if (pwf) {
- fclose(pwf);
- pwf = NULL;
- }
- UNLOCK;
+ *result = struct_buf;
+ return getXXent_r(0, buffer, buflen, result);
}
+#endif
+/****** getXXent */
-int getpwent_r(struct passwd *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct passwd **__restrict result)
+static void* FAST_FUNC getXXent(uintptr_t db_idx)
{
- int rv;
-
- LOCK;
- *result = NULL; /* In case of error... */
+ char *buf;
+ struct passdb *db = &get_S()->db[db_idx];
- if (!pwf) {
- pwf = fopen_for_read(_PATH_PASSWD);
- if (!pwf) {
- rv = errno;
- goto ERR;
+ if (!db->fp) {
+ db->fp = fopen_for_read(db->filename);
+ if (!db->fp) {
+ return NULL;
}
- close_on_exec_on(fileno(pwf));
+ close_on_exec_on(fileno(db->fp));
}
- rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
- if (!rv) {
- *result = resultbuf;
- }
-
- ERR:
- UNLOCK;
- return rv;
+ buf = parse_common(db->fp, db, /*no search key:*/ NULL, -1);
+ return massage_data_for_non_r_func(db, buf);
}
-static FILE *grf /*= NULL*/;
-void setgrent(void)
+struct passwd* FAST_FUNC getpwent(void)
{
- LOCK;
- if (grf) {
- rewind(grf);
- }
- UNLOCK;
+ return getXXent(0);
}
-void endgrent(void)
-{
- LOCK;
- if (grf) {
- fclose(grf);
- grf = NULL;
- }
- UNLOCK;
-}
+/****** getXXnam/id */
-int getgrent_r(struct group *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- struct group **__restrict result)
+static void* FAST_FUNC getXXnam(const char *name, unsigned db_and_field_pos)
{
- int rv;
-
- LOCK;
- *result = NULL; /* In case of error... */
+ char *buf;
+ struct passdb *db = &get_S()->db[db_and_field_pos >> 2];
- if (!grf) {
- grf = fopen_for_read(_PATH_GROUP);
- if (!grf) {
- rv = errno;
- goto ERR;
- }
- close_on_exec_on(fileno(grf));
- }
-
- rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
- if (!rv) {
- *result = resultbuf;
- }
-
- ERR:
- UNLOCK;
- return rv;
+ buf = parse_file(db, name, db_and_field_pos & 3);
+ return massage_data_for_non_r_func(db, buf);
}
-#ifdef UNUSED_FOR_NOW
-#if ENABLE_USE_BB_SHADOW
-static FILE *spf /*= NULL*/;
-void setspent(void)
+struct passwd* FAST_FUNC getpwnam(const char *name)
{
- LOCK;
- if (spf) {
- rewind(spf);
- }
- UNLOCK;
+ return getXXnam(name, (0 << 2) + 0);
}
-
-void endspent(void)
+struct group* FAST_FUNC getgrnam(const char *name)
{
- LOCK;
- if (spf) {
- fclose(spf);
- spf = NULL;
- }
- UNLOCK;
+ return getXXnam(name, (1 << 2) + 0);
}
-
-int getspent_r(struct spwd *resultbuf, char *buffer,
- size_t buflen, struct spwd **result)
+struct passwd* FAST_FUNC getpwuid(uid_t id)
{
- int rv;
-
- LOCK;
- *result = NULL; /* In case of error... */
-
- if (!spf) {
- spf = fopen_for_read(_PATH_SHADOW);
- if (!spf) {
- rv = errno;
- goto ERR;
- }
- close_on_exec_on(fileno(spf));
- }
-
- rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
- if (!rv) {
- *result = resultbuf;
- }
-
- ERR:
- UNLOCK;
- return rv;
+ return getXXnam(utoa(id), (0 << 2) + 2);
}
-#endif
-#endif /* UNUSED_FOR_NOW */
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-struct passwd *getpwent(void)
+struct group* FAST_FUNC getgrgid(gid_t id)
{
- static char line_buff[PWD_BUFFER_SIZE];
- static struct passwd pwd;
- struct passwd *result;
-
- getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
- return result;
+ return getXXnam(utoa(id), (1 << 2) + 2);
}
-struct group *getgrent(void)
-{
- static char line_buff[GRP_BUFFER_SIZE];
- static struct group gr;
- struct group *result;
+/****** end/setXXend */
- getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
- return result;
+void FAST_FUNC endpwent(void)
+{
+ if (has_S && S.db[0].fp) {
+ fclose(S.db[0].fp);
+ S.db[0].fp = NULL;
+ }
}
-
-#if ENABLE_USE_BB_SHADOW
-struct spwd *getspent(void)
+void FAST_FUNC setpwent(void)
{
- static char line_buff[PWD_BUFFER_SIZE];
- static struct spwd spwd;
- struct spwd *result;
-
- getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
- return result;
+ if (has_S && S.db[0].fp) {
+ rewind(S.db[0].fp);
+ }
}
-
-struct spwd *sgetspent(const char *string)
+void FAST_FUNC endgrent(void)
{
- static char line_buff[PWD_BUFFER_SIZE];
- static struct spwd spwd;
- struct spwd *result;
-
- sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
- return result;
+ if (has_S && S.db[1].fp) {
+ fclose(S.db[1].fp);
+ S.db[1].fp = NULL;
+ }
}
-#endif
-#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
-static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
+/****** initgroups and getgrouplist */
+
+static gid_t* FAST_FUNC getgrouplist_internal(int *ngroups_ptr,
+ const char *user, gid_t gid)
{
- FILE *grfile;
+ FILE *fp;
gid_t *group_list;
int ngroups;
- struct group group;
- char buff[PWD_BUFFER_SIZE];
/* We alloc space for 8 gids at a time. */
- group_list = xmalloc(8 * sizeof(group_list[0]));
+ group_list = xzalloc(8 * sizeof(group_list[0]));
group_list[0] = gid;
ngroups = 1;
- grfile = fopen_for_read(_PATH_GROUP);
- if (grfile) {
- while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
+ fp = fopen_for_read(_PATH_GROUP);
+ if (fp) {
+ struct passdb *db = &get_S()->db[1];
+ char *buf;
+ while ((buf = parse_common(fp, db, NULL, -1)) != NULL) {
char **m;
- assert(group.gr_mem); /* Must have at least a NULL terminator. */
+ struct group group;
+ if (!convert_to_struct(db, buf, &group))
+ goto next;
if (group.gr_gid == gid)
- continue;
+ goto next;
for (m = group.gr_mem; *m; m++) {
if (strcmp(*m, user) != 0)
continue;
group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
group_list[ngroups++] = group.gr_gid;
- break;
+ goto next;
}
+ next:
+ free(buf);
}
- fclose(grfile);
+ fclose(fp);
}
*ngroups_ptr = ngroups;
return group_list;
}
-int initgroups(const char *user, gid_t gid)
+int FAST_FUNC initgroups(const char *user, gid_t gid)
{
int ngroups;
gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
@@ -617,7 +544,7 @@ int initgroups(const char *user, gid_t gid)
return ngroups;
}
-int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
+int FAST_FUNC getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
{
int ngroups_old = *ngroups;
gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
@@ -631,411 +558,3 @@ int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
free(group_list);
return ngroups_old;
}
-
-#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
-int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
-{
- int rv = -1;
-
-#if 0
- /* glibc does this check */
- if (!p || !f) {
- errno = EINVAL;
- return rv;
- }
-#endif
-
- /* No extra thread locking is needed above what fprintf does. */
- if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
- p->pw_name, p->pw_passwd,
- (unsigned long)(p->pw_uid),
- (unsigned long)(p->pw_gid),
- p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
- ) {
- rv = 0;
- }
-
- return rv;
-}
-
-int putgrent(const struct group *__restrict p, FILE *__restrict f)
-{
- int rv = -1;
-
-#if 0
- /* glibc does this check */
- if (!p || !f) {
- errno = EINVAL;
- return rv;
- }
-#endif
-
- if (fprintf(f, "%s:%s:%lu:",
- p->gr_name, p->gr_passwd,
- (unsigned long)(p->gr_gid)) >= 0
- ) {
- static const char format[] ALIGN1 = ",%s";
-
- char **m;
- const char *fmt;
-
- fmt = format + 1;
-
- assert(p->gr_mem);
- m = p->gr_mem;
-
- while (1) {
- if (!*m) {
- if (fputc('\n', f) >= 0) {
- rv = 0;
- }
- break;
- }
- if (fprintf(f, fmt, *m) < 0) {
- break;
- }
- m++;
- fmt = format;
- }
- }
-
- return rv;
-}
-#endif
-
-#if ENABLE_USE_BB_SHADOW
-#ifdef UNUSED_FOR_NOW
-static const unsigned char put_sp_off[] ALIGN1 = {
- offsetof(struct spwd, sp_lstchg), /* 2 - not a char ptr */
- offsetof(struct spwd, sp_min), /* 3 - not a char ptr */
- offsetof(struct spwd, sp_max), /* 4 - not a char ptr */
- offsetof(struct spwd, sp_warn), /* 5 - not a char ptr */
- offsetof(struct spwd, sp_inact), /* 6 - not a char ptr */
- offsetof(struct spwd, sp_expire) /* 7 - not a char ptr */
-};
-
-int putspent(const struct spwd *p, FILE *stream)
-{
- const char *fmt;
- long x;
- int i;
- int rv = -1;
-
- /* Unlike putpwent and putgrent, glibc does not check the args. */
- if (fprintf(stream, "%s:%s:", p->sp_namp,
- (p->sp_pwdp ? p->sp_pwdp : "")) < 0
- ) {
- goto DO_UNLOCK;
- }
-
- for (i = 0; i < sizeof(put_sp_off); i++) {
- fmt = "%ld:";
- x = *(long *)((char *)p + put_sp_off[i]);
- if (x == -1) {
- fmt += 3;
- }
- if (fprintf(stream, fmt, x) < 0) {
- goto DO_UNLOCK;
- }
- }
-
- if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
- goto DO_UNLOCK;
- }
-
- if (fputc('\n', stream) > 0) {
- rv = 0;
- }
-
- DO_UNLOCK:
- return rv;
-}
-#endif
-#endif /* USE_BB_SHADOW */
-
-/**********************************************************************/
-/* Internal functions */
-/**********************************************************************/
-
-static const unsigned char pw_off[] ALIGN1 = {
- offsetof(struct passwd, pw_name), /* 0 */
- offsetof(struct passwd, pw_passwd), /* 1 */
- offsetof(struct passwd, pw_uid), /* 2 - not a char ptr */
- offsetof(struct passwd, pw_gid), /* 3 - not a char ptr */
-#ifndef __BIONIC__
- offsetof(struct passwd, pw_gecos), /* 4 */
-#endif
- offsetof(struct passwd, pw_dir), /* 5 */
- offsetof(struct passwd, pw_shell) /* 6 */
-};
-
-static int FAST_FUNC bb__parsepwent(void *data, char *line)
-{
- char *endptr;
- char *p;
- int i;
-
- i = 0;
- while (1) {
- p = (char *) data + pw_off[i];
-
- if (i < 2 || i > 3) {
- *((char **) p) = line;
- if (i == 6) {
- return 0;
- }
- /* NOTE: glibc difference - glibc allows omission of
- * ':' seperators after the gid field if all remaining
- * entries are empty. We require all separators. */
- line = strchr(line, ':');
- if (!line) {
- break;
- }
- } else {
- unsigned long t = strtoul(line, &endptr, 10);
- /* Make sure we had at least one digit, and that the
- * failing char is the next field seperator ':'. See
- * glibc difference note above. */
- /* TODO: Also check for leading whitespace? */
- if ((endptr == line) || (*endptr != ':')) {
- break;
- }
- line = endptr;
- if (i & 1) { /* i == 3 -- gid */
- *((gid_t *) p) = t;
- } else { /* i == 2 -- uid */
- *((uid_t *) p) = t;
- }
- }
-
- *line++ = '\0';
- i++;
- } /* while (1) */
-
- return -1;
-}
-
-/**********************************************************************/
-
-static const unsigned char gr_off[] ALIGN1 = {
- offsetof(struct group, gr_name), /* 0 */
- offsetof(struct group, gr_passwd), /* 1 */
- offsetof(struct group, gr_gid) /* 2 - not a char ptr */
-};
-
-static int FAST_FUNC bb__parsegrent(void *data, char *line)
-{
- char *endptr;
- char *p;
- int i;
- char **members;
- char *end_of_buf;
-
- end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
- i = 0;
- while (1) {
- p = (char *) data + gr_off[i];
-
- if (i < 2) {
- *((char **) p) = line;
- line = strchr(line, ':');
- if (!line) {
- break;
- }
- *line++ = '\0';
- i++;
- } else {
- *((gid_t *) p) = strtoul(line, &endptr, 10);
-
- /* NOTE: glibc difference - glibc allows omission of the
- * trailing colon when there is no member list. We treat
- * this as an error. */
-
- /* Make sure we had at least one digit, and that the
- * failing char is the next field seperator ':'. See
- * glibc difference note above. */
- if ((endptr == line) || (*endptr != ':')) {
- break;
- }
-
- i = 1; /* Count terminating NULL ptr. */
- p = endptr;
-
- if (p[1]) { /* We have a member list to process. */
- /* Overwrite the last ':' with a ',' before counting.
- * This allows us to (1) test for initial ','
- * and (2) adds one ',' so that the number of commas
- * equals the member count. */
- *p = ',';
- do {
- /* NOTE: glibc difference - glibc allows and trims leading
- * (but not trailing) space. We treat this as an error. */
- /* NOTE: glibc difference - glibc allows consecutive and
- * trailing commas, and ignores "empty string" users. We
- * treat this as an error. */
- if (*p == ',') {
- ++i;
- *p = 0; /* nul-terminate each member string. */
- if (!*++p || (*p == ',') || isspace(*p)) {
- goto ERR;
- }
- }
- } while (*++p);
- }
-
- /* Now align (p+1), rounding up. */
- /* Assumes sizeof(char **) is a power of 2. */
- members = (char **)( (((intptr_t) p) + sizeof(char **))
- & ~((intptr_t)(sizeof(char **) - 1)) );
-
- if (((char *)(members + i)) > end_of_buf) { /* No space. */
- break;
- }
-
- ((struct group *) data)->gr_mem = members;
-
- if (--i) {
- p = endptr; /* Pointing to char prior to first member. */
- while (1) {
- *members++ = ++p;
- if (!--i)
- break;
- while (*++p)
- continue;
- }
- }
- *members = NULL;
-
- return 0;
- }
- } /* while (1) */
-
- ERR:
- return -1;
-}
-
-/**********************************************************************/
-
-#if ENABLE_USE_BB_SHADOW
-static const unsigned char sp_off[] ALIGN1 = {
- offsetof(struct spwd, sp_namp), /* 0: char* */
- offsetof(struct spwd, sp_pwdp), /* 1: char* */
- offsetof(struct spwd, sp_lstchg), /* 2: long */
- offsetof(struct spwd, sp_min), /* 3: long */
- offsetof(struct spwd, sp_max), /* 4: long */
- offsetof(struct spwd, sp_warn), /* 5: long */
- offsetof(struct spwd, sp_inact), /* 6: long */
- offsetof(struct spwd, sp_expire), /* 7: long */
- offsetof(struct spwd, sp_flag) /* 8: unsigned long */
-};
-
-static int FAST_FUNC bb__parsespent(void *data, char *line)
-{
- char *endptr;
- char *p;
- int i;
-
- i = 0;
- while (1) {
- p = (char *) data + sp_off[i];
- if (i < 2) {
- *((char **) p) = line;
- line = strchr(line, ':');
- if (!line) {
- break; /* error */
- }
- } else {
- *((long *) p) = strtoul(line, &endptr, 10);
- if (endptr == line) {
- *((long *) p) = -1L;
- }
- line = endptr;
- if (i == 8) {
- if (*line != '\0') {
- break; /* error */
- }
- return 0; /* all ok */
- }
- if (*line != ':') {
- break; /* error */
- }
- }
- *line++ = '\0';
- i++;
- }
-
- return EINVAL;
-}
-#endif
-
-/**********************************************************************/
-
-/* Reads until EOF, or until it finds a line which fits in the buffer
- * and for which the parser function succeeds.
- *
- * Returns 0 on success and ENOENT for end-of-file (glibc convention).
- */
-static int bb__pgsreader(
- int FAST_FUNC (*parserfunc)(void *d, char *line),
- void *data,
- char *__restrict line_buff,
- size_t buflen,
- FILE *f)
-{
- int skip;
- int rv = ERANGE;
-
- if (buflen < PWD_BUFFER_SIZE) {
- errno = rv;
- return rv;
- }
-
- skip = 0;
- while (1) {
- if (!fgets(line_buff, buflen, f)) {
- if (feof(f)) {
- rv = ENOENT;
- }
- break;
- }
-
- {
- int line_len = strlen(line_buff) - 1;
- if (line_len >= 0 && line_buff[line_len] == '\n') {
- line_buff[line_len] = '\0';
- } else
- if (line_len + 2 == buflen) {
- /* A start (or continuation) of overlong line */
- skip = 1;
- continue;
- } /* else: a last line in the file, and it has no '\n' */
- }
-
- if (skip) {
- /* This "line" is a remainder of overlong line, ignore */
- skip = 0;
- continue;
- }
-
- /* NOTE: glibc difference - glibc strips leading whitespace from
- * records. We do not allow leading whitespace. */
-
- /* Skip empty lines, comment lines, and lines with leading
- * whitespace. */
- if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
- if (parserfunc == bb__parsegrent) {
- /* Do evil group hack:
- * The group entry parsing function needs to know where
- * the end of the buffer is so that it can construct the
- * group member ptr table. */
- ((struct group *) data)->gr_name = line_buff + buflen;
- }
- if (parserfunc(data, line_buff) == 0) {
- rv = 0;
- break;
- }
- }
- } /* while (1) */
-
- return rv;
-}
diff --git a/libpwdgrp/pwd_grp_internal.c b/libpwdgrp/pwd_grp_internal.c
deleted file mode 100644
index d6483be..0000000
--- a/libpwdgrp/pwd_grp_internal.c
+++ b/dev/null
@@ -1,61 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/* Copyright (C) 2003 Manuel Novoa III
- *
- * Licensed under GPLv2 or later, see file LICENSE in this source tree.
- */
-
-/* Nov 6, 2003 Initial version.
- *
- * NOTE: This implementation is quite strict about requiring all
- * field seperators. It also does not allow leading whitespace
- * except when processing the numeric fields. glibc is more
- * lenient. See the various glibc difference comments below.
- *
- * TODO:
- * Move to dynamic allocation of (currently statically allocated)
- * buffers; especially for the group-related functions since
- * large group member lists will cause error returns.
- */
-
-#ifndef GETXXKEY_R_FUNC
-#error GETXXKEY_R_FUNC is not defined!
-#endif
-
-int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
- GETXXKEY_R_ENTTYPE *__restrict resultbuf,
- char *__restrict buffer, size_t buflen,
- GETXXKEY_R_ENTTYPE **__restrict result)
-{
- FILE *stream;
- int rv;
-
- *result = NULL;
-
- stream = fopen_for_read(GETXXKEY_R_PATHNAME);
- if (!stream)
- return errno;
- while (1) {
- rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
- if (!rv) {
- if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
- *result = resultbuf;
- break;
- }
- } else {
- if (rv == ENOENT) { /* EOF encountered */
- rv = 0;
- }
- break;
- }
- }
- fclose(stream);
-
- return rv;
-}
-
-#undef GETXXKEY_R_FUNC
-#undef GETXXKEY_R_PARSER
-#undef GETXXKEY_R_ENTTYPE
-#undef GETXXKEY_R_TEST
-#undef GETXXKEY_R_KEYTYPE
-#undef GETXXKEY_R_PATHNAME
diff --git a/libpwdgrp/uidgid_get.c b/libpwdgrp/uidgid_get.c
index 8388be0..1199f23 100644
--- a/libpwdgrp/uidgid_get.c
+++ b/libpwdgrp/uidgid_get.c
@@ -28,7 +28,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "libbb.h"
/* Always sets uid and gid */
-int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
+int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug)
{
struct passwd *pwd;
struct group *gr;
@@ -43,18 +43,16 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
/* copies sz-1 bytes, stores terminating '\0' */
safe_strncpy(user, ug, sz);
}
- if (numeric_ok) {
- n = bb_strtou(user, NULL, 10);
- if (!errno) {
- u->uid = n;
- pwd = getpwuid(n);
- /* If we have e.g. "500" string without user */
- /* with uid 500 in /etc/passwd, we set gid == uid */
- u->gid = pwd ? pwd->pw_gid : n;
- goto skip;
- }
+ n = bb_strtou(user, NULL, 10);
+ if (!errno) {
+ u->uid = n;
+ pwd = getpwuid(n);
+ /* If we have e.g. "500" string without user */
+ /* with uid 500 in /etc/passwd, we set gid == uid */
+ u->gid = pwd ? pwd->pw_gid : n;
+ goto skip;
}
- /* Either it is not numeric, or caller disallows numeric username */
+ /* it is not numeric */
pwd = getpwnam(user);
if (!pwd)
return 0;
@@ -63,12 +61,10 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
skip:
if (group) {
- if (numeric_ok) {
- n = bb_strtou(group, NULL, 10);
- if (!errno) {
- u->gid = n;
- return 1;
- }
+ n = bb_strtou(group, NULL, 10);
+ if (!errno) {
+ u->gid = n;
+ return 1;
}
gr = getgrnam(group);
if (!gr)
@@ -79,7 +75,7 @@ int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
}
void FAST_FUNC xget_uidgid(struct bb_uidgid_t *u, const char *ug)
{
- if (!get_uidgid(u, ug, 1))
+ if (!get_uidgid(u, ug))
bb_error_msg_and_die("unknown user/group %s", ug);
}
@@ -94,6 +90,8 @@ void FAST_FUNC parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_gr
{
char *group;
+ u->uid = u->gid = (gid_t)-1L;
+
/* Check if there is a group name */
group = strchr(user_group, '.'); /* deprecated? */
if (!group)
@@ -119,16 +117,16 @@ int main()
{
unsigned u;
struct bb_uidgid_t ug;
- u = get_uidgid(&ug, "apache", 0);
+ u = get_uidgid(&ug, "apache");
printf("%u = %u:%u\n", u, ug.uid, ug.gid);
ug.uid = ug.gid = 1111;
- u = get_uidgid(&ug, "apache", 0);
+ u = get_uidgid(&ug, "apache");
printf("%u = %u:%u\n", u, ug.uid, ug.gid);
ug.uid = ug.gid = 1111;
- u = get_uidgid(&ug, "apache:users", 0);
+ u = get_uidgid(&ug, "apache:users");
printf("%u = %u:%u\n", u, ug.uid, ug.gid);
ug.uid = ug.gid = 1111;
- u = get_uidgid(&ug, "apache:users", 0);
+ u = get_uidgid(&ug, "apache:users");
printf("%u = %u:%u\n", u, ug.uid, ug.gid);
return 0;
}
diff --git a/loginutils/Config.src b/loginutils/Config.src
index 9bf79af..efb954b 100644
--- a/loginutils/Config.src
+++ b/loginutils/Config.src
@@ -5,8 +5,6 @@
menu "Login/Password Management Utilities"
-INSERT
-
config FEATURE_SHADOWPASSWDS
bool "Support for shadow passwords"
default y
@@ -93,239 +91,6 @@ config USE_BB_CRYPT_SHA
With this option off, login will fail password check for any
user which has password encrypted with these algorithms.
-config ADDUSER
- bool "adduser"
- default y
- help
- Utility for creating a new user account.
-
-config FEATURE_ADDUSER_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on ADDUSER && LONG_OPTS
- help
- Support long options for the adduser applet.
-
-config FEATURE_CHECK_NAMES
- bool "Enable sanity check on user/group names in adduser and addgroup"
- default n
- depends on ADDUSER || ADDGROUP
- help
- Enable sanity check on user and group names in adduser and addgroup.
- To avoid problems, the user or group name should consist only of
- letters, digits, underscores, periods, at signs and dashes,
- and not start with a dash (as defined by IEEE Std 1003.1-2001).
- For compatibility with Samba machine accounts "$" is also supported
- at the end of the user or group name.
-
-config FIRST_SYSTEM_ID
- int "First valid system uid or gid for adduser and addgroup"
- depends on ADDUSER || ADDGROUP
- range 0 64900
- default 100
- help
- First valid system uid or gid for adduser and addgroup
-
-config LAST_SYSTEM_ID
- int "Last valid system uid or gid for adduser and addgroup"
- depends on ADDUSER || ADDGROUP
- range 0 64900
- default 999
- help
- Last valid system uid or gid for adduser and addgroup
-
-config ADDGROUP
- bool "addgroup"
- default y
- help
- Utility for creating a new group account.
-
-config FEATURE_ADDGROUP_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on ADDGROUP && LONG_OPTS
- help
- Support long options for the addgroup applet.
-
-config FEATURE_ADDUSER_TO_GROUP
- bool "Support for adding users to groups"
- default y
- depends on ADDGROUP
- help
- If called with two non-option arguments,
- addgroup will add an existing user to an
- existing group.
-
-config DELUSER
- bool "deluser"
- default y
- help
- Utility for deleting a user account.
-
-config DELGROUP
- bool "delgroup"
- default y
- help
- Utility for deleting a group account.
-
-config FEATURE_DEL_USER_FROM_GROUP
- bool "Support for removing users from groups"
- default y
- depends on DELGROUP
- help
- If called with two non-option arguments, deluser
- or delgroup will remove an user from a specified group.
-
-config GETTY
- bool "getty"
- default y
- select FEATURE_SYSLOG
- help
- getty lets you log in on a tty. It is normally invoked by init.
-
- Note that you can save a few bytes by disabling it and
- using login applet directly.
- If you need to reset tty attributes before calling login,
- this script approximates getty:
-
- exec </dev/$1 >/dev/$1 2>&1 || exit 1
- reset
- stty sane; stty ispeed 38400; stty ospeed 38400
- printf "%s login: " "`hostname`"
- read -r login
- exec /bin/login "$login"
-
-config LOGIN
- bool "login"
- default y
- select FEATURE_SYSLOG
- help
- login is used when signing onto a system.
-
- Note that Busybox binary must be setuid root for this applet to
- work properly.
-
-config LOGIN_SESSION_AS_CHILD
- bool "Run logged in session in a child process"
- default y if PAM
- depends on LOGIN
- help
- Run the logged in session in a child process. This allows
- login to clean up things such as utmp entries or PAM sessions
- when the login session is complete. If you use PAM, you
- almost always would want this to be set to Y, else PAM session
- will not be cleaned up.
-
-config PAM
- bool "Support for PAM (Pluggable Authentication Modules)"
- default n
- depends on LOGIN
- help
- Use PAM in login(1) instead of direct access to password database.
-
-config LOGIN_SCRIPTS
- bool "Support for login scripts"
- depends on LOGIN
- default y
- help
- Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
- just prior to switching from root to logged-in user.
-
-config FEATURE_NOLOGIN
- bool "Support for /etc/nologin"
- default y
- depends on LOGIN
- help
- The file /etc/nologin is used by (some versions of) login(1).
- If it exists, non-root logins are prohibited.
-
-config FEATURE_SECURETTY
- bool "Support for /etc/securetty"
- default y
- depends on LOGIN
- help
- The file /etc/securetty is used by (some versions of) login(1).
- The file contains the device names of tty lines (one per line,
- without leading /dev/) on which root is allowed to login.
-
-config PASSWD
- bool "passwd"
- default y
- select FEATURE_SYSLOG
- help
- passwd changes passwords for user and group accounts. A normal user
- may only change the password for his/her own account, the super user
- may change the password for any account. The administrator of a group
- may change the password for the group.
-
- Note that Busybox binary must be setuid root for this applet to
- work properly.
-
-config FEATURE_PASSWD_WEAK_CHECK
- bool "Check new passwords for weakness"
- default y
- depends on PASSWD
- help
- With this option passwd will refuse new passwords which are "weak".
-
-config CRYPTPW
- bool "cryptpw"
- default y
- help
- Encrypts the given password with the crypt(3) libc function
- using the given salt. Debian has this utility under mkpasswd
- name. Busybox provides mkpasswd as an alias for cryptpw.
-
-config CHPASSWD
- bool "chpasswd"
- default y
- help
- Reads a file of user name and password pairs from standard input
- and uses this information to update a group of existing users.
-
-config FEATURE_DEFAULT_PASSWD_ALGO
- string "Default password encryption method (passwd -a, cryptpw -m parameter)"
- default "des"
- depends on PASSWD || CRYPTPW
- help
- Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512".
-
-config SU
- bool "su"
- default y
- select FEATURE_SYSLOG
- help
- su is used to become another user during a login session.
- Invoked without a username, su defaults to becoming the super user.
-
- Note that Busybox binary must be setuid root for this applet to
- work properly.
-
-config FEATURE_SU_SYSLOG
- bool "Enable su to write to syslog"
- default y
- depends on SU
-
-config FEATURE_SU_CHECKS_SHELLS
- bool "Enable su to check user's shell to be listed in /etc/shells"
- depends on SU
- default y
-
-config SULOGIN
- bool "sulogin"
- default y
- select FEATURE_SYSLOG
- help
- sulogin is invoked when the system goes into single user
- mode (this is done through an entry in inittab).
-
-config VLOCK
- bool "vlock"
- default y
- help
- Build the "vlock" applet which allows you to lock (virtual) terminals.
-
- Note that Busybox binary must be setuid root for this applet to
- work properly.
+INSERT
endmenu
diff --git a/loginutils/Kbuild.src b/loginutils/Kbuild.src
index ef416a7..6b4fb74 100644
--- a/loginutils/Kbuild.src
+++ b/loginutils/Kbuild.src
@@ -7,15 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_ADDGROUP) += addgroup.o
-lib-$(CONFIG_ADDUSER) += adduser.o
-lib-$(CONFIG_CRYPTPW) += cryptpw.o
-lib-$(CONFIG_CHPASSWD) += chpasswd.o
-lib-$(CONFIG_GETTY) += getty.o
-lib-$(CONFIG_LOGIN) += login.o
-lib-$(CONFIG_PASSWD) += passwd.o
-lib-$(CONFIG_SU) += su.o
-lib-$(CONFIG_SULOGIN) += sulogin.o
-lib-$(CONFIG_VLOCK) += vlock.o
-lib-$(CONFIG_DELUSER) += deluser.o
-lib-$(CONFIG_DELGROUP) += deluser.o
diff --git a/loginutils/README b/loginutils/README
index ce88510..847b371 100644
--- a/loginutils/README
+++ b/loginutils/README
@@ -23,7 +23,7 @@ Getty should establish a new session and process group, and ensure
that tty is a ctty.
??? Should getty ensure that other processes which might have opened
-fds to this tty be dusconnected? agetty has a -R option which makes
+fds to this tty be disconnected? agetty has a -R option which makes
agetty call vhangup() after tty is opened. (Then agetty opens it again,
since it probably vhangup'ed its own fd too).
diff --git a/loginutils/add-remove-shell.c b/loginutils/add-remove-shell.c
index e492b6e..ce4a7bb 100644
--- a/loginutils/add-remove-shell.c
+++ b/loginutils/add-remove-shell.c
@@ -7,13 +7,6 @@
* Licensed under GPLv2 or later, see the LICENSE file in this source tree
* for details.
*/
-
-//applet:IF_ADD_SHELL( APPLET_ODDNAME(add-shell , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell ))
-//applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell))
-
-//kbuild:lib-$(CONFIG_ADD_SHELL) += add-remove-shell.o
-//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
-
//config:config ADD_SHELL
//config: bool "add-shell"
//config: default y if DESKTOP
@@ -26,6 +19,12 @@
//config: help
//config: Remove shells from /etc/shells.
+//applet:IF_ADD_SHELL( APPLET_ODDNAME(add-shell , add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, add_shell ))
+//applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_SBIN, BB_SUID_DROP, remove_shell))
+
+//kbuild:lib-$(CONFIG_ADD_SHELL) += add-remove-shell.o
+//kbuild:lib-$(CONFIG_REMOVE_SHELL) += add-remove-shell.o
+
//usage:#define add_shell_trivial_usage
//usage: "SHELL..."
//usage:#define add_shell_full_usage "\n\n"
@@ -100,7 +99,7 @@ int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
cpp++;
}
/* copy shell name from old to new file */
- printf("%s\n", line);
+ puts(line);
next_line:
free(line);
}
@@ -112,7 +111,7 @@ int add_remove_shell_main(int argc UNUSED_PARAM, char **argv)
char **cpp = argv;
while (*cpp) {
if (*cpp != dont_add)
- printf("%s\n", *cpp);
+ puts(*cpp);
cpp++;
}
}
diff --git a/loginutils/addgroup.c b/loginutils/addgroup.c
index fd5b40b..a7c5227 100644
--- a/loginutils/addgroup.c
+++ b/loginutils/addgroup.c
@@ -9,11 +9,36 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
*/
+//config:config ADDGROUP
+//config: bool "addgroup"
+//config: default y
+//config: help
+//config: Utility for creating a new group account.
+//config:
+//config:config FEATURE_ADDGROUP_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on ADDGROUP && LONG_OPTS
+//config: help
+//config: Support long options for the addgroup applet.
+//config:
+//config:config FEATURE_ADDUSER_TO_GROUP
+//config: bool "Support for adding users to groups"
+//config: default y
+//config: depends on ADDGROUP
+//config: help
+//config: If called with two non-option arguments,
+//config: addgroup will add an existing user to an
+//config: existing group.
+
+//applet:IF_ADDGROUP(APPLET(addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ADDGROUP) += addgroup.o
//usage:#define addgroup_trivial_usage
-//usage: "[-g GID] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP"
+//usage: "[-g GID] [-S] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP"
//usage:#define addgroup_full_usage "\n\n"
-//usage: "Add a group " IF_FEATURE_ADDUSER_TO_GROUP("or add a user to a group") "\n"
+//usage: "Add a group" IF_FEATURE_ADDUSER_TO_GROUP(" or add a user to a group") "\n"
//usage: "\n -g GID Group id"
//usage: "\n -S Create a system group"
@@ -22,14 +47,16 @@
#if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
#endif
+#if CONFIG_LAST_ID < CONFIG_LAST_SYSTEM_ID
+#error Bad LAST_ID or LAST_SYSTEM_ID in .config
+#endif
#define OPT_GID (1 << 0)
#define OPT_SYSTEM_ACCOUNT (1 << 1)
-/* We assume GID_T_MAX == INT_MAX */
static void xgroup_study(struct group *g)
{
- unsigned max = INT_MAX;
+ unsigned max = CONFIG_LAST_ID;
/* Make sure gr_name is unused */
if (getgrnam(g->gr_name)) {
@@ -46,7 +73,6 @@ static void xgroup_study(struct group *g)
max = CONFIG_LAST_SYSTEM_ID;
} else {
g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1;
- max = 64999;
}
}
/* Check if the desired gid is free
@@ -125,7 +151,7 @@ int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int addgroup_main(int argc UNUSED_PARAM, char **argv)
{
unsigned opts;
- unsigned gid = 0;
+ const char *gid = "0";
/* need to be root */
if (geteuid()) {
@@ -139,7 +165,7 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv)
* addgroup -g num group
* addgroup user group
* Check for min, max and missing args */
- opt_complementary = "-1:?2:g+";
+ opt_complementary = "-1:?2";
opts = getopt32(argv, "g:S", &gid);
/* move past the commandline options */
argv += optind;
@@ -160,7 +186,7 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv)
gr = xgetgrnam(argv[1]); /* unknown group: exit */
/* check if user is already in this group */
for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
- if (!strcmp(argv[0], *(gr->gr_mem))) {
+ if (strcmp(argv[0], *(gr->gr_mem)) == 0) {
/* user is already in group: do nothing */
return EXIT_SUCCESS;
}
@@ -175,7 +201,7 @@ int addgroup_main(int argc UNUSED_PARAM, char **argv)
#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
{
die_if_bad_username(argv[0]);
- new_group(argv[0], gid);
+ new_group(argv[0], xatou_range(gid, 0, CONFIG_LAST_ID));
}
/* Reached only on success */
return EXIT_SUCCESS;
diff --git a/loginutils/adduser.c b/loginutils/adduser.c
index 79b9501..b9e46e7 100644
--- a/loginutils/adduser.c
+++ b/loginutils/adduser.c
@@ -7,6 +7,57 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config ADDUSER
+//config: bool "adduser"
+//config: default y
+//config: help
+//config: Utility for creating a new user account.
+//config:
+//config:config FEATURE_ADDUSER_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on ADDUSER && LONG_OPTS
+//config: help
+//config: Support long options for the adduser applet.
+//config:
+//config:config FEATURE_CHECK_NAMES
+//config: bool "Enable sanity check on user/group names in adduser and addgroup"
+//config: default n
+//config: depends on ADDUSER || ADDGROUP
+//config: help
+//config: Enable sanity check on user and group names in adduser and addgroup.
+//config: To avoid problems, the user or group name should consist only of
+//config: letters, digits, underscores, periods, at signs and dashes,
+//config: and not start with a dash (as defined by IEEE Std 1003.1-2001).
+//config: For compatibility with Samba machine accounts "$" is also supported
+//config: at the end of the user or group name.
+//config:
+//config:config LAST_ID
+//config: int "Last valid uid or gid for adduser and addgroup"
+//config: depends on ADDUSER || ADDGROUP
+//config: default 60000
+//config: help
+//config: Last valid uid or gid for adduser and addgroup
+//config:
+//config:config FIRST_SYSTEM_ID
+//config: int "First valid system uid or gid for adduser and addgroup"
+//config: depends on ADDUSER || ADDGROUP
+//config: range 0 LAST_ID
+//config: default 100
+//config: help
+//config: First valid system uid or gid for adduser and addgroup
+//config:
+//config:config LAST_SYSTEM_ID
+//config: int "Last valid system uid or gid for adduser and addgroup"
+//config: depends on ADDUSER || ADDGROUP
+//config: range FIRST_SYSTEM_ID LAST_ID
+//config: default 999
+//config: help
+//config: Last valid system uid or gid for adduser and addgroup
+
+//applet:IF_ADDUSER(APPLET(adduser, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ADDUSER) += adduser.o
//usage:#define adduser_trivial_usage
//usage: "[OPTIONS] USER [GROUP]"
@@ -20,12 +71,17 @@
//usage: "\n -D Don't assign a password"
//usage: "\n -H Don't create home directory"
//usage: "\n -u UID User id"
+//usage: "\n -k SKEL Skeleton directory (/etc/skel)"
#include "libbb.h"
#if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
#endif
+#if CONFIG_LAST_ID < CONFIG_LAST_SYSTEM_ID
+#error Bad LAST_ID or LAST_SYSTEM_ID in .config
+#endif
+
/* #define OPT_HOME (1 << 0) */ /* unused */
/* #define OPT_GECOS (1 << 1) */ /* unused */
@@ -35,13 +91,13 @@
#define OPT_SYSTEM_ACCOUNT (1 << 5)
#define OPT_DONT_MAKE_HOME (1 << 6)
#define OPT_UID (1 << 7)
+#define OPT_SKEL (1 << 8)
-/* We assume UID_T_MAX == INT_MAX */
/* remix */
/* recoded such that the uid may be passed in *p */
static void passwd_study(struct passwd *p)
{
- int max = UINT_MAX;
+ int max = CONFIG_LAST_ID;
if (getpwnam(p->pw_name)) {
bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name);
@@ -54,7 +110,6 @@ static void passwd_study(struct passwd *p)
max = CONFIG_LAST_SYSTEM_ID;
} else {
p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1;
- max = 64999;
}
}
/* check for a free uid (and maybe gid) */
@@ -132,6 +187,7 @@ static const char adduser_longopts[] ALIGN1 =
"system\0" No_argument "S"
"no-create-home\0" No_argument "H"
"uid\0" Required_argument "u"
+ "skel\0" Required_argument "k"
;
#endif
@@ -147,6 +203,8 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
const char *usegroup = NULL;
char *p;
unsigned opts;
+ char *uid;
+ const char *skel = "/etc/skel";
#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
applet_long_options = adduser_longopts;
@@ -164,16 +222,11 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
/* at least one and at most two non-option args */
/* disable interactive passwd for system accounts */
- opt_complementary = "-1:?2:SD:u+";
- if (sizeof(pw.pw_uid) == sizeof(int)) {
- opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &pw.pw_uid);
- } else {
- unsigned uid;
- opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid);
- if (opts & OPT_UID) {
- pw.pw_uid = uid;
- }
- }
+ opt_complementary = "-1:?2:SD";
+ opts = getopt32(argv, "h:g:s:G:DSHu:k:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid, &skel);
+ if (opts & OPT_UID)
+ pw.pw_uid = xatou_range(uid, 0, CONFIG_LAST_ID);
+
argv += optind;
pw.pw_name = argv[0];
@@ -252,8 +305,9 @@ int adduser_main(int argc UNUSED_PARAM, char **argv)
NULL
};
/* Be silent on any errors (like: no /etc/skel) */
- logmode = LOGMODE_NONE;
- copy_file("/etc/skel", pw.pw_dir, FILEUTILS_RECUR);
+ if (!(opts & OPT_SKEL))
+ logmode = LOGMODE_NONE;
+ copy_file(skel, pw.pw_dir, FILEUTILS_RECUR);
logmode = LOGMODE_STDIO;
chown_main(4, (char**)args);
}
diff --git a/loginutils/chpasswd.c b/loginutils/chpasswd.c
index 9eab99e..fb7a241 100644
--- a/loginutils/chpasswd.c
+++ b/loginutils/chpasswd.c
@@ -5,27 +5,46 @@
* Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org>
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-#include "libbb.h"
+//config:config CHPASSWD
+//config: bool "chpasswd"
+//config: default y
+//config: help
+//config: Reads a file of user name and password pairs from standard input
+//config: and uses this information to update a group of existing users.
+//config:
+//config:config FEATURE_DEFAULT_PASSWD_ALGO
+//config: string "Default password encryption method (passwd -a, cryptpw -m parameter)"
+//config: default "des"
+//config: depends on PASSWD || CRYPTPW
+//config: help
+//config: Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512".
+
+//applet:IF_CHPASSWD(APPLET(chpasswd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHPASSWD) += chpasswd.o
//usage:#define chpasswd_trivial_usage
-//usage: IF_LONG_OPTS("[--md5|--encrypted]") IF_NOT_LONG_OPTS("[-m|-e]")
+//usage: IF_LONG_OPTS("[--md5|--encrypted|--crypt-method]") IF_NOT_LONG_OPTS("[-m|-e|-c]")
//usage:#define chpasswd_full_usage "\n\n"
//usage: "Read user:password from stdin and update /etc/passwd\n"
//usage: IF_LONG_OPTS(
-//usage: "\n -e,--encrypted Supplied passwords are in encrypted form"
-//usage: "\n -m,--md5 Use MD5 encryption instead of DES"
+//usage: "\n -e,--encrypted Supplied passwords are in encrypted form"
+//usage: "\n -m,--md5 Use MD5 encryption instead of DES"
+//usage: "\n -c,--crypt-method Use the specified method to encrypt the passwords"
//usage: )
//usage: IF_NOT_LONG_OPTS(
//usage: "\n -e Supplied passwords are in encrypted form"
//usage: "\n -m Use MD5 encryption instead of DES"
+//usage: "\n -c Use the specified method to encrypt the passwords"
//usage: )
-//TODO: implement -c ALGO
+#include "libbb.h"
#if ENABLE_LONG_OPTS
static const char chpasswd_longopts[] ALIGN1 =
- "encrypted\0" No_argument "e"
- "md5\0" No_argument "m"
+ "encrypted\0" No_argument "e"
+ "md5\0" No_argument "m"
+ "crypt-method\0" Required_argument "c"
;
#endif
@@ -36,14 +55,15 @@ int chpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int chpasswd_main(int argc UNUSED_PARAM, char **argv)
{
char *name;
+ const char *algo = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
int opt;
if (getuid() != 0)
bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
- opt_complementary = "m--e:e--m";
+ opt_complementary = "m--ec:e--mc:c--em";
IF_LONG_OPTS(applet_long_options = chpasswd_longopts;)
- opt = getopt32(argv, "em");
+ opt = getopt32(argv, "emc:", &algo);
while ((name = xmalloc_fgetline(stdin)) != NULL) {
char *free_me;
@@ -59,15 +79,14 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
free_me = NULL;
if (!(opt & OPT_ENC)) {
- char salt[sizeof("$N$XXXXXXXX")];
+ char salt[MAX_PW_SALT_LEN];
- crypt_make_salt(salt, 1);
if (opt & OPT_MD5) {
- salt[0] = '$';
- salt[1] = '1';
- salt[2] = '$';
- crypt_make_salt(salt + 3, 4);
+ /* Force MD5 if the -m flag is set */
+ algo = "md5";
}
+
+ crypt_make_pw_salt(salt, algo);
free_me = pass = pw_encrypt(pass, salt, 0);
}
@@ -86,7 +105,7 @@ int chpasswd_main(int argc UNUSED_PARAM, char **argv)
if (rc < 0)
bb_error_msg_and_die("an error occurred updating password for %s", name);
if (rc)
- bb_info_msg("Password for '%s' changed", name);
+ bb_error_msg("password for '%s' changed", name);
logmode = LOGMODE_STDIO;
free(name);
free(free_me);
diff --git a/loginutils/cryptpw.c b/loginutils/cryptpw.c
index 29f0fbe..9f5f406 100644
--- a/loginutils/cryptpw.c
+++ b/loginutils/cryptpw.c
@@ -9,6 +9,27 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config CRYPTPW
+//config: bool "cryptpw"
+//config: default y
+//config: help
+//config: Encrypts the given password with the crypt(3) libc function
+//config: using the given salt.
+//config:
+//config:config MKPASSWD
+//config: bool "mkpasswd"
+//config: default y
+//config: help
+//config: Encrypts the given password with the crypt(3) libc function
+//config: using the given salt. Debian has this utility under mkpasswd
+//config: name. Busybox provides mkpasswd as an alias for cryptpw.
+
+//applet:IF_CRYPTPW(APPLET(cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP))
+// APPLET_ODDNAME:name main location suid_type help
+//applet:IF_MKPASSWD(APPLET_ODDNAME(mkpasswd, cryptpw, BB_DIR_USR_BIN, BB_SUID_DROP, cryptpw))
+
+//kbuild:lib-$(CONFIG_CRYPTPW) += cryptpw.o
+//kbuild:lib-$(CONFIG_MKPASSWD) += cryptpw.o
//usage:#define cryptpw_trivial_usage
//usage: "[OPTIONS] [PASSWORD] [SALT]"
@@ -28,25 +49,6 @@
//usage: "\n -S SALT"
//usage: )
-/* mkpasswd is an alias to cryptpw */
-//usage:#define mkpasswd_trivial_usage
-//usage: "[OPTIONS] [PASSWORD] [SALT]"
-/* We do support -s, we just don't mention it */
-//usage:#define mkpasswd_full_usage "\n\n"
-//usage: "Crypt PASSWORD using crypt(3)\n"
-//usage: IF_LONG_OPTS(
-//usage: "\n -P,--password-fd=N Read password from fd N"
-/* //usage: "\n -s,--stdin Use stdin; like -P0" */
-//usage: "\n -m,--method=TYPE Encryption method"
-//usage: "\n -S,--salt=SALT"
-//usage: )
-//usage: IF_NOT_LONG_OPTS(
-//usage: "\n -P N Read password from fd N"
-/* //usage: "\n -s Use stdin; like -P0" */
-//usage: "\n -m TYPE Encryption method TYPE"
-//usage: "\n -S SALT"
-//usage: )
-
#include "libbb.h"
/* Debian has 'mkpasswd' utility, manpage says:
@@ -109,8 +111,8 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
opt_S = NULL;
/* at most two non-option arguments; -P NUM */
- opt_complementary = "?2:P+";
- getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m);
+ opt_complementary = "?2";
+ getopt32(argv, "sP:+S:m:a:", &fd, &opt_S, &opt_m, &opt_m);
argv += optind;
/* have no idea how to handle -s... */
@@ -128,7 +130,7 @@ int cryptpw_main(int argc UNUSED_PARAM, char **argv)
if (!password) {
/* Only mkpasswd, and only from tty, prompts.
* Otherwise it is a plain read. */
- password = (isatty(STDIN_FILENO) && applet_name[0] == 'm')
+ password = (ENABLE_MKPASSWD && isatty(STDIN_FILENO) && applet_name[0] == 'm')
? bb_ask_stdin("Password: ")
: xmalloc_fgetline(stdin)
;
diff --git a/loginutils/deluser.c b/loginutils/deluser.c
index ee60efc..73fc1b8 100644
--- a/loginutils/deluser.c
+++ b/loginutils/deluser.c
@@ -7,13 +7,38 @@
* Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
*
* Licensed under GPLv2, see file LICENSE in this source tree.
- *
*/
+//config:config DELUSER
+//config: bool "deluser"
+//config: default y
+//config: help
+//config: Utility for deleting a user account.
+//config:
+//config:config DELGROUP
+//config: bool "delgroup"
+//config: default y
+//config: help
+//config: Utility for deleting a group account.
+//config:
+//config:config FEATURE_DEL_USER_FROM_GROUP
+//config: bool "Support for removing users from groups"
+//config: default y
+//config: depends on DELGROUP
+//config: help
+//config: If called with two non-option arguments, deluser
+//config: or delgroup will remove an user from a specified group.
+
+//applet:IF_DELUSER(APPLET(deluser, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_DELGROUP(APPLET_ODDNAME(delgroup, deluser, BB_DIR_USR_SBIN, BB_SUID_DROP, delgroup))
+
+//kbuild:lib-$(CONFIG_DELUSER) += deluser.o
+//kbuild:lib-$(CONFIG_DELGROUP) += deluser.o
//usage:#define deluser_trivial_usage
-//usage: "USER"
+//usage: IF_LONG_OPTS("[--remove-home] ") "USER"
//usage:#define deluser_full_usage "\n\n"
//usage: "Delete USER from the system"
+// --remove-home is self-explanatory enough to put it in --help
//usage:#define delgroup_trivial_usage
//usage: IF_FEATURE_DEL_USER_FROM_GROUP("[USER] ")"GROUP"
@@ -37,6 +62,19 @@ int deluser_main(int argc, char **argv)
/* Are we deluser or delgroup? */
int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u'));
+#if !ENABLE_LONG_OPTS
+ const int opt_delhome = 0;
+#else
+ int opt_delhome = 0;
+ if (do_deluser) {
+ applet_long_options =
+ "remove-home\0" No_argument "\xff";
+ opt_delhome = getopt32(argv, "");
+ argv += opt_delhome;
+ argc -= opt_delhome;
+ }
+#endif
+
if (geteuid() != 0)
bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
@@ -55,10 +93,14 @@ int deluser_main(int argc, char **argv)
case 2:
if (do_deluser) {
/* "deluser USER" */
- xgetpwnam(name); /* bail out if USER is wrong */
+ struct passwd *pw;
+
+ pw = xgetpwnam(name); /* bail out if USER is wrong */
pfile = bb_path_passwd_file;
if (ENABLE_FEATURE_SHADOWPASSWDS)
sfile = bb_path_shadow_file;
+ if (opt_delhome)
+ remove_file(pw->pw_dir, FILEUTILS_RECUR);
} else {
struct group *gr;
do_delgroup:
@@ -73,12 +115,11 @@ int deluser_main(int argc, char **argv)
if (!member) {
/* "delgroup GROUP" */
struct passwd *pw;
- struct passwd pwent;
/* Check if the group is in use */
-#define passwd_buf bb_common_bufsiz1
- while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) {
- if (pwent.pw_gid == gr->gr_gid)
- bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name);
+ while ((pw = getpwent()) != NULL) {
+ if (pw->pw_gid == gr->gr_gid)
+ bb_error_msg_and_die("'%s' still has '%s' as their primary group!",
+ pw->pw_name, name);
}
//endpwent();
}
@@ -97,16 +138,22 @@ int deluser_main(int argc, char **argv)
}
} while (ENABLE_FEATURE_SHADOWPASSWDS && pfile);
- if (ENABLE_DELGROUP && do_deluser > 0) {
- /* "deluser USER" also should try to delete
- * same-named group. IOW: do "delgroup USER"
- */
+ if (do_deluser > 0) {
+ /* Delete user from all groups */
+ if (update_passwd(bb_path_group_file, NULL, NULL, name) == -1)
+ return EXIT_FAILURE;
+
+ if (ENABLE_DELGROUP) {
+ /* "deluser USER" also should try to delete
+ * same-named group. IOW: do "delgroup USER"
+ */
// On debian deluser is a perl script that calls userdel.
// From man userdel:
// If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will
// delete the group with the same name as the user.
- do_deluser = -1;
- goto do_delgroup;
+ do_deluser = -1;
+ goto do_delgroup;
+ }
}
return EXIT_SUCCESS;
}
diff --git a/loginutils/getty.c b/loginutils/getty.c
index 6fd4ff2..e5a2533 100644
--- a/loginutils/getty.c
+++ b/loginutils/getty.c
@@ -21,6 +21,28 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config GETTY
+//config: bool "getty"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: getty lets you log in on a tty. It is normally invoked by init.
+//config:
+//config: Note that you can save a few bytes by disabling it and
+//config: using login applet directly.
+//config: If you need to reset tty attributes before calling login,
+//config: this script approximates getty:
+//config:
+//config: exec </dev/$1 >/dev/$1 2>&1 || exit 1
+//config: reset
+//config: stty sane; stty ispeed 38400; stty ospeed 38400
+//config: printf "%s login: " "`hostname`"
+//config: read -r login
+//config: exec /bin/login "$login"
+
+//applet:IF_GETTY(APPLET(getty, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_GETTY) += getty.o
#include "libbb.h"
#include <syslog.h>
@@ -114,7 +136,7 @@ struct globals {
//usage: "\n"
//usage: "\nBAUD_RATE of 0 leaves it unchanged"
-static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
+static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:+wn";
#define F_INITSTRING (1 << 0) /* -I */
#define F_LOCAL (1 << 1) /* -L */
#define F_FAKEHOST (1 << 2) /* -H */
@@ -162,7 +184,7 @@ static void parse_args(char **argv)
char *ts;
int flags;
- opt_complementary = "-2:t+"; /* at least 2 args; -t N */
+ opt_complementary = "-2"; /* at least 2 args; -t N */
flags = getopt32(argv, opt_string,
&G.initstring, &G.fakehost, &G.issue,
&G.login, &G.timeout
@@ -339,18 +361,19 @@ static void finalize_tty_attrs(void)
* observed to improve backspacing through Unicode chars
*/
- /* line buffered input (NL or EOL or EOF chars end a line);
- * recognize INT/QUIT/SUSP chars;
- * echo input chars;
- * echo BS-SP-BS on erase character;
- * echo kill char specially, not as ^c (ECHOKE controls how exactly);
- * erase all input via BS-SP-BS on kill char (else go to next line)
+ /* ICANON line buffered input (NL or EOL or EOF chars end a line);
+ * ISIG recognize INT/QUIT/SUSP chars;
+ * ECHO echo input chars;
+ * ECHOE echo BS-SP-BS on erase character;
+ * ECHOK echo kill char specially, not as ^c (ECHOKE controls how exactly);
+ * ECHOKE erase all input via BS-SP-BS on kill char (else go to next line)
+ * ECHOCTL Echo ctrl chars as ^c (else echo verbatim:
+ * e.g. up arrow emits "ESC-something" and thus moves cursor up!)
*/
- G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
+ G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE | ECHOCTL;
/* Other bits in c_lflag:
* XCASE Map uppercase to \lowercase [tried, doesn't work]
* ECHONL Echo NL even if ECHO is not set
- * ECHOCTL Echo ctrl chars as ^c (else don't echo) - maybe set this?
* ECHOPRT On erase, echo erased chars
* [qwe<BS><BS><BS> input looks like "qwe\ewq/" on screen]
* NOFLSH Don't flush input buffer after interrupt or quit chars
@@ -524,6 +547,11 @@ static void alarm_handler(int sig UNUSED_PARAM)
_exit(EXIT_SUCCESS);
}
+static void sleep10(void)
+{
+ sleep(10);
+}
+
int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int getty_main(int argc UNUSED_PARAM, char **argv)
{
@@ -561,6 +589,16 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
// pid, getppid(),
// getsid(0), getpgid(0));
bb_perror_msg_and_die("setsid");
+ /*
+ * When we can end up here?
+ * Example: setsid() fails when run alone in interactive shell:
+ * # getty 115200 /dev/tty2
+ * because shell's child (getty) is put in a new process group.
+ * But doesn't fail if shell is not interactive
+ * (and therefore doesn't create process groups for pipes),
+ * or if getty is not the first process in the process group:
+ * # true | getty 115200 /dev/tty2
+ */
}
/* Looks like we are already a session leader.
* In this case (setsid failed) we may still have ctty,
@@ -593,7 +631,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
close(n--);
/* Logging. We want special flavor of error_msg_and_die */
- die_sleep = 10;
+ die_func = sleep10;
msg_eol = "\r\n";
/* most likely will internally use fd #3 in CLOEXEC mode: */
openlog(applet_name, LOG_PID, LOG_AUTH);
diff --git a/loginutils/login.c b/loginutils/login.c
index 862104c..03ddadb 100644
--- a/loginutils/login.c
+++ b/loginutils/login.c
@@ -2,16 +2,67 @@
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LOGIN
+//config: bool "login"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: login is used when signing onto a system.
+//config:
+//config: Note that Busybox binary must be setuid root for this applet to
+//config: work properly.
+//config:
+//config:config LOGIN_SESSION_AS_CHILD
+//config: bool "Run logged in session in a child process"
+//config: default y if PAM
+//config: depends on LOGIN
+//config: help
+//config: Run the logged in session in a child process. This allows
+//config: login to clean up things such as utmp entries or PAM sessions
+//config: when the login session is complete. If you use PAM, you
+//config: almost always would want this to be set to Y, else PAM session
+//config: will not be cleaned up.
+//config:
+//config:config LOGIN_SCRIPTS
+//config: bool "Support for login scripts"
+//config: depends on LOGIN
+//config: default y
+//config: help
+//config: Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
+//config: just prior to switching from root to logged-in user.
+//config:
+//config:config FEATURE_NOLOGIN
+//config: bool "Support for /etc/nologin"
+//config: default y
+//config: depends on LOGIN
+//config: help
+//config: The file /etc/nologin is used by (some versions of) login(1).
+//config: If it exists, non-root logins are prohibited.
+//config:
+//config:config FEATURE_SECURETTY
+//config: bool "Support for /etc/securetty"
+//config: default y
+//config: depends on LOGIN
+//config: help
+//config: The file /etc/securetty is used by (some versions of) login(1).
+//config: The file contains the device names of tty lines (one per line,
+//config: without leading /dev/) on which root is allowed to login.
+
+//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
+//applet:IF_LOGIN(APPLET(login, BB_DIR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_LOGIN) += login.o
//usage:#define login_trivial_usage
//usage: "[-p] [-h HOST] [[-f] USER]"
//usage:#define login_full_usage "\n\n"
//usage: "Begin a new session on the system\n"
//usage: "\n -f Don't authenticate (user already authenticated)"
-//usage: "\n -h Name of the remote host"
+//usage: "\n -h HOST Host user came from (for network logins)"
//usage: "\n -p Preserve environment"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
#include <sys/resource.h>
@@ -19,7 +70,9 @@
# include <selinux/selinux.h> /* for is_selinux_enabled() */
#ifndef __BIONIC__
# include <selinux/get_context_list.h> /* for get_default_context() */
-# include <selinux/flask.h> /* for security class definitions */
+# /* from deprecated <selinux/flask.h>: */
+# undef SECCLASS_CHR_FILE
+# define SECCLASS_CHR_FILE 10
#endif
#endif
@@ -30,6 +83,49 @@
* Apparently they like to confuse people. */
# include <security/pam_appl.h>
# include <security/pam_misc.h>
+
+# if 0
+/* This supposedly can be used to avoid double password prompt,
+ * if used instead of standard misc_conv():
+ *
+ * "When we want to authenticate first with local method and then with tacacs for example,
+ * the password is asked for local method and if not good is asked a second time for tacacs.
+ * So if we want to authenticate a user with tacacs, and the user exists localy, the password is
+ * asked two times before authentication is accepted."
+ *
+ * However, code looks shaky. For example, why misc_conv() return value is ignored?
+ * Are msg[i] and resp[i] indexes handled correctly?
+ */
+static char *passwd = NULL;
+static int my_conv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *data)
+{
+ int i;
+ for (i = 0; i < num_msg; i++) {
+ switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ if (passwd == NULL) {
+ misc_conv(num_msg, msg, resp, data);
+ passwd = xstrdup(resp[i]->resp);
+ return PAM_SUCCESS;
+ }
+
+ resp[0] = xzalloc(sizeof(struct pam_response));
+ resp[0]->resp = passwd;
+ passwd = NULL;
+ resp[0]->resp_retcode = PAM_SUCCESS;
+ resp[1] = NULL;
+ return PAM_SUCCESS;
+
+ default:
+ break;
+ }
+ }
+
+ return PAM_SUCCESS;
+}
+# endif
+
static const struct pam_conv conv = {
misc_conv,
NULL
@@ -47,8 +143,8 @@ enum {
struct globals {
struct termios tty_attrs;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#if ENABLE_FEATURE_NOLOGIN
@@ -457,7 +553,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
else {
if (safe_waitpid(child_pid, NULL, 0) == -1)
bb_perror_msg("waitpid");
- update_utmp(child_pid, DEAD_PROCESS, NULL, NULL, NULL);
+ update_utmp_DEAD_PROCESS(child_pid);
}
IF_PAM(login_pam_end(pamh);)
return 0;
@@ -492,7 +588,8 @@ int login_main(int argc UNUSED_PARAM, char **argv)
}
#endif
- motd();
+ if (access(".hushlogin", F_OK) != 0)
+ motd();
if (pw->pw_uid == 0)
syslog(LOG_INFO, "root login%s", fromhost);
@@ -526,7 +623,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
signal(SIGINT, SIG_DFL);
/* Exec login shell with no additional parameters */
- run_shell(pw->pw_shell, 1, NULL, NULL);
+ run_shell(pw->pw_shell, 1, NULL);
/* return EXIT_FAILURE; - not reached */
}
diff --git a/loginutils/passwd.c b/loginutils/passwd.c
index 1509089..52b66ca 100644
--- a/loginutils/passwd.c
+++ b/loginutils/passwd.c
@@ -2,6 +2,30 @@
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PASSWD
+//config: bool "passwd"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: passwd changes passwords for user and group accounts. A normal user
+//config: may only change the password for his/her own account, the super user
+//config: may change the password for any account. The administrator of a group
+//config: may change the password for the group.
+//config:
+//config: Note that Busybox binary must be setuid root for this applet to
+//config: work properly.
+//config:
+//config:config FEATURE_PASSWD_WEAK_CHECK
+//config: bool "Check new passwords for weakness"
+//config: default y
+//config: depends on PASSWD
+//config: help
+//config: With this option passwd will refuse new passwords which are "weak".
+
+//applet:/* Needs to be run by root or be suid root - needs to change /etc/{passwd,shadow}: */
+//applet:IF_PASSWD(APPLET(passwd, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_PASSWD) += passwd.o
//usage:#define passwd_trivial_usage
//usage: "[OPTIONS] [USER]"
@@ -206,7 +230,7 @@ int passwd_main(int argc UNUSED_PARAM, char **argv)
/* LOGMODE_BOTH */
if (rc < 0)
bb_error_msg_and_die("can't update password file %s", filename);
- bb_info_msg("Password for %s changed by %s", name, myname);
+ bb_error_msg("password for %s changed by %s", name, myname);
/*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
skip:
diff --git a/loginutils/su.c b/loginutils/su.c
index c51f26f..24ffbde 100644
--- a/loginutils/su.c
+++ b/loginutils/su.c
@@ -4,19 +4,44 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-#include "libbb.h"
-#include <syslog.h>
+//config:config SU
+//config: bool "su"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: su is used to become another user during a login session.
+//config: Invoked without a username, su defaults to becoming the super user.
+//config:
+//config: Note that Busybox binary must be setuid root for this applet to
+//config: work properly.
+//config:
+//config:config FEATURE_SU_SYSLOG
+//config: bool "Enable su to write to syslog"
+//config: default y
+//config: depends on SU
+//config:
+//config:config FEATURE_SU_CHECKS_SHELLS
+//config: bool "Enable su to check user's shell to be listed in /etc/shells"
+//config: depends on SU
+//config: default y
+
+//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
+//applet:IF_SU(APPLET(su, BB_DIR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_SU) += su.o
//usage:#define su_trivial_usage
-//usage: "[OPTIONS] [-] [USER]"
+//usage: "[-lmp] [-] [-s SH] [USER [SCRIPT ARGS / -c 'CMD' ARG0 ARGS]]"
//usage:#define su_full_usage "\n\n"
//usage: "Run shell under USER (by default, root)\n"
-//usage: "\n -,-l Clear environment, run shell as login shell"
+//usage: "\n -,-l Clear environment, go to home dir, run shell as login shell"
//usage: "\n -p,-m Do not set new $HOME, $SHELL, $USER, $LOGNAME"
//usage: "\n -c CMD Command to pass to 'sh -c'"
//usage: "\n -s SH Shell to use instead of user's default"
+#include "libbb.h"
+#include <syslog.h>
+
#if ENABLE_FEATURE_SU_CHECKS_SHELLS
/* Return 1 if SHELL is a restricted shell (one not returned by
* getusershell), else 0, meaning it is a standard shell. */
@@ -56,8 +81,12 @@ int su_main(int argc UNUSED_PARAM, char **argv)
#endif
const char *old_user;
+ /* Note: we don't use "'+': stop at first non-option" idiom here.
+ * For su, "SCRIPT ARGS" or "-c CMD ARGS" do not stop option parsing:
+ * ARGS starting with dash will be treated as su options,
+ * not passed to shell. (Tested on util-linux 2.28).
+ */
flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
- //argc -= optind;
argv += optind;
if (argv[0] && LONE_DASH(argv[0])) {
@@ -101,6 +130,7 @@ int su_main(int argc UNUSED_PARAM, char **argv)
if (ENABLE_FEATURE_SU_SYSLOG)
syslog(LOG_NOTICE, "%c %s %s:%s",
'-', tty, old_user, opt_username);
+ bb_do_delay(LOGIN_FAIL_DELAY);
bb_error_msg_and_die("incorrect password");
}
@@ -136,8 +166,29 @@ int su_main(int argc UNUSED_PARAM, char **argv)
pw);
IF_SELINUX(set_current_security_context(NULL);)
+ if (opt_command) {
+ *--argv = opt_command;
+ *--argv = (char*)"-c";
+ }
+
+ /* A nasty ioctl exists which can stuff data into input queue:
+ * #include <sys/ioctl.h>
+ * int main() {
+ * const char *msg = "echo $UID\n";
+ * while (*msg) ioctl(0, TIOCSTI, *msg++);
+ * return 0;
+ * }
+ * With "su USER -c EXPLOIT" run by root, exploit can make root shell
+ * read as input and execute arbitrary command.
+ * It's debatable whether we need to protect against this
+ * (root may hesitate to run unknown scripts interactively).
+ *
+ * Some versions of su run -c CMD in a different session:
+ * ioctl(TIOCSTI) works only on the controlling tty.
+ */
+
/* Never returns */
- run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
+ run_shell(opt_shell, flags & SU_OPT_l, (const char**)argv);
/* return EXIT_FAILURE; - not reached */
}
diff --git a/loginutils/sulogin.c b/loginutils/sulogin.c
index c8e5fc9..2e32e2b 100644
--- a/loginutils/sulogin.c
+++ b/loginutils/sulogin.c
@@ -4,6 +4,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SULOGIN
+//config: bool "sulogin"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: sulogin is invoked when the system goes into single user
+//config: mode (this is done through an entry in inittab).
+
+//applet:IF_SULOGIN(APPLET(sulogin, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SULOGIN) += sulogin.o
//usage:#define sulogin_trivial_usage
//usage: "[-t N] [TTY]"
@@ -21,11 +32,18 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
struct passwd *pwd;
const char *shell;
+ /* Note: sulogin is not a suid app. It is meant to be run by init
+ * for single user / emergency mode. init starts it as root.
+ * Normal users (potentially malisious ones) can only run it under
+ * their UID, therefore no paranoia here is warranted:
+ * $LD_LIBRARY_PATH in env, TTY = /dev/sda
+ * are no more dangerous here than in e.g. cp applet.
+ */
+
logmode = LOGMODE_BOTH;
openlog(applet_name, 0, LOG_AUTH);
- opt_complementary = "t+"; /* -t N */
- getopt32(argv, "t:", &timeout);
+ getopt32(argv, "t:+", &timeout);
argv += optind;
if (argv[0]) {
@@ -36,18 +54,9 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
dup(0);
}
- /* Malicious use like "sulogin /dev/sda"? */
- if (!isatty(0) || !isatty(1) || !isatty(2)) {
- logmode = LOGMODE_SYSLOG;
- bb_error_msg_and_die("not a tty");
- }
-
- /* Clear dangerous stuff, set PATH */
- sanitize_env_if_suid();
-
- pwd = safegetpwuid(0);
+ pwd = getpwuid(0);
if (!pwd) {
- goto auth_error;
+ bb_error_msg_and_die("no password entry for root");
}
while (1) {
@@ -59,17 +68,17 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
);
if (r < 0) {
/* ^D, ^C, timeout, or read error */
- bb_info_msg("Normal startup");
+ bb_error_msg("normal startup");
return 0;
}
if (r > 0) {
break;
}
bb_do_delay(LOGIN_FAIL_DELAY);
- bb_info_msg("Login incorrect");
+ bb_error_msg("Login incorrect");
}
- bb_info_msg("System Maintenance Mode");
+ bb_error_msg("starting shell for system maintenance");
IF_SELINUX(renew_current_security_context());
@@ -80,8 +89,5 @@ int sulogin_main(int argc UNUSED_PARAM, char **argv)
shell = pwd->pw_shell;
/* Exec login shell with no additional parameters. Never returns. */
- run_shell(shell, 1, NULL, NULL);
-
- auth_error:
- bb_error_msg_and_die("no password entry for root");
+ run_shell(shell, 1, NULL);
}
diff --git a/loginutils/vlock.c b/loginutils/vlock.c
index 44b14e6..52ae607 100644
--- a/loginutils/vlock.c
+++ b/loginutils/vlock.c
@@ -13,7 +13,21 @@
* minimalistic vlock.
*/
/* Fixed by Erik Andersen to do passwords the tinylogin way...
- * It now works with md5, sha1, etc passwords. */
+ * It now works with md5, sha1, etc passwords.
+ */
+//config:config VLOCK
+//config: bool "vlock"
+//config: default y
+//config: help
+//config: Build the "vlock" applet which allows you to lock (virtual) terminals.
+//config:
+//config: Note that Busybox binary must be setuid root for this applet to
+//config: work properly.
+
+//applet:/* Needs to be run by root or be suid root - needs to change uid and gid: */
+//applet:IF_VLOCK(APPLET(vlock, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_VLOCK) += vlock.o
//usage:#define vlock_trivial_usage
//usage: "[-a]"
diff --git a/mailutils/Config.src b/mailutils/Config.src
index 2a9c5c0..e45a0f8 100644
--- a/mailutils/Config.src
+++ b/mailutils/Config.src
@@ -2,12 +2,6 @@ menu "Mail Utilities"
INSERT
-config MAKEMIME
- bool "makemime"
- default y
- help
- Create MIME-formatted messages.
-
config FEATURE_MIME_CHARSET
string "Default charset"
default "us-ascii"
@@ -15,41 +9,4 @@ config FEATURE_MIME_CHARSET
help
Default charset of the message.
-config POPMAILDIR
- bool "popmaildir"
- default y
- help
- Simple yet powerful POP3 mail popper. Delivers content
- of remote mailboxes to local Maildir.
-
-config FEATURE_POPMAILDIR_DELIVERY
- bool "Allow message filters and custom delivery program"
- default y
- depends on POPMAILDIR
- help
- Allow to use a custom program to filter the content
- of the message before actual delivery (-F "prog [args...]").
- Allow to use a custom program for message actual delivery
- (-M "prog [args...]").
-
-config REFORMIME
- bool "reformime"
- default y
- help
- Parse MIME-formatted messages.
-
-config FEATURE_REFORMIME_COMPAT
- bool "Accept and ignore options other than -x and -X"
- default y
- depends on REFORMIME
- help
- Accept (for compatibility only) and ignore options
- other than -x and -X.
-
-config SENDMAIL
- bool "sendmail"
- default y
- help
- Barebones sendmail.
-
endmenu
diff --git a/mailutils/mail.c b/mailutils/mail.c
index 199f644..a7e43c0 100644
--- a/mailutils/mail.c
+++ b/mailutils/mail.c
@@ -154,7 +154,7 @@ void FAST_FUNC encode_base64(char *fname, const char *text, const char *eol)
// encode the buffer we just read in
bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
if (fname) {
- printf("%s\n", eol);
+ puts(eol);
} else {
src_buf += size;
len -= size;
diff --git a/mailutils/makemime.c b/mailutils/makemime.c
index 1dadd71..78f78bb 100644
--- a/mailutils/makemime.c
+++ b/mailutils/makemime.c
@@ -6,6 +6,13 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MAKEMIME
+//config: bool "makemime"
+//config: default y
+//config: help
+//config: Create MIME-formatted messages.
+
+//applet:IF_MAKEMIME(APPLET(makemime, BB_DIR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o
@@ -184,9 +191,8 @@ int makemime_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
// parse options
- opt_complementary = "a::";
opts = getopt32(argv,
- "c:e:o:C:N:a:", // "m:j:",
+ "c:e:o:C:N:a:*", // "m:j:",
&content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers //, NULL, NULL
);
//argc -= optind;
diff --git a/mailutils/popmaildir.c b/mailutils/popmaildir.c
index 6203033..ffe3738 100644
--- a/mailutils/popmaildir.c
+++ b/mailutils/popmaildir.c
@@ -9,6 +9,24 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config POPMAILDIR
+//config: bool "popmaildir"
+//config: default y
+//config: help
+//config: Simple yet powerful POP3 mail popper. Delivers content
+//config: of remote mailboxes to local Maildir.
+//config:
+//config:config FEATURE_POPMAILDIR_DELIVERY
+//config: bool "Allow message filters and custom delivery program"
+//config: default y
+//config: depends on POPMAILDIR
+//config: help
+//config: Allow to use a custom program to filter the content
+//config: of the message before actual delivery (-F "prog [args...]").
+//config: Allow to use a custom program for message actual delivery
+//config: (-M "prog [args...]").
+
+//applet:IF_POPMAILDIR(APPLET(popmaildir, BB_DIR_USR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_POPMAILDIR) += popmaildir.o mail.o
@@ -107,9 +125,9 @@ int popmaildir_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
// parse options
- opt_complementary = "-1:dd:t+:R+:L+:H+";
+ opt_complementary = "-1:dd";
opts = getopt32(argv,
- "bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
+ "bdmVcasTkt:+" "R:+Z:L:+H:+" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
&timeout, NULL, NULL, NULL, &opt_nlines
IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
);
diff --git a/mailutils/reformime.c b/mailutils/reformime.c
index 8e7d455..e97bc01 100644
--- a/mailutils/reformime.c
+++ b/mailutils/reformime.c
@@ -6,6 +6,21 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config REFORMIME
+//config: bool "reformime"
+//config: default y
+//config: help
+//config: Parse MIME-formatted messages.
+//config:
+//config:config FEATURE_REFORMIME_COMPAT
+//config: bool "Accept and ignore options other than -x and -X"
+//config: default y
+//config: depends on REFORMIME
+//config: help
+//config: Accept (for compatibility only) and ignore options
+//config: other than -x and -X.
+
+//applet:IF_REFORMIME(APPLET(reformime, BB_DIR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_REFORMIME) += reformime.o mail.o
@@ -134,7 +149,6 @@ static int parse(const char *boundary, char **argv)
if (strcasecmp(type + 10, "mixed") != 0)
bb_error_msg_and_die("no support of content type '%s'", type);
parse(xfind_token(tokens, "boundary"), argv);
-
} else {
/* No, process one non-multipart section */
char *end;
@@ -266,9 +280,9 @@ int reformime_main(int argc UNUSED_PARAM, char **argv)
// parse options
// N.B. only -x and -X are supported so far
- opt_complementary = "x--X:X--x" IF_FEATURE_REFORMIME_COMPAT(":m::");
+ opt_complementary = "x--X:X--x";
opts = getopt32(argv,
- "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:h:o:O:"),
+ "x:X" IF_FEATURE_REFORMIME_COMPAT("deis:r:c:m:*h:o:O:"),
&opt_prefix
IF_FEATURE_REFORMIME_COMPAT(, NULL, NULL, &G.opt_charset, NULL, NULL, NULL, NULL)
);
diff --git a/mailutils/sendmail.c b/mailutils/sendmail.c
index b5aa1d1..8ddb782 100644
--- a/mailutils/sendmail.c
+++ b/mailutils/sendmail.c
@@ -6,40 +6,93 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SENDMAIL
+//config: bool "sendmail"
+//config: default y
+//config: help
+//config: Barebones sendmail.
+
+//applet:IF_SENDMAIL(APPLET(sendmail, BB_DIR_USR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_SENDMAIL) += sendmail.o mail.o
//usage:#define sendmail_trivial_usage
-//usage: "[OPTIONS] [RECIPIENT_EMAIL]..."
+//usage: "[-tv] [-f SENDER] [-amLOGIN 4<user_pass.txt | -auUSER -apPASS]"
+//usage: "\n [-w SECS] [-H 'PROG ARGS' | -S HOST] [RECIPIENT_EMAIL]..."
//usage:#define sendmail_full_usage "\n\n"
//usage: "Read email from stdin and send it\n"
//usage: "\nStandard options:"
//usage: "\n -t Read additional recipients from message body"
-//usage: "\n -f SENDER Sender (required)"
+//usage: "\n -f SENDER For use in MAIL FROM:<sender>. Can be empty string"
+//usage: "\n Default: -auUSER, or username of current UID"
//usage: "\n -o OPTIONS Various options. -oi implied, others are ignored"
-//usage: "\n -i -oi synonym. implied and ignored"
+//usage: "\n -i -oi synonym, implied and ignored"
//usage: "\n"
//usage: "\nBusybox specific options:"
//usage: "\n -v Verbose"
//usage: "\n -w SECS Network timeout"
-//usage: "\n -H 'PROG ARGS' Run connection helper"
-//usage: "\n Examples:"
-//usage: "\n -H 'exec openssl s_client -quiet -tls1 -starttls smtp"
-//usage: "\n -connect smtp.gmail.com:25' <email.txt"
-//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]"
-//usage: "\n -H 'exec openssl s_client -quiet -tls1"
-//usage: "\n -connect smtp.gmail.com:465' <email.txt"
-//usage: "\n [4<username_and_passwd.txt | -auUSER -apPASS]"
-//usage: "\n -S HOST[:PORT] Server"
-//usage: "\n -auUSER Username for AUTH LOGIN"
-//usage: "\n -apPASS Password for AUTH LOGIN"
-////usage: "\n -amMETHOD Authentication method. Ignored. LOGIN is implied"
+//usage: "\n -H 'PROG ARGS' Run connection helper. Examples:"
+//usage: "\n openssl s_client -quiet -tls1 -starttls smtp -connect smtp.gmail.com:25"
+//usage: "\n openssl s_client -quiet -tls1 -connect smtp.gmail.com:465"
+//usage: "\n $SMTP_ANTISPAM_DELAY: seconds to wait after helper connect"
+//usage: "\n -S HOST[:PORT] Server (default $SMTPHOST or 127.0.0.1)"
+//usage: "\n -amLOGIN Log in using AUTH LOGIN (-amCRAM-MD5 not supported)"
+//usage: "\n -auUSER Username for AUTH"
+//usage: "\n -apPASS Password for AUTH"
//usage: "\n"
-//usage: "\nOther options are silently ignored; -oi -t is implied"
+//usage: "\nIf no -a options are given, authentication is not done."
+//usage: "\nIf -amLOGIN is given but no -au/-ap, user/password is read from fd #4."
+//usage: "\nOther options are silently ignored; -oi is implied."
//usage: IF_MAKEMIME(
-//usage: "\nUse makemime to create emails with attachments"
+//usage: "\nUse makemime to create emails with attachments."
//usage: )
+/* Currently we don't sanitize or escape user-supplied SENDER and RECIPIENT_EMAILs.
+ * We may need to do so. For one, '.' in usernames seems to require escaping!
+ *
+ * From http://cr.yp.to/smtp/address.html:
+ *
+ * SMTP offers three ways to encode a character inside an address:
+ *
+ * "safe": the character, if it is not <>()[].,;:@, backslash,
+ * double-quote, space, or an ASCII control character;
+ * "quoted": the character, if it is not \012, \015, backslash,
+ * or double-quote; or
+ * "slashed": backslash followed by the character.
+ *
+ * An encoded box part is either (1) a sequence of one or more slashed
+ * or safe characters or (2) a double quote, a sequence of zero or more
+ * slashed or quoted characters, and a double quote. It represents
+ * the concatenation of the characters encoded inside it.
+ *
+ * For example, the encoded box parts
+ * angels
+ * \a\n\g\e\l\s
+ * "\a\n\g\e\l\s"
+ * "angels"
+ * "ang\els"
+ * all represent the 6-byte string "angels", and the encoded box parts
+ * a\,comma
+ * \a\,\c\o\m\m\a
+ * "a,comma"
+ * all represent the 7-byte string "a,comma".
+ *
+ * An encoded address contains
+ * the byte <;
+ * optionally, a route followed by a colon;
+ * an encoded box part, the byte @, and a domain; and
+ * the byte >.
+ *
+ * It represents an Internet mail address, given by concatenating
+ * the string represented by the encoded box part, the byte @,
+ * and the domain. For example, the encoded addresses
+ * <God@heaven.af.mil>
+ * <\God@heaven.af.mil>
+ * <"God"@heaven.af.mil>
+ * <@gateway.af.mil,@uucp.local:"\G\o\d"@heaven.af.mil>
+ * all represent the Internet mail address "God@heaven.af.mil".
+ */
+
#include "libbb.h"
#include "mail.h"
@@ -162,8 +215,8 @@ static void rcptto_list(const char *list)
int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int sendmail_main(int argc UNUSED_PARAM, char **argv)
{
- char *opt_connect = opt_connect;
- char *opt_from;
+ char *opt_connect;
+ char *opt_from = NULL;
char *s;
llist_t *list = NULL;
char *host = sane_address(safe_gethostname());
@@ -194,17 +247,22 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// init global variables
INIT_G();
+ // default HOST[:PORT] is $SMTPHOST, or localhost
+ opt_connect = getenv("SMTPHOST");
+ if (!opt_connect)
+ opt_connect = (char *)"127.0.0.1";
+
// save initial stdin since body is piped!
xdup2(STDIN_FILENO, 3);
G.fp0 = xfdopen_for_read(3);
// parse options
- // -v is a counter, -f is required. -H and -S are mutually exclusive, -a is a list
- opt_complementary = "vv:f:w+:H--S:S--H:a::";
+ // -v is a counter, -H and -S are mutually exclusive, -a is a list
+ opt_complementary = "vv:H--S:S--H";
// N.B. since -H and -S are mutually exclusive they do not interfere in opt_connect
// -a is for ssmtp (http://downloads.openwrt.org/people/nico/man/man8/ssmtp.8.html) compatibility,
// it is still under development.
- opts = getopt32(argv, "tf:o:iw:H:S:a::v", &opt_from, NULL,
+ opts = getopt32(argv, "tf:o:iw:+H:S:a:*:v", &opt_from, NULL,
&timeout, &opt_connect, &opt_connect, &list, &verbose);
//argc -= optind;
argv += optind;
@@ -223,12 +281,13 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// G.method = xstrdup(a+1);
}
// N.B. list == NULL here
- //bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);
+ //bb_error_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);
// connect to server
// connection helper ordered? ->
if (opts & OPT_H) {
+ const char *delay;
const char *args[] = { "sh", "-c", opt_connect, NULL };
// plug it in
launch_helper(args);
@@ -247,7 +306,12 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// before 220 reached it. The code below is unsafe in this regard:
// in non-STARTTLSed case, we potentially send NOOP before 220
// is sent by server.
- // Ideas? (--delay SECS opt? --assume-starttls-helper opt?)
+ //
+ // If $SMTP_ANTISPAM_DELAY is set, we pause before sending NOOP.
+ //
+ delay = getenv("SMTP_ANTISPAM_DELAY");
+ if (delay)
+ sleep(atoi(delay));
code = smtp_check("NOOP", -1);
if (code == 220)
// we got 220 - this is not STARTTLSed connection,
@@ -259,14 +323,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
} else {
// vanilla connection
int fd;
- // host[:port] not explicitly specified? -> use $SMTPHOST
- // no $SMTPHOST? -> use localhost
- if (!(opts & OPT_S)) {
- opt_connect = getenv("SMTPHOST");
- if (!opt_connect)
- opt_connect = (char *)"127.0.0.1";
- }
- // do connect
fd = create_and_connect_stream_or_die(opt_connect, 25);
// and make ourselves a simple IO filter
xmove_fd(fd, STDIN_FILENO);
@@ -279,7 +335,6 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// we should start with modern EHLO
if (250 != smtp_checkp("EHLO %s", host, -1))
smtp_checkp("HELO %s", host, 250);
- free(host);
// perform authentication
if (opts & OPT_a) {
@@ -304,13 +359,14 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// Since reading from console may defeat usability, the solution is either to read from a predefined
// file descriptor (e.g. 4), or again from a secured file.
- // got no sender address? -> use system username as a resort
- // N.B. we marked -f as required option!
- //if (!G.user) {
- // // N.B. IMHO getenv("USER") can be way easily spoofed!
- // G.user = xuid2uname(getuid());
- // opt_from = xasprintf("%s@%s", G.user, domain);
- //}
+ // got no sender address? use auth name, then UID username as a last resort
+ if (!opt_from) {
+ opt_from = xasprintf("%s@%s",
+ G.user ? G.user : xuid2uname(getuid()),
+ xgethostbyname(host)->h_name);
+ }
+ free(host);
+
smtp_checkp("MAIL FROM:<%s>", opt_from, 250);
// process message
@@ -328,7 +384,7 @@ int sendmail_main(int argc UNUSED_PARAM, char **argv)
// N.B. we need to escape the leading dot regardless of
// whether it is single or not character on the line
if ('.' == s[0] /*&& '\0' == s[1] */)
- printf(".");
+ bb_putchar('.');
// dump read line
send_r_n(s);
free(s);
diff --git a/make_single_applets.sh b/make_single_applets.sh
new file mode 100755
index 0000000..00f502e
--- a/dev/null
+++ b/make_single_applets.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# This script expects that the tree was built with the desired .config:
+# in particular, it expects that include/applets.h is generated already.
+#
+# The script will try to rebuild each enabled applet in isolation.
+# All other options which chose general bbox config, applet features, etc,
+# are not modified for the builds.
+
+makeopts="-j9"
+
+# The list of all applet config symbols
+test -f include/applets.h || { echo "No include/applets.h file"; exit 1; }
+apps="`
+grep ^IF_ include/applets.h \
+| grep -v ^IF_FEATURE_ \
+| sed 's/IF_\([A-Z0-9._-]*\)(.*/\1/' \
+| sort | uniq
+`"
+
+# Take existing config
+test -f .config || { echo "No .config file"; exit 1; }
+cfg="`cat .config`"
+
+# Make a config with all applet symbols off
+allno="$cfg"
+for app in $apps; do
+ allno="`echo "$allno" | sed "s/^CONFIG_${app}=y\$/# CONFIG_${app} is not set/"`"
+done
+#echo "$allno" >.config_allno
+
+# Turn on each applet individually and build single-applet executable
+fail=0
+for app in $apps; do
+ # Only if it was indeed originally enabled...
+ { echo "$cfg" | grep -q "^CONFIG_${app}=y\$"; } || continue
+
+ echo "Making ${app}..."
+ mv .config .config.SV
+ echo "CONFIG_${app}=y" >.config
+ echo "$allno" | sed "/^# CONFIG_${app} is not set\$/d" >>.config
+
+ if test x"${app}" != x"SH_IS_ASH" && test x"${app}" != x"SH_IS_HUSH"; then
+ # $allno has all choices for "sh" aliasing set to off.
+ # "sh" aliasing defaults to "ash", not none.
+ # without this fix, "make oldconfig" sets it wrong,
+ # resulting in NUM_APPLETS = 2 (the second applet is "sh")
+ sed '/CONFIG_SH_IS_NONE/d' -i .config
+ echo "CONFIG_SH_IS_NONE=y" >>.config
+ fi
+
+ if ! yes '' | make oldconfig >busybox_make_${app}.log 2>&1; then
+ : $((fail++))
+ echo "Config error for ${app}"
+ mv .config busybox_config_${app}
+ elif ! make $makeopts >>busybox_make_${app}.log 2>&1; then
+ : $((fail++))
+ echo "Build error for ${app}"
+ mv .config busybox_config_${app}
+ elif ! grep -q '^#define NUM_APPLETS 1$' include/NUM_APPLETS.h; then
+ mv busybox busybox_${app}
+ : $((fail++))
+ echo "NUM_APPLETS != 1 for ${app}: `cat include/NUM_APPLETS.h`"
+ mv .config busybox_config_${app}
+ else
+ mv busybox busybox_${app}
+ rm busybox_make_${app}.log
+ fi
+ mv .config.SV .config
+ #exit
+done
+touch .config # or else next "make" can be confused
+echo "Failures: $fail"
+test $fail = 0 # set exitcode
diff --git a/miscutils/Config.src b/miscutils/Config.src
index 1da9800..7325fb8 100644
--- a/miscutils/Config.src
+++ b/miscutils/Config.src
@@ -7,588 +7,4 @@ menu "Miscellaneous Utilities"
INSERT
-config ADJTIMEX
- bool "adjtimex"
- default y
- select PLATFORM_LINUX
- help
- Adjtimex reads and optionally sets adjustment parameters for
- the Linux clock adjustment algorithm.
-
-config BBCONFIG
- bool "bbconfig"
- default n
- help
- The bbconfig applet will print the config file with which
- busybox was built.
-
-config FEATURE_COMPRESS_BBCONFIG
- bool "Compress bbconfig data"
- default y
- depends on BBCONFIG
- help
- Store bbconfig data in compressed form, uncompress them on-the-fly
- before output.
-
- If you have a really tiny busybox with few applets enabled (and
- bunzip2 isn't one of them), the overhead of the decompressor might
- be noticeable. Also, if you run executables directly from ROM
- and have very little memory, this might not be a win. Otherwise,
- you probably want this.
-
-config BEEP
- bool "beep"
- default y
- select PLATFORM_LINUX
- help
- The beep applets beeps in a given freq/Hz.
-
-config FEATURE_BEEP_FREQ
- int "default frequency"
- range 0 2147483647
- default 4000
- depends on BEEP
- help
- Frequency for default beep.
-
-config FEATURE_BEEP_LENGTH_MS
- int "default length"
- range 0 2147483647
- default 30
- depends on BEEP
- help
- Length in ms for default beep.
-
-config CHAT
- bool "chat"
- default y
- help
- Simple chat utility.
-
-config FEATURE_CHAT_NOFAIL
- bool "Enable NOFAIL expect strings"
- depends on CHAT
- default y
- help
- When enabled expect strings which are started with a dash trigger
- no-fail mode. That is when expectation is not met within timeout
- the script is not terminated but sends next SEND string and waits
- for next EXPECT string. This allows to compose far more flexible
- scripts.
-
-config FEATURE_CHAT_TTY_HIFI
- bool "Force STDIN to be a TTY"
- depends on CHAT
- default n
- help
- Original chat always treats STDIN as a TTY device and sets for it
- so-called raw mode. This option turns on such behaviour.
-
-config FEATURE_CHAT_IMPLICIT_CR
- bool "Enable implicit Carriage Return"
- depends on CHAT
- default y
- help
- When enabled make chat to terminate all SEND strings with a "\r"
- unless "\c" is met anywhere in the string.
-
-config FEATURE_CHAT_SWALLOW_OPTS
- bool "Swallow options"
- depends on CHAT
- default y
- help
- Busybox chat require no options. To make it not fail when used
- in place of original chat (which has a bunch of options) turn
- this on.
-
-config FEATURE_CHAT_SEND_ESCAPES
- bool "Support weird SEND escapes"
- depends on CHAT
- default y
- help
- Original chat uses some escape sequences in SEND arguments which
- are not sent to device but rather performs special actions.
- E.g. "\K" means to send a break sequence to device.
- "\d" delays execution for a second, "\p" -- for a 1/100 of second.
- Before turning this option on think twice: do you really need them?
-
-config FEATURE_CHAT_VAR_ABORT_LEN
- bool "Support variable-length ABORT conditions"
- depends on CHAT
- default y
- help
- Original chat uses fixed 50-bytes length ABORT conditions. Say N here.
-
-config FEATURE_CHAT_CLR_ABORT
- bool "Support revoking of ABORT conditions"
- depends on CHAT
- default y
- help
- Support CLR_ABORT directive.
-
-config CHRT
- bool "chrt"
- default y
- help
- manipulate real-time attributes of a process.
- This requires sched_{g,s}etparam support in your libc.
-
-config CROND
- bool "crond"
- default y
- select FEATURE_SYSLOG
- help
- Crond is a background daemon that parses individual crontab
- files and executes commands on behalf of the users in question.
- This is a port of dcron from slackware. It uses files of the
- format /var/spool/cron/crontabs/<username> files, for example:
- $ cat /var/spool/cron/crontabs/root
- # Run daily cron jobs at 4:40 every day:
- 40 4 * * * /etc/cron/daily > /dev/null 2>&1
-
-config FEATURE_CROND_D
- bool "Support option -d to redirect output to stderr"
- depends on CROND
- default y
- help
- -d sets loglevel to 0 (most verbose) and directs all output to stderr.
-
-config FEATURE_CROND_CALL_SENDMAIL
- bool "Report command output via email (using sendmail)"
- default y
- depends on CROND
- help
- Command output will be sent to corresponding user via email.
-
-config FEATURE_CROND_DIR
- string "crond spool directory"
- default "/var/spool/cron"
- depends on CROND || CRONTAB
- help
- Location of crond spool.
-
-config CRONTAB
- bool "crontab"
- default y
- help
- Crontab manipulates the crontab for a particular user. Only
- the superuser may specify a different user and/or crontab directory.
- Note that Busybox binary must be setuid root for this applet to
- work properly.
-
-config DC
- bool "dc"
- default y
- help
- Dc is a reverse-polish desk calculator which supports unlimited
- precision arithmetic.
-
-config FEATURE_DC_LIBM
- bool "Enable power and exp functions (requires libm)"
- default y
- depends on DC
- help
- Enable power and exp functions.
- NOTE: This will require libm to be present for linking.
-
-config DEVFSD
- bool "devfsd (obsolete)"
- default n
- select PLATFORM_LINUX
- select FEATURE_SYSLOG
- help
- This is deprecated and should NOT be used anymore.
- Use linux >= 2.6 (optionally with hotplug) and mdev instead!
- See docs/mdev.txt for detailed instructions on how to use mdev
- instead.
-
- Provides compatibility with old device names on a devfs systems.
- You should set it to true if you have devfs enabled.
- The following keywords in devsfd.conf are supported:
- "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
- "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
- "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
-
- But only if they are written UPPERCASE!!!!!!!!
-
-config DEVFSD_MODLOAD
- bool "Adds support for MODLOAD keyword in devsfd.conf"
- default y
- depends on DEVFSD
- help
- This actually doesn't work with busybox modutils but needs
- the external modutils.
-
-config DEVFSD_FG_NP
- bool "Enables the -fg and -np options"
- default y
- depends on DEVFSD
- help
- -fg Run the daemon in the foreground.
- -np Exit after parsing the configuration file.
- Do not poll for events.
-
-config DEVFSD_VERBOSE
- bool "Increases logging (and size)"
- default y
- depends on DEVFSD
- help
- Increases logging to stderr or syslog.
-
-config FEATURE_DEVFS
- bool "Use devfs names for all devices (obsolete)"
- default n
- select PLATFORM_LINUX
- help
- This is obsolete and should NOT be used anymore.
- Use linux >= 2.6 (optionally with hotplug) and mdev instead!
-
- For legacy systems -- if there is no way around devfsd -- this
- tells busybox to look for names like /dev/loop/0 instead of
- /dev/loop0. If your /dev directory has normal names instead of
- devfs names, you don't want this.
-
-config DEVMEM
- bool "devmem"
- default y
- help
- devmem is a small program that reads and writes from physical
- memory using /dev/mem.
-
-config EJECT
- bool "eject"
- default y
- select PLATFORM_LINUX
- help
- Used to eject cdroms. (defaults to /dev/cdrom)
-
-config FEATURE_EJECT_SCSI
- bool "SCSI support"
- default y
- depends on EJECT
- help
- Add the -s option to eject, this allows to eject SCSI-Devices and
- usb-storage devices.
-
-config FBSPLASH
- bool "fbsplash"
- default y
- select PLATFORM_LINUX
- help
- Shows splash image and progress bar on framebuffer device.
- Can be used during boot phase of an embedded device. ~2kb.
- Usage:
- - use kernel option 'vga=xxx' or otherwise enable fb device.
- - put somewhere fbsplash.cfg file and an image in .ppm format.
- - $ setsid fbsplash [params] &
- -c: hide cursor
- -d /dev/fbN: framebuffer device (if not /dev/fb0)
- -s path_to_image_file (can be "-" for stdin)
- -i path_to_cfg_file (can be "-" for stdin)
- -f path_to_fifo (can be "-" for stdin)
- - if you want to run it only in presence of kernel parameter:
- grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] &
- - commands for fifo:
- "NN" (ASCII decimal number) - percentage to show on progress bar
- "exit" - well you guessed it
-
-config FLASHCP
- bool "flashcp"
- default n # doesn't build on Ubuntu 8.04
- help
- The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7.
- This utility is used to copy images into a MTD device.
-
-config FLASH_LOCK
- bool "flash_lock"
- default n # doesn't build on Ubuntu 8.04
- help
- The flash_lock binary from mtd-utils as of git head 5ec0c10d0. This
- utility locks part or all of the flash device.
-
-config FLASH_UNLOCK
- bool "flash_unlock"
- default n # doesn't build on Ubuntu 8.04
- help
- The flash_unlock binary from mtd-utils as of git head 5ec0c10d0. This
- utility unlocks part or all of the flash device.
-
-config FLASH_ERASEALL
- bool "flash_eraseall"
- default n # doesn't build on Ubuntu 8.04
- help
- The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
- This utility is used to erase the whole MTD device.
-
-config IONICE
- bool "ionice"
- default y
- select PLATFORM_LINUX
- help
- Set/set program io scheduling class and priority
- Requires kernel >= 2.6.13
-
-config INOTIFYD
- bool "inotifyd"
- default n # doesn't build on Knoppix 5
- help
- Simple inotify daemon. Reports filesystem changes. Requires
- kernel >= 2.6.13
-
-config LAST
- bool "last"
- default y
- depends on FEATURE_WTMP
- help
- 'last' displays a list of the last users that logged into the system.
-
-choice
- prompt "Choose last implementation"
- depends on LAST
- default FEATURE_LAST_FANCY
-
-config FEATURE_LAST_SMALL
- bool "small"
- help
- This is a small version of last with just the basic set of
- features.
-
-config FEATURE_LAST_FANCY
- bool "huge"
- help
- 'last' displays detailed information about the last users that
- logged into the system (mimics sysvinit last). +900 bytes.
-endchoice
-
-config HDPARM
- bool "hdparm"
- default y
- select PLATFORM_LINUX
- help
- Get/Set hard drive parameters. Primarily intended for ATA
- drives. Adds about 13k (or around 30k if you enable the
- FEATURE_HDPARM_GET_IDENTITY option)....
-
-config FEATURE_HDPARM_GET_IDENTITY
- bool "Support obtaining detailed information directly from drives"
- default y
- depends on HDPARM
- help
- Enables the -I and -i options to obtain detailed information
- directly from drives about their capabilities and supported ATA
- feature set. If no device name is specified, hdparm will read
- identify data from stdin. Enabling this option will add about 16k...
-
-config FEATURE_HDPARM_HDIO_SCAN_HWIF
- bool "Register an IDE interface (DANGEROUS)"
- default y
- depends on HDPARM
- help
- Enables the 'hdparm -R' option to register an IDE interface.
- This is dangerous stuff, so you should probably say N.
-
-config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
- bool "Un-register an IDE interface (DANGEROUS)"
- default y
- depends on HDPARM
- help
- Enables the 'hdparm -U' option to un-register an IDE interface.
- This is dangerous stuff, so you should probably say N.
-
-config FEATURE_HDPARM_HDIO_DRIVE_RESET
- bool "Perform device reset (DANGEROUS)"
- default y
- depends on HDPARM
- help
- Enables the 'hdparm -w' option to perform a device reset.
- This is dangerous stuff, so you should probably say N.
-
-config FEATURE_HDPARM_HDIO_TRISTATE_HWIF
- bool "Tristate device for hotswap (DANGEROUS)"
- default y
- depends on HDPARM
- help
- Enables the 'hdparm -x' option to tristate device for hotswap,
- and the '-b' option to get/set bus state. This is dangerous
- stuff, so you should probably say N.
-
-config FEATURE_HDPARM_HDIO_GETSET_DMA
- bool "Get/set using_dma flag"
- default y
- depends on HDPARM
- help
- Enables the 'hdparm -d' option to get/set using_dma flag.
-
-config MAKEDEVS
- bool "makedevs"
- default y
- help
- 'makedevs' is a utility used to create a batch of devices with
- one command.
-
- There are two choices for command line behaviour, the interface
- as used by LEAF/Linux Router Project, or a device table file.
-
- 'leaf' is traditionally what busybox follows, it allows multiple
- devices of a particluar type to be created per command.
- e.g. /dev/hda[0-9]
- Device properties are passed as command line arguments.
-
- 'table' reads device properties from a file or stdin, allowing
- a batch of unrelated devices to be made with one command.
- User/group names are allowed as an alternative to uid/gid.
-
-choice
- prompt "Choose makedevs behaviour"
- depends on MAKEDEVS
- default FEATURE_MAKEDEVS_TABLE
-
-config FEATURE_MAKEDEVS_LEAF
- bool "leaf"
-
-config FEATURE_MAKEDEVS_TABLE
- bool "table"
-
-endchoice
-
-config MAN
- bool "man"
- default y
- help
- Format and display manual pages.
-
-config MICROCOM
- bool "microcom"
- default y
- help
- The poor man's minicom utility for chatting with serial port devices.
-
-config MOUNTPOINT
- bool "mountpoint"
- default y
- help
- mountpoint checks if the directory is a mountpoint.
-
-config MT
- bool "mt"
- default y
- help
- mt is used to control tape devices. You can use the mt utility
- to advance or rewind a tape past a specified number of archive
- files on the tape.
-
-config RAIDAUTORUN
- bool "raidautorun"
- default y
- select PLATFORM_LINUX
- help
- raidautorun tells the kernel md driver to
- search and start RAID arrays.
-
-config READAHEAD
- bool "readahead"
- default y
- depends on LFS
- select PLATFORM_LINUX
- help
- Preload the files listed on the command line into RAM cache so that
- subsequent reads on these files will not block on disk I/O.
-
- This applet just calls the readahead(2) system call on each file.
- It is mainly useful in system startup scripts to preload files
- or executables before they are used. When used at the right time
- (in particular when a CPU bound process is running) it can
- significantly speed up system startup.
-
- As readahead(2) blocks until each file has been read, it is best to
- run this applet as a background job.
-
-config RUNLEVEL
- bool "runlevel"
- default y
- depends on FEATURE_UTMP
- help
- find the current and previous system runlevel.
-
- This applet uses utmp but does not rely on busybox supporing
- utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
-
-config RX
- bool "rx"
- default y
- select PLATFORM_LINUX
- help
- Receive files using the Xmodem protocol.
-
-config SETSID
- bool "setsid"
- default y
- help
- setsid runs a program in a new session
-
-config STRINGS
- bool "strings"
- default y
- help
- strings prints the printable character sequences for each file
- specified.
-
-config TASKSET
- bool "taskset"
- default n # doesn't build on some non-x86 targets (m68k)
- help
- Retrieve or set a processes's CPU affinity.
- This requires sched_{g,s}etaffinity support in your libc.
-
-config FEATURE_TASKSET_FANCY
- bool "Fancy output"
- default y
- depends on TASKSET
- help
- Add code for fancy output. This merely silences a compiler-warning
- and adds about 135 Bytes. May be needed for machines with alot
- of CPUs.
-
-config TIME
- bool "time"
- default y
- help
- The time command runs the specified program with the given arguments.
- When the command finishes, time writes a message to standard output
- giving timing statistics about this program run.
-
-config TIMEOUT
- bool "timeout"
- default y
- help
- Runs a program and watches it. If it does not terminate in
- specified number of seconds, it is sent a signal.
-
-config TTYSIZE
- bool "ttysize"
- default y
- help
- A replacement for "stty size". Unlike stty, can report only width,
- only height, or both, in any order. It also does not complain on
- error, but returns default 80x24.
- Usage in shell scripts: width=`ttysize w`.
-
-config VOLNAME
- bool "volname"
- default y
- help
- Prints a CD-ROM volume name.
-
-config WATCHDOG
- bool "watchdog"
- default y
- select PLATFORM_LINUX
- help
- The watchdog utility is used with hardware or software watchdog
- device drivers. It opens the specified watchdog device special file
- and periodically writes a magic character to the device. If the
- watchdog applet ever fails to write the magic character within a
- certain amount of time, the watchdog device assumes the system has
- hung, and will cause the hardware to reboot.
-
endmenu
diff --git a/miscutils/Kbuild.src b/miscutils/Kbuild.src
index 9e164f1..6b4fb74 100644
--- a/miscutils/Kbuild.src
+++ b/miscutils/Kbuild.src
@@ -7,42 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_ADJTIMEX) += adjtimex.o
-lib-$(CONFIG_BBCONFIG) += bbconfig.o
-lib-$(CONFIG_BEEP) += beep.o
-lib-$(CONFIG_CHAT) += chat.o
-lib-$(CONFIG_CHRT) += chrt.o
-lib-$(CONFIG_CROND) += crond.o
-lib-$(CONFIG_CRONTAB) += crontab.o
-lib-$(CONFIG_DC) += dc.o
-lib-$(CONFIG_DEVFSD) += devfsd.o
-lib-$(CONFIG_DEVMEM) += devmem.o
-lib-$(CONFIG_EJECT) += eject.o
-lib-$(CONFIG_FBSPLASH) += fbsplash.o
-lib-$(CONFIG_FLASHCP) += flashcp.o
-lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
-lib-$(CONFIG_FLASH_LOCK) += flash_lock_unlock.o
-lib-$(CONFIG_FLASH_UNLOCK) += flash_lock_unlock.o
-lib-$(CONFIG_IONICE) += ionice.o
-lib-$(CONFIG_HDPARM) += hdparm.o
-lib-$(CONFIG_INOTIFYD) += inotifyd.o
-lib-$(CONFIG_FEATURE_LAST_SMALL)+= last.o
-lib-$(CONFIG_FEATURE_LAST_FANCY)+= last_fancy.o
-lib-$(CONFIG_LESS) += less.o
-lib-$(CONFIG_MAKEDEVS) += makedevs.o
-lib-$(CONFIG_MAN) += man.o
-lib-$(CONFIG_MICROCOM) += microcom.o
-lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
-lib-$(CONFIG_MT) += mt.o
-lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
-lib-$(CONFIG_READAHEAD) += readahead.o
-lib-$(CONFIG_RUNLEVEL) += runlevel.o
-lib-$(CONFIG_RX) += rx.o
-lib-$(CONFIG_SETSID) += setsid.o
-lib-$(CONFIG_STRINGS) += strings.o
-lib-$(CONFIG_TASKSET) += taskset.o
-lib-$(CONFIG_TIME) += time.o
-lib-$(CONFIG_TIMEOUT) += timeout.o
-lib-$(CONFIG_TTYSIZE) += ttysize.o
-lib-$(CONFIG_VOLNAME) += volname.o
-lib-$(CONFIG_WATCHDOG) += watchdog.o
diff --git a/miscutils/adjtimex.c b/miscutils/adjtimex.c
index 1bc05b5..b4b54c4 100644
--- a/miscutils/adjtimex.c
+++ b/miscutils/adjtimex.c
@@ -10,16 +10,27 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config ADJTIMEX
+//config: bool "adjtimex"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Adjtimex reads and optionally sets adjustment parameters for
+//config: the Linux clock adjustment algorithm.
+
+//applet:IF_ADJTIMEX(APPLET(adjtimex, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ADJTIMEX) += adjtimex.o
//usage:#define adjtimex_trivial_usage
//usage: "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]"
//usage:#define adjtimex_full_usage "\n\n"
-//usage: "Read and optionally set system timebase parameters. See adjtimex(2)\n"
+//usage: "Read or set kernel time variables. See adjtimex(2)\n"
//usage: "\n -q Quiet"
//usage: "\n -o OFF Time offset, microseconds"
//usage: "\n -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)"
-//usage: "\n (positive values make clock run faster)"
//usage: "\n -t TICK Microseconds per tick, usually 10000"
+//usage: "\n (positive -t or -f values make clock run faster)"
//usage: "\n -p TCONST"
#include "libbb.h"
@@ -30,7 +41,7 @@ extern int adjtimex (struct timex *);
# include <sys/timex.h>
#endif
-static const uint16_t statlist_bit[] = {
+static const uint16_t statlist_bit[] ALIGN2 = {
STA_PLL,
STA_PPSFREQ,
STA_PPSTIME,
@@ -46,7 +57,7 @@ static const uint16_t statlist_bit[] = {
STA_CLOCKERR,
0
};
-static const char statlist_name[] =
+static const char statlist_name[] ALIGN1 =
"PLL" "\0"
"PPSFREQ" "\0"
"PPSTIME" "\0"
@@ -62,7 +73,7 @@ static const char statlist_name[] =
"CLOCKERR"
;
-static const char ret_code_descript[] =
+static const char ret_code_descript[] ALIGN1 =
"clock synchronized" "\0"
"insert leap second" "\0"
"delete leap second" "\0"
@@ -112,13 +123,13 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
}
if (!(opt & OPT_quiet)) {
- int sep;
+ const char *sep;
const char *name;
printf(
" mode: %d\n"
- "-o offset: %ld\n"
- "-f frequency: %ld\n"
+ "-o offset: %ld us\n"
+ "-f freq.adjust: %ld (65536 = 1ppm)\n"
" maxerror: %ld\n"
" esterror: %ld\n"
" status: %d (",
@@ -126,15 +137,14 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
txc.esterror, txc.status);
/* representative output of next code fragment:
- "PLL | PPSTIME" */
+ * "PLL | PPSTIME"
+ */
name = statlist_name;
- sep = 0;
+ sep = "";
for (i = 0; statlist_bit[i]; i++) {
if (txc.status & statlist_bit[i]) {
- if (sep)
- fputs(" | ", stdout);
- fputs(name, stdout);
- sep = 1;
+ printf("%s%s", sep, name);
+ sep = " | ";
}
name += strlen(name) + 1;
}
@@ -144,9 +154,9 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv)
descript = nth_string(ret_code_descript, ret);
printf(")\n"
"-p timeconstant: %ld\n"
- " precision: %ld\n"
+ " precision: %ld us\n"
" tolerance: %ld\n"
- "-t tick: %ld\n"
+ "-t tick: %ld us\n"
" time.tv_sec: %ld\n"
" time.tv_usec: %ld\n"
" return value: %d (%s)\n",
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c
index e5f4eb3..4781a42 100644
--- a/miscutils/bbconfig.c
+++ b/miscutils/bbconfig.c
@@ -1,6 +1,30 @@
/* vi: set sw=4 ts=4: */
/* This file was released into the public domain by Paul Fox.
*/
+//config:config BBCONFIG
+//config: bool "bbconfig"
+//config: default n
+//config: help
+//config: The bbconfig applet will print the config file with which
+//config: busybox was built.
+//config:
+//config:config FEATURE_COMPRESS_BBCONFIG
+//config: bool "Compress bbconfig data"
+//config: default y
+//config: depends on BBCONFIG
+//config: help
+//config: Store bbconfig data in compressed form, uncompress them on-the-fly
+//config: before output.
+//config:
+//config: If you have a really tiny busybox with few applets enabled (and
+//config: bunzip2 isn't one of them), the overhead of the decompressor might
+//config: be noticeable. Also, if you run executables directly from ROM
+//config: and have very little memory, this might not be a win. Otherwise,
+//config: you probably want this.
+
+//applet:IF_BBCONFIG(APPLET(bbconfig, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BBCONFIG) += bbconfig.o
//usage:#define bbconfig_trivial_usage
//usage: ""
diff --git a/miscutils/beep.c b/miscutils/beep.c
index 910e03e..14802b5 100644
--- a/miscutils/beep.c
+++ b/miscutils/beep.c
@@ -7,6 +7,32 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
*/
+//config:config BEEP
+//config: bool "beep"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The beep applets beeps in a given freq/Hz.
+//config:
+//config:config FEATURE_BEEP_FREQ
+//config: int "default frequency"
+//config: range 0 2147483647
+//config: default 4000
+//config: depends on BEEP
+//config: help
+//config: Frequency for default beep.
+//config:
+//config:config FEATURE_BEEP_LENGTH_MS
+//config: int "default length"
+//config: range 0 2147483647
+//config: default 30
+//config: depends on BEEP
+//config: help
+//config: Length in ms for default beep.
+
+//applet:IF_BEEP(APPLET(beep, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BEEP) += beep.o
//usage:#define beep_trivial_usage
//usage: "-f FREQ -l LEN -d DELAY -r COUNT -n"
@@ -88,7 +114,7 @@ int beep_main(int argc, char **argv)
bb_show_usage();
}
while (rep) {
-//bb_info_msg("rep[%d] freq=%d, length=%d, delay=%d", rep, freq, length, delay);
+//bb_error_msg("rep[%d] freq=%d, length=%d, delay=%d", rep, freq, length, delay);
xioctl(speaker, KIOCSOUND, (void*)(uintptr_t)tickrate_div_freq);
usleep(1000 * length);
ioctl(speaker, KIOCSOUND, (void*)0);
diff --git a/miscutils/chat.c b/miscutils/chat.c
index bd2abc2..dc85f82 100644
--- a/miscutils/chat.c
+++ b/miscutils/chat.c
@@ -7,6 +7,76 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config CHAT
+//config: bool "chat"
+//config: default y
+//config: help
+//config: Simple chat utility.
+//config:
+//config:config FEATURE_CHAT_NOFAIL
+//config: bool "Enable NOFAIL expect strings"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: When enabled expect strings which are started with a dash trigger
+//config: no-fail mode. That is when expectation is not met within timeout
+//config: the script is not terminated but sends next SEND string and waits
+//config: for next EXPECT string. This allows to compose far more flexible
+//config: scripts.
+//config:
+//config:config FEATURE_CHAT_TTY_HIFI
+//config: bool "Force STDIN to be a TTY"
+//config: depends on CHAT
+//config: default n
+//config: help
+//config: Original chat always treats STDIN as a TTY device and sets for it
+//config: so-called raw mode. This option turns on such behaviour.
+//config:
+//config:config FEATURE_CHAT_IMPLICIT_CR
+//config: bool "Enable implicit Carriage Return"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: When enabled make chat to terminate all SEND strings with a "\r"
+//config: unless "\c" is met anywhere in the string.
+//config:
+//config:config FEATURE_CHAT_SWALLOW_OPTS
+//config: bool "Swallow options"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: Busybox chat require no options. To make it not fail when used
+//config: in place of original chat (which has a bunch of options) turn
+//config: this on.
+//config:
+//config:config FEATURE_CHAT_SEND_ESCAPES
+//config: bool "Support weird SEND escapes"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: Original chat uses some escape sequences in SEND arguments which
+//config: are not sent to device but rather performs special actions.
+//config: E.g. "\K" means to send a break sequence to device.
+//config: "\d" delays execution for a second, "\p" -- for a 1/100 of second.
+//config: Before turning this option on think twice: do you really need them?
+//config:
+//config:config FEATURE_CHAT_VAR_ABORT_LEN
+//config: bool "Support variable-length ABORT conditions"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: Original chat uses fixed 50-bytes length ABORT conditions. Say N here.
+//config:
+//config:config FEATURE_CHAT_CLR_ABORT
+//config: bool "Support revoking of ABORT conditions"
+//config: depends on CHAT
+//config: default y
+//config: help
+//config: Support CLR_ABORT directive.
+
+//applet:IF_CHAT(APPLET(chat, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHAT) += chat.o
//usage:#define chat_trivial_usage
//usage: "EXPECT [SEND [EXPECT [SEND...]]]"
@@ -17,6 +87,7 @@
//usage: "chat '' ATZ OK ATD123456 CONNECT '' ogin: pppuser word: ppppass '~'"
#include "libbb.h"
+#include "common_bufsiz.h"
// default timeout: 45 sec
#define DEFAULT_CHAT_TIMEOUT 45*1000
@@ -285,9 +356,10 @@ int chat_main(int argc UNUSED_PARAM, char **argv)
&& poll(&pfd, 1, timeout) > 0
&& (pfd.revents & POLLIN)
) {
-#define buf bb_common_bufsiz1
llist_t *l;
ssize_t delta;
+#define buf bb_common_bufsiz1
+ setup_common_bufsiz();
// read next char from device
if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) {
diff --git a/miscutils/chrt.c b/miscutils/chrt.c
index f2f559f..1604a68 100644
--- a/miscutils/chrt.c
+++ b/miscutils/chrt.c
@@ -5,6 +5,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CHRT
+//config: bool "chrt"
+//config: default y
+//config: help
+//config: manipulate real-time attributes of a process.
+//config: This requires sched_{g,s}etparam support in your libc.
+
+//applet:IF_CHRT(APPLET(chrt, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHRT) += chrt.o
//usage:#define chrt_trivial_usage
//usage: "[-prfom] [PRIO] [PID | PROG ARGS]"
diff --git a/miscutils/conspy.c b/miscutils/conspy.c
index 1a46a43..d9d09d4 100644
--- a/miscutils/conspy.c
+++ b/miscutils/conspy.c
@@ -9,11 +9,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-//applet:IF_CONSPY(APPLET(conspy, BB_DIR_BIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_CONSPY) += conspy.o
-
//config:config CONSPY
//config: bool "conspy"
//config: default y
@@ -24,6 +19,10 @@
//config: or conspy -nd NUM screenshot of console num
//config: or conspy -cs NUM poor man's GNU screen like
+//applet:IF_CONSPY(APPLET(conspy, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CONSPY) += conspy.o
+
//usage:#define conspy_trivial_usage
//usage: "[-vcsndfFQ] [-x COL] [-y LINE] [CONSOLE_NO]"
//usage:#define conspy_full_usage "\n\n"
@@ -42,6 +41,7 @@
//usage: "\n -y LINE Starting line"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <sys/kd.h>
#define ESC "\033"
@@ -363,7 +363,6 @@ int conspy_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int conspy_main(int argc UNUSED_PARAM, char **argv)
{
char tty_name[sizeof(DEV_TTY "NN")];
-#define keybuf bb_common_bufsiz1
struct termios termbuf;
unsigned opts;
unsigned ttynum;
@@ -382,11 +381,14 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
applet_long_options = getopt_longopts;
#endif
+#define keybuf bb_common_bufsiz1
+ setup_common_bufsiz();
+
INIT_G();
strcpy(G.vcsa_name, DEV_VCSA);
- opt_complementary = "x+:y+"; // numeric params
- opts = getopt32(argv, "vcQsndfFx:y:", &G.x, &G.y);
+ // numeric params
+ opts = getopt32(argv, "vcQsndfFx:+y:+", &G.x, &G.y);
argv += optind;
ttynum = 0;
if (argv[0]) {
@@ -513,7 +515,7 @@ int conspy_main(int argc UNUSED_PARAM, char **argv)
default:
// Read the keys pressed
k = keybuf + G.key_count;
- bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count);
+ bytes_read = read(G.kbd_fd, k, COMMON_BUFSIZE - G.key_count);
if (bytes_read < 0)
goto abort;
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 2545618..f96c96e 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -1,7 +1,5 @@
/* vi: set sw=4 ts=4: */
/*
- * crond -d[#] -c <crondir> -f -b
- *
* run as root, but NOT setuid root
*
* Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
@@ -10,6 +8,43 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CROND
+//config: bool "crond"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: Crond is a background daemon that parses individual crontab
+//config: files and executes commands on behalf of the users in question.
+//config: This is a port of dcron from slackware. It uses files of the
+//config: format /var/spool/cron/crontabs/<username> files, for example:
+//config: $ cat /var/spool/cron/crontabs/root
+//config: # Run daily cron jobs at 4:40 every day:
+//config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1
+//config:
+//config:config FEATURE_CROND_D
+//config: bool "Support option -d to redirect output to stderr"
+//config: depends on CROND
+//config: default y
+//config: help
+//config: -d N sets loglevel (0:most verbose) and directs all output to stderr.
+//config:
+//config:config FEATURE_CROND_CALL_SENDMAIL
+//config: bool "Report command output via email (using sendmail)"
+//config: default y
+//config: depends on CROND
+//config: help
+//config: Command output will be sent to corresponding user via email.
+//config:
+//config:config FEATURE_CROND_DIR
+//config: string "crond spool directory"
+//config: default "/var/spool/cron"
+//config: depends on CROND || CRONTAB
+//config: help
+//config: Location of crond spool.
+
+//applet:IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CROND) += crond.o
//usage:#define crond_trivial_usage
//usage: "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
@@ -17,14 +52,15 @@
//usage: " -f Foreground"
//usage: "\n -b Background (default)"
//usage: "\n -S Log to syslog (default)"
-//usage: "\n -l Set log level. 0 is the most verbose, default 8"
+//usage: "\n -l N Set log level. Most verbose:0, default:8"
//usage: IF_FEATURE_CROND_D(
-//usage: "\n -d Set log level, log to stderr"
+//usage: "\n -d N Set log level, log to stderr"
//usage: )
-//usage: "\n -L Log to file"
-//usage: "\n -c Working dir"
+//usage: "\n -L FILE Log to FILE"
+//usage: "\n -c DIR Cron dir. Default:"CONFIG_FEATURE_CROND_DIR"/crontabs"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
/* glibc frees previous setenv'ed value when we do next setenv()
@@ -36,11 +72,7 @@
#endif
-#ifdef __BIONIC__
-#define TMPDIR "/data/local/tmp"
-#else
-#define TMPDIR CONFIG_FEATURE_CROND_DIR
-#endif
+#define CRON_DIR CONFIG_FEATURE_CROND_DIR
#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
#ifndef SENDMAIL
# define SENDMAIL "sendmail"
@@ -73,6 +105,7 @@ typedef struct CronLine {
int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */
char *cl_mailto; /* whom to mail results, may be NULL */
#endif
+ char *cl_shell;
/* ordered by size, not in natural order. makes code smaller: */
char cl_Dow[7]; /* 0-6, beginning sunday */
char cl_Mons[12]; /* 0-11 */
@@ -94,12 +127,6 @@ enum {
OPT_c = (1 << 5),
OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D,
};
-#if ENABLE_FEATURE_CROND_D
-# define DebugOpt (option_mask32 & OPT_d)
-#else
-# define DebugOpt 0
-#endif
-
struct globals {
unsigned log_level; /* = 8; */
@@ -110,64 +137,67 @@ struct globals {
#if SETENV_LEAKS
char *env_var_user;
char *env_var_home;
+ char *env_var_shell;
+ char *env_var_logname;
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
G.log_level = 8; \
G.crontab_dir_name = CRONTABS; \
} while (0)
+/* Log levels:
+ * 0 is the most verbose, default 8.
+ * For some reason, in fact only 5, 7 and 8 are used.
+ */
+static void crondlog(unsigned level, const char *msg, va_list va)
+{
+ if (level >= G.log_level) {
+ /*
+ * We are called only for info meesages.
+ * Warnings/errors use plain bb_[p]error_msg's, which
+ * need not touch syslog_level
+ * (they are ok with LOG_ERR default).
+ */
+ syslog_level = LOG_INFO;
+ bb_verror_msg(msg, va, /* strerr: */ NULL);
+ syslog_level = LOG_ERR;
+ }
+}
+
+static void log5(const char *msg, ...)
+{
+ va_list va;
+ va_start(va, msg);
+ crondlog(4, msg, va);
+ va_end(va);
+}
-/* 0 is the most verbose, default 8 */
-#define LVL5 "\x05"
-#define LVL7 "\x07"
-#define LVL8 "\x08"
-#define WARN9 "\x49"
-#define DIE9 "\xc9"
-/* level >= 20 is "error" */
-#define ERR20 "\x14"
+static void log7(const char *msg, ...)
+{
+ va_list va;
+ va_start(va, msg);
+ crondlog(7, msg, va);
+ va_end(va);
+}
-static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2)));
-static void crondlog(const char *ctl, ...)
+static void log8(const char *msg, ...)
{
va_list va;
- int level = (ctl[0] & 0x1f);
-
- va_start(va, ctl);
- if (level >= (int)G.log_level) {
- /* Debug mode: all to (non-redirected) stderr, */
- /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
- if (!DebugOpt && G.log_filename) {
- /* Otherwise (log to file): we reopen log file at every write: */
- int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND);
- if (logfd >= 0)
- xmove_fd(logfd, STDERR_FILENO);
- }
- /* When we log to syslog, level > 8 is logged at LOG_ERR
- * syslog level, level <= 8 is logged at LOG_INFO. */
- if (level > 8) {
- bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
- } else {
- char *msg = NULL;
- vasprintf(&msg, ctl + 1, va);
- bb_info_msg("%s: %s", applet_name, msg);
- free(msg);
- }
- }
+ va_start(va, msg);
+ crondlog(8, msg, va);
va_end(va);
- if (ctl[0] & 0x80)
- exit(20);
}
+
static const char DowAry[] ALIGN1 =
"sun""mon""tue""wed""thu""fri""sat"
- /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
;
static const char MonAry[] ALIGN1 =
"jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
- /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
;
static void ParseField(char *user, char *ary, int modvalue, int off,
@@ -271,12 +301,12 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
if (*ptr) {
err:
- crondlog(WARN9 "user %s: parse error at %s", user, base);
+ bb_error_msg("user %s: parse error at %s", user, base);
return;
}
- if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */
- /* can't use crondlog, it inserts '\n' */
+ /* can't use log5 (it inserts newlines), open-coding it */
+ if (G.log_level <= 5 && logmode != LOGMODE_SYSLOG) {
int i;
for (i = 0; i < modvalue; ++i)
fprintf(stderr, "%d", (unsigned char)ary[i]);
@@ -372,11 +402,12 @@ static void load_crontab(const char *fileName)
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
char *mailTo = NULL;
#endif
+ char *shell = NULL;
delete_cronfile(fileName);
if (!getpwnam(fileName)) {
- crondlog(LVL7 "ignoring file '%s' (no such user)", fileName);
+ log7("ignoring file '%s' (no such user)", fileName);
return;
}
@@ -397,23 +428,43 @@ static void load_crontab(const char *fileName)
while (1) {
CronLine *line;
- if (!--maxLines)
+ if (!--maxLines) {
+ bb_error_msg("user %s: too many lines", fileName);
break;
+ }
+
n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
if (!n)
break;
- if (DebugOpt)
- crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
+ log5("user:%s entry:%s", fileName, parser->data);
/* check if line is setting MAILTO= */
- if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
+ if (is_prefixed_with(tokens[0], "MAILTO=")) {
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
free(mailTo);
mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
#endif /* otherwise just ignore such lines */
continue;
}
+ if (is_prefixed_with(tokens[0], "SHELL=")) {
+ free(shell);
+ shell = xstrdup(&tokens[0][6]);
+ continue;
+ }
+//TODO: handle HOME= too? "man crontab" says:
+//name = value
+//
+//where the spaces around the equal-sign (=) are optional, and any subsequent
+//non-leading spaces in value will be part of the value assigned to name.
+//The value string may be placed in quotes (single or double, but matching)
+//to preserve leading or trailing blanks.
+//
+//Several environment variables are set up automatically by the cron(8) daemon.
+//SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
+//line of the crontab's owner. HOME and SHELL may be overridden by settings
+//in the crontab; LOGNAME may not.
+
/* check if a minimum of tokens is specified */
if (n < 6)
continue;
@@ -433,11 +484,9 @@ static void load_crontab(const char *fileName)
/* copy mailto (can be NULL) */
line->cl_mailto = xstrdup(mailTo);
#endif
+ line->cl_shell = xstrdup(shell);
/* copy command */
line->cl_cmd = xstrdup(tokens[5]);
- if (DebugOpt) {
- crondlog(LVL5 " command:%s", tokens[5]);
- }
pline = &line->cl_next;
//bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
}
@@ -445,12 +494,12 @@ static void load_crontab(const char *fileName)
file->cf_next = G.cron_files;
G.cron_files = file;
-
- if (maxLines == 0) {
- crondlog(WARN9 "user %s: too many lines", fileName);
- }
}
config_close(parser);
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+ free(mailTo);
+#endif
+ free(shell);
}
static void process_cron_update_file(void)
@@ -486,17 +535,16 @@ static void rescan_crontab_dir(void)
/* Remove cron update file */
unlink(CRONUPDATE);
/* Re-chdir, in case directory was renamed & deleted */
- if (chdir(G.crontab_dir_name) < 0) {
- crondlog(DIE9 "chdir(%s)", G.crontab_dir_name);
- }
+ xchdir(G.crontab_dir_name);
/* Scan directory and add associated users */
{
DIR *dir = opendir(".");
struct dirent *den;
+ /* xopendir exists, but "can't open '.'" is not informative */
if (!dir)
- crondlog(DIE9 "chdir(%s)", "."); /* exits */
+ bb_error_msg_and_die("can't open '%s'", G.crontab_dir_name);
while ((den = readdir(dir)) != NULL) {
if (strchr(den->d_name, '.') != NULL) {
continue;
@@ -523,19 +571,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val)
}
#endif
-static void set_env_vars(struct passwd *pas)
+static void set_env_vars(struct passwd *pas, const char *shell)
{
+ /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL.
+ * We assume crond inherited suitable PATH.
+ */
#if SETENV_LEAKS
+ safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name);
safe_setenv(&G.env_var_user, "USER", pas->pw_name);
safe_setenv(&G.env_var_home, "HOME", pas->pw_dir);
- /* if we want to set user's shell instead: */
- /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/
+ safe_setenv(&G.env_var_shell, "SHELL", shell);
#else
+ xsetenv("LOGNAME", pas->pw_name);
xsetenv("USER", pas->pw_name);
xsetenv("HOME", pas->pw_dir);
+ xsetenv("SHELL", shell);
#endif
- /* currently, we use constant one: */
- /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
}
static void change_user(struct passwd *pas)
@@ -543,10 +594,8 @@ static void change_user(struct passwd *pas)
/* careful: we're after vfork! */
change_identity(pas); /* - initgroups, setgid, setuid */
if (chdir(pas->pw_dir) < 0) {
- crondlog(WARN9 "chdir(%s)", pas->pw_dir);
- if (chdir(TMPDIR) < 0) {
- crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */
- }
+ bb_error_msg("can't change directory to '%s'", pas->pw_dir);
+ xchdir(CRON_DIR);
}
}
@@ -554,46 +603,53 @@ static void change_user(struct passwd *pas)
#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
static pid_t
-fork_job(const char *user, int mailFd,
- const char *prog,
- const char *shell_cmd /* if NULL, we run sendmail */
-) {
+fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
+{
struct passwd *pas;
+ const char *shell, *prog;
+ smallint sv_logmode;
pid_t pid;
/* prepare things before vfork */
pas = getpwnam(user);
if (!pas) {
- crondlog(WARN9 "can't get uid for %s", user);
+ bb_error_msg("can't get uid for %s", user);
goto err;
}
- set_env_vars(pas);
+ shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL;
+ prog = run_sendmail ? SENDMAIL : shell;
+
+ set_env_vars(pas, shell);
+
+ sv_logmode = logmode;
pid = vfork();
if (pid == 0) {
/* CHILD */
- /* initgroups, setgid, setuid, and chdir to home or TMPDIR */
+ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */
change_user(pas);
- if (DebugOpt) {
- crondlog(LVL5 "child running %s", prog);
- }
+ log5("child running %s", prog);
if (mailFd >= 0) {
- xmove_fd(mailFd, shell_cmd ? 1 : 0);
+ xmove_fd(mailFd, run_sendmail ? 0 : 1);
dup2(1, 2);
}
/* crond 3.0pl1-100 puts tasks in separate process groups */
bb_setpgrp();
- execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL);
- crondlog(ERR20 "can't execute '%s' for user %s", prog, user);
- if (shell_cmd) {
- fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd);
- }
- _exit(EXIT_SUCCESS);
+ if (!run_sendmail)
+ execlp(prog, prog, "-c", line->cl_cmd, (char *) NULL);
+ else
+ execlp(prog, prog, SENDMAIL_ARGS, (char *) NULL);
+ /*
+ * I want this error message on stderr too,
+ * even if other messages go only to syslog:
+ */
+ logmode |= LOGMODE_STDIO;
+ bb_error_msg_and_die("can't execute '%s' for user %s", prog, user);
}
+ logmode = sv_logmode;
if (pid < 0) {
- /* FORK FAILED */
- crondlog(ERR20 "can't vfork");
+ bb_perror_msg("vfork");
err:
pid = 0;
} /* else: PARENT, FORK SUCCESS */
@@ -618,7 +674,7 @@ static void start_one_job(const char *user, CronLine *line)
if (line->cl_mailto) {
/* Open mail file (owner is root so nobody can screw with it) */
- snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, getpid());
mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
if (mailFd >= 0) {
@@ -626,18 +682,18 @@ static void start_one_job(const char *user, CronLine *line)
line->cl_cmd);
line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR);
} else {
- crondlog(ERR20 "can't create mail file %s for user %s, "
+ bb_error_msg("can't create mail file %s for user %s, "
"discarding output", mailFile, user);
}
}
- line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd);
+ line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 0);
if (mailFd >= 0) {
if (line->cl_pid <= 0) {
unlink(mailFile);
} else {
/* rename mail-file based on pid of process */
- char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid);
+ char *mailFile2 = xasprintf("%s/cron.%s.%d", CRON_DIR, user, (int)line->cl_pid);
rename(mailFile, mailFile2); // TODO: xrename?
free(mailFile2);
}
@@ -669,7 +725,7 @@ static void process_finished_job(const char *user, CronLine *line)
* End of primary job - check for mail file.
* If size has changed and the file is still valid, we send it.
*/
- snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid);
+ snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, (int)pid);
mailFd = open(mailFile, O_RDONLY);
unlink(mailFile);
if (mailFd < 0) {
@@ -687,43 +743,41 @@ static void process_finished_job(const char *user, CronLine *line)
}
line->cl_empty_mail_size = 0;
/* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */
- line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL);
+ line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 1);
}
#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
static void start_one_job(const char *user, CronLine *line)
{
+ const char *shell;
struct passwd *pas;
pid_t pid;
- pas = safegetpwnam(user);
+ pas = getpwnam(user);
if (!pas) {
- crondlog(WARN9 "can't get uid for %s", user);
+ bb_error_msg("can't get uid for %s", user);
goto err;
}
/* Prepare things before vfork */
- set_env_vars(pas);
+ shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL;
+ set_env_vars(pas, shell);
/* Fork as the user in question and run program */
pid = vfork();
if (pid == 0) {
/* CHILD */
- /* initgroups, setgid, setuid, and chdir to home or TMPDIR */
+ /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */
change_user(pas);
- if (DebugOpt) {
- crondlog(LVL5 "child running %s", DEFAULT_SHELL);
- }
+ log5("child running %s", shell);
/* crond 3.0pl1-100 puts tasks in separate process groups */
bb_setpgrp();
- execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL);
- crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user);
- _exit(EXIT_SUCCESS);
+ execl(shell, shell, "-c", line->cl_cmd, (char *) NULL);
+ bb_error_msg_and_die("can't execute '%s' for user %s", shell, user);
}
if (pid < 0) {
- /* FORK FAILED */
- crondlog(ERR20 "can't vfork");
+ bb_perror_msg("vfork");
err:
pid = 0;
}
@@ -755,24 +809,20 @@ static void flag_starting_jobs(time_t t1, time_t t2)
ptm = localtime(&t);
for (file = G.cron_files; file; file = file->cf_next) {
- if (DebugOpt)
- crondlog(LVL5 "file %s:", file->cf_username);
+ log5("file %s:", file->cf_username);
if (file->cf_deleted)
continue;
for (line = file->cf_lines; line; line = line->cl_next) {
- if (DebugOpt)
- crondlog(LVL5 " line %s", line->cl_cmd);
+ log5(" line %s", line->cl_cmd);
if (line->cl_Mins[ptm->tm_min]
&& line->cl_Hrs[ptm->tm_hour]
&& (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday])
&& line->cl_Mons[ptm->tm_mon]
) {
- if (DebugOpt) {
- crondlog(LVL5 " job: %d %s",
+ log5(" job: %d %s",
(int)line->cl_pid, line->cl_cmd);
- }
if (line->cl_pid > 0) {
- crondlog(LVL8 "user %s: process already running: %s",
+ log8("user %s: process already running: %s",
file->cf_username, line->cl_cmd);
} else if (line->cl_pid == 0) {
line->cl_pid = -1;
@@ -801,7 +851,7 @@ static void start_jobs(void)
start_one_job(file->cf_username, line);
pid = line->cl_pid;
- crondlog(LVL8 "USER %s pid %3d cmd %s",
+ log8("USER %s pid %3d cmd %s",
file->cf_username, (int)pid, line->cl_cmd);
if (pid < 0) {
file->cf_wants_starting = 1;
@@ -853,12 +903,21 @@ static int check_completions(void)
return num_still_running;
}
+static void reopen_logfile_to_stderr(void)
+{
+ if (G.log_filename) {
+ int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND);
+ if (logfd >= 0)
+ xmove_fd(logfd, STDERR_FILENO);
+ }
+}
+
int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int crond_main(int argc UNUSED_PARAM, char **argv)
{
time_t t2;
- int rescan;
- int sleep_time;
+ unsigned rescan;
+ unsigned sleep_time;
unsigned opts;
INIT_G();
@@ -884,10 +943,11 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
logmode = LOGMODE_SYSLOG;
}
- xchdir(G.crontab_dir_name);
//signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
- xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
- crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level);
+
+ reopen_logfile_to_stderr();
+ xchdir(G.crontab_dir_name);
+ log8("crond (busybox "BB_VER") started, log level %d", G.log_level);
rescan_crontab_dir();
write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid");
@@ -900,14 +960,14 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
time_t t1;
long dt;
- t1 = t2;
-
/* Synchronize to 1 minute, minimum 1 second */
- sleep(sleep_time - (time(NULL) % sleep_time) + 1);
-
+ t1 = t2;
+ sleep(sleep_time - (time(NULL) % sleep_time));
t2 = time(NULL);
dt = (long)t2 - (long)t1;
+ reopen_logfile_to_stderr();
+
/*
* The file 'cron.update' is checked to determine new cron
* jobs. The directory is rescanned once an hour to deal
@@ -926,7 +986,7 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
*/
if (stat(G.crontab_dir_name, &sbuf) != 0)
sbuf.st_mtime = 0; /* force update (once) if dir was deleted */
- if ((time_t) G.crontab_dir_mtime != (time_t) sbuf.st_mtime) {
+ if (G.crontab_dir_mtime != sbuf.st_mtime) {
G.crontab_dir_mtime = sbuf.st_mtime;
rescan = 1;
}
@@ -935,20 +995,18 @@ int crond_main(int argc UNUSED_PARAM, char **argv)
rescan_crontab_dir();
}
process_cron_update_file();
- if (DebugOpt)
- crondlog(LVL5 "wakeup dt=%ld", dt);
+ log5("wakeup dt=%ld", dt);
if (dt < -60 * 60 || dt > 60 * 60) {
- crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
+ bb_error_msg("time disparity of %ld minutes detected", dt / 60);
/* and we do not run any jobs in this case */
} else if (dt > 0) {
/* Usual case: time advances forward, as expected */
flag_starting_jobs(t1, t2);
start_jobs();
+ sleep_time = 60;
if (check_completions() > 0) {
/* some jobs are still running */
sleep_time = 10;
- } else {
- sleep_time = 60;
}
}
/* else: time jumped back, do not run any jobs */
diff --git a/miscutils/crontab.c b/miscutils/crontab.c
index 94659bd..c6bece7 100644
--- a/miscutils/crontab.c
+++ b/miscutils/crontab.c
@@ -9,6 +9,19 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config CRONTAB
+//config: bool "crontab"
+//config: default y
+//config: help
+//config: Crontab manipulates the crontab for a particular user. Only
+//config: the superuser may specify a different user and/or crontab directory.
+//config: Note that Busybox binary must be setuid root for this applet to
+//config: work properly.
+
+/* Needs to be run by root or be suid root - needs to change /var/spool/cron* files: */
+//applet:IF_CRONTAB(APPLET(crontab, BB_DIR_USR_BIN, BB_SUID_REQUIRE))
+
+//kbuild:lib-$(CONFIG_CRONTAB) += crontab.o
//usage:#define crontab_trivial_usage
//usage: "[-c DIR] [-u USER] [-ler]|[FILE]"
@@ -170,7 +183,6 @@ int crontab_main(int argc UNUSED_PARAM, char **argv)
unlink(tmp_fname);
/*free(tmp_fname);*/
/*free(new_fname);*/
-
} /* switch */
/* Bump notification file. Handle window where crond picks file up
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 6bcfbe2..7986fef 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -2,9 +2,24 @@
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-#include "libbb.h"
-#include <math.h>
+//config:config DC
+//config: bool "dc"
+//config: default y
+//config: help
+//config: Dc is a reverse-polish desk calculator which supports unlimited
+//config: precision arithmetic.
+//config:
+//config:config FEATURE_DC_LIBM
+//config: bool "Enable power and exp functions (requires libm)"
+//config: default y
+//config: depends on DC
+//config: help
+//config: Enable power and exp functions.
+//config: NOTE: This will require libm to be present for linking.
+
+//applet:IF_DC(APPLET(dc, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DC) += dc.o
//usage:#define dc_trivial_usage
//usage: "EXPRESSION..."
@@ -29,6 +44,10 @@
//usage: "$ echo 72 9 div 8 mul p | dc\n"
//usage: "64\n"
+#include "libbb.h"
+#include "common_bufsiz.h"
+#include <math.h>
+
#if 0
typedef unsigned data_t;
#define DATA_FMT ""
@@ -47,15 +66,22 @@ struct globals {
double stack[1];
} FIX_ALIASING;
enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define pointer (G.pointer )
#define base (G.base )
#define stack (G.stack )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
base = 10; \
} while (0)
+static void check_under(void)
+{
+ if (pointer == 0)
+ bb_error_msg_and_die("stack underflow");
+}
+
static void push(double a)
{
if (pointer >= STACK_SIZE)
@@ -65,8 +91,7 @@ static void push(double a)
static double pop(void)
{
- if (pointer == 0)
- bb_error_msg_and_die("stack underflow");
+ check_under();
return stack[--pointer];
}
@@ -187,6 +212,7 @@ static void print_stack_no_pop(void)
static void print_no_pop(void)
{
+ check_under();
print_base(stack[pointer-1]);
}
@@ -196,14 +222,6 @@ struct op {
};
static const struct op operators[] = {
- {"+", add},
- {"add", add},
- {"-", sub},
- {"sub", sub},
- {"*", mul},
- {"mul", mul},
- {"/", divide},
- {"div", divide},
#if ENABLE_FEATURE_DC_LIBM
{"**", power},
{"exp", power},
@@ -216,28 +234,47 @@ static const struct op operators[] = {
{"not", not},
{"eor", eor},
{"xor", eor},
+ {"+", add},
+ {"add", add},
+ {"-", sub},
+ {"sub", sub},
+ {"*", mul},
+ {"mul", mul},
+ {"/", divide},
+ {"div", divide},
{"p", print_no_pop},
{"f", print_stack_no_pop},
{"o", set_output_base},
};
+/* Feed the stack machine */
static void stack_machine(const char *argument)
{
char *end;
- double d;
+ double number;
const struct op *o;
- d = strtod(argument, &end);
- if (end != argument && *end == '\0') {
- push(d);
- return;
+ next:
+ number = strtod(argument, &end);
+ if (end != argument) {
+ argument = end;
+ push(number);
+ goto next;
}
+ /* We might have matched a digit, eventually advance the argument */
+ argument = skip_whitespace(argument);
+
+ if (*argument == '\0')
+ return;
+
o = operators;
do {
- if (strcmp(o->name, argument) == 0) {
+ char *after_name = is_prefixed_with(argument, o->name);
+ if (after_name) {
+ argument = after_name;
o->function();
- return;
+ goto next;
}
o++;
} while (o != operators + ARRAY_SIZE(operators));
@@ -254,25 +291,11 @@ int dc_main(int argc UNUSED_PARAM, char **argv)
if (!argv[0]) {
/* take stuff from stdin if no args are given */
char *line;
- char *cursor;
- char *token;
while ((line = xmalloc_fgetline(stdin)) != NULL) {
- cursor = line;
- while (1) {
- token = skip_whitespace(cursor);
- if (*token == '\0')
- break;
- cursor = skip_non_whitespace(token);
- if (*cursor != '\0')
- *cursor++ = '\0';
- stack_machine(token);
- }
+ stack_machine(line);
free(line);
}
} else {
- // why? it breaks "dc -2 2 + p"
- //if (argv[0][0] == '-')
- // bb_show_usage();
do {
stack_machine(*argv);
} while (*++argv);
diff --git a/miscutils/devfsd.c b/miscutils/devfsd.c
index 96ffe07..99bdc72 100644
--- a/miscutils/devfsd.c
+++ b/miscutils/devfsd.c
@@ -53,6 +53,66 @@
The postal address is:
Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/
+//config:config DEVFSD
+//config: bool "devfsd (obsolete)"
+//config: default n
+//config: select PLATFORM_LINUX
+//config: select FEATURE_SYSLOG
+//config: help
+//config: This is deprecated and should NOT be used anymore.
+//config: Use linux >= 2.6 (optionally with hotplug) and mdev instead!
+//config: See docs/mdev.txt for detailed instructions on how to use mdev
+//config: instead.
+//config:
+//config: Provides compatibility with old device names on a devfs systems.
+//config: You should set it to true if you have devfs enabled.
+//config: The following keywords in devsfd.conf are supported:
+//config: "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+//config: "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
+//config: "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
+//config:
+//config: But only if they are written UPPERCASE!!!!!!!!
+//config:
+//config:config DEVFSD_MODLOAD
+//config: bool "Adds support for MODLOAD keyword in devsfd.conf"
+//config: default y
+//config: depends on DEVFSD
+//config: help
+//config: This actually doesn't work with busybox modutils but needs
+//config: the external modutils.
+//config:
+//config:config DEVFSD_FG_NP
+//config: bool "Enables the -fg and -np options"
+//config: default y
+//config: depends on DEVFSD
+//config: help
+//config: -fg Run the daemon in the foreground.
+//config: -np Exit after parsing the configuration file.
+//config: Do not poll for events.
+//config:
+//config:config DEVFSD_VERBOSE
+//config: bool "Increases logging (and size)"
+//config: default y
+//config: depends on DEVFSD
+//config: help
+//config: Increases logging to stderr or syslog.
+//config:
+//config:config FEATURE_DEVFS
+//config: bool "Use devfs names for all devices (obsolete)"
+//config: default n
+//config: select PLATFORM_LINUX
+//config: help
+//config: This is obsolete and should NOT be used anymore.
+//config: Use linux >= 2.6 (optionally with hotplug) and mdev instead!
+//config:
+//config: For legacy systems -- if there is no way around devfsd -- this
+//config: tells busybox to look for names like /dev/loop/0 instead of
+//config: /dev/loop0. If your /dev directory has normal names instead of
+//config: devfs names, you don't want this.
+
+//applet:IF_DEVFSD(APPLET(devfsd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DEVFSD) += devfsd.o
//usage:#define devfsd_trivial_usage
//usage: "mntpnt [-v]" IF_DEVFSD_FG_NP("[-fg][-np]")
@@ -284,7 +344,7 @@ static const char bb_msg_variable_not_found[] ALIGN1 = "variable: %s not found";
/* Busybox stuff */
#if ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG
-#define info_logger(p, fmt, args...) bb_info_msg(fmt, ## args)
+#define info_logger(p, fmt, args...) bb_error_msg(fmt, ## args)
#define msg_logger(p, fmt, args...) bb_error_msg(fmt, ## args)
#define msg_logger_and_die(p, fmt, args...) bb_error_msg_and_die(fmt, ## args)
#define error_logger(p, fmt, args...) bb_perror_msg(fmt, ## args)
@@ -1142,19 +1202,19 @@ static void signal_handler(int sig)
static const char *get_variable(const char *variable, void *info)
{
- static char sbuf[sizeof(int)*3 + 2]; /* sign and NUL */
static char *hostname;
struct get_variable_info *gv_info = info;
const char *field_names[] = {
- "hostname", "mntpt", "devpath", "devname",
- "uid", "gid", "mode", hostname, mount_point,
- gv_info->devpath, gv_info->devname, NULL
+ "hostname", "mntpt", "devpath", "devname", "uid", "gid", "mode",
+ NULL, mount_point, gv_info->devpath, gv_info->devname, NULL
};
int i;
if (!hostname)
hostname = safe_gethostname();
+ field_names[7] = hostname;
+
/* index_in_str_array returns i>=0 */
i = index_in_str_array(field_names, variable);
@@ -1164,12 +1224,11 @@ static const char *get_variable(const char *variable, void *info)
return field_names[i + 7];
if (i == 4)
- sprintf(sbuf, "%u", gv_info->info->uid);
- else if (i == 5)
- sprintf(sbuf, "%u", gv_info->info->gid);
- else if (i == 6)
- sprintf(sbuf, "%o", gv_info->info->mode);
- return sbuf;
+ return auto_string(xasprintf("%u", gv_info->info->uid));
+ if (i == 5)
+ return auto_string(xasprintf("%u", gv_info->info->gid));
+ /* i == 6 */
+ return auto_string(xasprintf("%o", gv_info->info->mode));
} /* End Function get_variable */
static void service(struct stat statbuf, char *path)
@@ -1405,7 +1464,6 @@ const char *get_old_name(const char *devname, unsigned int namelen,
int indexx;
const char *pty1;
const char *pty2;
- size_t len;
/* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
static const char *const fmt[] = {
NULL ,
@@ -1425,12 +1483,11 @@ const char *get_old_name(const char *devname, unsigned int namelen,
};
for (trans = translate_table; trans->match != NULL; ++trans) {
- len = strlen(trans->match);
-
- if (strncmp(devname, trans->match, len) == 0) {
+ char *after_match = is_prefixed_with(devname, trans->match);
+ if (after_match) {
if (trans->format == NULL)
- return devname + len;
- sprintf(buffer, trans->format, devname + len);
+ return after_match;
+ sprintf(buffer, trans->format, after_match);
return buffer;
}
}
diff --git a/miscutils/devmem.c b/miscutils/devmem.c
index ecd8738..89c9abe 100644
--- a/miscutils/devmem.c
+++ b/miscutils/devmem.c
@@ -3,6 +3,16 @@
* Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
* Copyright (C) 2008, BusyBox Team. -solar 4/26/08
*/
+//config:config DEVMEM
+//config: bool "devmem"
+//config: default y
+//config: help
+//config: devmem is a small program that reads and writes from physical
+//config: memory using /dev/mem.
+
+//applet:IF_DEVMEM(APPLET(devmem, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DEVMEM) += devmem.o
//usage:#define devmem_trivial_usage
//usage: "ADDRESS [WIDTH [VALUE]]"
diff --git a/miscutils/eject.c b/miscutils/eject.c
index a20e04b..667932f 100644
--- a/miscutils/eject.c
+++ b/miscutils/eject.c
@@ -12,6 +12,24 @@
* This is a simple hack of eject based on something Erik posted in #uclibc.
* Most of the dirty work blatantly ripped off from cat.c =)
*/
+//config:config EJECT
+//config: bool "eject"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Used to eject cdroms. (defaults to /dev/cdrom)
+//config:
+//config:config FEATURE_EJECT_SCSI
+//config: bool "SCSI support"
+//config: default y
+//config: depends on EJECT
+//config: help
+//config: Add the -s option to eject, this allows to eject SCSI-Devices and
+//config: usb-storage devices.
+
+//applet:IF_EJECT(APPLET(eject, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_EJECT) += eject.o
//usage:#define eject_trivial_usage
//usage: "[-t] [-T] [DEVICE]"
@@ -25,26 +43,22 @@
#include <sys/mount.h>
#include "libbb.h"
+#if ENABLE_FEATURE_EJECT_SCSI
/* Must be after libbb.h: they need size_t */
-#include "fix_u32.h"
-#include <scsi/sg.h>
-#include <scsi/scsi.h>
-
-/* various defines swiped from linux/cdrom.h */
-#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
-#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
-#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
-/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
-#define CDS_TRAY_OPEN 2
+# include "fix_u32.h"
+# include <scsi/sg.h>
+# include <scsi/scsi.h>
+#endif
#define dev_fd 3
/* Code taken from the original eject (http://eject.sourceforge.net/),
* refactored it a bit for busybox (ne-bb@nicoerfurth.de) */
+#if ENABLE_FEATURE_EJECT_SCSI
static void eject_scsi(const char *dev)
{
- static const char sg_commands[3][6] = {
+ static const char sg_commands[3][6] ALIGN1 = {
{ ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 },
{ START_STOP, 0, 0, 0, 1, 0 },
{ START_STOP, 0, 0, 0, 2, 0 }
@@ -76,6 +90,16 @@ static void eject_scsi(const char *dev)
/* force kernel to reread partition table when new disc is inserted */
ioctl(dev_fd, BLKRRPART);
}
+#else
+# define eject_scsi(dev) ((void)0)
+#endif
+
+/* various defines swiped from linux/cdrom.h */
+#define CDROMCLOSETRAY 0x5319 /* pendant of CDROMEJECT */
+#define CDROMEJECT 0x5309 /* Ejects the cdrom media */
+#define CDROM_DRIVE_STATUS 0x5326 /* Get tray position, etc. */
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_TRAY_OPEN 2
#define FLAG_CLOSE 1
#define FLAG_SMART 2
diff --git a/miscutils/fbsplash.c b/miscutils/fbsplash.c
index 383b829..1739a3e 100644
--- a/miscutils/fbsplash.c
+++ b/miscutils/fbsplash.c
@@ -20,6 +20,31 @@
* "NN" (ASCII decimal number) - percentage to show on progress bar.
* "exit" (or just close fifo) - well you guessed it.
*/
+//config:config FBSPLASH
+//config: bool "fbsplash"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Shows splash image and progress bar on framebuffer device.
+//config: Can be used during boot phase of an embedded device. ~2kb.
+//config: Usage:
+//config: - use kernel option 'vga=xxx' or otherwise enable fb device.
+//config: - put somewhere fbsplash.cfg file and an image in .ppm format.
+//config: - $ setsid fbsplash [params] &
+//config: -c: hide cursor
+//config: -d /dev/fbN: framebuffer device (if not /dev/fb0)
+//config: -s path_to_image_file (can be "-" for stdin)
+//config: -i path_to_cfg_file (can be "-" for stdin)
+//config: -f path_to_fifo (can be "-" for stdin)
+//config: - if you want to run it only in presence of kernel parameter:
+//config: grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] &
+//config: - commands for fifo:
+//config: "NN" (ASCII decimal number) - percentage to show on progress bar
+//config: "exit" - well you guessed it
+
+//applet:IF_FBSPLASH(APPLET(fbsplash, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FBSPLASH) += fbsplash.o
//usage:#define fbsplash_trivial_usage
//usage: "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]"
@@ -34,6 +59,7 @@
//usage: "\n commands: 'NN' (% for progress bar) or 'exit'"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <linux/fb.h>
/* If you want logging messages on /tmp/fbsplash.log... */
@@ -150,7 +176,7 @@ static void fb_open(const char *strfb_device)
// map the device in memory
G.addr = mmap(NULL,
- G.scr_var.yres * G.scr_fix.line_length,
+ (G.scr_var.yres_virtual ?: G.scr_var.yres) * G.scr_fix.line_length,
PROT_WRITE, MAP_SHARED, fbfd, 0);
if (G.addr == MAP_FAILED)
bb_perror_msg_and_die("mmap");
@@ -373,10 +399,12 @@ static void fb_drawimage(void)
* in pure binary by 1 or 2 bytes. (we support only 1 byte)
*/
#define concat_buf bb_common_bufsiz1
+ setup_common_bufsiz();
+
read_ptr = concat_buf;
while (1) {
int w, h, max_color_val;
- int rem = concat_buf + sizeof(concat_buf) - read_ptr;
+ int rem = concat_buf + COMMON_BUFSIZE - read_ptr;
if (rem < 2
|| fgets(read_ptr, rem, theme_file) == NULL
) {
@@ -516,7 +544,7 @@ int fbsplash_main(int argc UNUSED_PARAM, char **argv)
// handle a case when we have many buffered lines
// already in the pipe
while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
- if (strncmp(num_buf, "exit", 4) == 0) {
+ if (is_prefixed_with(num_buf, "exit")) {
DEBUG_MESSAGE("exit");
break;
}
diff --git a/miscutils/flash_eraseall.c b/miscutils/flash_eraseall.c
index bf9b739..2a9bd6c 100644
--- a/miscutils/flash_eraseall.c
+++ b/miscutils/flash_eraseall.c
@@ -9,6 +9,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FLASH_ERASEALL
+//config: bool "flash_eraseall"
+//config: default n # doesn't build on Ubuntu 8.04
+//config: help
+//config: The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
+//config: This utility is used to erase the whole MTD device.
+
+//applet:IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
//usage:#define flash_eraseall_trivial_usage
//usage: "[-jNq] MTD_DEVICE"
@@ -147,7 +157,7 @@ int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
ret = ioctl(fd, MEMGETBADBLOCK, &offset);
if (ret > 0) {
if (!(flags & OPTION_Q))
- bb_info_msg("\nSkipping bad block at 0x%08x", erase.start);
+ printf("\nSkipping bad block at 0x%08x\n", erase.start);
continue;
}
if (ret < 0) {
diff --git a/miscutils/flash_lock_unlock.c b/miscutils/flash_lock_unlock.c
index f0d9595..a2e67b0 100644
--- a/miscutils/flash_lock_unlock.c
+++ b/miscutils/flash_lock_unlock.c
@@ -3,6 +3,25 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FLASH_LOCK
+//config: bool "flash_lock"
+//config: default n # doesn't build on Ubuntu 8.04
+//config: help
+//config: The flash_lock binary from mtd-utils as of git head 5ec0c10d0. This
+//config: utility locks part or all of the flash device.
+//config:
+//config:config FLASH_UNLOCK
+//config: bool "flash_unlock"
+//config: default n # doesn't build on Ubuntu 8.04
+//config: help
+//config: The flash_unlock binary from mtd-utils as of git head 5ec0c10d0. This
+//config: utility unlocks part or all of the flash device.
+
+//applet:IF_FLASH_LOCK(APPLET_ODDNAME(flash_lock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_lock))
+//applet:IF_FLASH_UNLOCK(APPLET_ODDNAME(flash_unlock, flash_lock_unlock, BB_DIR_USR_SBIN, BB_SUID_DROP, flash_unlock))
+
+//kbuild:lib-$(CONFIG_FLASH_LOCK) += flash_lock_unlock.o
+//kbuild:lib-$(CONFIG_FLASH_UNLOCK) += flash_lock_unlock.o
//usage:#define flash_lock_trivial_usage
//usage: "MTD_DEVICE OFFSET SECTORS"
diff --git a/miscutils/flashcp.c b/miscutils/flashcp.c
index a834a7f..b27d75a 100644
--- a/miscutils/flashcp.c
+++ b/miscutils/flashcp.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FLASHCP
+//config: bool "flashcp"
+//config: default n # doesn't build on Ubuntu 8.04
+//config: help
+//config: The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7.
+//config: This utility is used to copy images into a MTD device.
+
+//applet:IF_FLASHCP(APPLET(flashcp, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FLASHCP) += flashcp.o
//usage:#define flashcp_trivial_usage
//usage: "-v FILE MTD_DEVICE"
@@ -21,7 +31,7 @@
#define OPT_v (1 << 0)
-#define BUFSIZE (8 * 1024)
+#define BUFSIZE (4 * 1024)
static void progress(int mode, uoff_t count, uoff_t total)
{
diff --git a/miscutils/hdparm.c b/miscutils/hdparm.c
index f0e9c9d..e43a0de 100644
--- a/miscutils/hdparm.c
+++ b/miscutils/hdparm.c
@@ -11,6 +11,68 @@
* hdparm.c - Command line interface to get/set hard disk parameters
* - by Mark Lord (C) 1994-2002 -- freely distributable
*/
+//config:config HDPARM
+//config: bool "hdparm"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Get/Set hard drive parameters. Primarily intended for ATA
+//config: drives. Adds about 13k (or around 30k if you enable the
+//config: FEATURE_HDPARM_GET_IDENTITY option)....
+//config:
+//config:config FEATURE_HDPARM_GET_IDENTITY
+//config: bool "Support obtaining detailed information directly from drives"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the -I and -i options to obtain detailed information
+//config: directly from drives about their capabilities and supported ATA
+//config: feature set. If no device name is specified, hdparm will read
+//config: identify data from stdin. Enabling this option will add about 16k...
+//config:
+//config:config FEATURE_HDPARM_HDIO_SCAN_HWIF
+//config: bool "Register an IDE interface (DANGEROUS)"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the 'hdparm -R' option to register an IDE interface.
+//config: This is dangerous stuff, so you should probably say N.
+//config:
+//config:config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+//config: bool "Un-register an IDE interface (DANGEROUS)"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the 'hdparm -U' option to un-register an IDE interface.
+//config: This is dangerous stuff, so you should probably say N.
+//config:
+//config:config FEATURE_HDPARM_HDIO_DRIVE_RESET
+//config: bool "Perform device reset (DANGEROUS)"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the 'hdparm -w' option to perform a device reset.
+//config: This is dangerous stuff, so you should probably say N.
+//config:
+//config:config FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+//config: bool "Tristate device for hotswap (DANGEROUS)"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the 'hdparm -x' option to tristate device for hotswap,
+//config: and the '-b' option to get/set bus state. This is dangerous
+//config: stuff, so you should probably say N.
+//config:
+//config:config FEATURE_HDPARM_HDIO_GETSET_DMA
+//config: bool "Get/set using_dma flag"
+//config: default y
+//config: depends on HDPARM
+//config: help
+//config: Enables the 'hdparm -d' option to get/set using_dma flag.
+
+//applet:IF_HDPARM(APPLET(hdparm, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_HDPARM) += hdparm.o
//usage:#define hdparm_trivial_usage
//usage: "[OPTIONS] [DEVICE]"
@@ -63,6 +125,7 @@
//usage: "\n -z Reread partition table"
#include "libbb.h"
+#include "common_bufsiz.h"
/* must be _after_ libbb.h: */
#include <linux/hdreg.h>
#include <sys/mount.h>
@@ -367,10 +430,7 @@ struct globals {
unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 };
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
+#define G (*(struct globals*)bb_common_bufsiz1)
#define get_identity (G.get_identity )
#define get_geom (G.get_geom )
#define do_flush (G.do_flush )
@@ -433,7 +493,10 @@ struct BUG_G_too_big {
#define hwif_data (G.hwif_data )
#define hwif_ctrl (G.hwif_ctrl )
#define hwif_irq (G.hwif_irq )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+} while (0)
/* Busybox messages and functions */
@@ -763,9 +826,9 @@ static void identify(uint16_t *val)
) {
like_std = 5;
if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL))
- printf("powers-up in standby; SET FEATURES subcmd spins-up.\n");
+ puts("powers-up in standby; SET FEATURES subcmd spins-up.");
if (((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE))
- printf("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
+ puts("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n");
}
/* output the model and serial numbers and the fw revision */
@@ -875,7 +938,7 @@ static void identify(uint16_t *val)
if (min_std == 0xffff)
min_std = like_std > 4 ? like_std - 3 : 1;
- printf("Configuration:\n");
+ puts("Configuration:");
/* more info from the general configuration word */
if ((eqpt != CDROM) && (like_std == 1)) {
jj = val[GEN_CONFIG] >> 1;
@@ -909,7 +972,7 @@ static void identify(uint16_t *val)
mm = 0;
bbbig = 0;
if ((ll > 0x00FBFC10) && (!val[LCYLS]))
- printf("\tCHS addressing not supported\n");
+ puts("\tCHS addressing not supported");
else {
jj = val[WHATS_VALID] & OK_W54_58;
printf("\tLogical\t\tmax\tcurrent\n"
@@ -980,7 +1043,7 @@ static void identify(uint16_t *val)
!(val[CAPAB_0] & IORDY_SUP) ? "(may be)" : "",
(val[CAPAB_0] & IORDY_OFF) ? "" :"not");
} else
- printf("no IORDY\n");
+ puts("no IORDY");
if ((like_std == 1) && val[BUF_TYPE]) {
printf("\tBuffer type: %04x: %s%s\n", val[BUF_TYPE],
@@ -1012,13 +1075,13 @@ static void identify(uint16_t *val)
}
printf("\tR/W multiple sector transfer: ");
if ((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
- printf("not supported\n");
+ puts("not supported");
else {
printf("Max = %u\tCurrent = ", val[SECTOR_XFER_MAX] & SECTOR_XFER);
if (val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID)
printf("%u\n", val[SECTOR_XFER_CUR] & SECTOR_XFER);
else
- printf("?\n");
+ puts("?");
}
if ((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) {
/* We print out elsewhere whether the APM feature is enabled or
@@ -1040,7 +1103,7 @@ static void identify(uint16_t *val)
} else {
/* ATAPI */
if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ))
- printf("\tATA sw reset required\n");
+ puts("\tATA sw reset required");
if (val[PKT_REL] || val[SVC_NBSY]) {
printf("\tOverlap support:");
@@ -1056,7 +1119,7 @@ static void identify(uint16_t *val)
/* DMA stuff. Check that only one DMA mode is selected. */
printf("\tDMA: ");
if (!(val[CAPAB_0] & DMA_SUP))
- printf("not supported\n");
+ puts("not supported");
else {
if (val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA])
printf(" sdma%u\n", (val[DMA_MODE] & MODE) >> 8);
@@ -1079,7 +1142,7 @@ static void identify(uint16_t *val)
bb_putchar('\n');
if ((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP))
- printf("\t\tInterleaved DMA support\n");
+ puts("\t\tInterleaved DMA support");
if ((val[WHATS_VALID] & OK_W64_70)
&& (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])
@@ -1121,8 +1184,8 @@ static void identify(uint16_t *val)
}
if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) {
- printf("Commands/features:\n"
- "\tEnabled\tSupported:\n");
+ puts("Commands/features:\n"
+ "\tEnabled\tSupported:");
jj = val[CMDS_SUPP_0];
kk = val[CMDS_EN_0];
for (ii = 0; ii < NUM_CMD_FEAT_STR; ii++) {
@@ -1150,7 +1213,7 @@ static void identify(uint16_t *val)
if ((eqpt != CDROM) && (like_std > 3)
&& (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])
) {
- printf("Security:\n");
+ puts("Security:");
if (val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1))
printf("\tMaster password revision code = %u\n", val[PSWD_CODE]);
jj = val[SECU_STATUS];
@@ -1366,7 +1429,7 @@ static NOINLINE void dump_identity(const struct hd_driveid *id)
}
}
#endif /* __NEW_HD_DRIVE_ID */
- printf("\n\n * current active mode\n\n");
+ puts("\n\n * current active mode\n");
}
#endif
@@ -1507,7 +1570,7 @@ static void bus_state_value(unsigned value)
else if (value == BUSSTATE_OFF)
on_off(0);
else if (value == BUSSTATE_TRISTATE)
- printf(" (tristate)\n");
+ puts(" (tristate)");
else
printf(" (unknown: %u)\n", value);
}
@@ -1531,7 +1594,7 @@ static void interpret_standby(uint8_t standby)
printf("vendor-specific");
if (standby == 254)
printf("reserved");
- printf(")\n");
+ puts(")");
}
static const uint8_t xfermode_val[] ALIGN1 = {
@@ -1582,7 +1645,7 @@ static void interpret_xfermode(unsigned xfermode)
printf("UltraDMA mode%u", xfermode - 64);
else
printf("unknown");
- printf(")\n");
+ puts(")");
}
#endif /* HDIO_DRIVE_CMD */
@@ -1633,7 +1696,7 @@ static void process_dev(char *devname)
if (noisy_piomode) {
printf(" attempting to ");
if (piomode == 255)
- printf("auto-tune PIO mode\n");
+ puts("auto-tune PIO mode");
else if (piomode < 100)
printf("set PIO mode to %d\n", piomode);
else if (piomode < 200)
@@ -1762,7 +1825,7 @@ static void process_dev(char *devname)
#ifndef WIN_STANDBYNOW2
#define WIN_STANDBYNOW2 0x94
#endif
- printf(" issuing standby command\n");
+ puts(" issuing standby command");
args[0] = WIN_STANDBYNOW1;
ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_STANDBYNOW2);
}
@@ -1773,13 +1836,13 @@ static void process_dev(char *devname)
#ifndef WIN_SLEEPNOW2
#define WIN_SLEEPNOW2 0x99
#endif
- printf(" issuing sleep command\n");
+ puts(" issuing sleep command");
args[0] = WIN_SLEEPNOW1;
ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_SLEEPNOW2);
}
if (set_seagate) {
args[0] = 0xfb;
- printf(" disabling Seagate auto powersaving mode\n");
+ puts(" disabling Seagate auto powersaving mode");
ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
}
if (getset_standby == IS_SET) {
@@ -1815,17 +1878,17 @@ static void process_dev(char *devname)
if (!ioctl_or_warn(fd, HDIO_GET_32BIT, &parm)) {
printf(" IO_support\t=%3ld (", parm);
if (parm == 0)
- printf("default 16-bit)\n");
+ puts("default 16-bit)");
else if (parm == 2)
- printf("16-bit)\n");
+ puts("16-bit)");
else if (parm == 1)
- printf("32-bit)\n");
+ puts("32-bit)");
else if (parm == 3)
- printf("32-bit w/sync)\n");
+ puts("32-bit w/sync)");
else if (parm == 8)
- printf("Request-Queue-Bypass)\n");
+ puts("Request-Queue-Bypass)");
else
- printf("\?\?\?)\n");
+ puts("\?\?\?)");
}
}
if (getset_unmask) {
@@ -1837,7 +1900,7 @@ static void process_dev(char *devname)
if (!ioctl_or_warn(fd, HDIO_GET_DMA, &parm)) {
printf(fmt, "using_dma", parm);
if (parm == 8)
- printf(" (DMA-Assisted-PIO)\n");
+ puts(" (DMA-Assisted-PIO)");
else
on_off(parm != 0);
}
@@ -1921,7 +1984,7 @@ static void process_dev(char *devname)
id.multsect_valid &= ~1;
dump_identity(&id);
} else if (errno == -ENOMSG)
- printf(" no identification info available\n");
+ puts(" no identification info available");
else if (ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn */
bb_perror_msg("HDIO_GET_IDENTITY");
else
diff --git a/miscutils/i2c_tools.c b/miscutils/i2c_tools.c
new file mode 100644
index 0000000..7731466
--- a/dev/null
+++ b/miscutils/i2c_tools.c
@@ -0,0 +1,1343 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Minimal i2c-tools implementation for busybox.
+ * Parts of code ported from i2c-tools:
+ * http://www.lm-sensors.org/wiki/I2CTools.
+ *
+ * Copyright (C) 2014 by Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config I2CGET
+//config: bool "i2cget"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Read from I2C/SMBus chip registers.
+//config:
+//config:config I2CSET
+//config: bool "i2cset"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Set I2C registers.
+//config:
+//config:config I2CDUMP
+//config: bool "i2cdump"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Examine I2C registers.
+//config:
+//config:config I2CDETECT
+//config: bool "i2cdetect"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Detect I2C chips.
+//config:
+
+//applet:IF_I2CGET(APPLET(i2cget, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_I2CSET(APPLET(i2cset, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_I2CDUMP(APPLET(i2cdump, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_I2CDETECT(APPLET(i2cdetect, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_I2CGET) += i2c_tools.o
+//kbuild:lib-$(CONFIG_I2CSET) += i2c_tools.o
+//kbuild:lib-$(CONFIG_I2CDUMP) += i2c_tools.o
+//kbuild:lib-$(CONFIG_I2CDETECT) += i2c_tools.o
+
+/*
+ * Unsupported stuff:
+ *
+ * - upstream i2c-tools can also look-up i2c busses by name, we only accept
+ * numbers,
+ * - bank and bankreg parameters for i2cdump are not supported because of
+ * their limited usefulness (see i2cdump manual entry for more info),
+ * - i2cdetect doesn't look for bus info in /proc as it does in upstream, but
+ * it shouldn't be a problem in modern kernels.
+ */
+
+#include "libbb.h"
+#include "common_bufsiz.h"
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#define I2CDUMP_NUM_REGS 256
+
+#define I2CDETECT_MODE_AUTO 0
+#define I2CDETECT_MODE_QUICK 1
+#define I2CDETECT_MODE_READ 2
+
+/*
+ * This is needed for ioctl_or_perror_and_die() since it only accepts pointers.
+ */
+static ALWAYS_INLINE void *itoptr(int i)
+{
+ return (void*)(intptr_t)i;
+}
+
+static int32_t i2c_smbus_access(int fd, char read_write, uint8_t cmd,
+ int size, union i2c_smbus_data *data)
+{
+ struct i2c_smbus_ioctl_data args;
+
+ args.read_write = read_write;
+ args.command = cmd;
+ args.size = size;
+ args.data = data;
+
+ return ioctl(fd, I2C_SMBUS, &args);
+}
+
+static int32_t i2c_smbus_read_byte(int fd)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(fd, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &data);
+ if (err < 0)
+ return err;
+
+ return data.byte;
+}
+
+#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
+static int32_t i2c_smbus_write_byte(int fd, uint8_t val)
+{
+ return i2c_smbus_access(fd, I2C_SMBUS_WRITE,
+ val, I2C_SMBUS_BYTE, NULL);
+}
+
+static int32_t i2c_smbus_read_byte_data(int fd, uint8_t cmd)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (err < 0)
+ return err;
+
+ return data.byte;
+}
+
+static int32_t i2c_smbus_read_word_data(int fd, uint8_t cmd)
+{
+ union i2c_smbus_data data;
+ int err;
+
+ err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
+ I2C_SMBUS_WORD_DATA, &data);
+ if (err < 0)
+ return err;
+
+ return data.word;
+}
+#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
+
+#if ENABLE_I2CSET
+static int32_t i2c_smbus_write_byte_data(int file,
+ uint8_t cmd, uint8_t value)
+{
+ union i2c_smbus_data data;
+
+ data.byte = value;
+
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
+ I2C_SMBUS_BYTE_DATA, &data);
+}
+
+static int32_t i2c_smbus_write_word_data(int file, uint8_t cmd, uint16_t value)
+{
+ union i2c_smbus_data data;
+
+ data.word = value;
+
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
+ I2C_SMBUS_WORD_DATA, &data);
+}
+
+static int32_t i2c_smbus_write_block_data(int file, uint8_t cmd,
+ uint8_t length, const uint8_t *values)
+{
+ union i2c_smbus_data data;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+
+ memcpy(data.block+1, values, length);
+ data.block[0] = length;
+
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
+ I2C_SMBUS_BLOCK_DATA, &data);
+}
+
+static int32_t i2c_smbus_write_i2c_block_data(int file, uint8_t cmd,
+ uint8_t length, const uint8_t *values)
+{
+ union i2c_smbus_data data;
+
+ if (length > I2C_SMBUS_BLOCK_MAX)
+ length = I2C_SMBUS_BLOCK_MAX;
+
+ memcpy(data.block+1, values, length);
+ data.block[0] = length;
+
+ return i2c_smbus_access(file, I2C_SMBUS_WRITE, cmd,
+ I2C_SMBUS_I2C_BLOCK_BROKEN, &data);
+}
+#endif /* ENABLE_I2CSET */
+
+#if ENABLE_I2CDUMP
+/*
+ * Returns the number of bytes read, vals must hold at
+ * least I2C_SMBUS_BLOCK_MAX bytes.
+ */
+static int32_t i2c_smbus_read_block_data(int fd, uint8_t cmd, uint8_t *vals)
+{
+ union i2c_smbus_data data;
+ int i, err;
+
+ err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
+ I2C_SMBUS_BLOCK_DATA, &data);
+ if (err < 0)
+ return err;
+
+ for (i = 1; i <= data.block[0]; i++)
+ *vals++ = data.block[i];
+ return data.block[0];
+}
+
+static int32_t i2c_smbus_read_i2c_block_data(int fd, uint8_t cmd,
+ uint8_t len, uint8_t *vals)
+{
+ union i2c_smbus_data data;
+ int i, err;
+
+ if (len > I2C_SMBUS_BLOCK_MAX)
+ len = I2C_SMBUS_BLOCK_MAX;
+ data.block[0] = len;
+
+ err = i2c_smbus_access(fd, I2C_SMBUS_READ, cmd,
+ len == 32 ? I2C_SMBUS_I2C_BLOCK_BROKEN :
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+ if (err < 0)
+ return err;
+
+ for (i = 1; i <= data.block[0]; i++)
+ *vals++ = data.block[i];
+ return data.block[0];
+}
+#endif /* ENABLE_I2CDUMP */
+
+#if ENABLE_I2CDETECT
+static int32_t i2c_smbus_write_quick(int fd, uint8_t val)
+{
+ return i2c_smbus_access(fd, val, 0, I2C_SMBUS_QUICK, NULL);
+}
+#endif /* ENABLE_I2CDETECT */
+
+static int i2c_bus_lookup(const char *bus_str)
+{
+ return xstrtou_range(bus_str, 10, 0, 0xfffff);
+}
+
+#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
+static int i2c_parse_bus_addr(const char *addr_str)
+{
+ /* Slave address must be in range 0x03 - 0x77. */
+ return xstrtou_range(addr_str, 16, 0x03, 0x77);
+}
+
+static void i2c_set_pec(int fd, int pec)
+{
+ ioctl_or_perror_and_die(fd, I2C_PEC,
+ itoptr(pec ? 1 : 0),
+ "can't set PEC");
+}
+
+static void i2c_set_slave_addr(int fd, int addr, int force)
+{
+ ioctl_or_perror_and_die(fd, force ? I2C_SLAVE_FORCE : I2C_SLAVE,
+ itoptr(addr),
+ "can't set address to 0x%02x", addr);
+}
+#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
+
+#if ENABLE_I2CGET || ENABLE_I2CSET
+static int i2c_parse_data_addr(const char *data_addr)
+{
+ /* Data address must be an 8 bit integer. */
+ return xstrtou_range(data_addr, 16, 0, 0xff);
+}
+#endif /* ENABLE_I2CGET || ENABLE_I2CSET */
+
+/*
+ * Opens the device file associated with given i2c bus.
+ *
+ * Upstream i2c-tools also support opening devices by i2c bus name
+ * but we drop it here for size reduction.
+ */
+static int i2c_dev_open(int i2cbus)
+{
+ char filename[sizeof("/dev/i2c-%d") + sizeof(int)*3];
+ int fd;
+
+ sprintf(filename, "/dev/i2c-%d", i2cbus);
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ filename[8] = '/'; /* change to "/dev/i2c/%d" */
+ fd = xopen(filename, O_RDWR);
+ } else {
+ bb_perror_msg_and_die("can't open '%s'", filename);
+ }
+ }
+
+ return fd;
+}
+
+/* Size reducing helpers for xxx_check_funcs(). */
+static void get_funcs_matrix(int fd, unsigned long *funcs)
+{
+ ioctl_or_perror_and_die(fd, I2C_FUNCS, funcs,
+ "can't get adapter functionality matrix");
+}
+
+#if ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP
+static void check_funcs_test_end(int funcs, int pec, const char *err)
+{
+ if (pec && !(funcs & (I2C_FUNC_SMBUS_PEC | I2C_FUNC_I2C)))
+ bb_error_msg("warning: adapter does not support PEC");
+
+ if (err)
+ bb_error_msg_and_die(
+ "adapter has no %s capability", err);
+}
+#endif /* ENABLE_I2CGET || ENABLE_I2CSET || ENABLE_I2CDUMP */
+
+/*
+ * The below functions emit an error message and exit if the adapter doesn't
+ * support desired functionalities.
+ */
+#if ENABLE_I2CGET || ENABLE_I2CDUMP
+static void check_read_funcs(int fd, int mode, int data_addr, int pec)
+{
+ unsigned long funcs;
+ const char *err = NULL;
+
+ get_funcs_matrix(fd, &funcs);
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
+ err = "SMBus receive byte";
+ break;
+ }
+ if (data_addr >= 0 && !(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
+ err = "SMBus send byte";
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA))
+ err = "SMBus read byte";
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
+ err = "SMBus read word";
+ break;
+#if ENABLE_I2CDUMP
+ case I2C_SMBUS_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+ err = "SMBus block read";
+ break;
+
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ err = "I2C block read";
+ break;
+#endif /* ENABLE_I2CDUMP */
+ default:
+ bb_error_msg_and_die("internal error");
+ }
+ check_funcs_test_end(funcs, pec, err);
+}
+#endif /* ENABLE_I2CGET || ENABLE_I2CDUMP */
+
+#if ENABLE_I2CSET
+static void check_write_funcs(int fd, int mode, int pec)
+{
+ unsigned long funcs;
+ const char *err = NULL;
+
+ get_funcs_matrix(fd, &funcs);
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE))
+ err = "SMBus send byte";
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ err = "SMBus write byte";
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_WORD_DATA))
+ err = "SMBus write word";
+ break;
+
+ case I2C_SMBUS_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
+ err = "SMBus block write";
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ if (!(funcs & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK))
+ err = "I2C block write";
+ break;
+ }
+ check_funcs_test_end(funcs, pec, err);
+}
+#endif /* ENABLE_I2CSET */
+
+static void confirm_or_abort(void)
+{
+ fprintf(stderr, "Continue? [y/N] ");
+ fflush_all();
+ if (!bb_ask_confirmation())
+ bb_error_msg_and_die("aborting");
+}
+
+/*
+ * Return only if user confirms the action, abort otherwise.
+ *
+ * The messages displayed here are much less elaborate than their i2c-tools
+ * counterparts - this is done for size reduction.
+ */
+static void confirm_action(int bus_addr, int mode, int data_addr, int pec)
+{
+ bb_error_msg("WARNING! This program can confuse your I2C bus");
+
+ /* Don't let the user break his/her EEPROMs */
+ if (bus_addr >= 0x50 && bus_addr <= 0x57 && pec) {
+ bb_error_msg_and_die("this is I2C not smbus - using PEC on I2C "
+ "devices may result in data loss, aborting");
+ }
+
+ if (mode == I2C_SMBUS_BYTE && data_addr >= 0 && pec)
+ bb_error_msg("WARNING! May interpret a write byte command "
+ "with PEC as a write byte data command");
+
+ if (pec)
+ bb_error_msg("PEC checking enabled");
+
+ confirm_or_abort();
+}
+
+#if ENABLE_I2CGET
+//usage:#define i2cget_trivial_usage
+//usage: "[-f] [-y] BUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]"
+//usage:#define i2cget_full_usage "\n\n"
+//usage: "Read from I2C/SMBus chip registers\n"
+//usage: "\n I2CBUS i2c bus number"
+//usage: "\n ADDRESS 0x03 - 0x77"
+//usage: "\nMODE is:"
+//usage: "\n b read byte data (default)"
+//usage: "\n w read word data"
+//usage: "\n c write byte/read byte"
+//usage: "\n Append p for SMBus PEC"
+//usage: "\n"
+//usage: "\n -f force access"
+//usage: "\n -y disable interactive mode"
+int i2cget_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int i2cget_main(int argc UNUSED_PARAM, char **argv)
+{
+ const unsigned opt_f = (1 << 0), opt_y = (1 << 1);
+ const char *const optstr = "fy";
+
+ int bus_num, bus_addr, data_addr = -1, status;
+ int mode = I2C_SMBUS_BYTE, pec = 0, fd;
+ unsigned opts;
+
+ opt_complementary = "-2:?4"; /* from 2 to 4 args */
+ opts = getopt32(argv, optstr);
+ argv += optind;
+
+ bus_num = i2c_bus_lookup(argv[0]);
+ bus_addr = i2c_parse_bus_addr(argv[1]);
+
+ if (argv[2]) {
+ data_addr = i2c_parse_data_addr(argv[2]);
+ mode = I2C_SMBUS_BYTE_DATA;
+ if (argv[3]) {
+ switch (argv[3][0]) {
+ case 'b': /* Already set */ break;
+ case 'w': mode = I2C_SMBUS_WORD_DATA; break;
+ case 'c': mode = I2C_SMBUS_BYTE; break;
+ default:
+ bb_error_msg("invalid mode");
+ bb_show_usage();
+ }
+ pec = argv[3][1] == 'p';
+ }
+ }
+
+ fd = i2c_dev_open(bus_num);
+ check_read_funcs(fd, mode, data_addr, pec);
+ i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
+
+ if (!(opts & opt_y))
+ confirm_action(bus_addr, mode, data_addr, pec);
+
+ if (pec)
+ i2c_set_pec(fd, 1);
+
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ if (data_addr >= 0) {
+ status = i2c_smbus_write_byte(fd, data_addr);
+ if (status < 0)
+ bb_error_msg("warning - write failed");
+ }
+ status = i2c_smbus_read_byte(fd);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ status = i2c_smbus_read_word_data(fd, data_addr);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ status = i2c_smbus_read_byte_data(fd, data_addr);
+ }
+ close(fd);
+
+ if (status < 0)
+ bb_perror_msg_and_die("read failed");
+
+ printf("0x%0*x\n", mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
+
+ return 0;
+}
+#endif /* ENABLE_I2CGET */
+
+#if ENABLE_I2CSET
+//usage:#define i2cset_trivial_usage
+//usage: "[-f] [-y] [-m MASK] BUS CHIP-ADDR DATA-ADDR [VALUE] ... [MODE]"
+//usage:#define i2cset_full_usage "\n\n"
+//usage: "Set I2C registers\n"
+//usage: "\n I2CBUS i2c bus number"
+//usage: "\n ADDRESS 0x03 - 0x77"
+//usage: "\nMODE is:"
+//usage: "\n c byte, no value"
+//usage: "\n b byte data (default)"
+//usage: "\n w word data"
+//usage: "\n i I2C block data"
+//usage: "\n s SMBus block data"
+//usage: "\n Append p for SMBus PEC"
+//usage: "\n"
+//usage: "\n -f force access"
+//usage: "\n -y disable interactive mode"
+//usage: "\n -r read back and compare the result"
+//usage: "\n -m MASK mask specifying which bits to write"
+int i2cset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int i2cset_main(int argc, char **argv)
+{
+ const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
+ opt_m = (1 << 2), opt_r = (1 << 3);
+ const char *const optstr = "fym:r";
+
+ int bus_num, bus_addr, data_addr, mode = I2C_SMBUS_BYTE, pec = 0;
+ int val, blen = 0, mask = 0, fd, status;
+ unsigned char block[I2C_SMBUS_BLOCK_MAX];
+ char *opt_m_arg = NULL;
+ unsigned opts;
+
+ opt_complementary = "-3"; /* from 3 to ? args */
+ opts = getopt32(argv, optstr, &opt_m_arg);
+ argv += optind;
+ argc -= optind;
+
+ bus_num = i2c_bus_lookup(argv[0]);
+ bus_addr = i2c_parse_bus_addr(argv[1]);
+ data_addr = i2c_parse_data_addr(argv[2]);
+
+ if (argv[3]) {
+ if (!argv[4] && argv[3][0] != 'c') {
+ mode = I2C_SMBUS_BYTE_DATA; /* Implicit b */
+ } else {
+ switch (argv[argc-1][0]) {
+ case 'c': /* Already set */ break;
+ case 'b': mode = I2C_SMBUS_BYTE_DATA; break;
+ case 'w': mode = I2C_SMBUS_WORD_DATA; break;
+ case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
+ case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
+ default:
+ bb_error_msg("invalid mode");
+ bb_show_usage();
+ }
+
+ pec = argv[argc-1][1] == 'p';
+ if (mode == I2C_SMBUS_BLOCK_DATA ||
+ mode == I2C_SMBUS_I2C_BLOCK_DATA) {
+ if (pec && mode == I2C_SMBUS_I2C_BLOCK_DATA)
+ bb_error_msg_and_die(
+ "PEC not supported for I2C "
+ "block writes");
+ if (opts & opt_m)
+ bb_error_msg_and_die(
+ "mask not supported for block "
+ "writes");
+ }
+ }
+ }
+
+ /* Prepare the value(s) to be written according to current mode. */
+ switch (mode) {
+ case I2C_SMBUS_BYTE_DATA:
+ val = xstrtou_range(argv[3], 0, 0, 0xff);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ val = xstrtou_range(argv[3], 0, 0, 0xffff);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ for (blen = 3; blen < (argc - 1); blen++)
+ block[blen] = xstrtou_range(argv[blen], 0, 0, 0xff);
+ val = -1;
+ break;
+ default:
+ val = -1;
+ break;
+ }
+
+ if (opts & opt_m) {
+ mask = xstrtou_range(opt_m_arg, 0, 0,
+ (mode == I2C_SMBUS_BYTE ||
+ mode == I2C_SMBUS_BYTE_DATA) ? 0xff : 0xffff);
+ }
+
+ fd = i2c_dev_open(bus_num);
+ check_write_funcs(fd, mode, pec);
+ i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
+
+ if (!(opts & opt_y))
+ confirm_action(bus_addr, mode, data_addr, pec);
+
+ /*
+ * If we're using mask - read the current value here and adjust the
+ * value to be written.
+ */
+ if (opts & opt_m) {
+ int tmpval;
+
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ tmpval = i2c_smbus_read_byte(fd);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ tmpval = i2c_smbus_read_word_data(fd, data_addr);
+ break;
+ default:
+ tmpval = i2c_smbus_read_byte_data(fd, data_addr);
+ }
+
+ if (tmpval < 0)
+ bb_perror_msg_and_die("can't read old value");
+
+ val = (val & mask) | (tmpval & ~mask);
+
+ if (!(opts & opt_y)) {
+ bb_error_msg("old value 0x%0*x, write mask "
+ "0x%0*x, will write 0x%0*x to register "
+ "0x%02x",
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, tmpval,
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, mask,
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
+ data_addr);
+ confirm_or_abort();
+ }
+ }
+
+ if (pec)
+ i2c_set_pec(fd, 1);
+
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ status = i2c_smbus_write_byte(fd, data_addr);
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ status = i2c_smbus_write_word_data(fd, data_addr, val);
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ status = i2c_smbus_write_block_data(fd, data_addr,
+ blen, block);
+ break;
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+ status = i2c_smbus_write_i2c_block_data(fd, data_addr,
+ blen, block);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ status = i2c_smbus_write_byte_data(fd, data_addr, val);
+ break;
+ }
+ if (status < 0)
+ bb_perror_msg_and_die("write failed");
+
+ if (pec)
+ i2c_set_pec(fd, 0); /* Clear PEC. */
+
+ /* No readback required - we're done. */
+ if (!(opts & opt_r))
+ return 0;
+
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ status = i2c_smbus_read_byte(fd);
+ val = data_addr;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ status = i2c_smbus_read_word_data(fd, data_addr);
+ break;
+ default: /* I2C_SMBUS_BYTE_DATA */
+ status = i2c_smbus_read_byte_data(fd, data_addr);
+ }
+
+ if (status < 0) {
+ puts("Warning - readback failed");
+ } else
+ if (status != val) {
+ printf("Warning - data mismatch - wrote "
+ "0x%0*x, read back 0x%0*x\n",
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val,
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, status);
+ } else {
+ printf("Value 0x%0*x written, readback matched\n",
+ mode == I2C_SMBUS_WORD_DATA ? 4 : 2, val);
+ }
+
+ return 0;
+}
+#endif /* ENABLE_I2CSET */
+
+#if ENABLE_I2CDUMP
+static int read_block_data(int buf_fd, int mode, int *block)
+{
+ uint8_t cblock[I2C_SMBUS_BLOCK_MAX + I2CDUMP_NUM_REGS];
+ int res, blen = 0, tmp, i;
+
+ if (mode == I2C_SMBUS_BLOCK_DATA) {
+ blen = i2c_smbus_read_block_data(buf_fd, 0, cblock);
+ if (blen <= 0)
+ goto fail;
+ } else {
+ for (res = 0; res < I2CDUMP_NUM_REGS; res += tmp) {
+ tmp = i2c_smbus_read_i2c_block_data(
+ buf_fd, res, I2C_SMBUS_BLOCK_MAX,
+ cblock + res);
+ if (tmp <= 0) {
+ blen = tmp;
+ goto fail;
+ }
+ }
+
+ if (res >= I2CDUMP_NUM_REGS)
+ res = I2CDUMP_NUM_REGS;
+
+ for (i = 0; i < res; i++)
+ block[i] = cblock[i];
+
+ if (mode != I2C_SMBUS_BLOCK_DATA)
+ for (i = res; i < I2CDUMP_NUM_REGS; i++)
+ block[i] = -1;
+ }
+
+ return blen;
+
+ fail:
+ bb_error_msg_and_die("block read failed: %d", blen);
+}
+
+/* Dump all but word data. */
+static void dump_data(int bus_fd, int mode, unsigned first,
+ unsigned last, int *block, int blen)
+{
+ int i, j, res;
+
+ puts(" 0 1 2 3 4 5 6 7 8 9 a b c d e f"
+ " 0123456789abcdef");
+
+ for (i = 0; i < I2CDUMP_NUM_REGS; i += 0x10) {
+ if (mode == I2C_SMBUS_BLOCK_DATA && i >= blen)
+ break;
+ if (i/16 < first/16)
+ continue;
+ if (i/16 > last/16)
+ break;
+
+ printf("%02x: ", i);
+ for (j = 0; j < 16; j++) {
+ fflush_all();
+ /* Skip unwanted registers */
+ if (i+j < first || i+j > last) {
+ printf(" ");
+ if (mode == I2C_SMBUS_WORD_DATA) {
+ printf(" ");
+ j++;
+ }
+ continue;
+ }
+
+ switch (mode) {
+ case I2C_SMBUS_BYTE_DATA:
+ res = i2c_smbus_read_byte_data(bus_fd, i+j);
+ block[i+j] = res;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ res = i2c_smbus_read_word_data(bus_fd, i+j);
+ if (res < 0) {
+ block[i+j] = res;
+ block[i+j+1] = res;
+ } else {
+ block[i+j] = res & 0xff;
+ block[i+j+1] = res >> 8;
+ }
+ break;
+ case I2C_SMBUS_BYTE:
+ res = i2c_smbus_read_byte(bus_fd);
+ block[i+j] = res;
+ break;
+ default:
+ res = block[i+j];
+ }
+
+ if (mode == I2C_SMBUS_BLOCK_DATA &&
+ i+j >= blen) {
+ printf(" ");
+ } else if (res < 0) {
+ printf("XX ");
+ if (mode == I2C_SMBUS_WORD_DATA)
+ printf("XX ");
+ } else {
+ printf("%02x ", block[i+j]);
+ if (mode == I2C_SMBUS_WORD_DATA)
+ printf("%02x ", block[i+j+1]);
+ }
+
+ if (mode == I2C_SMBUS_WORD_DATA)
+ j++;
+ }
+ printf(" ");
+
+ for (j = 0; j < 16; j++) {
+ if (mode == I2C_SMBUS_BLOCK_DATA && i+j >= blen)
+ break;
+ /* Skip unwanted registers */
+ if (i+j < first || i+j > last) {
+ bb_putchar(' ');
+ continue;
+ }
+
+ res = block[i+j];
+ if (res < 0) {
+ bb_putchar('X');
+ } else if (res == 0x00 || res == 0xff) {
+ bb_putchar('.');
+ } else if (res < 32 || res >= 127) {
+ bb_putchar('?');
+ } else {
+ bb_putchar(res);
+ }
+ }
+ bb_putchar('\n');
+ }
+}
+
+static void dump_word_data(int bus_fd, unsigned first, unsigned last)
+{
+ int i, j, rv;
+
+ /* Word data. */
+ puts(" 0,8 1,9 2,a 3,b 4,c 5,d 6,e 7,f");
+ for (i = 0; i < 256; i += 8) {
+ if (i/8 < first/8)
+ continue;
+ if (i/8 > last/8)
+ break;
+
+ printf("%02x: ", i);
+ for (j = 0; j < 8; j++) {
+ /* Skip unwanted registers. */
+ if (i+j < first || i+j > last) {
+ printf(" ");
+ continue;
+ }
+
+ rv = i2c_smbus_read_word_data(bus_fd, i+j);
+ if (rv < 0)
+ printf("XXXX ");
+ else
+ printf("%04x ", rv & 0xffff);
+ }
+ bb_putchar('\n');
+ }
+}
+
+//usage:#define i2cdump_trivial_usage
+//usage: "[-f] [-r FIRST-LAST] [-y] BUS ADDR [MODE]"
+//usage:#define i2cdump_full_usage "\n\n"
+//usage: "Examine I2C registers\n"
+//usage: "\n I2CBUS i2c bus number"
+//usage: "\n ADDRESS 0x03 - 0x77"
+//usage: "\nMODE is:"
+//usage: "\n b byte (default)"
+//usage: "\n w word"
+//usage: "\n W word on even register addresses"
+//usage: "\n i I2C block"
+//usage: "\n s SMBus block"
+//usage: "\n c consecutive byte"
+//usage: "\n Append p for SMBus PEC"
+//usage: "\n"
+//usage: "\n -f force access"
+//usage: "\n -y disable interactive mode"
+//usage: "\n -r limit the number of registers being accessed"
+int i2cdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int i2cdump_main(int argc UNUSED_PARAM, char **argv)
+{
+ const unsigned opt_f = (1 << 0), opt_y = (1 << 1),
+ opt_r = (1 << 2);
+ const char *const optstr = "fyr:";
+
+ int bus_num, bus_addr, mode = I2C_SMBUS_BYTE_DATA, even = 0, pec = 0;
+ unsigned first = 0x00, last = 0xff, opts;
+ int block[I2CDUMP_NUM_REGS];
+ char *opt_r_str, *dash;
+ int fd, res;
+
+ opt_complementary = "-2:?3"; /* from 2 to 3 args */
+ opts = getopt32(argv, optstr, &opt_r_str);
+ argv += optind;
+
+ bus_num = i2c_bus_lookup(argv[0]);
+ bus_addr = i2c_parse_bus_addr(argv[1]);
+
+ if (argv[2]) {
+ switch (argv[2][0]) {
+ case 'b': /* Already set. */ break;
+ case 'c': mode = I2C_SMBUS_BYTE; break;
+ case 'w': mode = I2C_SMBUS_WORD_DATA; break;
+ case 'W':
+ mode = I2C_SMBUS_WORD_DATA;
+ even = 1;
+ break;
+ case 's': mode = I2C_SMBUS_BLOCK_DATA; break;
+ case 'i': mode = I2C_SMBUS_I2C_BLOCK_DATA; break;
+ default:
+ bb_error_msg_and_die("invalid mode");
+ }
+
+ if (argv[2][1] == 'p') {
+ if (argv[2][0] == 'W' || argv[2][0] == 'i') {
+ bb_error_msg_and_die(
+ "pec not supported for -W and -i");
+ } else {
+ pec = 1;
+ }
+ }
+ }
+
+ if (opts & opt_r) {
+ first = strtol(opt_r_str, &dash, 0);
+ if (dash == opt_r_str || *dash != '-' || first > 0xff)
+ bb_error_msg_and_die("invalid range");
+ last = xstrtou_range(++dash, 0, first, 0xff);
+
+ /* Range is not available for every mode. */
+ switch (mode) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (!even || (!(first % 2) && last % 2))
+ break;
+ /* Fall through */
+ default:
+ bb_error_msg_and_die(
+ "range not compatible with selected mode");
+ }
+ }
+
+ fd = i2c_dev_open(bus_num);
+ check_read_funcs(fd, mode, -1 /* data_addr */, pec);
+ i2c_set_slave_addr(fd, bus_addr, opts & opt_f);
+
+ if (pec)
+ i2c_set_pec(fd, 1);
+
+ if (!(opts & opt_y))
+ confirm_action(bus_addr, mode, -1 /* data_addr */, pec);
+
+ /* All but word data. */
+ if (mode != I2C_SMBUS_WORD_DATA || even) {
+ int blen = 0;
+
+ if (mode == I2C_SMBUS_BLOCK_DATA || mode == I2C_SMBUS_I2C_BLOCK_DATA)
+ blen = read_block_data(fd, mode, block);
+
+ if (mode == I2C_SMBUS_BYTE) {
+ res = i2c_smbus_write_byte(fd, first);
+ if (res < 0)
+ bb_perror_msg_and_die("write start address");
+ }
+
+ dump_data(fd, mode, first, last, block, blen);
+ } else {
+ dump_word_data(fd, first, last);
+ }
+
+ return 0;
+}
+#endif /* ENABLE_I2CDUMP */
+
+#if ENABLE_I2CDETECT
+enum adapter_type {
+ ADT_DUMMY = 0,
+ ADT_ISA,
+ ADT_I2C,
+ ADT_SMBUS,
+};
+
+struct adap_desc {
+ const char *funcs;
+ const char *algo;
+};
+
+static const struct adap_desc adap_descs[] = {
+ { .funcs = "dummy",
+ .algo = "Dummy bus", },
+ { .funcs = "isa",
+ .algo = "ISA bus", },
+ { .funcs = "i2c",
+ .algo = "I2C adapter", },
+ { .funcs = "smbus",
+ .algo = "SMBus adapter", },
+};
+
+struct i2c_func
+{
+ long value;
+ const char* name;
+};
+
+static const struct i2c_func i2c_funcs_tab[] = {
+ { .value = I2C_FUNC_I2C,
+ .name = "I2C" },
+ { .value = I2C_FUNC_SMBUS_QUICK,
+ .name = "SMBus quick command" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BYTE,
+ .name = "SMBus send byte" },
+ { .value = I2C_FUNC_SMBUS_READ_BYTE,
+ .name = "SMBus receive byte" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BYTE_DATA,
+ .name = "SMBus write byte" },
+ { .value = I2C_FUNC_SMBUS_READ_BYTE_DATA,
+ .name = "SMBus read byte" },
+ { .value = I2C_FUNC_SMBUS_WRITE_WORD_DATA,
+ .name = "SMBus write word" },
+ { .value = I2C_FUNC_SMBUS_READ_WORD_DATA,
+ .name = "SMBus read word" },
+ { .value = I2C_FUNC_SMBUS_PROC_CALL,
+ .name = "SMBus process call" },
+ { .value = I2C_FUNC_SMBUS_WRITE_BLOCK_DATA,
+ .name = "SMBus block write" },
+ { .value = I2C_FUNC_SMBUS_READ_BLOCK_DATA,
+ .name = "SMBus block read" },
+ { .value = I2C_FUNC_SMBUS_BLOCK_PROC_CALL,
+ .name = "SMBus block process call" },
+ { .value = I2C_FUNC_SMBUS_PEC,
+ .name = "SMBus PEC" },
+ { .value = I2C_FUNC_SMBUS_WRITE_I2C_BLOCK,
+ .name = "I2C block write" },
+ { .value = I2C_FUNC_SMBUS_READ_I2C_BLOCK,
+ .name = "I2C block read" },
+ { .value = 0, .name = NULL }
+};
+
+static enum adapter_type i2cdetect_get_funcs(int bus)
+{
+ enum adapter_type ret;
+ unsigned long funcs;
+ int fd;
+
+ fd = i2c_dev_open(bus);
+
+ get_funcs_matrix(fd, &funcs);
+ if (funcs & I2C_FUNC_I2C)
+ ret = ADT_I2C;
+ else if (funcs & (I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ ret = ADT_SMBUS;
+ else
+ ret = ADT_DUMMY;
+
+ close(fd);
+
+ return ret;
+}
+
+static void NORETURN list_i2c_busses_and_exit(void)
+{
+ const char *const i2cdev_path = "/sys/class/i2c-dev";
+
+ char path[NAME_MAX], name[128];
+ struct dirent *de, *subde;
+ enum adapter_type adt;
+ DIR *dir, *subdir;
+ int rv, bus;
+ char *pos;
+ FILE *fp;
+
+ /*
+ * XXX Upstream i2cdetect also looks for i2c bus info in /proc/bus/i2c,
+ * but we won't bother since it's only useful on older kernels (before
+ * 2.6.5). We expect sysfs to be present and mounted at /sys/.
+ */
+
+ dir = xopendir(i2cdev_path);
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] == '.')
+ continue;
+
+ /* Simple version for ISA chips. */
+ snprintf(path, NAME_MAX, "%s/%s/name",
+ i2cdev_path, de->d_name);
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ snprintf(path, NAME_MAX,
+ "%s/%s/device/name",
+ i2cdev_path, de->d_name);
+ fp = fopen(path, "r");
+ }
+
+ /* Non-ISA chips require the hard-way. */
+ if (fp == NULL) {
+ snprintf(path, NAME_MAX,
+ "%s/%s/device/name",
+ i2cdev_path, de->d_name);
+ subdir = opendir(path);
+ if (subdir == NULL)
+ continue;
+
+ while ((subde = readdir(subdir))) {
+ if (subde->d_name[0] == '.')
+ continue;
+
+ if (is_prefixed_with(subde->d_name, "i2c-")) {
+ snprintf(path, NAME_MAX,
+ "%s/%s/device/%s/name",
+ i2cdev_path, de->d_name,
+ subde->d_name);
+ fp = fopen(path, "r");
+ break;
+ }
+ }
+ }
+
+ if (fp != NULL) {
+ /*
+ * Get the rest of the info and display a line
+ * for a single bus.
+ */
+ memset(name, 0, sizeof(name));
+ pos = fgets(name, sizeof(name), fp);
+ fclose(fp);
+ if (pos == NULL)
+ continue;
+
+ pos = strchr(name, '\n');
+ if (pos != NULL)
+ *pos = '\0';
+
+ rv = sscanf(de->d_name, "i2c-%d", &bus);
+ if (rv != 1)
+ continue;
+
+ if (is_prefixed_with(name, "ISA"))
+ adt = ADT_ISA;
+ else
+ adt = i2cdetect_get_funcs(bus);
+
+ printf(
+ "i2c-%d\t%-10s\t%-32s\t%s\n",
+ bus, adap_descs[adt].funcs,
+ name, adap_descs[adt].algo);
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void NORETURN no_support(const char *cmd)
+{
+ bb_error_msg_and_die("bus doesn't support %s", cmd);
+}
+
+static void will_skip(const char *cmd)
+{
+ bb_error_msg(
+ "warning: can't use %s command, "
+ "will skip some addresses", cmd);
+}
+
+//usage:#define i2cdetect_trivial_usage
+//usage: "[-F I2CBUS] [-l] [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]"
+//usage:#define i2cdetect_full_usage "\n\n"
+//usage: "Detect I2C chips.\n"
+//usage: "\n I2CBUS i2c bus number"
+//usage: "\n FIRST and LAST limit the probing range"
+//usage: "\n"
+//usage: "\n -l output list of installed busses"
+//usage: "\n -y disable interactive mode"
+//usage: "\n -a force scanning of non-regular addresses"
+//usage: "\n -q use smbus quick write commands for probing (default)"
+//usage: "\n -r use smbus read byte commands for probing"
+//usage: "\n -F display list of functionalities"
+int i2cdetect_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int i2cdetect_main(int argc UNUSED_PARAM, char **argv)
+{
+ const unsigned opt_y = (1 << 0), opt_a = (1 << 1),
+ opt_q = (1 << 2), opt_r = (1 << 3),
+ opt_F = (1 << 4), opt_l = (1 << 5);
+ const char *const optstr = "yaqrFl";
+
+ int fd, bus_num, i, j, mode = I2CDETECT_MODE_AUTO, status, cmd;
+ unsigned first = 0x03, last = 0x77, opts;
+ unsigned long funcs;
+
+ opt_complementary = "q--r:r--q:" /* mutually exclusive */
+ "?3"; /* up to 3 args */
+ opts = getopt32(argv, optstr);
+ argv += optind;
+
+ if (opts & opt_l)
+ list_i2c_busses_and_exit();
+
+ if (!argv[0])
+ bb_show_usage();
+
+ bus_num = i2c_bus_lookup(argv[0]);
+ fd = i2c_dev_open(bus_num);
+ get_funcs_matrix(fd, &funcs);
+
+ if (opts & opt_F) {
+ /* Only list the functionalities. */
+ printf("Functionalities implemented by bus #%d\n", bus_num);
+ for (i = 0; i2c_funcs_tab[i].value; i++) {
+ printf("%-32s %s\n", i2c_funcs_tab[i].name,
+ funcs & i2c_funcs_tab[i].value ? "yes" : "no");
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+ if (opts & opt_r)
+ mode = I2CDETECT_MODE_READ;
+ else if (opts & opt_q)
+ mode = I2CDETECT_MODE_QUICK;
+
+ if (opts & opt_a) {
+ first = 0x00;
+ last = 0x7f;
+ }
+
+ /* Read address range. */
+ if (argv[1]) {
+ first = xstrtou_range(argv[1], 16, first, last);
+ if (argv[2])
+ last = xstrtou_range(argv[2], 16, first, last);
+ }
+
+ if (!(funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) {
+ no_support("detection commands");
+ } else
+ if (mode == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)) {
+ no_support("SMBus quick write");
+ } else
+ if (mode == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE)) {
+ no_support("SMBus receive byte");
+ }
+
+ if (mode == I2CDETECT_MODE_AUTO) {
+ if (!(funcs & I2C_FUNC_SMBUS_QUICK))
+ will_skip("SMBus quick write");
+ if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+ will_skip("SMBus receive byte");
+ }
+
+ if (!(opts & opt_y))
+ confirm_action(-1, -1, -1, 0);
+
+ puts(" 0 1 2 3 4 5 6 7 8 9 a b c d e f");
+ for (i = 0; i < 128; i += 16) {
+ printf("%02x: ", i);
+ for (j = 0; j < 16; j++) {
+ fflush_all();
+
+ cmd = mode;
+ if (mode == I2CDETECT_MODE_AUTO) {
+ if ((i+j >= 0x30 && i+j <= 0x37) ||
+ (i+j >= 0x50 && i+j <= 0x5F))
+ cmd = I2CDETECT_MODE_READ;
+ else
+ cmd = I2CDETECT_MODE_QUICK;
+ }
+
+ /* Skip unwanted addresses. */
+ if (i+j < first
+ || i+j > last
+ || (cmd == I2CDETECT_MODE_READ && !(funcs & I2C_FUNC_SMBUS_READ_BYTE))
+ || (cmd == I2CDETECT_MODE_QUICK && !(funcs & I2C_FUNC_SMBUS_QUICK)))
+ {
+ printf(" ");
+ continue;
+ }
+
+ status = ioctl(fd, I2C_SLAVE, itoptr(i + j));
+ if (status < 0) {
+ if (errno == EBUSY) {
+ printf("UU ");
+ continue;
+ }
+
+ bb_perror_msg_and_die(
+ "can't set address to 0x%02x", i + j);
+ }
+
+ switch (cmd) {
+ case I2CDETECT_MODE_READ:
+ /*
+ * This is known to lock SMBus on various
+ * write-only chips (mainly clock chips).
+ */
+ status = i2c_smbus_read_byte(fd);
+ break;
+ default: /* I2CDETECT_MODE_QUICK: */
+ /*
+ * This is known to corrupt the Atmel
+ * AT24RF08 EEPROM.
+ */
+ status = i2c_smbus_write_quick(fd,
+ I2C_SMBUS_WRITE);
+ break;
+ }
+
+ if (status < 0)
+ printf("-- ");
+ else
+ printf("%02x ", i+j);
+ }
+ bb_putchar('\n');
+ }
+
+ return 0;
+}
+#endif /* ENABLE_I2CDETECT */
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c
index 7a1a6a2..601df64 100644
--- a/miscutils/inotifyd.c
+++ b/miscutils/inotifyd.c
@@ -26,6 +26,16 @@
*
* See below for mask names explanation.
*/
+//config:config INOTIFYD
+//config: bool "inotifyd"
+//config: default n # doesn't build on Knoppix 5
+//config: help
+//config: Simple inotify daemon. Reports filesystem changes. Requires
+//config: kernel >= 2.6.13
+
+//applet:IF_INOTIFYD(APPLET(inotifyd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_INOTIFYD) += inotifyd.o
//usage:#define inotifyd_trivial_usage
//usage: "PROG FILE1[:MASK]..."
@@ -47,8 +57,8 @@
//usage: "\n o Event queue overflowed"
//usage: "\n x File can't be watched anymore"
//usage: "\nIf watching a directory:"
-//usage: "\n m Subfile is moved into dir"
-//usage: "\n y Subfile is moved out of dir"
+//usage: "\n y Subfile is moved into dir"
+//usage: "\n m Subfile is moved out of dir"
//usage: "\n n Subfile is created"
//usage: "\n d Subfile is deleted"
//usage: "\n"
@@ -56,6 +66,7 @@
//usage: "\nWhen x event happens for all FILEs, inotifyd exits."
#include "libbb.h"
+#include "common_bufsiz.h"
#include <sys/inotify.h>
static const char mask_names[] ALIGN1 =
@@ -161,9 +172,10 @@ int inotifyd_main(int argc, char **argv)
// read out all pending events
// (NB: len must be int, not ssize_t or long!)
- xioctl(pfd.fd, FIONREAD, &len);
#define eventbuf bb_common_bufsiz1
- ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
+ setup_common_bufsiz();
+ xioctl(pfd.fd, FIONREAD, &len);
+ ie = buf = (len <= COMMON_BUFSIZE) ? eventbuf : xmalloc(len);
len = full_read(pfd.fd, buf, len);
// process events. N.B. events may vary in length
while (len > 0) {
diff --git a/miscutils/ionice.c b/miscutils/ionice.c
index bd30060..c54b3a6 100644
--- a/miscutils/ionice.c
+++ b/miscutils/ionice.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IONICE
+//config: bool "ionice"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Set/set program io scheduling class and priority
+//config: Requires kernel >= 2.6.13
+
+//applet:IF_IONICE(APPLET(ionice, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IONICE) += ionice.o
//usage:#define ionice_trivial_usage
//usage: "[-c 1-3] [-n 0-7] [-p PID] [PROG]"
@@ -41,7 +52,7 @@ enum {
IOPRIO_CLASS_IDLE
};
-static const char to_prio[] = "none\0realtime\0best-effort\0idle";
+static const char to_prio[] ALIGN1 = "none\0realtime\0best-effort\0idle";
#define IOPRIO_CLASS_SHIFT 13
@@ -60,9 +71,8 @@ int ionice_main(int argc UNUSED_PARAM, char **argv)
};
/* Numeric params */
- opt_complementary = "n+:c+:p+";
/* '+': stop at first non-option */
- opt = getopt32(argv, "+n:c:p:", &pri, &ioclass, &pid);
+ opt = getopt32(argv, "+n:+c:+p:+", &pri, &ioclass, &pid);
argv += optind;
if (opt & OPT_c) {
diff --git a/miscutils/last.c b/miscutils/last.c
index 24f6e1c..67c1343 100644
--- a/miscutils/last.c
+++ b/miscutils/last.c
@@ -6,6 +6,28 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config LAST
+//config: bool "last"
+//config: default y
+//config: depends on FEATURE_WTMP
+//config: help
+//config: 'last' displays a list of the last users that logged into the system.
+//config:
+//config:config FEATURE_LAST_FANCY
+//config: bool "Turn on output of extra information"
+//config: default y
+//config: depends on LAST
+//config: help
+//config: 'last' displays detailed information about the last users that
+//config: logged into the system (mimics sysvinit last). +900 bytes.
+
+//applet:IF_LAST(APPLET(last, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:ifeq ($(CONFIG_FEATURE_LAST_FANCY),y)
+//kbuild:lib-$(CONFIG_FEATURE_LAST_FANCY) += last_fancy.o
+//kbuild:else
+//kbuild:lib-$(CONFIG_LAST) += last.o
+//kbuild:endif
//usage:#define last_trivial_usage
//usage: ""IF_FEATURE_LAST_FANCY("[-HW] [-f FILE]")
@@ -32,21 +54,22 @@
#if defined UT_LINESIZE \
&& ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256))
-#error struct utmp member char[] size(s) have changed!
+#error struct utmpx member char[] size(s) have changed!
#elif defined __UT_LINESIZE \
- && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256))
-#error struct utmp member char[] size(s) have changed!
+ && ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 32) || (__UT_HOSTSIZE != 256))
+/* __UT_NAMESIZE was checked with 64 above, but glibc-2.11 definitely uses 32! */
+#error struct utmpx member char[] size(s) have changed!
#endif
#if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \
OLD_TIME != 4
-#error Values for the ut_type field of struct utmp changed
+#error Values for the ut_type field of struct utmpx changed
#endif
int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
- struct utmp ut;
+ struct utmpx ut;
int n, file = STDIN_FILENO;
time_t t_tmp;
off_t pos;
@@ -87,11 +110,11 @@ int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
if (++n > 0)
ut.ut_type = n != 3 ? n : SHUTDOWN_TIME;
#else
- if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+ if (is_prefixed_with(ut.ut_user, "shutdown"))
ut.ut_type = SHUTDOWN_TIME;
- else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+ else if (is_prefixed_with(ut.ut_user, "reboot"))
ut.ut_type = BOOT_TIME;
- else if (strncmp(ut.ut_user, "runlevel", 8) == 0)
+ else if (is_prefixed_with(ut.ut_user, "runlevel"))
ut.ut_type = RUN_LVL;
#endif
} else {
diff --git a/miscutils/last_fancy.c b/miscutils/last_fancy.c
index f687d7e..e56e0ba 100644
--- a/miscutils/last_fancy.c
+++ b/miscutils/last_fancy.c
@@ -22,6 +22,10 @@
#define HEADER_LINE_WIDE "USER", "TTY", \
INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", " TIME", ""
+#if !defined __UT_LINESIZE && defined UT_LINESIZE
+# define __UT_LINESIZE UT_LINESIZE
+#endif
+
enum {
NORMAL,
LOGGED,
@@ -39,10 +43,10 @@ enum {
#define show_wide (option_mask32 & LAST_OPT_W)
-static void show_entry(struct utmp *ut, int state, time_t dur_secs)
+static void show_entry(struct utmpx *ut, int state, time_t dur_secs)
{
unsigned days, hours, mins;
- char duration[32];
+ char duration[sizeof("(%u+02:02)") + sizeof(int)*3];
char login_time[17];
char logout_time[8];
const char *logout_str;
@@ -53,7 +57,8 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs)
* but some systems have it wrong */
tmp = ut->ut_tv.tv_sec;
safe_strncpy(login_time, ctime(&tmp), 17);
- snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
+ tmp = dur_secs;
+ snprintf(logout_time, 8, "- %s", ctime(&tmp) + 11);
dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
/* unsigned int is easier to divide than time_t (which may be signed long) */
@@ -103,7 +108,7 @@ static void show_entry(struct utmp *ut, int state, time_t dur_secs)
duration_str);
}
-static int get_ut_type(struct utmp *ut)
+static int get_ut_type(struct utmpx *ut)
{
if (ut->ut_line[0] == '~') {
if (strcmp(ut->ut_user, "shutdown") == 0) {
@@ -141,7 +146,7 @@ static int get_ut_type(struct utmp *ut)
return ut->ut_type;
}
-static int is_runlevel_shutdown(struct utmp *ut)
+static int is_runlevel_shutdown(struct utmpx *ut)
{
if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
return 1;
@@ -153,7 +158,7 @@ static int is_runlevel_shutdown(struct utmp *ut)
int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int last_main(int argc UNUSED_PARAM, char **argv)
{
- struct utmp ut;
+ struct utmpx ut;
const char *filename = _PATH_WTMP;
llist_t *zlist;
off_t pos;
@@ -228,7 +233,7 @@ int last_main(int argc UNUSED_PARAM, char **argv)
break;
}
/* add_entry */
- llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+ llist_add_to(&zlist, xmemdup(&ut, sizeof(ut)));
break;
case USER_PROCESS: {
int show;
@@ -241,9 +246,9 @@ int last_main(int argc UNUSED_PARAM, char **argv)
{
llist_t *el, *next;
for (el = zlist; el; el = next) {
- struct utmp *up = (struct utmp *)el->data;
+ struct utmpx *up = (struct utmpx *)el->data;
next = el->link;
- if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
+ if (strncmp(up->ut_line, ut.ut_line, __UT_LINESIZE) == 0) {
if (show) {
show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
show = 0;
@@ -270,7 +275,7 @@ int last_main(int argc UNUSED_PARAM, char **argv)
show_entry(&ut, state, boot_time);
}
/* add_entry */
- llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+ llist_add_to(&zlist, xmemdup(&ut, sizeof(ut)));
break;
}
}
diff --git a/miscutils/less.c b/miscutils/less.c
index 3f4d50f..e90691b 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -48,6 +48,14 @@
//config: help
//config: The -M/-m flag enables a more sophisticated status line.
//config:
+//config:config FEATURE_LESS_TRUNCATE
+//config: bool "Enable -S"
+//config: default y
+//config: depends on LESS
+//config: help
+//config: The -S flag causes long lines to be truncated rather than
+//config: wrapped.
+//config:
//config:config FEATURE_LESS_MARKS
//config: bool "Enable marks"
//config: default y
@@ -97,22 +105,33 @@
//config: help
//config: Enables "-N" command.
+//applet:IF_LESS(APPLET(less, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LESS) += less.o
+
//usage:#define less_trivial_usage
-//usage: "[-E" IF_FEATURE_LESS_FLAGS("Mm") "Nh~I?] [FILE]..."
+//usage: "[-E" IF_FEATURE_LESS_REGEXP("I")IF_FEATURE_LESS_FLAGS("Mm")
+//usage: "N" IF_FEATURE_LESS_TRUNCATE("S") "h~] [FILE]..."
//usage:#define less_full_usage "\n\n"
//usage: "View FILE (or stdin) one screenful at a time\n"
//usage: "\n -E Quit once the end of a file is reached"
+//usage: IF_FEATURE_LESS_REGEXP(
+//usage: "\n -I Ignore case in all searches"
+//usage: )
//usage: IF_FEATURE_LESS_FLAGS(
//usage: "\n -M,-m Display status line with line numbers"
//usage: "\n and percentage through the file"
//usage: )
//usage: "\n -N Prefix line number to each line"
-//usage: "\n -I Ignore case in all searches"
+//usage: IF_FEATURE_LESS_TRUNCATE(
+//usage: "\n -S Truncate long lines"
+//usage: )
//usage: "\n -~ Suppress ~s displayed past EOF"
#include <sched.h> /* sched_yield() */
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_LESS_REGEXP
#include "xregex.h"
#endif
@@ -142,7 +161,7 @@ enum {
FLAG_N = 1 << 3,
FLAG_TILDE = 1 << 4,
FLAG_I = 1 << 5,
- FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD,
+ FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_TRUNCATE,
/* hijack command line options variable for internal state vars */
LESS_STATE_MATCH_BACKWARDS = 1 << 15,
};
@@ -154,6 +173,7 @@ enum { pattern_valid = 0 };
struct globals {
int cur_fline; /* signed */
int kbd_fd; /* fd to get input from */
+ int kbd_fd_orig_flags;
int less_gets_pos;
/* last position in last line, taking into account tabs */
size_t last_line_pos;
@@ -174,6 +194,12 @@ struct globals {
unsigned current_file;
char *filename;
char **files;
+#if ENABLE_FEATURE_LESS_FLAGS
+ int num_lines; /* a flag if < 0, line count if >= 0 */
+# define REOPEN_AND_COUNT (-1)
+# define REOPEN_STDIN (-2)
+# define NOT_REGULAR_FILE (-3)
+#endif
#if ENABLE_FEATURE_LESS_MARKS
unsigned num_marks;
unsigned mark_lines[15][2];
@@ -215,6 +241,7 @@ struct globals {
#define current_file (G.current_file )
#define filename (G.filename )
#define files (G.files )
+#define num_lines (G.num_lines )
#define num_marks (G.num_marks )
#define mark_lines (G.mark_lines )
#if ENABLE_FEATURE_LESS_REGEXP
@@ -282,6 +309,8 @@ static void print_statusline(const char *str)
static void less_exit(int code)
{
set_tty_cooked();
+ if (!(G.kbd_fd_orig_flags & O_NONBLOCK))
+ ndelay_off(kbd_fd);
clear_line();
if (code < 0)
kill_myself_with_sig(- code); /* does not return */
@@ -317,8 +346,10 @@ static void re_wrap(void)
*d = *s;
if (*d != '\0') {
new_line_pos++;
- if (*d == '\t') /* tab */
+ if (*d == '\t') { /* tab */
new_line_pos += 7;
+ new_line_pos &= (~7);
+ }
s++;
d++;
if (new_line_pos >= w) {
@@ -335,7 +366,7 @@ static void re_wrap(void)
dst_idx++;
if (new_line_pos < w) {
/* if we came here thru "goto next_new" */
- if (src_idx > (int) max_fline)
+ if (src_idx > max_fline)
break;
lineno = LINENO(s);
}
@@ -351,7 +382,7 @@ static void re_wrap(void)
new_cur_fline = dst_idx;
src_idx++;
/* no more lines? finish last new line (and exit the loop) */
- if (src_idx > (int) max_fline)
+ if (src_idx > max_fline)
goto next_new;
s = old_flines[src_idx];
if (lineno != LINENO(s)) {
@@ -380,6 +411,14 @@ static void fill_match_lines(unsigned pos);
#define fill_match_lines(pos) ((void)0)
#endif
+static int at_end(void)
+{
+ return (option_mask32 & FLAG_S)
+ ? !(cur_fline <= max_fline &&
+ max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
+ : !(max_fline > cur_fline + max_displayed_line);
+}
+
/* Devilishly complex routine.
*
* Has to deal with EOF and EPIPE on input,
@@ -402,33 +441,39 @@ static void fill_match_lines(unsigned pos);
* last_line_pos - screen line position of next char to be read
* (takes into account tabs and backspaces)
* eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
+ *
+ * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs,
+ * "/search on very long input" and "reaching max line count" corner cases.
*/
static void read_lines(void)
{
-#define readbuf bb_common_bufsiz1
char *current_line, *p;
int w = width;
char last_terminated = terminated;
+ time_t last_time = 0;
+ int retry_EAGAIN = 2;
#if ENABLE_FEATURE_LESS_REGEXP
unsigned old_max_fline = max_fline;
- time_t last_time = 0;
- unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
#endif
+#define readbuf bb_common_bufsiz1
+ setup_common_bufsiz();
+
+ /* (careful: max_fline can be -1) */
+ if (max_fline + 1 > MAXLINES)
+ return;
+
if (option_mask32 & FLAG_N)
w -= 8;
- IF_FEATURE_LESS_REGEXP(again0:)
-
- p = current_line = ((char*)xmalloc(w + 4)) + 4;
- max_fline += last_terminated;
+ p = current_line = ((char*)xmalloc(w + 5)) + 4;
if (!last_terminated) {
const char *cp = flines[max_fline];
- strcpy(p, cp);
- p += strlen(current_line);
- free(MEMPTR(flines[max_fline]));
+ p = stpcpy(p, cp);
+ free(MEMPTR(cp));
/* last_line_pos is still valid from previous read_lines() */
} else {
+ max_fline++;
last_line_pos = 0;
}
@@ -439,13 +484,29 @@ static void read_lines(void)
char c;
/* if no unprocessed chars left, eat more */
if (readpos >= readeof) {
- ndelay_on(0);
- eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
- ndelay_off(0);
+ int flags = ndelay_on(0);
+
+ while (1) {
+ time_t t;
+
+ errno = 0;
+ eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
+ if (errno != EAGAIN)
+ break;
+ t = time(NULL);
+ if (t != last_time) {
+ last_time = t;
+ if (--retry_EAGAIN < 0)
+ break;
+ }
+ sched_yield();
+ }
+ fcntl(0, F_SETFL, flags); /* ndelay_off(0) */
readpos = 0;
readeof = eof_error;
if (eof_error <= 0)
goto reached_eof;
+ retry_EAGAIN = 1;
}
c = readbuf[readpos];
/* backspace? [needed for manpages] */
@@ -464,7 +525,7 @@ static void read_lines(void)
new_last_line_pos += 7;
new_last_line_pos &= (~7);
}
- if ((int)new_last_line_pos >= w)
+ if ((int)new_last_line_pos > w)
break;
last_line_pos = new_last_line_pos;
}
@@ -480,6 +541,11 @@ static void read_lines(void)
*p++ = c;
*p = '\0';
} /* end of "read chars until we have a line" loop */
+#if 0
+//BUG: also triggers on this:
+// { printf "\nfoo\n"; sleep 1; printf "\nbar\n"; } | less
+// (resulting in lost empty line between "foo" and "bar" lines)
+// the "terminated" logic needs fixing (or explaining)
/* Corner case: linewrap with only "" wrapping to next line */
/* Looks ugly on screen, so we do not store this empty line */
if (!last_terminated && !current_line[0]) {
@@ -487,6 +553,7 @@ static void read_lines(void)
max_lineno++;
continue;
}
+#endif
reached_eof:
last_terminated = terminated;
flines = xrealloc_vector(flines, 8, max_fline);
@@ -500,11 +567,7 @@ static void read_lines(void)
eof_error = 0; /* Pretend we saw EOF */
break;
}
- if (!(option_mask32 & FLAG_S)
- ? (max_fline > cur_fline + max_displayed_line)
- : (max_fline >= (unsigned) cur_fline
- && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
- ) {
+ if (!at_end()) {
#if !ENABLE_FEATURE_LESS_REGEXP
break;
#else
@@ -517,38 +580,26 @@ static void read_lines(void)
#endif
}
if (eof_error <= 0) {
- if (eof_error < 0) {
- if (errno == EAGAIN) {
- /* not yet eof or error, reset flag (or else
- * we will hog CPU - select() will return
- * immediately */
- eof_error = 1;
- } else {
- print_statusline(bb_msg_read_error);
- }
- }
-#if !ENABLE_FEATURE_LESS_REGEXP
break;
-#else
- if (wanted_match < num_matches) {
- break;
- } else { /* goto_match called us */
- time_t t = time(NULL);
- if (t != last_time) {
- last_time = t;
- if (--seconds_p1 == 0)
- break;
- }
- sched_yield();
- goto again0; /* go loop again (max 2 seconds) */
- }
-#endif
}
max_fline++;
- current_line = ((char*)xmalloc(w + 4)) + 4;
+ current_line = ((char*)xmalloc(w + 5)) + 4;
p = current_line;
last_line_pos = 0;
} /* end of "read lines until we reach cur_fline" loop */
+
+ if (eof_error < 0) {
+ if (errno == EAGAIN) {
+ eof_error = 1;
+ } else {
+ print_statusline(bb_msg_read_error);
+ }
+ }
+#if ENABLE_FEATURE_LESS_FLAGS
+ else if (eof_error == 0)
+ num_lines = max_lineno;
+#endif
+
fill_match_lines(old_max_fline);
#if ENABLE_FEATURE_LESS_REGEXP
/* prevent us from being stuck in search for a match */
@@ -558,18 +609,61 @@ static void read_lines(void)
}
#if ENABLE_FEATURE_LESS_FLAGS
-/* Interestingly, writing calc_percent as a function saves around 32 bytes
- * on my build. */
-static int calc_percent(void)
+static int safe_lineno(int fline)
+{
+ if (fline >= max_fline)
+ fline = max_fline - 1;
+
+ /* also catches empty file (max_fline == 0) */
+ if (fline < 0)
+ return 0;
+
+ return LINENO(flines[fline]) + 1;
+}
+
+/* count number of lines in file */
+static void update_num_lines(void)
{
- unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
- return p <= 100 ? p : 100;
+ int count, fd;
+ struct stat stbuf;
+ ssize_t len, i;
+ char buf[4096];
+
+ /* only do this for regular files */
+ if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) {
+ count = 0;
+ fd = open("/proc/self/fd/0", O_RDONLY);
+ if (fd < 0 && num_lines == REOPEN_AND_COUNT) {
+ /* "filename" is valid only if REOPEN_AND_COUNT */
+ fd = open(filename, O_RDONLY);
+ }
+ if (fd < 0) {
+ /* somebody stole my file! */
+ num_lines = NOT_REGULAR_FILE;
+ return;
+ }
+ if (fstat(fd, &stbuf) != 0 || !S_ISREG(stbuf.st_mode)) {
+ num_lines = NOT_REGULAR_FILE;
+ goto do_close;
+ }
+ while ((len = safe_read(fd, buf, sizeof(buf))) > 0) {
+ for (i = 0; i < len; ++i) {
+ if (buf[i] == '\n' && ++count == MAXLINES)
+ goto done;
+ }
+ }
+ done:
+ num_lines = count;
+ do_close:
+ close(fd);
+ }
}
/* Print a status line if -M was specified */
static void m_status_print(void)
{
- int percentage;
+ int first, last;
+ unsigned percent;
if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
return;
@@ -578,17 +672,26 @@ static void m_status_print(void)
printf(HIGHLIGHT"%s", filename);
if (num_files > 1)
printf(" (file %i of %i)", current_file, num_files);
- printf(" lines %i-%i/%i ",
- cur_fline + 1, cur_fline + max_displayed_line + 1,
- max_fline + 1);
- if (cur_fline >= (int)(max_fline - max_displayed_line)) {
- printf("(END)"NORMAL);
+
+ first = safe_lineno(cur_fline);
+ last = (option_mask32 & FLAG_S)
+ ? MIN(first + max_displayed_line, max_lineno)
+ : safe_lineno(cur_fline + max_displayed_line);
+ printf(" lines %i-%i", first, last);
+
+ update_num_lines();
+ if (num_lines >= 0)
+ printf("/%i", num_lines);
+
+ if (at_end()) {
+ printf(" (END)");
if (num_files > 1 && current_file != num_files)
- printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
- return;
+ printf(" - next: %s", files[current_file]);
+ } else if (num_lines > 0) {
+ percent = (100 * last + num_lines/2) / num_lines;
+ printf(" %i%%", percent <= 100 ? percent : 100);
}
- percentage = calc_percent();
- printf("%i%%"NORMAL, percentage);
+ printf(NORMAL);
}
#endif
@@ -610,7 +713,7 @@ static void status_print(void)
#endif
clear_line();
- if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
+ if (cur_fline && !at_end()) {
bb_putchar(':');
return;
}
@@ -625,23 +728,6 @@ static void status_print(void)
print_hilite(p);
}
-static void cap_cur_fline(int nlines)
-{
- int diff;
- if (cur_fline < 0)
- cur_fline = 0;
- if (cur_fline + max_displayed_line > max_fline + TILDES) {
- cur_fline -= nlines;
- if (cur_fline < 0)
- cur_fline = 0;
- diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
- /* As the number of lines requested was too large, we just move
- * to the end of the file */
- if (diff > 0)
- cur_fline += diff;
- }
-}
-
static const char controls[] ALIGN1 =
/* NUL: never encountered; TAB: not converted */
/**/"\x01\x02\x03\x04\x05\x06\x07\x08" "\x0a\x0b\x0c\x0d\x0e\x0f"
@@ -653,27 +739,21 @@ static const char ctrlconv[] ALIGN1 =
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
-static void lineno_str(char *nbuf9, const char *line)
+static void print_lineno(const char *line)
{
- nbuf9[0] = '\0';
- if (option_mask32 & FLAG_N) {
- const char *fmt;
- unsigned n;
-
- if (line == empty_line_marker) {
- memset(nbuf9, ' ', 8);
- nbuf9[8] = '\0';
- return;
- }
+ const char *fmt = " ";
+ unsigned n = n; /* for compiler */
+
+ if (line != empty_line_marker) {
/* Width of 7 preserves tab spacing in the text */
fmt = "%7u ";
n = LINENO(line) + 1;
- if (n > 9999999) {
+ if (n > 9999999 && MAXLINES > 9999999) {
n %= 10000000;
fmt = "%07u ";
}
- sprintf(nbuf9, fmt, n);
}
+ printf(fmt, n);
}
@@ -685,8 +765,7 @@ static void print_found(const char *line)
char *growline;
regmatch_t match_structs;
- char buf[width];
- char nbuf9[9];
+ char buf[width+1];
const char *str = line;
char *p = buf;
size_t n;
@@ -736,12 +815,7 @@ static void print_found(const char *line)
match_status = 1;
}
- lineno_str(nbuf9, line);
- if (!growline) {
- printf(CLEAR_2_EOL"%s%s\n", nbuf9, str);
- return;
- }
- printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str);
+ printf("%s%s\n", growline ? growline : "", str);
free(growline);
}
#else
@@ -750,14 +824,10 @@ void print_found(const char *line);
static void print_ascii(const char *str)
{
- char buf[width];
- char nbuf9[9];
+ char buf[width+1];
char *p;
size_t n;
- lineno_str(nbuf9, str);
- printf(CLEAR_2_EOL"%s", nbuf9);
-
while (*str) {
n = strcspn(str, controls);
if (n) {
@@ -790,18 +860,28 @@ static void buffer_print(void)
unsigned i;
move_cursor(0, 0);
- for (i = 0; i <= max_displayed_line; i++)
+ for (i = 0; i <= max_displayed_line; i++) {
+ printf(CLEAR_2_EOL);
+ if (option_mask32 & FLAG_N)
+ print_lineno(buffer[i]);
if (pattern_valid)
print_found(buffer[i]);
else
print_ascii(buffer[i]);
+ }
+ if ((option_mask32 & FLAG_E)
+ && eof_error <= 0
+ && (max_fline - cur_fline) <= max_displayed_line
+ ) {
+ less_exit(EXIT_SUCCESS);
+ }
status_print();
}
static void buffer_fill_and_print(void)
{
unsigned i;
-#if ENABLE_FEATURE_LESS_DASHCMD
+#if ENABLE_FEATURE_LESS_TRUNCATE
int fpos = cur_fline;
if (option_mask32 & FLAG_S) {
@@ -833,45 +913,112 @@ static void buffer_fill_and_print(void)
buffer_print();
}
+/* move cur_fline to a given line number, reading lines if necessary */
+static void goto_lineno(int target)
+{
+ if (target <= 0 ) {
+ cur_fline = 0;
+ }
+ else if (target > LINENO(flines[cur_fline])) {
+ retry:
+ while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline)
+ ++cur_fline;
+ /* target not reached but more input is available */
+ if (LINENO(flines[cur_fline]) != target && eof_error > 0) {
+ read_lines();
+ goto retry;
+ }
+ }
+ else {
+ /* search backwards through already-read lines */
+ while (LINENO(flines[cur_fline]) != target && cur_fline > 0)
+ --cur_fline;
+ }
+}
+
+static void cap_cur_fline(void)
+{
+ if ((option_mask32 & FLAG_S)) {
+ if (cur_fline > max_fline)
+ cur_fline = max_fline;
+ if (LINENO(flines[cur_fline]) + max_displayed_line > max_lineno + TILDES) {
+ goto_lineno(max_lineno - max_displayed_line + TILDES);
+ read_lines();
+ }
+ }
+ else {
+ if (cur_fline + max_displayed_line > max_fline + TILDES)
+ cur_fline = max_fline - max_displayed_line + TILDES;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ }
+}
+
/* Move the buffer up and down in the file in order to scroll */
static void buffer_down(int nlines)
{
- cur_fline += nlines;
+ if ((option_mask32 & FLAG_S))
+ goto_lineno(LINENO(flines[cur_fline]) + nlines);
+ else
+ cur_fline += nlines;
read_lines();
- cap_cur_fline(nlines);
+ cap_cur_fline();
buffer_fill_and_print();
}
static void buffer_up(int nlines)
{
- cur_fline -= nlines;
- if (cur_fline < 0) cur_fline = 0;
+ if ((option_mask32 & FLAG_S)) {
+ goto_lineno(LINENO(flines[cur_fline]) - nlines);
+ }
+ else {
+ cur_fline -= nlines;
+ if (cur_fline < 0)
+ cur_fline = 0;
+ }
read_lines();
buffer_fill_and_print();
}
-static void buffer_line(int linenum)
+/* display a given line where the argument can be either an index into
+ * the flines array or a line number */
+static void buffer_to_line(int linenum, int is_lineno)
{
- if (linenum < 0)
- linenum = 0;
- cur_fline = linenum;
+ if (linenum <= 0)
+ cur_fline = 0;
+ else if (is_lineno)
+ goto_lineno(linenum);
+ else
+ cur_fline = linenum;
read_lines();
- if (linenum + max_displayed_line > max_fline)
- linenum = max_fline - max_displayed_line + TILDES;
- if (linenum < 0)
- linenum = 0;
- cur_fline = linenum;
+ cap_cur_fline();
buffer_fill_and_print();
}
+static void buffer_line(int linenum)
+{
+ buffer_to_line(linenum, FALSE);
+}
+
+static void buffer_lineno(int lineno)
+{
+ buffer_to_line(lineno, TRUE);
+}
+
static void open_file_and_read_lines(void)
{
if (filename) {
xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
+#if ENABLE_FEATURE_LESS_FLAGS
+ num_lines = REOPEN_AND_COUNT;
+#endif
} else {
/* "less" with no arguments in argv[] */
/* For status line only */
filename = xstrdup(bb_msg_standard_input);
+#if ENABLE_FEATURE_LESS_FLAGS
+ num_lines = REOPEN_STDIN;
+#endif
}
readpos = 0;
readeof = 0;
@@ -923,12 +1070,7 @@ static int64_t getch_nowait(void)
*/
rd = 1;
/* Are we interested in stdin? */
-//TODO: reuse code for determining this
- if (!(option_mask32 & FLAG_S)
- ? !(max_fline > cur_fline + max_displayed_line)
- : !(max_fline >= (unsigned) cur_fline
- && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
- ) {
+ if (at_end()) {
if (eof_error > 0) /* did NOT reach eof yet */
rd = 0; /* yes, we are interested in stdin */
}
@@ -1239,7 +1381,7 @@ static void number_process(int first_digit)
i = 1;
while (i < sizeof(num_input)-1) {
keypress = less_getch(i + 1);
- if ((unsigned)keypress > 255 || !isdigit(num_input[i]))
+ if ((unsigned)keypress > 255 || !isdigit(keypress))
break;
num_input[i] = keypress;
bb_putchar(keypress);
@@ -1263,15 +1405,16 @@ static void number_process(int first_digit)
buffer_up(num);
break;
case 'g': case '<': case 'G': case '>':
- cur_fline = num + max_displayed_line;
- read_lines();
- buffer_line(num - 1);
+ buffer_lineno(num - 1);
break;
case 'p': case '%':
- num = num * (max_fline / 100); /* + max_fline / 2; */
- cur_fline = num + max_displayed_line;
- read_lines();
- buffer_line(num);
+#if ENABLE_FEATURE_LESS_FLAGS
+ update_num_lines();
+ num = num * (num_lines > 0 ? num_lines : max_lineno) / 100;
+#else
+ num = num * max_lineno / 100;
+#endif
+ buffer_lineno(num);
break;
#if ENABLE_FEATURE_LESS_REGEXP
case 'n':
@@ -1311,10 +1454,12 @@ static void flag_change(void)
case '~':
option_mask32 ^= FLAG_TILDE;
break;
+#if ENABLE_FEATURE_LESS_TRUNCATE
case 'S':
option_mask32 ^= FLAG_S;
buffer_fill_and_print();
break;
+#endif
#if ENABLE_FEATURE_LESS_LINENUMS
case 'N':
option_mask32 ^= FLAG_N;
@@ -1412,7 +1557,7 @@ static void add_mark(void)
static void goto_mark(void)
{
int letter;
- unsigned i;
+ int i;
print_statusline("Go to mark: ");
letter = less_getch(sizeof("Go to mark: ") - 1);
@@ -1420,11 +1565,11 @@ static void goto_mark(void)
if (isalpha(letter)) {
for (i = 0; i <= num_marks; i++)
- if ((unsigned) letter == mark_lines[i][0]) {
+ if (letter == mark_lines[i][0]) {
buffer_line(mark_lines[i][1]);
break;
}
- if (num_marks == 14 && (unsigned) letter != mark_lines[14][0])
+ if (num_marks == 14 && letter != mark_lines[14][0])
print_statusline("Mark not set");
} else
print_statusline("Invalid mark letter");
@@ -1451,16 +1596,23 @@ static char opp_bracket(char bracket)
static void match_right_bracket(char bracket)
{
- unsigned i;
+ unsigned i = cur_fline;
- if (strchr(flines[cur_fline], bracket) == NULL) {
+ if (i >= max_fline
+ || strchr(flines[i], bracket) == NULL
+ ) {
print_statusline("No bracket in top line");
return;
}
+
bracket = opp_bracket(bracket);
- for (i = cur_fline + 1; i < max_fline; i++) {
+ for (; i < max_fline; i++) {
if (strchr(flines[i], bracket) != NULL) {
- buffer_line(i);
+ /*
+ * Line with matched right bracket becomes
+ * last visible line
+ */
+ buffer_line(i - max_displayed_line);
return;
}
}
@@ -1469,16 +1621,22 @@ static void match_right_bracket(char bracket)
static void match_left_bracket(char bracket)
{
- int i;
+ int i = cur_fline + max_displayed_line;
- if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
+ if (i >= max_fline
+ || strchr(flines[i], bracket) == NULL
+ ) {
print_statusline("No bracket in bottom line");
return;
}
bracket = opp_bracket(bracket);
- for (i = cur_fline + max_displayed_line; i >= 0; i--) {
+ for (; i >= 0; i--) {
if (strchr(flines[i], bracket) != NULL) {
+ /*
+ * Line with matched left bracket becomes
+ * first visible line
+ */
buffer_line(i);
return;
}
@@ -1613,10 +1771,13 @@ int less_main(int argc, char **argv)
INIT_G();
- /* TODO: -x: do not interpret backspace, -xx: tab also */
- /* -xxx: newline also */
- /* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
- getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S"));
+ /* TODO: -x: do not interpret backspace, -xx: tab also
+ * -xxx: newline also
+ * -w N: assume width N (-xxx -w 32: hex viewer of sorts)
+ * -s: condense many empty lines to one
+ * (used by some setups for manpage display)
+ */
+ getopt32(argv, "EMmN~I" IF_FEATURE_LESS_TRUNCATE("S") /*ignored:*/"s");
argc -= optind;
argv += optind;
num_files = argc;
@@ -1643,9 +1804,10 @@ int less_main(int argc, char **argv)
/* Some versions of less can survive w/o controlling tty,
* try to do the same. This also allows to specify an alternative
* tty via "less 1<>TTY".
- * We don't try to use STDOUT_FILENO directly,
+ *
+ * We prefer not to use STDOUT_FILENO directly,
* since we want to set this fd to non-blocking mode,
- * and not bother with restoring it on exit.
+ * and not interfere with other processes which share stdout with us.
*/
tty_name = xmalloc_ttyname(STDOUT_FILENO);
if (tty_name) {
@@ -1657,10 +1819,12 @@ int less_main(int argc, char **argv)
/* Try controlling tty */
try_ctty:
tty_fd = open(CURRENT_TTY, O_RDONLY);
- if (tty_fd < 0)
- return bb_cat(argv);
+ if (tty_fd < 0) {
+ /* If all else fails, less 481 uses stdout. Mimic that */
+ tty_fd = STDOUT_FILENO;
+ }
}
- ndelay_on(tty_fd);
+ G.kbd_fd_orig_flags = ndelay_on(tty_fd);
kbd_fd = tty_fd; /* save in a global */
tcgetattr(kbd_fd, &term_orig);
@@ -1826,7 +1990,7 @@ key and/or command line switch compatibility is a good idea:
Most options may be changed either on the command line,
or from within less by using the - or -- command.
Options may be given in one of two forms: either a single
- character preceded by a -, or a name preceeded by --.
+ character preceded by a -, or a name preceded by --.
-? ........ --help
Display help (from command line).
-a ........ --search-skip-screen
diff --git a/miscutils/makedevs.c b/miscutils/makedevs.c
index c945a13..9e7ca34 100644
--- a/miscutils/makedevs.c
+++ b/miscutils/makedevs.c
@@ -6,6 +6,41 @@
* Make ranges of device files quickly.
* known bugs: can't deal with alpha ranges
*/
+//config:config MAKEDEVS
+//config: bool "makedevs"
+//config: default y
+//config: help
+//config: 'makedevs' is a utility used to create a batch of devices with
+//config: one command.
+//config:
+//config: There are two choices for command line behaviour, the interface
+//config: as used by LEAF/Linux Router Project, or a device table file.
+//config:
+//config: 'leaf' is traditionally what busybox follows, it allows multiple
+//config: devices of a particluar type to be created per command.
+//config: e.g. /dev/hda[0-9]
+//config: Device properties are passed as command line arguments.
+//config:
+//config: 'table' reads device properties from a file or stdin, allowing
+//config: a batch of unrelated devices to be made with one command.
+//config: User/group names are allowed as an alternative to uid/gid.
+//config:
+//config:choice
+//config: prompt "Choose makedevs behaviour"
+//config: depends on MAKEDEVS
+//config: default FEATURE_MAKEDEVS_TABLE
+//config:
+//config:config FEATURE_MAKEDEVS_LEAF
+//config: bool "leaf"
+//config:
+//config:config FEATURE_MAKEDEVS_TABLE
+//config: bool "table"
+//config:
+//config:endchoice
+
+//applet:IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o
//usage:#if ENABLE_FEATURE_MAKEDEVS_LEAF
//usage:#define makedevs_trivial_usage
@@ -122,8 +157,11 @@ int makedevs_main(int argc, char **argv)
/* if mode != S_IFCHR and != S_IFBLK,
* third param in mknod() ignored */
- if (mknod(nodname, mode, makedev(Smajor, Sminor)))
+ if (mknod(nodname, mode, makedev(Smajor, Sminor)) != 0
+ && errno != EEXIST
+ ) {
bb_perror_msg("can't create '%s'", nodname);
+ }
/*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
nodname = buf;
@@ -244,7 +282,9 @@ int makedevs_main(int argc UNUSED_PARAM, char **argv)
for (i = start; i <= start + count; i++) {
sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
rdev = makedev(major, minor + (i - start) * increment);
- if (mknod(full_name_inc, mode, rdev) < 0) {
+ if (mknod(full_name_inc, mode, rdev) != 0
+ && errno != EEXIST
+ ) {
bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
ret = EXIT_FAILURE;
} else if (chown(full_name_inc, uid, gid) < 0) {
diff --git a/miscutils/man.c b/miscutils/man.c
index 9d58ef4..6a636f1 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -2,6 +2,15 @@
* Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MAN
+//config: bool "man"
+//config: default y
+//config: help
+//config: Format and display manual pages.
+
+//applet:IF_MAN(APPLET(man, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MAN) += man.o
//usage:#define man_trivial_usage
//usage: "[-aw] [MANPAGE]..."
@@ -9,8 +18,11 @@
//usage: "Format and display manual page\n"
//usage: "\n -a Display all pages"
//usage: "\n -w Show page locations"
+//usage: "\n"
+//usage: "\n$COLUMNS overrides output width"
#include "libbb.h"
+#include "common_bufsiz.h"
enum {
OPT_a = 1, /* all */
@@ -18,7 +30,6 @@ enum {
};
/* This is what I see on my desktop system being executed:
-
(
echo ".ll 12.4i"
echo ".nr LL 12.4i"
@@ -28,11 +39,41 @@ echo ".\\\""
echo ".pl \n(nlu+10"
) | gtbl | nroff -Tlatin1 -mandoc | less
-*/
+Some systems use -Tascii.
+
+On another system I see this:
-static int show_manpage(const char *pager, char *man_filename, int man, int level);
+... | tbl | nroff -mandoc -rLL=<NNN>n -rLT=<NNN>n -Tutf8 | less
-static int run_pipe(const char *pager, char *man_filename, int man, int level)
+where <NNN> is screen width minus 5.
+Replacing "DEFINE nroff nroff -mandoc" in /etc/man_db.conf
+changes "nroff -mandoc" part; -rLL=<NNN>n, -rLT=<NNN>n and -Tutf8 parts are
+appended to the user-specified command.
+
+Redirecting to a pipe or file sets GROFF_NO_SGR=1 to prevent color escapes,
+and uses "col -b -p -x" instead of pager, this filters out backspace
+and underscore tricks.
+*/
+
+struct globals {
+ const char *col;
+ const char *tbl;
+ const char *nroff;
+ const char *pager;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ G.col = "col"; \
+ G.tbl = "tbl"; \
+ /* Removed -Tlatin1. Assuming system nroff has suitable default */ \
+ G.nroff = "nroff -mandoc"; \
+ G.pager = ENABLE_LESS ? "less" : "more"; \
+} while (0)
+
+static int show_manpage(char *man_filename, int man, int level);
+
+static int run_pipe(char *man_filename, int man, int level)
{
char *cmd;
@@ -66,7 +107,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
goto ordinary_manpage;
line = xmalloc_open_zipped_read_close(man_filename, NULL);
- if (!line || strncmp(line, ".so ", 4) != 0) {
+ if (!line || !is_prefixed_with(line, ".so ")) {
free(line);
goto ordinary_manpage;
}
@@ -95,7 +136,7 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
man_filename = xasprintf("%s/%s", man_filename, linkname);
free(line);
/* Note: we leak "new" man_filename string as well... */
- if (show_manpage(pager, man_filename, man, level + 1))
+ if (show_manpage(man_filename, man, level + 1))
return 1;
/* else: show the link, it's better than nothing */
}
@@ -103,19 +144,26 @@ static int run_pipe(const char *pager, char *man_filename, int man, int level)
ordinary_manpage:
close(STDIN_FILENO);
open_zipped(man_filename, /*fail_if_not_compressed:*/ 0); /* guaranteed to use fd 0 (STDIN_FILENO) */
- /* "2>&1" is added so that nroff errors are shown in pager too.
- * Otherwise it may show just empty screen */
- cmd = xasprintf(
- man ? "gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s"
- : "%s",
- pager);
+ if (man) {
+ int w = get_terminal_width(-1);
+ if (w > 10)
+ w -= 2;
+ /* "2>&1" is added so that nroff errors are shown in pager too.
+ * Otherwise it may show just empty screen.
+ */
+ cmd = xasprintf("%s | %s -rLL=%un -rLT=%un 2>&1 | %s",
+ G.tbl, G.nroff, w, w,
+ G.pager);
+ } else {
+ cmd = xstrdup(G.pager);
+ }
system(cmd);
free(cmd);
return 1;
}
/* man_filename is of the form "/dir/dir/dir/name.s" */
-static int show_manpage(const char *pager, char *man_filename, int man, int level)
+static int show_manpage(char *man_filename, int man, int level)
{
#if SEAMLESS_COMPRESSION
/* We leak this allocation... */
@@ -124,58 +172,105 @@ static int show_manpage(const char *pager, char *man_filename, int man, int leve
#endif
#if ENABLE_FEATURE_SEAMLESS_LZMA
- if (run_pipe(pager, filename_with_zext, man, level))
+ if (run_pipe(filename_with_zext, man, level))
return 1;
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
strcpy(ext, "xz");
- if (run_pipe(pager, filename_with_zext, man, level))
+ if (run_pipe(filename_with_zext, man, level))
return 1;
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
strcpy(ext, "bz2");
- if (run_pipe(pager, filename_with_zext, man, level))
+ if (run_pipe(filename_with_zext, man, level))
return 1;
#endif
#if ENABLE_FEATURE_SEAMLESS_GZ
strcpy(ext, "gz");
- if (run_pipe(pager, filename_with_zext, man, level))
+ if (run_pipe(filename_with_zext, man, level))
return 1;
#endif
- return run_pipe(pager, man_filename, man, level);
+ return run_pipe(man_filename, man, level);
+}
+
+static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
+{
+ if (path) while (*path) {
+ char *next_path;
+ char **path_element;
+
+ next_path = strchr(path, ':');
+ if (next_path) {
+ if (next_path == path) /* "::"? */
+ goto next;
+ *next_path = '\0';
+ }
+ /* Do we already have path? */
+ path_element = man_path_list;
+ if (path_element) while (*path_element) {
+ if (strcmp(*path_element, path) == 0)
+ goto skip;
+ path_element++;
+ }
+ man_path_list = xrealloc_vector(man_path_list, 4, *count_mp);
+ man_path_list[*count_mp] = xstrdup(path);
+ (*count_mp)++;
+ /* man_path_list is NULL terminated */
+ /* man_path_list[*count_mp] = NULL; - xrealloc_vector did it */
+ skip:
+ if (!next_path)
+ break;
+ /* "path" may be a result of getenv(), be nice and don't mangle it */
+ *next_path = ':';
+ next:
+ path = next_path + 1;
+ }
+ return man_path_list;
+}
+
+static const char *if_redefined(const char *var, const char *key, const char *line)
+{
+ if (!is_prefixed_with(line, key))
+ return var;
+ line += strlen(key);
+ if (!isspace(line[0]))
+ return var;
+ return xstrdup(skip_whitespace(line));
}
int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int man_main(int argc UNUSED_PARAM, char **argv)
{
parser_t *parser;
- const char *pager;
- char **man_path_list;
char *sec_list;
char *cur_path, *cur_sect;
- int count_mp, cur_mp;
+ char **man_path_list;
+ int count_mp;
+ int cur_mp;
int opt, not_found;
char *token[2];
+ INIT_G();
+
opt_complementary = "-1"; /* at least one argument */
opt = getopt32(argv, "+aw");
argv += optind;
sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
- /* Last valid man_path_list[] is [0x10] */
+
count_mp = 0;
- man_path_list = xzalloc(0x11 * sizeof(man_path_list[0]));
- man_path_list[0] = getenv("MANPATH");
- if (!man_path_list[0]) /* default, may be overridden by /etc/man.conf */
+ man_path_list = add_MANPATH(NULL, &count_mp,
+ getenv("MANDATORY_MANPATH"+10) /* "MANPATH" */
+ );
+ if (!man_path_list) {
+ /* default, may be overridden by /etc/man.conf */
+ man_path_list = xzalloc(2 * sizeof(man_path_list[0]));
man_path_list[0] = (char*)"/usr/man";
- else
- count_mp++;
- pager = getenv("MANPAGER");
- if (!pager) {
- pager = getenv("PAGER");
- if (!pager)
- pager = "more";
+ /* count_mp stays 0.
+ * Thus, man.conf will overwrite man_path_list[0]
+ * if a path is defined there.
+ */
}
/* Parse man.conf[ig] or man_db.conf */
@@ -190,38 +285,16 @@ int man_main(int argc UNUSED_PARAM, char **argv)
while (config_read(parser, token, 2, 0, "# \t", PARSE_NORMAL)) {
if (!token[1])
continue;
+ if (strcmp("DEFINE", token[0]) == 0) {
+ G.col = if_redefined(G.tbl , "col", token[1]);
+ G.tbl = if_redefined(G.tbl , "tbl", token[1]);
+ G.nroff = if_redefined(G.nroff, "nroff", token[1]);
+ G.pager = if_redefined(G.pager, "pager", token[1]);
+ } else
if (strcmp("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
|| strcmp("MANDATORY_MANPATH", token[0]) == 0
) {
- char *path = token[1];
- while (*path) {
- char *next_path;
- char **path_element;
-
- next_path = strchr(path, ':');
- if (next_path) {
- *next_path = '\0';
- if (next_path++ == path) /* "::"? */
- goto next;
- }
- /* Do we already have path? */
- path_element = man_path_list;
- while (*path_element) {
- if (strcmp(*path_element, path) == 0)
- goto skip;
- path_element++;
- }
- man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
- man_path_list[count_mp] = xstrdup(path);
- count_mp++;
- /* man_path_list is NULL terminated */
- /*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
- skip:
- if (!next_path)
- break;
- next:
- path = next_path;
- }
+ man_path_list = add_MANPATH(man_path_list, &count_mp, token[1]);
}
if (strcmp("MANSECT", token[0]) == 0) {
free(sec_list);
@@ -230,13 +303,27 @@ int man_main(int argc UNUSED_PARAM, char **argv)
}
config_close(parser);
+ {
+ /* environment overrides setting from man.config */
+ char *env_pager = getenv("MANPAGER");
+ if (!env_pager)
+ env_pager = getenv("PAGER");
+ if (env_pager)
+ G.pager = env_pager;
+ }
+
+ if (!isatty(STDOUT_FILENO)) {
+ putenv((char*)"GROFF_NO_SGR=1");
+ G.pager = xasprintf("%s -b -p -x", G.col);
+ }
+
not_found = 0;
do { /* for each argv[] */
int found = 0;
cur_mp = 0;
if (strchr(*argv, '/')) {
- found = show_manpage(pager, *argv, /*man:*/ 1, 0);
+ found = show_manpage(*argv, /*man:*/ 1, 0);
goto check_found;
}
while ((cur_path = man_path_list[cur_mp++]) != NULL) {
@@ -257,7 +344,7 @@ int man_main(int argc UNUSED_PARAM, char **argv)
sect_len, cur_sect,
*argv,
sect_len, cur_sect);
- found_here = show_manpage(pager, man_filename, cat0man1, 0);
+ found_here = show_manpage(man_filename, cat0man1, 0);
found |= found_here;
cat0man1 += found_here + 1;
free(man_filename);
diff --git a/miscutils/microcom.c b/miscutils/microcom.c
index 5e29a1a..04605d8 100644
--- a/miscutils/microcom.c
+++ b/miscutils/microcom.c
@@ -7,6 +7,15 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MICROCOM
+//config: bool "microcom"
+//config: default y
+//config: help
+//config: The poor man's minicom utility for chatting with serial port devices.
+
+//applet:IF_MICROCOM(APPLET(microcom, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MICROCOM) += microcom.o
//usage:#define microcom_trivial_usage
//usage: "[-d DELAY] [-t TIMEOUT] [-s SPEED] [-X] TTY"
@@ -19,6 +28,7 @@
//usage: "\n -X Disable special meaning of NUL and Ctrl-X from stdin"
#include "libbb.h"
+#include "common_bufsiz.h"
// set raw tty mode
static void xget1(int fd, struct termios *t, struct termios *oldt)
@@ -63,8 +73,7 @@ int microcom_main(int argc UNUSED_PARAM, char **argv)
unsigned opts;
// fetch options
- opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
- opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
+ opts = getopt32(argv, "Xs:+d:+t:+", &speed, &delay, &timeout);
// argc -= optind;
argv += optind;
@@ -155,10 +164,11 @@ int microcom_main(int argc UNUSED_PARAM, char **argv)
skip_write: ;
}
if (pfd[0].revents) {
-#define iobuf bb_common_bufsiz1
ssize_t len;
+#define iobuf bb_common_bufsiz1
+ setup_common_bufsiz();
// read from device -> write to stdout
- len = safe_read(sfd, iobuf, sizeof(iobuf));
+ len = safe_read(sfd, iobuf, COMMON_BUFSIZE);
if (len > 0)
full_write(STDOUT_FILENO, iobuf, len);
else {
diff --git a/miscutils/mountpoint.c b/miscutils/mountpoint.c
index 7041f7c..8b9e1d7 100644
--- a/miscutils/mountpoint.c
+++ b/miscutils/mountpoint.c
@@ -8,6 +8,15 @@
*
* Based on sysvinit's mountpoint
*/
+//config:config MOUNTPOINT
+//config: bool "mountpoint"
+//config: default y
+//config: help
+//config: mountpoint checks if the directory is a mountpoint.
+
+//applet:IF_MOUNTPOINT(APPLET(mountpoint, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MOUNTPOINT) += mountpoint.o
//usage:#define mountpoint_trivial_usage
//usage: "[-q] <[-dn] DIR | -x DEVICE>"
diff --git a/miscutils/mt.c b/miscutils/mt.c
index 20afd3a..6b31696 100644
--- a/miscutils/mt.c
+++ b/miscutils/mt.c
@@ -2,6 +2,17 @@
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config MT
+//config: bool "mt"
+//config: default y
+//config: help
+//config: mt is used to control tape devices. You can use the mt utility
+//config: to advance or rewind a tape past a specified number of archive
+//config: files on the tape.
+
+//applet:IF_MT(APPLET(mt, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MT) += mt.o
//usage:#define mt_trivial_usage
//usage: "[-f device] opcode value"
diff --git a/miscutils/nandwrite.c b/miscutils/nandwrite.c
index d5ae1e7..395d01e 100644
--- a/miscutils/nandwrite.c
+++ b/miscutils/nandwrite.c
@@ -29,21 +29,27 @@
//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o
//usage:#define nandwrite_trivial_usage
-//usage: "[-p] [-s ADDR] MTD_DEVICE [FILE]"
+//usage: "[-np] [-s ADDR] MTD_DEVICE [FILE]"
//usage:#define nandwrite_full_usage "\n\n"
//usage: "Write to MTD_DEVICE\n"
+//usage: "\n -n Write without ecc"
//usage: "\n -p Pad to page size"
//usage: "\n -s ADDR Start address"
//usage:#define nanddump_trivial_usage
-//usage: "[-o] [-b] [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE"
+//usage: "[-no]" IF_LONG_OPTS(" [--bb=padbad|skipbad]") " [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE"
//usage:#define nanddump_full_usage "\n\n"
//usage: "Dump MTD_DEVICE\n"
+//usage: "\n -n Read without ecc"
//usage: "\n -o Dump oob data"
-//usage: "\n -b Omit bad block from the dump"
//usage: "\n -s ADDR Start address"
//usage: "\n -l LEN Length"
//usage: "\n -f FILE Dump to file ('-' for stdout)"
+//usage: IF_LONG_OPTS(
+//usage: "\n --bb=METHOD:"
+//usage: "\n skipbad: skip bad blocks"
+//usage: "\n padbad: substitute bad blocks by 0xff (default)"
+//usage: )
#include "libbb.h"
#include <mtd/mtd-user.h>
@@ -53,10 +59,14 @@
#define OPT_p (1 << 0) /* nandwrite only */
#define OPT_o (1 << 0) /* nanddump only */
-#define OPT_s (1 << 1)
-#define OPT_b (1 << 2)
+#define OPT_n (1 << 1)
+#define OPT_s (1 << 2)
#define OPT_f (1 << 3)
#define OPT_l (1 << 4)
+#define OPT_bb (1 << 5) /* must be the last one in the list */
+
+#define BB_PADBAD (1 << 0)
+#define BB_SKIPBAD (1 << 1)
/* helper for writing out 0xff for bad blocks pad */
static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob)
@@ -64,8 +74,8 @@ static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob)
unsigned char buf[meminfo->writesize];
unsigned count;
- /* round len to the next page */
- len = (len | ~(meminfo->writesize - 1)) + 1;
+ /* round len to the next page only if len is not already on a page */
+ len = ((len - 1) | (meminfo->writesize - 1)) + 1;
memset(buf, 0xff, sizeof(buf));
for (count = 0; count < len; count += meminfo->writesize) {
@@ -102,6 +112,7 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
/* Buffer for OOB data */
unsigned char *oobbuf;
unsigned opts;
+ unsigned bb_method = BB_SKIPBAD;
int fd;
ssize_t cnt;
unsigned mtdoffset, meminfo_writesize, blockstart, limit;
@@ -109,14 +120,18 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
struct mtd_info_user meminfo;
struct mtd_oob_buf oob;
unsigned char *filebuf;
- const char *opt_s = "0", *opt_f = "-", *opt_l;
+ const char *opt_s = "0", *opt_f = "-", *opt_l, *opt_bb;
if (IS_NANDDUMP) {
opt_complementary = "=1";
- opts = getopt32(argv, "os:bf:l:", &opt_s, &opt_f, &opt_l);
+#if ENABLE_LONG_OPTS
+ applet_long_options =
+ "bb\0" Required_argument "\xff"; /* no short equivalent */
+#endif
+ opts = getopt32(argv, "ons:f:l:", &opt_s, &opt_f, &opt_l, &opt_bb);
} else { /* nandwrite */
opt_complementary = "-1:?2";
- opts = getopt32(argv, "ps:", &opt_s);
+ opts = getopt32(argv, "pns:", &opt_s);
}
argv += optind;
@@ -132,12 +147,23 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
fd = xopen(argv[0], IS_NANDWRITE ? O_RDWR : O_RDONLY);
xioctl(fd, MEMGETINFO, &meminfo);
+ if (opts & OPT_n)
+ xioctl(fd, MTDFILEMODE, (void *)MTD_FILE_MODE_RAW);
+
mtdoffset = xstrtou(opt_s, 0);
if (IS_NANDDUMP && (opts & OPT_l)) {
unsigned length = xstrtou(opt_l, 0);
if (length < meminfo.size - mtdoffset)
end_addr = mtdoffset + length;
}
+ if (IS_NANDDUMP && (opts & OPT_bb)) {
+ if (strcmp("skipbad", opt_bb) == 0)
+ bb_method = BB_SKIPBAD;
+ else if (strcmp("padbad", opt_bb) == 0)
+ bb_method = BB_PADBAD;
+ else
+ bb_show_usage();
+ }
/* Pull it into a CPU register (hopefully) - smaller code that way */
meminfo_writesize = meminfo.writesize;
@@ -162,9 +188,15 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
tmp = next_good_eraseblock(fd, &meminfo, blockstart);
if (tmp != blockstart) {
/* bad block(s), advance mtdoffset */
- if (IS_NANDDUMP && !(opts & OPT_b)) {
- int bad_len = MIN(tmp, end_addr) - mtdoffset;
- dump_bad(&meminfo, bad_len, opts & OPT_o);
+ if (IS_NANDDUMP) {
+ if (bb_method == BB_PADBAD) {
+ int bad_len = MIN(tmp, end_addr) - mtdoffset;
+ dump_bad(&meminfo, bad_len, opts & OPT_o);
+ }
+ /* with option skipbad, increase the total length */
+ if (bb_method == BB_SKIPBAD) {
+ end_addr += (tmp - blockstart);
+ }
}
mtdoffset = tmp;
}
@@ -182,9 +214,19 @@ int nandwrite_main(int argc UNUSED_PARAM, char **argv)
mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart);
if (IS_NANDWRITE)
printf("Writing at 0x%08x\n", mtdoffset);
- else if (mtdoffset > blockstart && !(opts & OPT_b)) {
- int bad_len = MIN(mtdoffset, limit) - blockstart;
- dump_bad(&meminfo, bad_len, opts & OPT_o);
+ else if (mtdoffset > blockstart) {
+ if (bb_method == BB_PADBAD) {
+ /* dump FF padded bad block */
+ int bad_len = MIN(mtdoffset, limit) - blockstart;
+ dump_bad(&meminfo, bad_len, opts & OPT_o);
+ } else if (bb_method == BB_SKIPBAD) {
+ /* for skipbad, increase the length */
+ if ((end_addr + mtdoffset - blockstart) > end_addr)
+ end_addr += (mtdoffset - blockstart);
+ else
+ end_addr = ~0;
+ limit = MIN(meminfo.size, end_addr);
+ }
}
if (mtdoffset >= limit)
break;
diff --git a/miscutils/raidautorun.c b/miscutils/raidautorun.c
index b72d890..c6d8e62 100644
--- a/miscutils/raidautorun.c
+++ b/miscutils/raidautorun.c
@@ -7,6 +7,17 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
*/
+//config:config RAIDAUTORUN
+//config: bool "raidautorun"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: raidautorun tells the kernel md driver to
+//config: search and start RAID arrays.
+
+//applet:IF_RAIDAUTORUN(APPLET(raidautorun, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RAIDAUTORUN) += raidautorun.o
//usage:#define raidautorun_trivial_usage
//usage: "DEVICE"
diff --git a/miscutils/readahead.c b/miscutils/readahead.c
index e22aaa4..b8e9b25 100644
--- a/miscutils/readahead.c
+++ b/miscutils/readahead.c
@@ -9,6 +9,27 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config READAHEAD
+//config: bool "readahead"
+//config: default y
+//config: depends on LFS
+//config: select PLATFORM_LINUX
+//config: help
+//config: Preload the files listed on the command line into RAM cache so that
+//config: subsequent reads on these files will not block on disk I/O.
+//config:
+//config: This applet just calls the readahead(2) system call on each file.
+//config: It is mainly useful in system startup scripts to preload files
+//config: or executables before they are used. When used at the right time
+//config: (in particular when a CPU bound process is running) it can
+//config: significantly speed up system startup.
+//config:
+//config: As readahead(2) blocks until each file has been read, it is best to
+//config: run this applet as a background job.
+
+//applet:IF_READAHEAD(APPLET(readahead, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_READAHEAD) += readahead.o
//usage:#define readahead_trivial_usage
//usage: "[FILE]..."
diff --git a/miscutils/runlevel.c b/miscutils/runlevel.c
index 76231df..b6412a6 100644
--- a/miscutils/runlevel.c
+++ b/miscutils/runlevel.c
@@ -11,6 +11,19 @@
*
* initially busyboxified by Bernhard Reutner-Fischer
*/
+//config:config RUNLEVEL
+//config: bool "runlevel"
+//config: default y
+//config: depends on FEATURE_UTMP
+//config: help
+//config: find the current and previous system runlevel.
+//config:
+//config: This applet uses utmp but does not rely on busybox supporing
+//config: utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
+
+//applet:IF_RUNLEVEL(APPLET(runlevel, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RUNLEVEL) += runlevel.o
//usage:#define runlevel_trivial_usage
//usage: "[FILE]"
@@ -29,19 +42,19 @@
int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int runlevel_main(int argc UNUSED_PARAM, char **argv)
{
- struct utmp *ut;
+ struct utmpx *ut;
char prev;
- if (argv[1]) utmpname(argv[1]);
+ if (argv[1]) utmpxname(argv[1]);
- setutent();
- while ((ut = getutent()) != NULL) {
+ setutxent();
+ while ((ut = getutxent()) != NULL) {
if (ut->ut_type == RUN_LVL) {
prev = ut->ut_pid / 256;
if (prev == 0) prev = 'N';
printf("%c %c\n", prev, ut->ut_pid % 256);
if (ENABLE_FEATURE_CLEAN_UP)
- endutent();
+ endutxent();
return 0;
}
}
@@ -49,6 +62,6 @@ int runlevel_main(int argc UNUSED_PARAM, char **argv)
puts("unknown");
if (ENABLE_FEATURE_CLEAN_UP)
- endutent();
+ endutxent();
return 1;
}
diff --git a/miscutils/rx.c b/miscutils/rx.c
index 2f47643..f794abe 100644
--- a/miscutils/rx.c
+++ b/miscutils/rx.c
@@ -14,6 +14,16 @@
*
* This was originally written for blob and then adapted for busybox.
*/
+//config:config RX
+//config: bool "rx"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Receive files using the Xmodem protocol.
+
+//applet:IF_RX(APPLET(rx, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RX) += rx.o
//usage:#define rx_trivial_usage
//usage: "FILE"
@@ -101,7 +111,7 @@ static int receive(/*int read_fd, */int file_fd)
&& blockBuf[blockLength - 3] == PAD
) {
while (blockLength
- && blockBuf[blockLength - 1] == PAD
+ && blockBuf[blockLength - 1] == PAD
) {
blockLength--;
}
diff --git a/miscutils/setserial.c b/miscutils/setserial.c
index dfed330..8b5c4a9 100644
--- a/miscutils/setserial.c
+++ b/miscutils/setserial.c
@@ -257,7 +257,7 @@ enum print_mode
#define CTL_CLOSE (1 << 3)
#define CTL_NODIE (1 << 4)
-static const char serial_types[] =
+static const char serial_types[] ALIGN1 =
"unknown\0" /* 0 */
"8250\0" /* 1 */
"16450\0" /* 2 */
@@ -288,7 +288,7 @@ static const char serial_types[] =
# define MAX_SERIAL_TYPE 13
#endif
-static const char commands[] =
+static const char commands[] ALIGN1 =
"spd_normal\0"
"spd_hi\0"
"spd_vhi\0"
@@ -404,8 +404,8 @@ static const uint16_t setbits[CMD_FLAG_LAST + 1] =
ASYNC_LOW_LATENCY
};
-static const char STR_INFINITE[] = "infinite";
-static const char STR_NONE[] = "none";
+#define STR_INFINITE "infinite"
+#define STR_NONE "none"
static const char *uart_type(int type)
{
diff --git a/miscutils/setsid.c b/miscutils/setsid.c
index 637081b..143a8f8 100644
--- a/miscutils/setsid.c
+++ b/miscutils/setsid.c
@@ -13,21 +13,33 @@
* 2004-11-12 Paul Fox
* - busyboxed
*/
+//config:config SETSID
+//config: bool "setsid"
+//config: default y
+//config: help
+//config: setsid runs a program in a new session
+
+//applet:IF_SETSID(APPLET(setsid, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETSID) += setsid.o
//usage:#define setsid_trivial_usage
-//usage: "PROG ARGS"
+//usage: "[-c] PROG ARGS"
//usage:#define setsid_full_usage "\n\n"
//usage: "Run PROG in a new session. PROG will have no controlling terminal\n"
-//usage: "and will not be affected by keyboard signals (Ctrl-C etc).\n"
-//usage: "See setsid(2) for details."
+//usage: "and will not be affected by keyboard signals (^C etc).\n"
+//usage: "\n -c Set controlling terminal to stdin"
#include "libbb.h"
int setsid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int setsid_main(int argc UNUSED_PARAM, char **argv)
{
- if (!argv[1])
- bb_show_usage();
+ unsigned opt;
+
+ opt_complementary = "-1"; /* at least one arg */
+ opt = getopt32(argv, "+c"); /* +: stop on first non-opt */
+ argv += optind;
/* setsid() is allowed only when we are not a process group leader.
* Otherwise our PID serves as PGID of some existing process group
@@ -61,6 +73,10 @@ int setsid_main(int argc UNUSED_PARAM, char **argv)
setsid();
}
- argv++;
+ if (opt) {
+ /* -c: set (with stealing) controlling tty */
+ ioctl(0, TIOCSCTTY, 1);
+ }
+
BB_EXECVP_or_die(argv);
}
diff --git a/miscutils/strings.c b/miscutils/strings.c
index b040508..edd0e66 100644
--- a/miscutils/strings.c
+++ b/miscutils/strings.c
@@ -6,15 +6,28 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config STRINGS
+//config: bool "strings"
+//config: default y
+//config: help
+//config: strings prints the printable character sequences for each file
+//config: specified.
+
+//applet:IF_STRINGS(APPLET(strings, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_STRINGS) += strings.o
//usage:#define strings_trivial_usage
-//usage: "[-afo] [-n LEN] [FILE]..."
+//usage: "[-fo] [-t o/d/x] [-n LEN] [FILE]..."
//usage:#define strings_full_usage "\n\n"
//usage: "Display printable strings in a binary file\n"
-//usage: "\n -a Scan whole file (default)"
-//usage: "\n -f Precede strings with filenames"
-//usage: "\n -n LEN At least LEN characters form a string (default 4)"
-//usage: "\n -o Precede strings with decimal offsets"
+//We usually don't bother user with "nop" options. They work, but are not shown:
+////usage: "\n -a Scan whole file (default)"
+//unimplemented alternative is -d: Only strings from initialized, loaded data sections
+//usage: "\n -f Precede strings with filenames"
+//usage: "\n -o Precede strings with octal offsets"
+//usage: "\n -t o/d/x Precede strings with offsets in base 8/10/16"
+//usage: "\n -n LEN At least LEN characters form a string (default 4)"
#include "libbb.h"
@@ -22,6 +35,7 @@
#define PRINT_NAME 2
#define PRINT_OFFSET 4
#define SIZE 8
+#define PRINT_RADIX 16
int strings_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int strings_main(int argc UNUSED_PARAM, char **argv)
@@ -33,8 +47,11 @@ int strings_main(int argc UNUSED_PARAM, char **argv)
char *string;
const char *fmt = "%s: ";
const char *n_arg = "4";
+ /* default for -o */
+ const char *radix = "o";
+ char *radix_fmt;
- getopt32(argv, "afon:", &n_arg);
+ getopt32(argv, "afon:t:", &n_arg, &radix);
/* -a is our default behaviour */
/*argc -= optind;*/
argv += optind;
@@ -43,6 +60,11 @@ int strings_main(int argc UNUSED_PARAM, char **argv)
string = xzalloc(n + 1);
n--;
+ if ((radix[0] != 'd' && radix[0] != 'o' && radix[0] != 'x') || radix[1] != 0)
+ bb_show_usage();
+
+ radix_fmt = xasprintf("%%7"OFF_FMT"%s ", radix);
+
if (!*argv) {
fmt = "{%s}: ";
*--argv = (char *)bb_msg_standard_input;
@@ -67,8 +89,8 @@ int strings_main(int argc UNUSED_PARAM, char **argv)
if (option_mask32 & PRINT_NAME) {
printf(fmt, *argv);
}
- if (option_mask32 & PRINT_OFFSET) {
- printf("%7"OFF_FMT"o ", offset - n);
+ if (option_mask32 & (PRINT_OFFSET | PRINT_RADIX)) {
+ printf(radix_fmt, offset - n);
}
fputs(string, stdout);
}
@@ -85,8 +107,10 @@ int strings_main(int argc UNUSED_PARAM, char **argv)
fclose_if_not_stdin(file);
} while (*++argv);
- if (ENABLE_FEATURE_CLEAN_UP)
+ if (ENABLE_FEATURE_CLEAN_UP) {
free(string);
+ free(radix_fmt);
+ }
fflush_stdout_and_exit(status);
}
diff --git a/miscutils/taskset.c b/miscutils/taskset.c
index 71ad825..b0fcb2e 100644
--- a/miscutils/taskset.c
+++ b/miscutils/taskset.c
@@ -6,6 +6,25 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config TASKSET
+//config: bool "taskset"
+//config: default n # doesn't build on some non-x86 targets (m68k)
+//config: help
+//config: Retrieve or set a processes's CPU affinity.
+//config: This requires sched_{g,s}etaffinity support in your libc.
+//config:
+//config:config FEATURE_TASKSET_FANCY
+//config: bool "Fancy output"
+//config: default y
+//config: depends on TASKSET
+//config: help
+//config: Add code for fancy output. This merely silences a compiler-warning
+//config: and adds about 135 Bytes. May be needed for machines with alot
+//config: of CPUs.
+
+//applet:IF_TASKSET(APPLET(taskset, BB_DIR_USR_BIN, BB_SUID_DROP))
+//kbuild:lib-$(CONFIG_TASKSET) += taskset.o
+
//usage:#define taskset_trivial_usage
//usage: "[-p] [MASK] [PID | PROG ARGS]"
//usage:#define taskset_full_usage "\n\n"
@@ -22,6 +41,11 @@
//usage: "pid 6671's new affinity mask: 1\n"
//usage: "$ taskset -p 1\n"
//usage: "pid 1's current affinity mask: 3\n"
+/*
+ Not yet implemented:
+ * -a/--all-tasks (affect all threads)
+ * -c/--cpu-list (specify CPUs via "1,3,5-7")
+ */
#ifdef BIONIC_ICS
#define _GNU_SOURCE 1
@@ -55,29 +79,26 @@ static char *from_cpuset(cpu_set_t *mask)
#define TASKSET_PRINTF_MASK "%llx"
static unsigned long long from_cpuset(cpu_set_t *mask)
{
- struct BUG_CPU_SETSIZE_is_too_small {
- char BUG_CPU_SETSIZE_is_too_small[
- CPU_SETSIZE < sizeof(int) ? -1 : 1];
- };
- char *p = (void*)mask;
-
- /* Take the least significant bits. Careful!
- * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases
+ BUILD_BUG_ON(CPU_SETSIZE < 8*sizeof(int));
+
+ /* Take the least significant bits. Assume cpu_set_t is
+ * implemented as an array of unsigned long or unsigned
+ * int.
*/
-#if BB_BIG_ENDIAN
- /* For big endian, it means LAST bits */
- if (CPU_SETSIZE < sizeof(long))
- p += CPU_SETSIZE - sizeof(int);
- else if (CPU_SETSIZE < sizeof(long long))
- p += CPU_SETSIZE - sizeof(long);
- else
- p += CPU_SETSIZE - sizeof(long long);
-#endif
- if (CPU_SETSIZE < sizeof(long))
- return *(unsigned*)p;
- if (CPU_SETSIZE < sizeof(long long))
- return *(unsigned long*)p;
- return *(unsigned long long*)p;
+ if (CPU_SETSIZE < 8*sizeof(long))
+ return *(unsigned*)mask;
+ if (CPU_SETSIZE < 8*sizeof(long long))
+ return *(unsigned long*)mask;
+# if BB_BIG_ENDIAN
+ if (sizeof(long long) > sizeof(long)) {
+ /* We can put two long in the long long, but they have to
+ * be swapped: the least significant word comes first in the
+ * array */
+ unsigned long *p = (void*)mask;
+ return p[0] + ((unsigned long long)p[1] << (8*sizeof(long)));
+ }
+# endif
+ return *(unsigned long long*)mask;
}
#endif
@@ -132,17 +153,65 @@ int taskset_main(int argc UNUSED_PARAM, char **argv)
current_new += 8; /* "new" */
}
- { /* Affinity was specified, translate it into cpu_set_t */
+ /* Affinity was specified, translate it into cpu_set_t */
+ CPU_ZERO(&mask);
+ if (!ENABLE_FEATURE_TASKSET_FANCY) {
unsigned i;
+ unsigned long long m;
+
/* Do not allow zero mask: */
- unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
- enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 };
+ m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
+ i = 0;
+ do {
+ if (m & 1)
+ CPU_SET(i, &mask);
+ i++;
+ m >>= 1;
+ } while (m != 0);
+ } else {
+ unsigned i;
+ char *last_byte;
+ char *bin;
+ uint8_t bit_in_byte;
+
+ /* Cheap way to get "long enough" buffer */
+ bin = xstrdup(aff);
+
+ if (aff[0] != '0' || (aff[1]|0x20) != 'x') {
+/* TODO: decimal/octal masks are still limited to 2^64 */
+ unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
+ bin += strlen(bin);
+ last_byte = bin - 1;
+ while (m) {
+ *--bin = m & 0xff;
+ m >>= 8;
+ }
+ } else {
+ /* aff is "0x.....", we accept very long masks in this form */
+ last_byte = hex2bin(bin, aff + 2, INT_MAX);
+ if (!last_byte) {
+ bad_aff:
+ bb_error_msg_and_die("bad affinity '%s'", aff);
+ }
+ last_byte--; /* now points to the last byte */
+ }
- CPU_ZERO(&mask);
- for (i = 0; i < CNT_BIT; i++) {
- unsigned long long bit = (1ULL << i);
- if (bit & m)
+ i = 0;
+ bit_in_byte = 1;
+ while (last_byte >= bin) {
+ if (bit_in_byte & *last_byte) {
+ if (i >= CPU_SETSIZE)
+ goto bad_aff;
CPU_SET(i, &mask);
+ //bb_error_msg("bit %d set", i);
+ }
+ i++;
+ /* bit_in_byte is uint8_t! & 0xff is implied */
+ bit_in_byte = (bit_in_byte << 1);
+ if (!bit_in_byte) {
+ bit_in_byte = 1;
+ last_byte--;
+ }
}
}
diff --git a/miscutils/time.c b/miscutils/time.c
index 19b0b44..23d18e1 100644
--- a/miscutils/time.c
+++ b/miscutils/time.c
@@ -8,6 +8,17 @@
Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
*/
+//config:config TIME
+//config: bool "time"
+//config: default y
+//config: help
+//config: The time command runs the specified program with the given arguments.
+//config: When the command finishes, time writes a message to standard output
+//config: giving timing statistics about this program run.
+
+//applet:IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TIME) += time.o
//usage:#define time_trivial_usage
//usage: "[-v] PROG ARGS"
@@ -17,6 +28,7 @@
#include "libbb.h"
#include <sys/resource.h> /* getrusage */
+#include <android.h>
/* Information on the resources used by a child process. */
typedef struct {
diff --git a/miscutils/timeout.c b/miscutils/timeout.c
index 9d56593..f29dc8a 100644
--- a/miscutils/timeout.c
+++ b/miscutils/timeout.c
@@ -27,6 +27,16 @@
* splitszf 12-05-2006 by Roberto A. Foglietta
* rewrite 14-11-2008 vda
*/
+//config:config TIMEOUT
+//config: bool "timeout"
+//config: default y
+//config: help
+//config: Runs a program and watches it. If it does not terminate in
+//config: specified number of seconds, it is sent a signal.
+
+//applet:IF_TIMEOUT(APPLET(timeout, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TIMEOUT) += timeout.o
//usage:#define timeout_trivial_usage
//usage: "[-t SECS] [-s SIG] PROG ARGS"
@@ -52,9 +62,8 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
/* -p option is not documented, it is needed to support NOMMU. */
/* -t SECONDS; -p PARENT_PID */
- opt_complementary = "t+" USE_FOR_NOMMU(":p+");
/* '+': stop at first non-option */
- getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent);
+ getopt32(argv, "+s:t:+" USE_FOR_NOMMU("p:+"), &opt_s, &timeout, &parent);
/*argv += optind; - no, wait for bb_daemonize_or_rexec! */
signo = get_signum(opt_s);
if (signo < 0)
diff --git a/miscutils/ttysize.c b/miscutils/ttysize.c
index d2d48d0..135ce85 100644
--- a/miscutils/ttysize.c
+++ b/miscutils/ttysize.c
@@ -9,6 +9,18 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config TTYSIZE
+//config: bool "ttysize"
+//config: default y
+//config: help
+//config: A replacement for "stty size". Unlike stty, can report only width,
+//config: only height, or both, in any order. It also does not complain on
+//config: error, but returns default 80x24.
+//config: Usage in shell scripts: width=`ttysize w`.
+
+//applet:IF_TTYSIZE(APPLET(ttysize, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TTYSIZE) += ttysize.o
//usage:#define ttysize_trivial_usage
//usage: "[w] [h]"
diff --git a/miscutils/ubi_tools.c b/miscutils/ubi_tools.c
index b713935..8e55e95 100644
--- a/miscutils/ubi_tools.c
+++ b/miscutils/ubi_tools.c
@@ -105,6 +105,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
int mtd_num;
int dev_num = UBI_DEV_NUM_AUTO;
int vol_id = UBI_VOL_NUM_AUTO;
+ int vid_hdr_offset = 0;
char *vol_name;
unsigned long long size_bytes = size_bytes; /* for compiler */
char *size_bytes_str;
@@ -125,26 +126,32 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
strcpy(path, "/sys/class/ubi/ubi");
memset(&req_structs, 0, sizeof(req_structs));
+#define OPTION_m (1 << 0)
+#define OPTION_d (1 << 1)
+#define OPTION_n (1 << 2)
+#define OPTION_N (1 << 3)
+#define OPTION_s (1 << 4)
+#define OPTION_a (1 << 5)
+#define OPTION_t (1 << 6)
if (do_mkvol) {
- opt_complementary = "-1:d+:n+:a+";
- opts = getopt32(argv, "md:n:N:s:a:t:",
+ opt_complementary = "-1";
+ opts = getopt32(argv, "md:+n:+N:s:a:+t:O:+",
&dev_num, &vol_id,
- &vol_name, &size_bytes_str, &alignment, &type
+ &vol_name, &size_bytes_str, &alignment, &type,
+ &vid_hdr_offset
);
+ } else
+ if (do_update) {
+ opt_complementary = "-1";
+ opts = getopt32(argv, "s:at", &size_bytes_str);
+ opts *= OPTION_s;
} else {
- opt_complementary = "-1:m+:d+:n+:a+";
- opts = getopt32(argv, "m:d:n:N:s:a:t:",
+ opt_complementary = "-1";
+ opts = getopt32(argv, "m:+d:+n:+N:s:a:+t:",
&mtd_num, &dev_num, &vol_id,
&vol_name, &size_bytes_str, &alignment, &type
);
}
-#define OPTION_m (1 << 0)
-#define OPTION_d (1 << 1)
-#define OPTION_n (1 << 2)
-#define OPTION_N (1 << 3)
-#define OPTION_s (1 << 4)
-#define OPTION_a (1 << 5)
-#define OPTION_t (1 << 6)
if (opts & OPTION_s)
size_bytes = xatoull_sfx(size_bytes_str, size_suffixes);
@@ -157,17 +164,19 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
// bb_error_msg_and_die("%s: not a char device", ubi_ctrl);
//usage:#define ubiattach_trivial_usage
-//usage: "-m MTD_NUM [-d UBI_NUM] UBI_CTRL_DEV"
+//usage: "-m MTD_NUM [-d UBI_NUM] [-O VID_HDR_OFF] UBI_CTRL_DEV"
//usage:#define ubiattach_full_usage "\n\n"
//usage: "Attach MTD device to UBI\n"
//usage: "\n -m MTD_NUM MTD device number to attach"
//usage: "\n -d UBI_NUM UBI device number to assign"
+//usage: "\n -O VID_HDR_OFF VID header offset"
if (do_attach) {
if (!(opts & OPTION_m))
bb_error_msg_and_die("%s device not specified", "MTD");
attach_req.mtd_num = mtd_num;
attach_req.ubi_num = dev_num;
+ attach_req.vid_hdr_offset = vid_hdr_offset;
xioctl(fd, UBI_IOCATT, &attach_req);
} else
@@ -186,7 +195,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
} else
//usage:#define ubimkvol_trivial_usage
-//usage: "UBI_DEVICE -N NAME [-s SIZE | -m]"
+//usage: "-N NAME [-s SIZE | -m] UBI_DEVICE"
//usage:#define ubimkvol_full_usage "\n\n"
//usage: "Create UBI volume\n"
//usage: "\n -a ALIGNMENT Volume alignment (default 1)"
@@ -203,9 +212,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
unsigned num;
char *p;
- if (sscanf(ubi_ctrl, "/dev/ubi%u", &num) != 1)
- bb_error_msg_and_die("wrong format of UBI device name");
-
+ num = ubi_devnum_from_devname(ubi_ctrl);
p = path_sys_class_ubi_ubi + sprintf(path_sys_class_ubi_ubi, "%u/", num);
strcpy(p, "avail_eraseblocks");
@@ -239,20 +246,31 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
} else
//usage:#define ubirmvol_trivial_usage
-//usage: "UBI_DEVICE -n VOLID"
+//usage: "-n VOLID / -N VOLNAME UBI_DEVICE"
//usage:#define ubirmvol_full_usage "\n\n"
//usage: "Remove UBI volume\n"
//usage: "\n -n VOLID Volume ID"
+//usage: "\n -N VOLNAME Volume name"
if (do_rmvol) {
- if (!(opts & OPTION_n))
+ if (!(opts & (OPTION_n|OPTION_N)))
bb_error_msg_and_die("volume id not specified");
- /* FIXME? kernel expects int32_t* here: */
- xioctl(fd, UBI_IOCRMVOL, &vol_id);
+ if (opts & OPTION_N) {
+ unsigned num = ubi_devnum_from_devname(ubi_ctrl);
+ vol_id = ubi_get_volid_by_name(num, vol_name);
+ }
+
+ if (sizeof(vol_id) != 4) {
+ /* kernel expects int32_t* in this ioctl */
+ int32_t t = vol_id;
+ xioctl(fd, UBI_IOCRMVOL, &t);
+ } else {
+ xioctl(fd, UBI_IOCRMVOL, &vol_id);
+ }
} else
//usage:#define ubirsvol_trivial_usage
-//usage: "UBI_DEVICE -n VOLID -s SIZE"
+//usage: "-n VOLID -s SIZE UBI_DEVICE"
//usage:#define ubirsvol_full_usage "\n\n"
//usage: "Resize UBI volume\n"
//usage: "\n -n VOLID Volume ID"
@@ -270,7 +288,7 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
} else
//usage:#define ubiupdatevol_trivial_usage
-//usage: "UBI_DEVICE [-t | [-s SIZE] IMG_FILE]"
+//usage: "[-t | [-s SIZE] IMG_FILE] UBI_DEVICE"
//usage:#define ubiupdatevol_full_usage "\n\n"
//usage: "Update UBI volume\n"
//usage: "\n -t Truncate to zero size"
@@ -302,9 +320,9 @@ int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
if (!(opts & OPTION_s)) {
if (!*argv)
bb_show_usage();
- xstat(*argv, &st);
- size_bytes = st.st_size;
xmove_fd(xopen(*argv, O_RDONLY), STDIN_FILENO);
+ xfstat(STDIN_FILENO, &st, *argv);
+ size_bytes = st.st_size;
}
bytes64 = size_bytes;
diff --git a/miscutils/ubirename.c b/miscutils/ubirename.c
new file mode 100644
index 0000000..8b1c378
--- a/dev/null
+++ b/miscutils/ubirename.c
@@ -0,0 +1,94 @@
+/* ubirename - port of the ubirename from the mtd-utils package
+ *
+ * A utility to rename one UBI volume.
+ *
+ * 2016-03-01 Sven Eisenberg <sven.eisenberg@novero.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//config:config UBIRENAME
+//config: bool "ubirename"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Utility to rename UBI volumes
+
+//applet:IF_UBIRENAME(APPLET(ubirename, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UBIRENAME) += ubirename.o
+
+//usage:#define ubirename_trivial_usage
+//usage: "UBI_DEVICE OLD_VOLNAME NEW_VOLNAME [OLD2 NEW2]..."
+//usage:#define ubirename_full_usage "\n\n"
+//usage: "Rename UBI volumes on UBI_DEVICE"
+
+#include "libbb.h"
+#include <mtd/mtd-user.h>
+
+#ifndef __packed
+# define __packed __attribute__((packed))
+#endif
+
+// from ubi-media.h
+#define UBI_MAX_VOLUME_NAME 127
+#define UBI_MAX_VOLUMES 128
+// end ubi-media.h
+
+// from ubi-user.h
+/* ioctl commands of UBI character devices */
+#define UBI_IOC_MAGIC 'o'
+
+/* Re-name volumes */
+#define UBI_IOCRNVOL _IOW(UBI_IOC_MAGIC, 3, struct ubi_rnvol_req)
+
+/* Maximum amount of UBI volumes that can be re-named at one go */
+#define UBI_MAX_RNVOL 32
+
+struct ubi_rnvol_req {
+ int32_t count;
+ int8_t padding1[12];
+ struct {
+ int32_t vol_id;
+ int16_t name_len;
+ int8_t padding2[2];
+ char name[UBI_MAX_VOLUME_NAME + 1];
+ } ents[UBI_MAX_RNVOL];
+} __packed;
+// end ubi-user.h
+
+int ubirename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ubirename_main(int argc, char **argv)
+{
+ struct ubi_rnvol_req *rnvol;
+ const char *ubi_devname;
+ unsigned ubi_devnum;
+ unsigned n;
+
+ /* argc can be 4, 6, 8, ... */
+ if ((argc & 1) || (argc >>= 1) < 2)
+ bb_show_usage();
+
+ rnvol = xzalloc(sizeof(*rnvol));
+ rnvol->count = --argc;
+ if (argc > ARRAY_SIZE(rnvol->ents))
+ bb_error_msg_and_die("too many renames requested");
+
+ ubi_devname = argv[1];
+ ubi_devnum = ubi_devnum_from_devname(ubi_devname);
+
+ n = 0;
+ argv += 2;
+ while (argv[0]) {
+ rnvol->ents[n].vol_id = ubi_get_volid_by_name(ubi_devnum, argv[0]);
+ rnvol->ents[n].name_len = strlen(argv[1]);
+ if (rnvol->ents[n].name_len >= sizeof(rnvol->ents[n].name))
+ bb_error_msg_and_die("new name '%s' is too long", argv[1]);
+ strcpy(rnvol->ents[n].name, argv[1]);
+ n++;
+ argv += 2;
+ }
+
+ xioctl(xopen(ubi_devname, O_RDONLY), UBI_IOCRNVOL, rnvol);
+
+ return 0;
+}
diff --git a/miscutils/volname.c b/miscutils/volname.c
index b50e795..6d1addb 100644
--- a/miscutils/volname.c
+++ b/miscutils/volname.c
@@ -27,6 +27,15 @@
* mods from distributed source (eject-2.0.13) are by
* Matthew Stoltenberg <d3matt@gmail.com>
*/
+//config:config VOLNAME
+//config: bool "volname"
+//config: default y
+//config: help
+//config: Prints a CD-ROM volume name.
+
+//applet:IF_VOLNAME(APPLET(volname, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_VOLNAME) += volname.o
//usage:#define volname_trivial_usage
//usage: "[DEVICE]"
diff --git a/miscutils/wall.c b/miscutils/wall.c
index bb709ee..50658f4 100644
--- a/miscutils/wall.c
+++ b/miscutils/wall.c
@@ -32,7 +32,7 @@
int wall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int wall_main(int argc UNUSED_PARAM, char **argv)
{
- struct utmp *ut;
+ struct utmpx *ut;
char *msg;
int fd;
@@ -46,8 +46,8 @@ int wall_main(int argc UNUSED_PARAM, char **argv)
msg = xmalloc_read(fd, NULL);
if (ENABLE_FEATURE_CLEAN_UP && argv[1])
close(fd);
- setutent();
- while ((ut = getutent()) != NULL) {
+ setutxent();
+ while ((ut = getutxent()) != NULL) {
char *line;
if (ut->ut_type != USER_PROCESS)
continue;
@@ -56,7 +56,7 @@ int wall_main(int argc UNUSED_PARAM, char **argv)
free(line);
}
if (ENABLE_FEATURE_CLEAN_UP) {
- endutent();
+ endutxent();
free(msg);
}
return EXIT_SUCCESS;
diff --git a/miscutils/watchdog.c b/miscutils/watchdog.c
index d3a76ed..07ae64e 100644
--- a/miscutils/watchdog.c
+++ b/miscutils/watchdog.c
@@ -8,6 +8,21 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config WATCHDOG
+//config: bool "watchdog"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The watchdog utility is used with hardware or software watchdog
+//config: device drivers. It opens the specified watchdog device special file
+//config: and periodically writes a magic character to the device. If the
+//config: watchdog applet ever fails to write the magic character within a
+//config: certain amount of time, the watchdog device assumes the system has
+//config: hung, and will cause the hardware to reboot.
+
+//applet:IF_WATCHDOG(APPLET(watchdog, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WATCHDOG) += watchdog.o
//usage:#define watchdog_trivial_usage
//usage: "[-t N[ms]] [-T N[ms]] [-F] DEV"
diff --git a/modutils/Config.src b/modutils/Config.src
index fae747d..84ff34a 100644
--- a/modutils/Config.src
+++ b/modutils/Config.src
@@ -5,8 +5,6 @@
menu "Linux Module Utilities"
-INSERT
-
config MODPROBE_SMALL
bool "Simplified modutils"
default y
@@ -38,88 +36,7 @@ config MODPROBE_SMALL
- rmmod is an alias to modprobe -r
- depmod generates modules.dep.bb
- As of 2008-07, this code is experimental. It is 14kb smaller
- than "non-small" modutils.
-
-config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
- bool "Accept module options on modprobe command line"
- default y
- depends on MODPROBE_SMALL || MODPROBE
- select PLATFORM_LINUX
- help
- Allow insmod and modprobe take module options from command line.
-
-config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
- bool "Skip loading of already loaded modules"
- default y
- depends on MODPROBE_SMALL || MODPROBE
- help
- Check if the module is already loaded.
-
-config INSMOD
- bool "insmod"
- default n
- depends on !MODPROBE_SMALL
- select PLATFORM_LINUX
- help
- insmod is used to load specified modules in the running kernel.
-
-config RMMOD
- bool "rmmod"
- default n
- depends on !MODPROBE_SMALL
- select PLATFORM_LINUX
- help
- rmmod is used to unload specified modules from the kernel.
-
-config LSMOD
- bool "lsmod"
- default n
- depends on !MODPROBE_SMALL
- select PLATFORM_LINUX
- help
- lsmod is used to display a list of loaded modules.
-
-config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
- bool "Pretty output"
- default n
- depends on LSMOD
- select PLATFORM_LINUX
- help
- This option makes output format of lsmod adjusted to
- the format of module-init-tools for Linux kernel 2.6.
- Increases size somewhat.
-
-config MODPROBE
- bool "modprobe"
- default n
- depends on !MODPROBE_SMALL
- select PLATFORM_LINUX
- help
- Handle the loading of modules, and their dependencies on a high
- level.
-
-config FEATURE_MODPROBE_BLACKLIST
- bool "Blacklist support"
- default n
- depends on MODPROBE
- select PLATFORM_LINUX
- help
- Say 'y' here to enable support for the 'blacklist' command in
- modprobe.conf. This prevents the alias resolver to resolve
- blacklisted modules. This is useful if you want to prevent your
- hardware autodetection scripts to load modules like evdev, frame
- buffer drivers etc.
-
-config DEPMOD
- bool "depmod"
- default n
- depends on !MODPROBE_SMALL
- select PLATFORM_LINUX
- help
- depmod generates modules.dep (and potentially modules.alias
- and modules.symbols) that contain dependency information
- for modprobe.
+INSERT
comment "Options common to multiple modutils"
@@ -244,6 +161,14 @@ config FEATURE_MODUTILS_SYMBOLS
Say Y if unsure.
+config DEFAULT_MODULES_DIR
+ string "Default directory containing modules"
+ default "/lib/modules"
+ depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO
+ help
+ Directory that contains kernel modules.
+ Defaults to "/lib/modules"
+
config DEFAULT_DEPMOD_FILE
string "Default name of modules.dep"
default "modules.dep"
diff --git a/modutils/Kbuild.src b/modutils/Kbuild.src
index 1a7ac87..6b4fb74 100644
--- a/modutils/Kbuild.src
+++ b/modutils/Kbuild.src
@@ -7,10 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o
-lib-$(CONFIG_DEPMOD) += depmod.o modutils.o
-lib-$(CONFIG_INSMOD) += insmod.o modutils.o
-lib-$(CONFIG_LSMOD) += lsmod.o modutils.o
-lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o
-lib-$(CONFIG_RMMOD) += rmmod.o modutils.o
-lib-$(CONFIG_FEATURE_2_4_MODULES) += modutils-24.o
diff --git a/modutils/depmod.c b/modutils/depmod.c
index 1455d94..b7965eb 100644
--- a/modutils/depmod.c
+++ b/modutils/depmod.c
@@ -7,8 +7,20 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config DEPMOD
+//config: bool "depmod"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: depmod generates modules.dep (and potentially modules.alias
+//config: and modules.symbols) that contain dependency information
+//config: for modprobe.
-//applet:IF_DEPMOD(APPLET(depmod, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_DEPMOD(IF_NOT_MODPROBE_SMALL(APPLET(depmod, BB_DIR_SBIN, BB_SUID_DROP)))
+
+//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
+//kbuild:lib-$(CONFIG_DEPMOD) += depmod.o modutils.o
+//kbuild:endif
#include "libbb.h"
#include "modutils.h"
@@ -21,22 +33,13 @@
* for each depends, look through our list of full paths and emit if found
*/
-typedef struct module_info {
- struct module_info *next;
- char *name, *modname;
- llist_t *dependencies;
- llist_t *aliases;
- llist_t *symbols;
- struct module_info *dnext, *dprev;
-} module_info;
-
static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM,
void *data, int depth UNUSED_PARAM)
{
- char modname[MODULE_NAME_LEN];
- module_info **first = (module_info **) data;
+ module_db *modules = data;
char *image, *ptr;
- module_info *info;
+ module_entry *e;
+
/* Arbitrary. Was sb->st_size, but that breaks .gz etc */
size_t len = (64*1024*1024 - 4096);
@@ -44,41 +47,34 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA
return TRUE;
image = xmalloc_open_zipped_read_close(fname, &len);
- info = xzalloc(sizeof(*info));
- info->next = *first;
- *first = info;
+ e = moddb_get_or_create(modules, bb_get_last_path_component_nostrip(fname));
+ e->name = xstrdup(fname + 2); /* skip "./" */
- info->dnext = info->dprev = info;
- if (strncmp(fname, "./", 2) == 0)
- info->name = xstrdup(fname + 2);
- else
- info->name = xstrdup(fname);
- info->modname = xstrdup(filename2modname(fname, modname));
for (ptr = image; ptr < image + len - 10; ptr++) {
- if (strncmp(ptr, "depends=", 8) == 0) {
+ if (is_prefixed_with(ptr, "depends=")) {
char *u;
ptr += 8;
for (u = ptr; *u; u++)
if (*u == '-')
*u = '_';
- ptr += string_to_llist(ptr, &info->dependencies, ",");
+ ptr += string_to_llist(ptr, &e->deps, ",");
} else if (ENABLE_FEATURE_MODUTILS_ALIAS
- && strncmp(ptr, "alias=", 6) == 0
+ && is_prefixed_with(ptr, "alias=")
) {
- llist_add_to(&info->aliases, xstrdup(ptr + 6));
+ llist_add_to(&e->aliases, xstrdup(ptr + 6));
ptr += strlen(ptr);
} else if (ENABLE_FEATURE_MODUTILS_SYMBOLS
- && strncmp(ptr, "__ksymtab_", 10) == 0
+ && is_prefixed_with(ptr, "__ksymtab_")
) {
ptr += 10;
- if (strncmp(ptr, "gpl", 3) == 0
+ if (is_prefixed_with(ptr, "gpl")
|| strcmp(ptr, "strings") == 0
) {
continue;
}
- llist_add_to(&info->symbols, xstrdup(ptr));
+ llist_add_to(&e->symbols, xstrdup(ptr));
ptr += strlen(ptr);
}
}
@@ -87,24 +83,13 @@ static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARA
return TRUE;
}
-static module_info *find_module(module_info *modules, const char *modname)
+static void order_dep_list(module_db *modules, module_entry *start, llist_t *add)
{
- module_info *m;
-
- for (m = modules; m != NULL; m = m->next)
- if (strcmp(m->modname, modname) == 0)
- return m;
- return NULL;
-}
-
-static void order_dep_list(module_info *modules, module_info *start,
- llist_t *add)
-{
- module_info *m;
+ module_entry *m;
llist_t *n;
for (n = add; n != NULL; n = n->link) {
- m = find_module(modules, n->data);
+ m = moddb_get(modules, n->data);
if (m == NULL)
continue;
@@ -119,7 +104,7 @@ static void order_dep_list(module_info *modules, module_info *start,
start->dprev = m;
/* recurse */
- order_dep_list(modules, start, m->dependencies);
+ order_dep_list(modules, start, m->deps);
}
}
@@ -130,10 +115,11 @@ static void xfreopen_write(const char *file, FILE *f)
}
//usage:#if !ENABLE_MODPROBE_SMALL
-//usage:#define depmod_trivial_usage "[-n] [MODFILES]..."
+//usage:#define depmod_trivial_usage "[-n] [-b BASE] [VERSION] [MODFILES]..."
//usage:#define depmod_full_usage "\n\n"
//usage: "Generate modules.dep, alias, and symbols files"
//usage: "\n"
+//usage: "\n -b BASE Use BASE/lib/modules/VERSION"
//usage: "\n -n Dry run: print files to stdout"
//usage:#endif
@@ -184,11 +170,12 @@ enum {
int depmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int depmod_main(int argc UNUSED_PARAM, char **argv)
{
- module_info *modules, *m, *dep;
+ module_db modules;
+ module_entry *m, *dep;
const char *moddir_base = "/";
- char *version;
+ char *moddir, *version;
struct utsname uts;
- struct stat info;
+ unsigned i;
int tmp;
getopt32(argv, "aAb:eF:nruqC:", &moddir_base, NULL, NULL);
@@ -206,13 +193,13 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
uname(&uts);
version = uts.release;
}
- xchdir(&CONFIG_DEFAULT_MODULES_DIR[1]);
- if (stat(version, &info) == 0) {
- xchdir(version);
- }
+ moddir = concat_path_file(&CONFIG_DEFAULT_MODULES_DIR[1], version);
+ xchdir(moddir);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(moddir);
/* Scan modules */
- modules = NULL;
+ memset(&modules, 0, sizeof(modules));
if (*argv) {
do {
parse_module(*argv, /*sb (unused):*/ NULL, &modules, 0);
@@ -225,10 +212,11 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
/* Generate dependency and alias files */
if (!(option_mask32 & OPT_n))
xfreopen_write(CONFIG_DEFAULT_DEPMOD_FILE, stdout);
- for (m = modules; m != NULL; m = m->next) {
+
+ moddb_foreach_module(&modules, m, i) {
printf("%s:", m->name);
- order_dep_list(modules, m, m->dependencies);
+ order_dep_list(&modules, m, m->deps);
while (m->dnext != m) {
dep = m->dnext;
printf(" %s", dep->name);
@@ -244,44 +232,34 @@ int depmod_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_MODUTILS_ALIAS
if (!(option_mask32 & OPT_n))
xfreopen_write("modules.alias", stdout);
- for (m = modules; m != NULL; m = m->next) {
- const char *fname = bb_basename(m->name);
- int fnlen = strchrnul(fname, '.') - fname;
+ moddb_foreach_module(&modules, m, i) {
while (m->aliases) {
- /* Last word can well be m->modname instead,
- * but depmod from module-init-tools 3.4
- * uses module basename, i.e., no s/-/_/g.
- * (pathname and .ko.* are still stripped)
- * Mimicking that... */
- printf("alias %s %.*s\n",
+ /*
+ * Last word used to be a basename
+ * (filename with path and .ko.* stripped)
+ * at the time of module-init-tools 3.4.
+ * kmod v.12 uses module name, i.e., s/-/_/g.
+ */
+ printf("alias %s %s\n",
(char*)llist_pop(&m->aliases),
- fnlen, fname);
+ m->modname);
}
}
#endif
#if ENABLE_FEATURE_MODUTILS_SYMBOLS
if (!(option_mask32 & OPT_n))
xfreopen_write("modules.symbols", stdout);
- for (m = modules; m != NULL; m = m->next) {
- const char *fname = bb_basename(m->name);
- int fnlen = strchrnul(fname, '.') - fname;
+ moddb_foreach_module(&modules, m, i) {
while (m->symbols) {
- printf("alias symbol:%s %.*s\n",
+ printf("alias symbol:%s %s\n",
(char*)llist_pop(&m->symbols),
- fnlen, fname);
+ m->modname);
}
}
#endif
- if (ENABLE_FEATURE_CLEAN_UP) {
- while (modules) {
- module_info *old = modules;
- modules = modules->next;
- free(old->name);
- free(old->modname);
- free(old);
- }
- }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ moddb_free(&modules);
return EXIT_SUCCESS;
}
diff --git a/modutils/insmod.c b/modutils/insmod.c
index 887d9f2..f2c70e1 100644
--- a/modutils/insmod.c
+++ b/modutils/insmod.c
@@ -6,8 +6,18 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config INSMOD
+//config: bool "insmod"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: insmod is used to load specified modules in the running kernel.
-//applet:IF_INSMOD(APPLET(insmod, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_INSMOD(IF_NOT_MODPROBE_SMALL(APPLET(insmod, BB_DIR_SBIN, BB_SUID_DROP)))
+
+//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
+//kbuild:lib-$(CONFIG_INSMOD) += insmod.o modutils.o
+//kbuild:endif
#include "libbb.h"
#include "modutils.h"
@@ -21,7 +31,7 @@
//usage: IF_NOT_FEATURE_2_4_MODULES("FILE ")
//usage: "[SYMBOL=VALUE]..."
//usage:#define insmod_full_usage "\n\n"
-//usage: "Load the specified kernel modules into the kernel"
+//usage: "Load kernel module"
//usage: IF_FEATURE_2_4_MODULES( "\n"
//usage: "\n -f Force module to load into the wrong kernel version"
//usage: "\n -k Make module autoclean-able"
diff --git a/modutils/lsmod.c b/modutils/lsmod.c
index 3b3c166..2458942 100644
--- a/modutils/lsmod.c
+++ b/modutils/lsmod.c
@@ -7,8 +7,28 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LSMOD
+//config: bool "lsmod"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: lsmod is used to display a list of loaded modules.
+//config:
+//config:config FEATURE_LSMOD_PRETTY_2_6_OUTPUT
+//config: bool "Pretty output"
+//config: default y
+//config: depends on LSMOD && !MODPROBE_SMALL
+//config: select PLATFORM_LINUX
+//config: help
+//config: This option makes output format of lsmod adjusted to
+//config: the format of module-init-tools for Linux kernel 2.6.
+//config: Increases size somewhat.
-//applet:IF_LSMOD(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_LSMOD(IF_NOT_MODPROBE_SMALL(APPLET(lsmod, BB_DIR_SBIN, BB_SUID_DROP)))
+
+//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
+//kbuild:lib-$(CONFIG_LSMOD) += lsmod.o modutils.o
+//kbuild:endif
//usage:#if !ENABLE_MODPROBE_SMALL
//usage:#define lsmod_trivial_usage
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index a0b2ce7..aa641ad 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -5,11 +5,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
-
//config:config MODINFO
//config: bool "modinfo"
//config: default y
@@ -17,122 +12,125 @@
//config: help
//config: Show information about a Linux Kernel module
+//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
+
#include <fnmatch.h>
#include <sys/utsname.h> /* uname() */
#include "libbb.h"
#include "modutils.h"
-#if defined(ANDROID) || defined(__ANDROID__)
-#define DONT_USE_UTS_REL_FOLDER
-#endif
-
-enum {
- OPT_TAGS = (1 << 12) - 1, /* shortcut count */
- OPT_F = (1 << 12), /* field name */
- OPT_0 = (1 << 13), /* \0 as separator */
+static const char *const shortcuts[] = {
+ "filename", // -n
+ "author", // -a
+ "description", // -d
+ "license", // -l
+ "parm", // -p
+ "version", // the rest has no shortcut options
+ "alias",
+ "srcversion",
+ "depends",
+ "uts_release",
+ "intree",
+ "vermagic",
+ "firmware",
};
-struct modinfo_env {
- char *field;
- int tags;
+enum {
+ OPT_0 = (1 << 0), /* \0 as separator */
+ OPT_F = (1 << 1), /* field name */
+ /* first bits are for -nadlp options, the rest are for
+ * fields not selectable with "shortcut" options
+ */
+ OPT_n = (1 << 2),
+ OPT_TAGS = ((1 << ARRAY_SIZE(shortcuts)) - 1) << 2,
};
-static int display(const char *data, const char *pattern, int flag)
+static void display(const char *data, const char *pattern)
{
- if (flag) {
+ int flag = option_mask32 >> 1; /* shift out -0 bit */
+ if (flag & (flag-1)) {
+ /* more than one field to show: print "FIELD:" pfx */
int n = printf("%s:", pattern);
while (n++ < 16)
bb_putchar(' ');
}
- return printf("%s%c", data, (option_mask32 & OPT_0) ? '\0' : '\n');
+ printf("%s%c", data, (option_mask32 & OPT_0) ? '\0' : '\n');
}
static void modinfo(const char *path, const char *version,
- const struct modinfo_env *env)
+ const char *field)
{
- static const char *const shortcuts[] = {
- "filename",
- "license",
- "author",
- "description",
- "version",
- "alias",
- "srcversion",
- "depends",
- "uts_release",
- "vermagic",
- "parm",
- "firmware",
- };
size_t len;
- int j, length;
- char *ptr, *fullpath, *the_module;
- const char *field = env->field;
- int tags = env->tags;
-
- if (tags & 1) { /* filename */
- display(path, shortcuts[0], 1 != tags);
- }
+ int j;
+ char *ptr, *the_module;
+ char *allocated;
+ int tags = option_mask32;
+ allocated = NULL;
len = MAXINT(ssize_t);
the_module = xmalloc_open_zipped_read_close(path, &len);
if (!the_module) {
if (path[0] == '/')
return;
/* Newer depmod puts relative paths in modules.dep */
- fullpath = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path);
- the_module = xmalloc_open_zipped_read_close(fullpath, &len);
-#ifdef DONT_USE_UTS_REL_FOLDER
- if (!the_module) {
- free((char*)fullpath);
- fullpath = xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, path);
- the_module = xmalloc_open_zipped_read_close(fullpath, &len);
- }
-#endif
- free((char*)fullpath);
+ path = allocated = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path);
+ the_module = xmalloc_open_zipped_read_close(path, &len);
if (!the_module) {
- // outputs system error msg
- bb_perror_msg("\n");
- return;
+ bb_error_msg("module '%s' not found", path);
+ goto ret;
}
}
- if (field)
- tags |= OPT_F;
- for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) {
+ for (j = 1; (1<<j) & (OPT_TAGS|OPT_F); j++) {
const char *pattern;
if (!((1<<j) & tags))
continue;
+
pattern = field;
if ((1<<j) & OPT_TAGS)
- pattern = shortcuts[j];
- length = strlen(pattern);
+ pattern = shortcuts[j-2];
+
+ if (strcmp(pattern, shortcuts[0]) == 0) {
+ /* "-n" or "-F filename" */
+ display(path, shortcuts[0]);
+ continue;
+ }
+
ptr = the_module;
while (1) {
+ char *after_pattern;
+
ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module));
if (ptr == NULL) /* no occurance left, done */
break;
- if (strncmp(ptr, pattern, length) == 0 && ptr[length] == '=') {
+ after_pattern = is_prefixed_with(ptr, pattern);
+ if (after_pattern && *after_pattern == '=') {
/* field prefixes are 0x80 or 0x00 */
- if ((ptr[-1] & 0x7F) == '\0') {
- ptr += length + 1;
- ptr += display(ptr, pattern, (1<<j) != tags);
+ if ((ptr[-1] & 0x7F) == 0x00) {
+ ptr = after_pattern + 1;
+ display(ptr, pattern);
+ ptr += strlen(ptr);
}
}
++ptr;
}
}
free(the_module);
+ ret:
+ free(allocated);
}
//usage:#define modinfo_trivial_usage
-//usage: "[-adlp0] [-F keyword] MODULE"
+//usage: "[-adlpn0] [-F keyword] MODULE"
//usage:#define modinfo_full_usage "\n\n"
//usage: " -a Shortcut for '-F author'"
//usage: "\n -d Shortcut for '-F description'"
//usage: "\n -l Shortcut for '-F license'"
//usage: "\n -p Shortcut for '-F parm'"
+////usage: "\n -n Shortcut for '-F filename'"
//usage: "\n -F keyword Keyword to look for"
//usage: "\n -0 Separate output with NULs"
//usage:#define modinfo_example_usage
@@ -141,7 +139,7 @@ static void modinfo(const char *path, const char *version,
int modinfo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int modinfo_main(int argc UNUSED_PARAM, char **argv)
{
- struct modinfo_env env;
+ const char *field;
char name[MODULE_NAME_LEN];
struct utsname uts;
parser_t *parser;
@@ -149,40 +147,29 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv)
unsigned opts;
unsigned i;
- env.field = NULL;
+ field = NULL;
opt_complementary = "-1"; /* minimum one param */
- opts = getopt32(argv, "nladvAsDumpF:0", &env.field);
- env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS;
+ opts = getopt32(argv, "0F:nadlp", &field);
+ /* If no field selected, show all */
+ if (!(opts & (OPT_TAGS|OPT_F)))
+ option_mask32 |= OPT_TAGS;
argv += optind;
uname(&uts);
parser = config_open2(
xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, uts.release, CONFIG_DEFAULT_DEPMOD_FILE),
- fopen_for_read
+ xfopen_for_read
);
-#ifdef DONT_USE_UTS_REL_FOLDER
- if (!parser) {
- parser = config_open2(
- xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, CONFIG_DEFAULT_DEPMOD_FILE),
- fopen_for_read
- );
- }
-#endif
- if (!parser) {
- strcpy(uts.release,"");
- goto no_modules_dep;
- }
-
while (config_read(parser, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
colon = last_char_is(tokens[0], ':');
if (colon == NULL)
continue;
*colon = '\0';
- filename2modname(tokens[0], name);
+ filename2modname(bb_basename(tokens[0]), name);
for (i = 0; argv[i]; i++) {
if (fnmatch(argv[i], name, 0) == 0) {
- modinfo(tokens[0], uts.release, &env);
+ modinfo(tokens[0], uts.release, field);
argv[i] = (char *) "";
}
}
@@ -190,10 +177,9 @@ int modinfo_main(int argc UNUSED_PARAM, char **argv)
if (ENABLE_FEATURE_CLEAN_UP)
config_close(parser);
-no_modules_dep:
for (i = 0; argv[i]; i++) {
if (argv[i][0]) {
- modinfo(argv[i], uts.release, &env);
+ modinfo(argv[i], uts.release, field);
}
}
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 906d525..0fc9ea4 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -8,20 +8,45 @@
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-//applet:IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))
-//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
-//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
-//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
-//applet:IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, modprobe))
+/* config MODPROBE_SMALL is defined in Config.src to ensure better "make config" order */
+
+//config:config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+//config: bool "Accept module options on modprobe command line"
+//config: default y
+//config: depends on MODPROBE_SMALL
+//config: select PLATFORM_LINUX
+//config: help
+//config: Allow insmod and modprobe take module options from command line.
+//config:
+//config:config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
+//config: bool "Skip loading of already loaded modules"
+//config: default y
+//config: depends on MODPROBE_SMALL
+//config: help
+//config: Check if the module is already loaded.
+
+//applet:IF_MODPROBE(IF_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)))
+//applet:IF_DEPMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(depmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, depmod)))
+//applet:IF_INSMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(insmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, insmod)))
+//applet:IF_LSMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, lsmod)))
+//applet:IF_RMMOD(IF_MODPROBE_SMALL(APPLET_ODDNAME(rmmod, modprobe, BB_DIR_SBIN, BB_SUID_DROP, rmmod)))
+
+//kbuild:lib-$(CONFIG_MODPROBE_SMALL) += modprobe-small.o
#include "libbb.h"
/* After libbb.h, since it needs sys/types.h on some systems */
#include <sys/utsname.h> /* uname() */
#include <fnmatch.h>
+#include <sys/syscall.h>
-extern int init_module(void *module, unsigned long len, const char *options);
-extern int delete_module(const char *module, unsigned flags);
-extern int query_module(const char *name, int which, void *buf, size_t bufsize, size_t *ret);
+#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+#ifdef __NR_finit_module
+# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
+#endif
+/* linux/include/linux/module.h has limit of 64 chars on module names */
+#undef MODULE_NAME_LEN
+#define MODULE_NAME_LEN 64
#if 1
@@ -43,6 +68,7 @@ typedef struct module_info {
char *pathname;
char *aliases;
char *deps;
+ smallint open_read_failed;
} module_info;
/*
@@ -113,21 +139,21 @@ static char* copy_stringbuf(void)
static char* find_keyword(char *ptr, size_t len, const char *word)
{
- int wlen;
-
if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */
return NULL;
- wlen = strlen(word);
- len -= wlen - 1;
+ len -= strlen(word) - 1;
while ((ssize_t)len > 0) {
char *old = ptr;
+ char *after_word;
+
/* search for the first char in word */
- ptr = memchr(ptr, *word, len);
+ ptr = memchr(ptr, word[0], len);
if (ptr == NULL) /* no occurance left, done */
break;
- if (strncmp(ptr, word, wlen) == 0)
- return ptr + wlen; /* found, return ptr past it */
+ after_word = is_prefixed_with(ptr, word);
+ if (after_word)
+ return after_word; /* found, return ptr past it */
++ptr;
len -= (ptr - old);
}
@@ -143,6 +169,32 @@ static void replace(char *s, char what, char with)
}
}
+static char *filename2modname(const char *filename, char *modname)
+{
+ int i;
+ const char *from;
+
+ // Disabled since otherwise "modprobe dir/name" would work
+ // as if it is "modprobe name". It is unclear why
+ // 'basenamization' was here in the first place.
+ //from = bb_get_last_path_component_nostrip(filename);
+ from = filename;
+ for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
+ modname[i] = (from[i] == '-') ? '_' : from[i];
+ modname[i] = '\0';
+
+ return modname;
+}
+
+static int pathname_matches_modname(const char *pathname, const char *modname)
+{
+ int r;
+ char name[MODULE_NAME_LEN];
+ filename2modname(bb_get_last_path_component_nostrip(pathname), name);
+ r = (strcmp(name, modname) == 0);
+ return r;
+}
+
/* Take "word word", return malloced "word",NUL,"word",NUL,NUL */
static char* str_2_list(const char *str)
{
@@ -180,11 +232,34 @@ static int load_module(const char *fname, const char *options)
int r;
size_t len = MAXINT(ssize_t);
char *module_image;
+
+ if (!options)
+ options = "";
+
dbg1_error_msg("load_module('%s','%s')", fname, options);
- module_image = xmalloc_open_zipped_read_close(fname, &len);
- r = (!module_image || init_module(module_image, len, options ? options : "") != 0);
- free(module_image);
+ /*
+ * First we try finit_module if available. Some kernels are configured
+ * to only allow loading of modules off of secure storage (like a read-
+ * only rootfs) which needs the finit_module call. If it fails, we fall
+ * back to normal module loading to support compressed modules.
+ */
+ r = 1;
+# ifdef __NR_finit_module
+ {
+ int fd = open(fname, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ r = finit_module(fd, options, 0) != 0;
+ close(fd);
+ }
+ }
+# endif
+ if (r != 0) {
+ module_image = xmalloc_open_zipped_read_close(fname, &len);
+ r = (!module_image || init_module(module_image, len, options) != 0);
+ free(module_image);
+ }
+
dbg1_error_msg("load_module:%d", r);
return r; /* 0 = success */
#else
@@ -194,7 +269,8 @@ static int load_module(const char *fname, const char *options)
#endif
}
-static void parse_module(module_info *info, const char *pathname)
+/* Returns !0 if open/read was unsuccessful */
+static int parse_module(module_info *info, const char *pathname)
{
char *module_image;
char *ptr;
@@ -203,6 +279,7 @@ static void parse_module(module_info *info, const char *pathname)
dbg1_error_msg("parse_module('%s')", pathname);
/* Read (possibly compressed) module */
+ errno = 0;
len = 64 * 1024 * 1024; /* 64 Mb at most */
module_image = xmalloc_open_zipped_read_close(pathname, &len);
/* module_image == NULL is ok here, find_keyword handles it */
@@ -212,6 +289,7 @@ static void parse_module(module_info *info, const char *pathname)
reset_stringbuf();
pos = 0;
while (1) {
+ unsigned start = stringbuf_idx;
ptr = find_keyword(module_image + pos, len - pos, "alias=");
if (!ptr) {
ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_");
@@ -228,6 +306,31 @@ static void parse_module(module_info *info, const char *pathname)
}
append(ptr);
appendc(' ');
+ /*
+ * Don't add redundant aliases, such as:
+ * libcrc32c.ko symbol:crc32c symbol:crc32c
+ */
+ if (start) { /* "if we aren't the first alias" */
+ char *found, *last;
+ stringbuf[stringbuf_idx] = '\0';
+ last = stringbuf + start;
+ /*
+ * String at last-1 is " symbol:crc32c "
+ * (with both leading and trailing spaces).
+ */
+ if (strncmp(stringbuf, last, stringbuf_idx - start) == 0)
+ /* First alias matches us */
+ found = stringbuf;
+ else
+ /* Does any other alias match? */
+ found = strstr(stringbuf, last-1);
+ if (found < last-1) {
+ /* There is absolutely the same string before us */
+ dbg2_error_msg("redundant:'%s'", last);
+ stringbuf_idx = start;
+ goto skip;
+ }
+ }
skip:
pos = (ptr - module_image);
}
@@ -244,22 +347,11 @@ static void parse_module(module_info *info, const char *pathname)
dbg2_error_msg("dep:'%s'", ptr);
append(ptr);
}
+ free(module_image);
info->deps = copy_stringbuf();
- free(module_image);
-}
-
-static int pathname_matches_modname(const char *pathname, const char *modname)
-{
- const char *fname = bb_get_last_path_component_nostrip(pathname);
- const char *suffix = strrstr(fname, ".ko");
-//TODO: can do without malloc?
- char *name = xstrndup(fname, suffix - fname);
- int r;
- replace(name, '-', '_');
- r = (strcmp(name, modname) == 0);
- free(name);
- return r;
+ info->open_read_failed = (module_image == NULL);
+ return info->open_read_failed;
}
static FAST_FUNC int fileAction(const char *pathname,
@@ -290,7 +382,8 @@ static FAST_FUNC int fileAction(const char *pathname,
dbg1_error_msg("'%s' module name matches", pathname);
module_found_idx = cur;
- parse_module(&modinfo[cur], pathname);
+ if (parse_module(&modinfo[cur], pathname) != 0)
+ return TRUE; /* failed to open/read it, no point in trying loading */
if (!(option_mask32 & OPT_r)) {
if (load_module(pathname, module_load_options) == 0) {
@@ -421,11 +514,12 @@ static void write_out_dep_bb(int fd)
}
}
-static module_info* find_alias(const char *alias)
+static module_info** find_alias(const char *alias)
{
int i;
int dep_bb_fd;
- module_info *result;
+ int infoidx;
+ module_info **infovec;
dbg1_error_msg("find_alias('%s')", alias);
try_again:
@@ -438,7 +532,9 @@ static module_info* find_alias(const char *alias)
if (!modinfo[i].aliases) {
parse_module(&modinfo[i], modinfo[i].pathname);
}
- return &modinfo[i];
+ infovec = xzalloc(2 * sizeof(infovec[0]));
+ infovec[0] = &modinfo[i];
+ return infovec;
}
i++;
}
@@ -451,16 +547,13 @@ static module_info* find_alias(const char *alias)
/* Scan all module bodies, extract modinfo (it contains aliases) */
i = 0;
- result = NULL;
+ infoidx = 0;
+ infovec = NULL;
while (modinfo[i].pathname) {
char *desc, *s;
if (!modinfo[i].aliases) {
parse_module(&modinfo[i], modinfo[i].pathname);
}
- if (result) {
- i++;
- continue;
- }
/* "alias1 symbol:sym1 alias2 symbol:sym2" */
desc = str_2_list(modinfo[i].aliases);
/* Does matching substring exist? */
@@ -472,13 +565,12 @@ static module_info* find_alias(const char *alias)
if (fnmatch(s, alias, 0) == 0) {
dbg1_error_msg("found alias '%s' in module '%s'",
alias, modinfo[i].pathname);
- result = &modinfo[i];
+ infovec = xrealloc_vector(infovec, 1, infoidx);
+ infovec[infoidx++] = &modinfo[i];
break;
}
}
free(desc);
- if (result && dep_bb_fd < 0)
- return result;
i++;
}
@@ -487,30 +579,77 @@ static module_info* find_alias(const char *alias)
write_out_dep_bb(dep_bb_fd);
}
- dbg1_error_msg("find_alias '%s' returns %p", alias, result);
- return result;
+ dbg1_error_msg("find_alias '%s' returns %d results", alias, infoidx);
+ return infovec;
}
#if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
// TODO: open only once, invent config_rewind()
static int already_loaded(const char *name)
{
- int ret = 0;
- char *s;
- parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
- while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
- if (strcmp(s, name) == 0) {
- ret = 1;
- break;
+ int ret;
+ char *line;
+ FILE *fp;
+
+ ret = 5 * 2;
+ again:
+ fp = fopen_for_read("/proc/modules");
+ if (!fp)
+ return 0;
+ while ((line = xmalloc_fgetline(fp)) != NULL) {
+ char *live;
+ char *after_name;
+
+ // Examples from kernel 3.14.6:
+ //pcspkr 12718 0 - Live 0xffffffffa017e000
+ //snd_timer 28690 2 snd_seq,snd_pcm, Live 0xffffffffa025e000
+ //i915 801405 2 - Live 0xffffffffa0096000
+ after_name = is_prefixed_with(line, name);
+ if (!after_name || *after_name != ' ') {
+ free(line);
+ continue;
+ }
+ live = strstr(line, " Live");
+ free(line);
+ if (!live) {
+ /* State can be Unloading, Loading, or Live.
+ * modprobe must not return prematurely if we see "Loading":
+ * it can cause further programs to assume load completed,
+ * but it did not (yet)!
+ * Wait up to 5*20 ms for it to resolve.
+ */
+ ret -= 2;
+ if (ret == 0)
+ break; /* huh? report as "not loaded" */
+ fclose(fp);
+ usleep(20*1000);
+ goto again;
}
+ ret = 1;
+ break;
}
- config_close(parser);
- return ret;
+ fclose(fp);
+
+ return ret & 1;
}
#else
-#define already_loaded(name) is_rmmod
+#define already_loaded(name) 0
#endif
+static int rmmod(const char *filename)
+{
+ int r;
+ char modname[MODULE_NAME_LEN];
+
+ filename2modname(filename, modname);
+ r = delete_module(modname, O_NONBLOCK | O_EXCL);
+ dbg1_error_msg("delete_module('%s', O_NONBLOCK | O_EXCL):%d", modname, r);
+ if (r != 0 && !(option_mask32 & OPT_q)) {
+ bb_perror_msg("remove '%s'", modname);
+ }
+ return r;
+}
+
/*
* Given modules definition and module name (or alias, or symbol)
* load/remove the module respecting dependencies.
@@ -521,23 +660,43 @@ static int already_loaded(const char *name)
#define process_module(a,b) process_module(a)
#define cmdline_options ""
#endif
-static void process_module(char *name, const char *cmdline_options)
+static int process_module(char *name, const char *cmdline_options)
{
char *s, *deps, *options;
+ module_info **infovec;
module_info *info;
- int is_rmmod = (option_mask32 & OPT_r) != 0;
+ int infoidx;
+ int is_remove = (option_mask32 & OPT_r) != 0;
+ int exitcode = EXIT_SUCCESS;
+
dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
replace(name, '-', '_');
- dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod);
- if (already_loaded(name) != is_rmmod) {
+ dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
+
+ if (applet_name[0] == 'r') {
+ /* rmmod.
+ * Does not remove dependencies, no need to scan, just remove.
+ * (compat note: this allows and strips .ko suffix)
+ */
+ rmmod(name);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * We used to have "is_remove != already_loaded(name)" check here, but
+ * modprobe -r pci:v00008086d00007010sv00000000sd00000000bc01sc01i80
+ * won't unload modules (there are more than one)
+ * which have this alias.
+ */
+ if (!is_remove && already_loaded(name)) {
dbg1_error_msg("nothing to do for '%s'", name);
- return;
+ return EXIT_SUCCESS;
}
options = NULL;
- if (!is_rmmod) {
+ if (!is_remove) {
char *opt_filename = xasprintf("/etc/modules/%s", name);
options = xmalloc_open_read_close(opt_filename, NULL);
if (options)
@@ -560,46 +719,53 @@ static void process_module(char *name, const char *cmdline_options)
if (!module_count) {
/* Scan module directory. This is done only once.
* It will attempt module load, and will exit(EXIT_SUCCESS)
- * on success. */
+ * on success.
+ */
module_found_idx = -1;
recursive_action(".",
ACTION_RECURSE, /* flags */
fileAction, /* file action */
NULL, /* dir action */
name, /* user data */
- 0); /* depth */
+ 0 /* depth */
+ );
dbg1_error_msg("dirscan complete");
- /* Module was not found, or load failed, or is_rmmod */
+ /* Module was not found, or load failed, or is_remove */
if (module_found_idx >= 0) { /* module was found */
- info = &modinfo[module_found_idx];
+ infovec = xzalloc(2 * sizeof(infovec[0]));
+ infovec[0] = &modinfo[module_found_idx];
} else { /* search for alias, not a plain module name */
- info = find_alias(name);
+ infovec = find_alias(name);
}
} else {
- info = find_alias(name);
- }
-
-// Problem here: there can be more than one module
-// for the given alias. For example,
-// "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
-// ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
-// and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*"
-// Standard modprobe would load them both.
-// In this code, find_alias() returns only the first matching module.
-
- /* rmmod? unload it by name */
- if (is_rmmod) {
- if (delete_module(name, O_NONBLOCK | O_EXCL) != 0) {
- if (!(option_mask32 & OPT_q))
- bb_perror_msg("remove '%s'", name);
- goto ret;
- }
+ infovec = find_alias(name);
+ }
- if (applet_name[0] == 'r') {
- /* rmmod: do not remove dependencies, exit */
- goto ret;
- }
+ if (!infovec) {
+ /* both dirscan and find_alias found nothing */
+ if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
+ bb_error_msg("module '%s' not found", name);
+//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
+ goto ret;
+ }
+ /* There can be more than one module for the given alias. For example,
+ * "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
+ * ata_piix because it has alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
+ * and ata_generic, it has alias "pci:v*d*sv*sd*bc01sc01i*"
+ * Standard modprobe loads them both. We achieve it by returning
+ * a *list* of modinfo pointers from find_alias().
+ */
+
+ /* modprobe -r? unload module(s) */
+ if (is_remove) {
+ infoidx = 0;
+ while ((info = infovec[infoidx++]) != NULL) {
+ int r = rmmod(bb_get_last_path_component_nostrip(info->pathname));
+ if (r != 0) {
+ goto ret; /* error */
+ }
+ }
/* modprobe -r: we do not stop here -
* continue to unload modules on which the module depends:
* "-r --remove: option causes modprobe to remove a module.
@@ -608,47 +774,50 @@ static void process_module(char *name, const char *cmdline_options)
*/
}
- if (!info) {
- /* both dirscan and find_alias found nothing */
- if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
- bb_error_msg("module '%s' not found", name);
-//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
- goto ret;
- }
+ infoidx = 0;
+ while ((info = infovec[infoidx++]) != NULL) {
+ /* Iterate thru dependencies, trying to (un)load them */
+ deps = str_2_list(info->deps);
+ for (s = deps; *s; s += strlen(s) + 1) {
+ //if (strcmp(name, s) != 0) // N.B. do loops exist?
+ dbg1_error_msg("recurse on dep '%s'", s);
+ process_module(s, NULL);
+ dbg1_error_msg("recurse on dep '%s' done", s);
+ }
+ free(deps);
- /* Iterate thru dependencies, trying to (un)load them */
- deps = str_2_list(info->deps);
- for (s = deps; *s; s += strlen(s) + 1) {
- //if (strcmp(name, s) != 0) // N.B. do loops exist?
- dbg1_error_msg("recurse on dep '%s'", s);
- process_module(s, NULL);
- dbg1_error_msg("recurse on dep '%s' done", s);
- }
- free(deps);
-
- /* modprobe -> load it */
- if (!is_rmmod) {
- if (!options || strstr(options, "blacklist") == NULL) {
- errno = 0;
- if (load_module(info->pathname, options) != 0) {
- if (EEXIST != errno) {
- bb_error_msg("'%s': %s",
- info->pathname,
- moderror(errno));
- } else {
- dbg1_error_msg("'%s': %s",
- info->pathname,
- moderror(errno));
- }
- }
- } else {
+ if (is_remove)
+ continue;
+
+ /* We are modprobe: load it */
+ if (options && strstr(options, "blacklist")) {
dbg1_error_msg("'%s': blacklisted", info->pathname);
+ continue;
+ }
+ if (info->open_read_failed) {
+ /* We already tried it, didn't work. Don't try load again */
+ exitcode = EXIT_FAILURE;
+ continue;
+ }
+ errno = 0;
+ if (load_module(info->pathname, options) != 0) {
+ if (EEXIST != errno) {
+ bb_error_msg("'%s': %s",
+ info->pathname,
+ moderror(errno));
+ } else {
+ dbg1_error_msg("'%s': %s",
+ info->pathname,
+ moderror(errno));
+ }
+ exitcode = EXIT_FAILURE;
}
}
ret:
+ free(infovec);
free(options);
-//TODO: return load attempt result from process_module.
-//If dep didn't load ok, continuing makes little sense.
+
+ return exitcode;
}
#undef cmdline_options
@@ -701,16 +870,10 @@ The following options are useful for people managing distributions:
Use the file instead of the current kernel symbols
*/
-//usage:#if ENABLE_MODPROBE_SMALL || ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
-
-//// Note: currently, help system shows modprobe --help text for all aliased cmds
-//// (see APPLET_ODDNAME macro definition).
-//// All other help texts defined below are not used. FIXME?
-
//usage:#if ENABLE_MODPROBE_SMALL
+
//usage:#define depmod_trivial_usage NOUSAGE_STR
//usage:#define depmod_full_usage ""
-//usage:#endif
//usage:#define lsmod_trivial_usage
//usage: ""
@@ -722,7 +885,7 @@ The following options are useful for people managing distributions:
//usage: IF_NOT_FEATURE_2_4_MODULES("FILE ")
//usage: "[SYMBOL=VALUE]..."
//usage:#define insmod_full_usage "\n\n"
-//usage: "Load the specified kernel modules into the kernel"
+//usage: "Load kernel module"
//usage: IF_FEATURE_2_4_MODULES( "\n"
//usage: "\n -f Force module to load into the wrong kernel version"
//usage: "\n -k Make module autoclean-able"
@@ -747,7 +910,7 @@ The following options are useful for people managing distributions:
//usage: "$ rmmod tulip\n"
//usage:#define modprobe_trivial_usage
-//usage: "[-qfwrsv] MODULE [symbol=value]..."
+//usage: "[-qfwrsv] MODULE [SYMBOL=VALUE]..."
//usage:#define modprobe_full_usage "\n\n"
//usage: " -r Remove MODULE (stacks) or do autoclean"
//usage: "\n -q Quiet"
@@ -761,13 +924,13 @@ The following options are useful for people managing distributions:
int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int modprobe_main(int argc UNUSED_PARAM, char **argv)
{
+ int exitcode;
struct utsname uts;
- struct stat info;
char applet0 = applet_name[0];
IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
/* are we lsmod? -> just dump /proc/modules */
- if ('l' == applet0) {
+ if (ENABLE_LSMOD && 'l' == applet0) {
xprint_and_close_file(xfopen_for_read("/proc/modules"));
return EXIT_SUCCESS;
}
@@ -777,14 +940,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
/* Prevent ugly corner cases with no modules at all */
modinfo = xzalloc(sizeof(modinfo[0]));
- if ('i' != applet0 && 'r' != applet0) { /* not insmod and not rmmod */
+ if (!ENABLE_INSMOD || 'i' != applet0) { /* not insmod */
/* Goto modules directory */
xchdir(CONFIG_DEFAULT_MODULES_DIR);
}
uname(&uts); /* never fails */
/* depmod? */
- if ('d' == applet0) {
+ if (ENABLE_DEPMOD && 'd' == applet0) {
/* Supported:
* -n: print result to stdout
* -a: process all modules (default)
@@ -807,14 +970,8 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL);
argv += optind;
/* if (argv[0] && argv[1]) bb_show_usage(); */
- if (argv[0]) {
- xchdir(argv[0]);
- } else {
- if (stat(uts.release, &info) == 0) {
- /* Goto $VERSION directory */
- xchdir(uts.release);
- }
- }
+ /* Goto $VERSION directory */
+ xchdir(argv[0] ? argv[0] : uts.release);
/* Force full module scan by asking to find a bogus module.
* This will generate modules.dep.bb as a side effect. */
process_module((char*)"/", NULL);
@@ -829,22 +986,20 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
argv += optind;
/* are we rmmod? -> simulate modprobe -r */
- if ('r' == applet0) {
+ if (ENABLE_RMMOD && 'r' == applet0) {
option_mask32 |= OPT_r;
}
- if ('i' != applet0 && 'r' != applet0) { /* not insmod and not rmmod */
- if (stat(uts.release, &info) == 0) {
- /* Goto $VERSION directory */
- xchdir(uts.release);
- }
+ if (!ENABLE_INSMOD || 'i' != applet0) { /* not insmod */
+ /* Goto $VERSION directory */
+ xchdir(uts.release);
}
#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
- /* If not rmmod, parse possible module options given on command line.
+ /* If not rmmod/-r, parse possible module options given on command line.
* insmod/modprobe takes one module name, the rest are parameters. */
options = NULL;
- if ('r' != applet0) {
+ if (!(option_mask32 & OPT_r)) {
char **arg = argv;
while (*++arg) {
/* Enclose options in quotes */
@@ -855,11 +1010,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
}
}
#else
- if ('r' != applet0)
+ if (!(option_mask32 & OPT_r))
argv[1] = NULL;
#endif
- if ('i' == applet0) { /* insmod */
+ if (ENABLE_INSMOD && 'i' == applet0) { /* insmod */
size_t len;
void *map;
@@ -875,20 +1030,23 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("can't insert '%s': %s",
*argv, moderror(errno));
}
- return 0;
+ return EXIT_SUCCESS;
}
/* Try to load modprobe.dep.bb */
- load_dep_bb();
+ if (!ENABLE_RMMOD || 'r' != applet0) { /* not rmmod */
+ load_dep_bb();
+ }
/* Load/remove modules.
- * Only rmmod loops here, modprobe has only argv[0] */
+ * Only rmmod/modprobe -r loops here, insmod/modprobe has only argv[0] */
+ exitcode = EXIT_SUCCESS;
do {
- process_module(*argv, options);
+ exitcode |= process_module(*argv, options);
} while (*++argv);
if (ENABLE_FEATURE_CLEAN_UP) {
IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
}
- return EXIT_SUCCESS;
+ return exitcode;
}
diff --git a/modutils/modprobe.c b/modutils/modprobe.c
index f6f45f3..09e3de6 100644
--- a/modutils/modprobe.c
+++ b/modutils/modprobe.c
@@ -7,16 +7,42 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-//applet:IF_MODPROBE(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))
+//config:config MODPROBE
+//config: bool "modprobe"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Handle the loading of modules, and their dependencies on a high
+//config: level.
+//config:
+//config:config FEATURE_MODPROBE_BLACKLIST
+//config: bool "Blacklist support"
+//config: default y
+//config: depends on MODPROBE && !MODPROBE_SMALL
+//config: select PLATFORM_LINUX
+//config: help
+//config: Say 'y' here to enable support for the 'blacklist' command in
+//config: modprobe.conf. This prevents the alias resolver to resolve
+//config: blacklisted modules. This is useful if you want to prevent your
+//config: hardware autodetection scripts to load modules like evdev, frame
+//config: buffer drivers etc.
+
+//applet:IF_MODPROBE(IF_NOT_MODPROBE_SMALL(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP)))
+
+//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
+//kbuild:lib-$(CONFIG_MODPROBE) += modprobe.o modutils.o
+//kbuild:endif
#include "libbb.h"
#include "modutils.h"
#include <sys/utsname.h>
#include <fnmatch.h>
-//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
+#if 1
#define DBG(...) ((void)0)
+#else
+#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
+#endif
/* Note that unlike older versions of modules.dep/depmod (busybox and m-i-t),
* we expect the full dependency list to be specified in modules.dep.
@@ -24,7 +50,7 @@
*/
-//usage:#if !ENABLE_MODPROBE_SMALL && !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+//usage:#if !ENABLE_MODPROBE_SMALL
//usage:#define modprobe_notes_usage
//usage: "modprobe can (un)load a stack of modules, passing each module options (when\n"
//usage: "loading). modprobe uses a configuration file to determine what option(s) to\n"
@@ -87,7 +113,7 @@
//usage:
//usage:#define modprobe_trivial_usage
//usage: "[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]"
-//usage: " MODULE [symbol=value]..."
+//usage: " MODULE [SYMBOL=VALUE]..."
//usage:#define modprobe_full_usage "\n\n"
//usage: " -a Load multiple MODULEs"
//usage: "\n -l List (MODULE is a pattern)"
@@ -147,23 +173,6 @@ static const char modprobe_longopts[] ALIGN1 =
#define MODULE_FLAG_FOUND_IN_MODDEP 0x0004
#define MODULE_FLAG_BLACKLISTED 0x0008
-#if defined(ANDROID) || defined(__ANDROID__)
-#define DONT_USE_UTS_REL_FOLDER
-#endif
-
-struct module_entry { /* I'll call it ME. */
- unsigned flags;
- char *modname; /* stripped of /path/, .ext and s/-/_/g */
- const char *probed_name; /* verbatim as seen on cmdline */
- char *options; /* options from config files */
- llist_t *realnames; /* strings. if this module is an alias, */
- /* real module name is one of these. */
-//Can there really be more than one? Example from real kernel?
- llist_t *deps; /* strings. modules we depend on */
-};
-
-#define DB_HASH_SIZE 256
-
struct globals {
llist_t *probes; /* MEs of module(s) requested on cmdline */
char *cmdline_mopts; /* module options from cmdline */
@@ -171,7 +180,7 @@ struct globals {
/* bool. "Did we have 'symbol:FOO' requested on cmdline?" */
smallint need_symbols;
struct utsname uts;
- llist_t *db[DB_HASH_SIZE]; /* MEs of all modules ever seen (caching for speed) */
+ module_db db;
} FIX_ALIASING;
#define G (*ptr_to_globals)
#define INIT_G() do { \
@@ -196,46 +205,9 @@ static char *gather_options_str(char *opts, const char *append)
return opts;
}
-/* These three functions called many times, optimizing for speed.
- * Users reported minute-long delays when they runn iptables repeatedly
- * (iptables use modprobe to install needed kernel modules).
- */
-static struct module_entry *helper_get_module(const char *module, int create)
+static struct module_entry *get_or_add_modentry(const char *module)
{
- char modname[MODULE_NAME_LEN];
- struct module_entry *e;
- llist_t *l;
- unsigned i;
- unsigned hash;
-
- filename2modname(module, modname);
-
- hash = 0;
- for (i = 0; modname[i]; i++)
- hash = ((hash << 5) + hash) + modname[i];
- hash %= DB_HASH_SIZE;
-
- for (l = G.db[hash]; l; l = l->link) {
- e = (struct module_entry *) l->data;
- if (strcmp(e->modname, modname) == 0)
- return e;
- }
- if (!create)
- return NULL;
-
- e = xzalloc(sizeof(*e));
- e->modname = xstrdup(modname);
- llist_add_to(&G.db[hash], e);
-
- return e;
-}
-static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module)
-{
- return helper_get_module(module, 1);
-}
-static ALWAYS_INLINE struct module_entry *get_modentry(const char *module)
-{
- return helper_get_module(module, 0);
+ return moddb_get_or_create(&G.db, module);
}
static void add_probe(const char *name)
@@ -256,7 +228,7 @@ static void add_probe(const char *name)
llist_add_to_end(&G.probes, m);
G.num_unresolved_deps++;
if (ENABLE_FEATURE_MODUTILS_SYMBOLS
- && strncmp(m->modname, "symbol:", 7) == 0
+ && is_prefixed_with(m->modname, "symbol:")
) {
G.need_symbols = 1;
}
@@ -265,16 +237,29 @@ static void add_probe(const char *name)
static int FAST_FUNC config_file_action(const char *filename,
struct stat *statbuf UNUSED_PARAM,
void *userdata UNUSED_PARAM,
- int depth UNUSED_PARAM)
+ int depth)
{
char *tokens[3];
parser_t *p;
struct module_entry *m;
int rc = TRUE;
+ const char *base, *ext;
- if (bb_basename(filename)[0] == '.')
+ /* Skip files that begin with a "." */
+ base = bb_basename(filename);
+ if (base[0] == '.')
goto error;
+ /* In dir recursion, skip files that do not end with a ".conf"
+ * depth==0: read_config("modules.{symbols,alias}") must work,
+ * "include FILE_NOT_ENDING_IN_CONF" must work too.
+ */
+ if (depth != 0) {
+ ext = strrchr(base, '.');
+ if (ext == NULL || strcmp(ext + 1, "conf"))
+ goto error;
+ }
+
p = config_open2(filename, fopen_for_read);
if (p == NULL) {
rc = FALSE;
@@ -318,7 +303,7 @@ static int FAST_FUNC config_file_action(const char *filename,
m = get_or_add_modentry(tokens[1]);
m->options = gather_options_str(m->options, tokens[2]);
} else if (strcmp(tokens[0], "include") == 0) {
- /* include <filename> */
+ /* include <filename>/<dirname> (yes, directories also must work) */
read_config(tokens[1]);
} else if (ENABLE_FEATURE_MODPROBE_BLACKLIST
&& strcmp(tokens[0], "blacklist") == 0
@@ -335,7 +320,8 @@ static int FAST_FUNC config_file_action(const char *filename,
static int read_config(const char *path)
{
return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
- config_file_action, NULL, NULL, 1);
+ config_file_action, NULL, NULL,
+ /*depth:*/ 0);
}
static const char *humanly_readable_name(struct module_entry *m)
@@ -344,27 +330,55 @@ static const char *humanly_readable_name(struct module_entry *m)
return m->probed_name ? m->probed_name : m->modname;
}
+/* Like strsep(&stringp, "\n\t ") but quoted text goes to single token
+ * even if it contains whitespace.
+ */
+static char *strsep_quotes(char **stringp)
+{
+ char *s, *start = *stringp;
+
+ if (!start)
+ return NULL;
+
+ for (s = start; ; s++) {
+ switch (*s) {
+ case '"':
+ s = strchrnul(s + 1, '"'); /* find trailing quote */
+ if (*s != '\0')
+ s++; /* skip trailing quote */
+ /* fall through */
+ case '\0':
+ case '\n':
+ case '\t':
+ case ' ':
+ if (*s != '\0') {
+ *s = '\0';
+ *stringp = s + 1;
+ } else {
+ *stringp = NULL;
+ }
+ return start;
+ }
+ }
+}
+
static char *parse_and_add_kcmdline_module_options(char *options, const char *modulename)
{
char *kcmdline_buf;
char *kcmdline;
char *kptr;
- int len;
kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL);
if (!kcmdline_buf)
return options;
kcmdline = kcmdline_buf;
- len = strlen(modulename);
- while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) {
- if (strncmp(modulename, kptr, len) != 0)
- continue;
- kptr += len;
- if (*kptr != '.')
+ while ((kptr = strsep_quotes(&kcmdline)) != NULL) {
+ char *after_modulename = is_prefixed_with(kptr, modulename);
+ if (!after_modulename || *after_modulename != '.')
continue;
/* It is "modulename.xxxx" */
- kptr++;
+ kptr = after_modulename + 1;
if (strchr(kptr, '=') != NULL) {
/* It is "modulename.opt=[val]" */
options = gather_options_str(options, kptr);
@@ -421,7 +435,7 @@ static int do_modprobe(struct module_entry *m)
rc = 0;
fn = llist_pop(&m->deps); /* we leak it */
- m2 = get_or_add_modentry(fn);
+ m2 = get_or_add_modentry(bb_get_last_path_component_nostrip(fn));
if (option_mask32 & OPT_REMOVE) {
/* modprobe -r */
@@ -429,9 +443,8 @@ static int do_modprobe(struct module_entry *m)
rc = bb_delete_module(m2->modname, O_EXCL);
if (rc) {
if (first) {
- bb_error_msg("can't unload module %s: %s",
- humanly_readable_name(m2),
- moderror(rc));
+ bb_perror_msg("can't unload module '%s'",
+ humanly_readable_name(m2));
break;
}
} else {
@@ -450,17 +463,10 @@ static int do_modprobe(struct module_entry *m)
options = gather_options_str(options, G.cmdline_mopts);
if (option_mask32 & OPT_SHOW_DEPS) {
-#ifndef DONT_USE_UTS_REL_FOLDER
printf(options ? "insmod %s/%s/%s %s\n"
: "insmod %s/%s/%s\n",
CONFIG_DEFAULT_MODULES_DIR, G.uts.release, fn,
options);
-#else
- printf(options ? "insmod %s/%s %s\n"
- : "insmod %s/%s\n",
- CONFIG_DEFAULT_MODULES_DIR, fn,
- options);
-#endif
free(options);
continue;
}
@@ -510,9 +516,9 @@ static void load_modules_dep(void)
colon = last_char_is(tokens[0], ':');
if (colon == NULL)
continue;
- *colon = 0;
+ *colon = '\0';
- m = get_modentry(tokens[0]);
+ m = moddb_get(&G.db, bb_get_last_path_component_nostrip(tokens[0]));
if (m == NULL)
continue;
@@ -542,7 +548,6 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
int rc;
unsigned opt;
struct module_entry *me;
- struct stat info;
INIT_G();
@@ -553,16 +558,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
/* Goto modules location */
xchdir(CONFIG_DEFAULT_MODULES_DIR);
-#ifndef DONT_USE_UTS_REL_FOLDER
uname(&G.uts);
- if (stat(G.uts.release, &info) == 0) {
- xchdir(G.uts.release);
- }
-#endif
+ xchdir(G.uts.release);
if (opt & OPT_LIST_ONLY) {
int i;
- char name[MODULE_NAME_LEN];
char *colon, *tokens[2];
parser_t *p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
@@ -574,10 +574,14 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
if (!colon)
continue;
*colon = '\0';
- filename2modname(tokens[0], name);
if (!argv[0])
puts(tokens[0]);
else {
+ char name[MODULE_NAME_LEN];
+ filename2modname(
+ bb_get_last_path_component_nostrip(tokens[0]),
+ name
+ );
for (i = 0; argv[i]; i++) {
if (fnmatch(argv[i], name, 0) == 0) {
puts(tokens[0]);
@@ -599,20 +603,11 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
* autoclean will be removed".
*/
if (bb_delete_module(NULL, O_NONBLOCK | O_EXCL) != 0)
- bb_perror_msg_and_die("rmmod");
+ bb_perror_nomsg_and_die();
}
return EXIT_SUCCESS;
}
- /* Goto modules location */
- xchdir(CONFIG_DEFAULT_MODULES_DIR);
-#ifndef DONT_USE_UTS_REL_FOLDER
- uname(&G.uts);
- if (stat(G.uts.release, &info) == 0) {
- xchdir(G.uts.release);
- }
-#endif
-
/* Retrieve module names of already loaded modules */
{
char *s;
@@ -684,5 +679,8 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
} while (me->realnames != NULL);
}
+ if (ENABLE_FEATURE_CLEAN_UP)
+ moddb_free(&G.db);
+
return (rc != 0);
}
diff --git a/modutils/modutils-24.c b/modutils/modutils-24.c
index 12cb75c..9ce9135 100644
--- a/modutils/modutils-24.c
+++ b/modutils/modutils-24.c
@@ -58,6 +58,8 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//kbuild:lib-$(CONFIG_FEATURE_2_4_MODULES) += modutils-24.o
+
#include "libbb.h"
#include "modutils.h"
#include <sys/utsname.h>
@@ -2255,7 +2257,7 @@ static int add_symbols_from(struct obj_file *f,
* symbols so they cannot fudge it by adding the prefix on
* their references.
*/
- if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+ if (is_prefixed_with((char *)s->name, "GPLONLY_")) {
#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
if (gpl)
s->name += 8;
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 6187ca7..4204f06 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -7,14 +7,64 @@
*/
#include "modutils.h"
-#ifdef __UCLIBC__
-extern int init_module(void *module, unsigned long len, const char *options);
-extern int delete_module(const char *module, unsigned int flags);
-#else
-# include <sys/syscall.h>
-# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
-# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+#include <sys/syscall.h>
+
+#define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+#if defined(__NR_finit_module)
+# define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
#endif
+#define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+
+static module_entry *helper_get_module(module_db *db, const char *module, int create)
+{
+ char modname[MODULE_NAME_LEN];
+ struct module_entry *e;
+ unsigned i, hash;
+
+ filename2modname(module, modname);
+
+ hash = 0;
+ for (i = 0; modname[i]; i++)
+ hash = ((hash << 5) + hash) + modname[i];
+ hash %= MODULE_HASH_SIZE;
+
+ for (e = db->buckets[hash]; e; e = e->next)
+ if (strcmp(e->modname, modname) == 0)
+ return e;
+ if (!create)
+ return NULL;
+
+ e = xzalloc(sizeof(*e));
+ e->modname = xstrdup(modname);
+ e->next = db->buckets[hash];
+ db->buckets[hash] = e;
+ IF_DEPMOD(e->dnext = e->dprev = e;)
+
+ return e;
+}
+module_entry* FAST_FUNC moddb_get(module_db *db, const char *module)
+{
+ return helper_get_module(db, module, 0);
+}
+module_entry* FAST_FUNC moddb_get_or_create(module_db *db, const char *module)
+{
+ return helper_get_module(db, module, 1);
+}
+
+void FAST_FUNC moddb_free(module_db *db)
+{
+ module_entry *e, *n;
+ unsigned i;
+
+ for (i = 0; i < MODULE_HASH_SIZE; i++) {
+ for (e = db->buckets[i]; e; e = n) {
+ n = e->next;
+ free(e->name);
+ free(e->modname);
+ free(e);
+ }
+ }
+}
void FAST_FUNC replace(char *s, char what, char with)
{
@@ -47,18 +97,26 @@ int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
char* FAST_FUNC filename2modname(const char *filename, char *modname)
{
+ char local_modname[MODULE_NAME_LEN];
int i;
- char *from;
+ const char *from;
if (filename == NULL)
return NULL;
if (modname == NULL)
- modname = xmalloc(MODULE_NAME_LEN);
- from = bb_get_last_path_component_nostrip(filename);
+ modname = local_modname;
+ // Disabled since otherwise "modprobe dir/name" would work
+ // as if it is "modprobe name". It is unclear why
+ // 'basenamization' was here in the first place.
+ //from = bb_get_last_path_component_nostrip(filename);
+ from = filename;
for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
modname[i] = (from[i] == '-') ? '_' : from[i];
modname[i] = '\0';
+ if (modname == local_modname)
+ return xstrdup(modname);
+
return modname;
}
@@ -153,6 +211,24 @@ int FAST_FUNC bb_init_module(const char *filename, const char *options)
return bb_init_module_24(filename, options);
#endif
+ /*
+ * First we try finit_module if available. Some kernels are configured
+ * to only allow loading of modules off of secure storage (like a read-
+ * only rootfs) which needs the finit_module call. If it fails, we fall
+ * back to normal module loading to support compressed modules.
+ */
+# ifdef __NR_finit_module
+ {
+ int fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ rc = finit_module(fd, options, 0) != 0;
+ close(fd);
+ if (rc == 0)
+ return rc;
+ }
+ }
+# endif
+
image_size = INT_MAX - 4095;
mmaped = 0;
image = try_to_mmap_module(filename, &image_size);
@@ -182,6 +258,11 @@ int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
return errno;
}
+/* Note: not suitable for delete_module() errnos.
+ * For them, probably only EWOULDBLOCK needs explaining:
+ * "Other modules depend on us". So far we don't do such
+ * translation and don't use moderror() for removal errors.
+ */
const char* FAST_FUNC moderror(int err)
{
switch (err) {
diff --git a/modutils/modutils.h b/modutils/modutils.h
index 5f059c7..2cbd144 100644
--- a/modutils/modutils.h
+++ b/modutils/modutils.h
@@ -16,6 +16,36 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
/* linux/include/linux/module.h has 64, but this is also used
* internally for the maximum alias name length, which can be quite long */
#define MODULE_NAME_LEN 256
+#define MODULE_HASH_SIZE 256
+
+typedef struct module_entry {
+ struct module_entry *next;
+ char *name, *modname;
+ llist_t *deps;
+ IF_MODPROBE(
+ llist_t *realnames;
+ unsigned flags;
+ const char *probed_name; /* verbatim as seen on cmdline */
+ char *options; /* options from config files */
+ )
+ IF_DEPMOD(
+ llist_t *aliases;
+ llist_t *symbols;
+ struct module_entry *dnext, *dprev;
+ )
+} module_entry;
+
+typedef struct module_db {
+ module_entry *buckets[MODULE_HASH_SIZE];
+} module_db;
+
+#define moddb_foreach_module(db, module, index) \
+ for ((index) = 0; (index) < MODULE_HASH_SIZE; (index)++) \
+ for (module = (db)->buckets[index]; module; module = module->next)
+
+module_entry *moddb_get(module_db *db, const char *s) FAST_FUNC;
+module_entry *moddb_get_or_create(module_db *db, const char *s) FAST_FUNC;
+void moddb_free(module_db *db) FAST_FUNC;
void replace(char *s, char what, char with) FAST_FUNC;
char *replace_underscores(char *s) FAST_FUNC;
diff --git a/modutils/rmmod.c b/modutils/rmmod.c
index f13ff9e..527696f 100644
--- a/modutils/rmmod.c
+++ b/modutils/rmmod.c
@@ -7,8 +7,18 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config RMMOD
+//config: bool "rmmod"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: rmmod is used to unload specified modules from the kernel.
-//applet:IF_RMMOD(APPLET(rmmod, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_RMMOD(IF_NOT_MODPROBE_SMALL(APPLET(rmmod, BB_DIR_SBIN, BB_SUID_DROP)))
+
+//kbuild:ifneq ($(CONFIG_MODPROBE_SMALL),y)
+//kbuild:lib-$(CONFIG_RMMOD) += rmmod.o modutils.o
+//kbuild:endif
//usage:#if !ENABLE_MODPROBE_SMALL
//usage:#define rmmod_trivial_usage
@@ -28,7 +38,7 @@
int rmmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int rmmod_main(int argc UNUSED_PARAM, char **argv)
{
- int n;
+ int n, err;
unsigned flags = O_NONBLOCK | O_EXCL;
/* Parse command line. */
@@ -40,7 +50,8 @@ int rmmod_main(int argc UNUSED_PARAM, char **argv)
flags |= O_TRUNC;
if (n & 4) {
/* Unload _all_ unused modules via NULL delete_module() call */
- if (bb_delete_module(NULL, flags) != 0 && errno != EFAULT)
+ err = bb_delete_module(NULL, flags);
+ if (err && err != EFAULT)
bb_perror_msg_and_die("rmmod");
return EXIT_SUCCESS;
}
@@ -58,9 +69,10 @@ int rmmod_main(int argc UNUSED_PARAM, char **argv)
safe_strncpy(modname, bname, MODULE_NAME_LEN);
else
filename2modname(bname, modname);
- if (bb_delete_module(modname, flags))
- bb_error_msg_and_die("can't unload '%s': %s",
- modname, moderror(errno));
+ err = bb_delete_module(modname, flags);
+ if (err)
+ bb_perror_msg_and_die("can't unload module '%s'",
+ modname);
}
return EXIT_SUCCESS;
diff --git a/networking/Config.src b/networking/Config.src
index ca0ddcd..0adb1e2 100644
--- a/networking/Config.src
+++ b/networking/Config.src
@@ -5,8 +5,6 @@
menu "Networking Utilities"
-INSERT
-
config FEATURE_IPV6
bool "Enable IPv6 support"
default y
@@ -48,952 +46,17 @@ config VERBOSE_RESOLUTION_ERRORS
"can't resolve 'hostname.com'" and want to know more.
This may increase size of your executable a bit.
-config ARP
- bool "arp"
- default y
- select PLATFORM_LINUX
- help
- Manipulate the system ARP cache.
-
-config ARPING
- bool "arping"
- default y
- select PLATFORM_LINUX
- help
- Ping hosts by ARP packets.
-
-config BRCTL
- bool "brctl"
- default y
- select PLATFORM_LINUX
- help
- Manage ethernet bridges.
- Supports addbr/delbr and addif/delif.
-
-config FEATURE_BRCTL_FANCY
- bool "Fancy options"
- default y
- depends on BRCTL
- help
- Add support for extended option like:
- setageing, setfd, sethello, setmaxage,
- setpathcost, setportprio, setbridgeprio,
- stp
- This adds about 600 bytes.
-
-config FEATURE_BRCTL_SHOW
- bool "Support show"
- default y
- depends on BRCTL && FEATURE_BRCTL_FANCY
- help
- Add support for option which prints the current config:
- show
-
-config DNSD
- bool "dnsd"
- default y
- help
- Small and static DNS server daemon.
-
-config ETHER_WAKE
- bool "ether-wake"
- default y
- select PLATFORM_LINUX
- help
- Send a magic packet to wake up sleeping machines.
-
-config FAKEIDENTD
- bool "fakeidentd"
- default y
- select FEATURE_SYSLOG
- help
- fakeidentd listens on the ident port and returns a predefined
- fake value on any query.
-
-config FTPD
- bool "ftpd"
- default y
- help
- simple FTP daemon. You have to run it via inetd.
-
-config FEATURE_FTP_WRITE
- bool "Enable upload commands"
- default y
- depends on FTPD
- help
- Enable all kinds of FTP upload commands (-w option)
-
-config FEATURE_FTPD_ACCEPT_BROKEN_LIST
- bool "Enable workaround for RFC-violating clients"
- default y
- depends on FTPD
- help
- Some ftp clients (among them KDE's Konqueror) issue illegal
- "LIST -l" requests. This option works around such problems.
- It might prevent you from listing files starting with "-" and
- it increases the code size by ~40 bytes.
- Most other ftp servers seem to behave similar to this.
-
-config FTPGET
- bool "ftpget"
- default y
- help
- Retrieve a remote file via FTP.
-
-config FTPPUT
- bool "ftpput"
- default y
- help
- Store a remote file via FTP.
-
-config FEATURE_FTPGETPUT_LONG_OPTIONS
- bool "Enable long options in ftpget/ftpput"
- default y
- depends on LONG_OPTS && (FTPGET || FTPPUT)
- help
- Support long options for the ftpget/ftpput applet.
-
-config HOSTNAME
- bool "hostname"
- default y
- help
- Show or set the system's host name.
-
-config HTTPD
- bool "httpd"
- default y
- help
- Serve web pages via an HTTP server.
-
-config FEATURE_HTTPD_RANGES
- bool "Support 'Ranges:' header"
- default y
- depends on HTTPD
- help
- Makes httpd emit "Accept-Ranges: bytes" header and understand
- "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
- downloads, seeking in multimedia players etc.
-
-config FEATURE_HTTPD_USE_SENDFILE
- bool "Use sendfile system call"
- default y
- depends on HTTPD
- help
- When enabled, httpd will use the kernel sendfile() function
- instead of read/write loop.
-
-config FEATURE_HTTPD_SETUID
- bool "Enable -u <user> option"
- default y
- depends on HTTPD
- help
- This option allows the server to run as a specific user
- rather than defaulting to the user that starts the server.
- Use of this option requires special privileges to change to a
- different user.
-
-config FEATURE_HTTPD_BASIC_AUTH
- bool "Enable Basic http Authentication"
- default y
- depends on HTTPD
- help
- Utilizes password settings from /etc/httpd.conf for basic
- authentication on a per url basis.
- Example for httpd.conf file:
- /adm:toor:PaSsWd
-
-config FEATURE_HTTPD_AUTH_MD5
- bool "Support MD5 crypted passwords for http Authentication"
- default y
- depends on FEATURE_HTTPD_BASIC_AUTH
- help
- Enables encrypted passwords, and wildcard user/passwords
- in httpd.conf file.
- User '*' means 'any system user name is ok',
- password of '*' means 'use system password for this user'
- Examples:
- /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0
- /adm:root:*
- /wiki:*:*
-
-config FEATURE_HTTPD_CGI
- bool "Support Common Gateway Interface (CGI)"
- default y
- depends on HTTPD
- help
- This option allows scripts and executables to be invoked
- when specific URLs are requested.
-
-config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
- bool "Support for running scripts through an interpreter"
- default y
- depends on FEATURE_HTTPD_CGI
- help
- This option enables support for running scripts through an
- interpreter. Turn this on if you want PHP scripts to work
- properly. You need to supply an additional line in your
- httpd.conf file:
- *.php:/path/to/your/php
-
-config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
- bool "Set REMOTE_PORT environment variable for CGI"
- default y
- depends on FEATURE_HTTPD_CGI
- help
- Use of this option can assist scripts in generating
- references that contain a unique port number.
-
-config FEATURE_HTTPD_ENCODE_URL_STR
- bool "Enable -e option (useful for CGIs written as shell scripts)"
- default y
- depends on HTTPD
- help
- This option allows html encoding of arbitrary strings for display
- by the browser. Output goes to stdout.
- For example, httpd -e "<Hello World>" produces
- "&#60Hello&#32World&#62".
-
-config FEATURE_HTTPD_ERROR_PAGES
- bool "Support for custom error pages"
- default y
- depends on HTTPD
- help
- This option allows you to define custom error pages in
- the configuration file instead of the default HTTP status
- error pages. For instance, if you add the line:
- E404:/path/e404.html
- in the config file, the server will respond the specified
- '/path/e404.html' file instead of the terse '404 NOT FOUND'
- message.
-
-config FEATURE_HTTPD_PROXY
- bool "Support for reverse proxy"
- default y
- depends on HTTPD
- help
- This option allows you to define URLs that will be forwarded
- to another HTTP server. To setup add the following line to the
- configuration file
- P:/url/:http://hostname[:port]/new/path/
- Then a request to /url/myfile will be forwarded to
- http://hostname[:port]/new/path/myfile.
-
-config FEATURE_HTTPD_GZIP
- bool "Support for GZIP content encoding"
- default y
- depends on HTTPD
- help
- Makes httpd send files using GZIP content encoding if the
- client supports it and a pre-compressed <file>.gz exists.
-
-config IFCONFIG
- bool "ifconfig"
- default y
- select PLATFORM_LINUX
- help
- Ifconfig is used to configure the kernel-resident network interfaces.
-
-config FEATURE_IFCONFIG_STATUS
- bool "Enable status reporting output (+7k)"
- default y
- depends on IFCONFIG
- help
- If ifconfig is called with no arguments it will display the status
- of the currently active interfaces.
-
-config FEATURE_IFCONFIG_SLIP
- bool "Enable slip-specific options \"keepalive\" and \"outfill\""
- default y
- depends on IFCONFIG
- help
- Allow "keepalive" and "outfill" support for SLIP. If you're not
- planning on using serial lines, leave this unchecked.
-
-config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
- bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
- default y
- depends on IFCONFIG
- help
- Allow the start address for shared memory, start address for I/O,
- and/or the interrupt line used by the specified device.
-
-config FEATURE_IFCONFIG_HW
- bool "Enable option \"hw\" (ether only)"
- default y
- depends on IFCONFIG
- help
- Set the hardware address of this interface, if the device driver
- supports this operation. Currently, we only support the 'ether'
- class.
-
-config FEATURE_IFCONFIG_BROADCAST_PLUS
- bool "Set the broadcast automatically"
- default y
- depends on IFCONFIG
- help
- Setting this will make ifconfig attempt to find the broadcast
- automatically if the value '+' is used.
-
-config IFENSLAVE
- bool "ifenslave"
- default y
- select PLATFORM_LINUX
- help
- Userspace application to bind several interfaces
- to a logical interface (use with kernel bonding driver).
-
-config IFPLUGD
- bool "ifplugd"
- default y
- select PLATFORM_LINUX
- help
- Network interface plug detection daemon.
-
-config IFUPDOWN
- bool "ifupdown"
- default y
- help
- Activate or deactivate the specified interfaces. This applet makes
- use of either "ifconfig" and "route" or the "ip" command to actually
- configure network interfaces. Therefore, you will probably also want
- to enable either IFCONFIG and ROUTE, or enable
- FEATURE_IFUPDOWN_IP and the various IP options. Of
- course you could use non-busybox versions of these programs, so
- against my better judgement (since this will surely result in plenty
- of support questions on the mailing list), I do not force you to
- enable these additional options. It is up to you to supply either
- "ifconfig", "route" and "run-parts" or the "ip" command, either
- via busybox or via standalone utilities.
-
-config IFUPDOWN_IFSTATE_PATH
- string "Absolute path to ifstate file"
- default "/var/run/ifstate"
- depends on IFUPDOWN
- help
- ifupdown keeps state information in a file called ifstate.
- Typically it is located in /var/run/ifstate, however
- some distributions tend to put it in other places
- (debian, for example, uses /etc/network/run/ifstate).
- This config option defines location of ifstate.
-
-config FEATURE_IFUPDOWN_IP
- bool "Use ip applet"
- default y
- depends on IFUPDOWN
- help
- Use the iproute "ip" command to implement "ifup" and "ifdown", rather
- than the default of using the older 'ifconfig' and 'route' utilities.
-
-config FEATURE_IFUPDOWN_IP_BUILTIN
- bool "Use busybox ip applet"
- default y
- depends on FEATURE_IFUPDOWN_IP
- select PLATFORM_LINUX
- select IP
- select FEATURE_IP_ADDRESS
- select FEATURE_IP_LINK
- select FEATURE_IP_ROUTE
- help
- Use the busybox iproute "ip" applet to implement "ifupdown".
-
- If left disabled, you must install the full-blown iproute2
- utility or the "ifup" and "ifdown" applets will not work.
-
-config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN
- bool "Use busybox ifconfig and route applets"
- default n
- depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP
- select IFCONFIG
- select ROUTE
- help
- Use the busybox iproute "ifconfig" and "route" applets to
- implement the "ifup" and "ifdown" utilities.
-
- If left disabled, you must install the full-blown ifconfig
- and route utilities, or the "ifup" and "ifdown" applets will not
- work.
-
-config FEATURE_IFUPDOWN_IPV4
- bool "Support for IPv4"
- default y
- depends on IFUPDOWN
- help
- If you want ifup/ifdown to talk IPv4, leave this on.
-
-config FEATURE_IFUPDOWN_IPV6
- bool "Support for IPv6"
- default y
- depends on IFUPDOWN && FEATURE_IPV6
- help
- If you need support for IPv6, turn this option on.
-
-### UNUSED
-###config FEATURE_IFUPDOWN_IPX
-### bool "Support for IPX"
-### default y
-### depends on IFUPDOWN
-### help
-### If this option is selected you can use busybox to work with IPX
-### networks.
-
-config FEATURE_IFUPDOWN_MAPPING
- bool "Enable mapping support"
- default y
- depends on IFUPDOWN
- help
- This enables support for the "mapping" stanza, unless you have
- a weird network setup you don't need it.
-
-config FEATURE_IFUPDOWN_EXTERNAL_DHCP
- bool "Support for external dhcp clients"
- default n
- depends on IFUPDOWN
- help
- This enables support for the external dhcp clients. Clients are
- tried in the following order: dhcpcd, dhclient, pump and udhcpc.
- Otherwise, if udhcpc applet is enabled, it is used.
- Otherwise, ifup/ifdown will have no support for DHCP.
-
-config INETD
- bool "inetd"
- default y
- select FEATURE_SYSLOG
- help
- Internet superserver daemon
-
-config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
- bool "Support echo service"
- default y
- depends on INETD
- help
- Echo received data internal inetd service
-
-config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
- bool "Support discard service"
- default y
- depends on INETD
- help
- Internet /dev/null internal inetd service
-
-config FEATURE_INETD_SUPPORT_BUILTIN_TIME
- bool "Support time service"
- default y
- depends on INETD
- help
- Return 32 bit time since 1900 internal inetd service
-
-config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
- bool "Support daytime service"
- default y
- depends on INETD
- help
- Return human-readable time internal inetd service
-
-config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
- bool "Support chargen service"
- default y
- depends on INETD
- help
- Familiar character generator internal inetd service
-
-config FEATURE_INETD_RPC
- bool "Support RPC services"
- default y
- depends on INETD
- select FEATURE_HAVE_RPC
- help
- Support Sun-RPC based services
-
-config IP
- bool "ip"
- default y
- select PLATFORM_LINUX
- help
- The "ip" applet is a TCP/IP interface configuration and routing
- utility. You generally don't need "ip" to use busybox with
- TCP/IP.
-
-config FEATURE_IP_ADDRESS
- bool "ip address"
- default y
- depends on IP
- help
- Address manipulation support for the "ip" applet.
-
-config FEATURE_IP_LINK
- bool "ip link"
- default y
- depends on IP
- help
- Configure network devices with "ip".
-
-config FEATURE_IP_ROUTE
- bool "ip route"
- default y
- depends on IP
- help
- Add support for routing table management to "ip".
-
-config FEATURE_IP_TUNNEL
- bool "ip tunnel"
- default y
- depends on IP
- help
- Add support for tunneling commands to "ip".
-
-config FEATURE_IP_RULE
- bool "ip rule"
- default y
- depends on IP
- help
- Add support for rule commands to "ip".
-
-config FEATURE_IP_SHORT_FORMS
- bool "Support short forms of ip commands"
- default y
- depends on IP
- help
- Also support short-form of ip <OBJECT> commands:
- ip addr -> ipaddr
- ip link -> iplink
- ip route -> iproute
- ip tunnel -> iptunnel
- ip rule -> iprule
-
- Say N unless you desparately need the short form of the ip
- object commands.
-
-config FEATURE_IP_RARE_PROTOCOLS
- bool "Support displaying rarely used link types"
- default n
- depends on IP
- help
- If you are not going to use links of type "frad", "econet",
- "bif" etc, you probably don't need to enable this.
- Ethernet, wireless, infrared, ppp/slip, ip tunnelling
- link types are supported without this option selected.
-
-config IPADDR
- bool
- default y
- depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
-
-config IPLINK
- bool
- default y
- depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
-
-config IPROUTE
- bool
- default y
- depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
-
-config IPTUNNEL
- bool
- default y
- depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
-
-config IPRULE
- bool
- default y
- depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
-
-config IPCALC
- bool "ipcalc"
- default y
- help
- ipcalc takes an IP address and netmask and calculates the
- resulting broadcast, network, and host range.
-
-config FEATURE_IPCALC_FANCY
- bool "Fancy IPCALC, more options, adds 1 kbyte"
- default y
- depends on IPCALC
- help
- Adds the options hostname, prefix and silent to the output of
- "ipcalc".
-
-config FEATURE_IPCALC_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on IPCALC && LONG_OPTS
- help
- Support long options for the ipcalc applet.
-
-config NETSTAT
- bool "netstat"
- default y
- select PLATFORM_LINUX
- help
- netstat prints information about the Linux networking subsystem.
-
-config FEATURE_NETSTAT_WIDE
- bool "Enable wide netstat output"
- default y
- depends on NETSTAT
- help
- Add support for wide columns. Useful when displaying IPv6 addresses
- (-W option).
-
-config FEATURE_NETSTAT_PRG
- bool "Enable PID/Program name output"
- default y
- depends on NETSTAT
- help
- Add support for -p flag to print out PID and program name.
- +700 bytes of code.
-
-config NSLOOKUP
- bool "nslookup"
- default y
- help
- nslookup is a tool to query Internet name servers.
-
-config NTPD
- bool "ntpd"
- default y
- select PLATFORM_LINUX
- help
- The NTP client/server daemon.
-
-config FEATURE_NTPD_SERVER
- bool "Make ntpd usable as a NTP server"
- default y
- depends on NTPD
- help
- Make ntpd usable as a NTP server. If you disable this option
- ntpd will be usable only as a NTP client.
-
-config PSCAN
- bool "pscan"
- default y
- help
- Simple network port scanner.
-
-config ROUTE
- bool "route"
- default y
- select PLATFORM_LINUX
- help
- Route displays or manipulates the kernel's IP routing tables.
-
-config SLATTACH
- bool "slattach"
- default y
- select PLATFORM_LINUX
- help
- slattach is a small utility to attach network interfaces to serial
- lines.
-
-#config TC
-# bool "tc"
-# default y
-# help
-# show / manipulate traffic control settings
-#
-#config FEATURE_TC_INGRESS
-# def_bool n
-# depends on TC
-
-config TCPSVD
- bool "tcpsvd"
- default y
- help
- tcpsvd listens on a TCP port and runs a program for each new
- connection.
-
-config TELNET
- bool "telnet"
- default y
- help
- Telnet is an interface to the TELNET protocol, but is also commonly
- used to test other simple protocols.
-
-config FEATURE_TELNET_TTYPE
- bool "Pass TERM type to remote host"
- default y
- depends on TELNET
- help
- Setting this option will forward the TERM environment variable to the
- remote host you are connecting to. This is useful to make sure that
- things like ANSI colors and other control sequences behave.
-
-config FEATURE_TELNET_AUTOLOGIN
- bool "Pass USER type to remote host"
- default y
- depends on TELNET
- help
- Setting this option will forward the USER environment variable to the
- remote host you are connecting to. This is useful when you need to
- log into a machine without telling the username (autologin). This
- option enables `-a' and `-l USER' arguments.
-
-config TELNETD
- bool "telnetd"
- default y
- select FEATURE_SYSLOG
- help
- A daemon for the TELNET protocol, allowing you to log onto the host
- running the daemon. Please keep in mind that the TELNET protocol
- sends passwords in plain text. If you can't afford the space for an
- SSH daemon and you trust your network, you may say 'y' here. As a
- more secure alternative, you should seriously consider installing the
- very small Dropbear SSH daemon instead:
- http://matt.ucc.asn.au/dropbear/dropbear.html
-
- Note that for busybox telnetd to work you need several things:
- First of all, your kernel needs:
- UNIX98_PTYS=y
- DEVPTS_FS=y
-
- Next, you need a /dev/pts directory on your root filesystem:
-
- $ ls -ld /dev/pts
- drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
-
- Next you need the pseudo terminal master multiplexer /dev/ptmx:
-
- $ ls -la /dev/ptmx
- crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
-
- Any /dev/ttyp[0-9]* files you may have can be removed.
- Next, you need to mount the devpts filesystem on /dev/pts using:
-
- mount -t devpts devpts /dev/pts
-
- You need to be sure that busybox has LOGIN and
- FEATURE_SUID enabled. And finally, you should make
- certain that Busybox has been installed setuid root:
-
- chown root.root /bin/busybox
- chmod 4755 /bin/busybox
-
- with all that done, telnetd _should_ work....
-
-
-config FEATURE_TELNETD_STANDALONE
- bool "Support standalone telnetd (not inetd only)"
- default y
- depends on TELNETD
- help
- Selecting this will make telnetd able to run standalone.
-
-config FEATURE_TELNETD_INETD_WAIT
- bool "Support -w SEC option (inetd wait mode)"
- default y
- depends on FEATURE_TELNETD_STANDALONE
- help
- This option allows you to run telnetd in "inet wait" mode.
- Example inetd.conf line (note "wait", not usual "nowait"):
-
- telnet stream tcp wait root /bin/telnetd telnetd -w10
-
- In this example, inetd passes _listening_ socket_ as fd 0
- to telnetd when connection appears.
- telnetd will wait for connections until all existing
- connections are closed, and no new connections
- appear during 10 seconds. Then it exits, and inetd continues
- to listen for new connections.
-
- This option is rarely used. "tcp nowait" is much more usual
- way of running tcp services, including telnetd.
- You most probably want to say N here.
-
-config TFTP
- bool "tftp"
- default y
- help
- This enables the Trivial File Transfer Protocol client program. TFTP
- is usually used for simple, small transfers such as a root image
- for a network-enabled bootloader.
-
-config TFTPD
- bool "tftpd"
- default y
- help
- This enables the Trivial File Transfer Protocol server program.
- It expects that stdin is a datagram socket and a packet
- is already pending on it. It will exit after one transfer.
- In other words: it should be run from inetd in nowait mode,
- or from udpsvd. Example: "udpsvd -E 0 69 tftpd DIR"
-
-comment "Common options for tftp/tftpd"
- depends on TFTP || TFTPD
-
-config FEATURE_TFTP_GET
- bool "Enable 'tftp get' and/or tftpd upload code"
- default y
- depends on TFTP || TFTPD
- help
- Add support for the GET command within the TFTP client. This allows
- a client to retrieve a file from a TFTP server.
- Also enable upload support in tftpd, if tftpd is selected.
-
- Note: this option does _not_ make tftpd capable of download
- (the usual operation people need from it)!
-
-config FEATURE_TFTP_PUT
- bool "Enable 'tftp put' and/or tftpd download code"
- default y
- depends on TFTP || TFTPD
- help
- Add support for the PUT command within the TFTP client. This allows
- a client to transfer a file to a TFTP server.
- Also enable download support in tftpd, if tftpd is selected.
-
-config FEATURE_TFTP_BLOCKSIZE
- bool "Enable 'blksize' and 'tsize' protocol options"
- default y
- depends on TFTP || TFTPD
- help
- Allow tftp to specify block size, and tftpd to understand
- "blksize" and "tsize" options.
-
-config FEATURE_TFTP_PROGRESS_BAR
- bool "Enable tftp progress meter"
- default y
- depends on TFTP && FEATURE_TFTP_BLOCKSIZE
- help
- Show progress bar.
-
-config TFTP_DEBUG
- bool "Enable debug"
- default n
- depends on TFTP || TFTPD
- help
- Make tftp[d] print debugging messages on stderr.
- This is useful if you are diagnosing a bug in tftp[d].
-
-config TRACEROUTE
- bool "traceroute"
- default y
- select PLATFORM_LINUX
- help
- Utility to trace the route of IP packets.
-
-config TRACEROUTE6
- bool "traceroute6"
- default y
- depends on FEATURE_IPV6 && TRACEROUTE
- help
- Utility to trace the route of IPv6 packets.
-
-config FEATURE_TRACEROUTE_VERBOSE
- bool "Enable verbose output"
- default y
- depends on TRACEROUTE
- help
- Add some verbosity to traceroute. This includes among other things
- hostnames and ICMP response types.
-
-config FEATURE_TRACEROUTE_SOURCE_ROUTE
- bool "Enable loose source route"
- default n
- depends on TRACEROUTE
- help
- Add option to specify a loose source route gateway
- (8 maximum).
-
-config FEATURE_TRACEROUTE_USE_ICMP
- bool "Use ICMP instead of UDP"
- default n
- depends on TRACEROUTE
- help
- Add option -I to use ICMP ECHO instead of UDP datagrams.
-
-config TUNCTL
- bool "tunctl"
- default y
- select PLATFORM_LINUX
- help
- tunctl creates or deletes tun devices.
-
-config FEATURE_TUNCTL_UG
- bool "Support owner:group assignment"
- default y
- depends on TUNCTL
- help
- Allow to specify owner and group of newly created interface.
- 340 bytes of pure bloat. Say no here.
+INSERT
source networking/udhcp/Config.in
config IFUPDOWN_UDHCPC_CMD_OPTIONS
string "ifup udhcpc command line options"
default "-R -n"
- depends on IFUPDOWN && UDHCPC
+ depends on IFUP || IFDOWN
help
Command line options to pass to udhcpc from ifup.
Intended to alter options not available in /etc/network/interfaces.
(IE: --syslog --background etc...)
-config UDPSVD
- bool "udpsvd"
- default y
- help
- udpsvd listens on an UDP port and runs a program for each new
- connection.
-
-config VCONFIG
- bool "vconfig"
- default y
- select PLATFORM_LINUX
- help
- Creates, removes, and configures VLAN interfaces
-
-config WGET
- bool "wget"
- default y
- help
- wget is a utility for non-interactive download of files from HTTP
- and FTP servers.
-
-config FEATURE_WGET_STATUSBAR
- bool "Enable a nifty process meter (+2k)"
- default y
- depends on WGET
- help
- Enable the transfer progress bar for wget transfers.
-
-config FEATURE_WGET_AUTHENTICATION
- bool "Enable HTTP authentication"
- default y
- depends on WGET
- help
- Support authenticated HTTP transfers.
-
-config FEATURE_WGET_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on WGET && LONG_OPTS
- help
- Support long options for the wget applet.
-
-config FEATURE_WGET_TIMEOUT
- bool "Enable timeout option -T SEC"
- default y
- depends on WGET
- help
- Supports network read and connect timeouts for wget,
- so that wget will give up and timeout, through the -T
- command line option.
-
- Currently only connect and network data read timeout are
- supported (i.e., timeout is not applied to the DNS query). When
- FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option
- will work in addition to -T.
-
-config ZCIP
- bool "zcip"
- default y
- select PLATFORM_LINUX
- select FEATURE_SYSLOG
- help
- ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
- It's a daemon that allocates and defends a dynamically assigned
- address on the 169.254/16 network, requiring no system administrator.
-
- See http://www.zeroconf.org for further details, and "zcip.script"
- in the busybox examples.
-
endmenu
diff --git a/networking/Kbuild.src b/networking/Kbuild.src
index 944f27b..6b4fb74 100644
--- a/networking/Kbuild.src
+++ b/networking/Kbuild.src
@@ -7,42 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_ARP) += arp.o interface.o
-lib-$(CONFIG_ARPING) += arping.o
-lib-$(CONFIG_BRCTL) += brctl.o
-lib-$(CONFIG_DNSD) += dnsd.o
-lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
-lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
-lib-$(CONFIG_FTPD) += ftpd.o
-lib-$(CONFIG_FTPGET) += ftpgetput.o
-lib-$(CONFIG_FTPPUT) += ftpgetput.o
-lib-$(CONFIG_HOSTNAME) += hostname.o
-lib-$(CONFIG_HTTPD) += httpd.o
-lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
-lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
-lib-$(CONFIG_IFPLUGD) += ifplugd.o
-lib-$(CONFIG_IFUPDOWN) += ifupdown.o
-lib-$(CONFIG_INETD) += inetd.o
-lib-$(CONFIG_IP) += ip.o
-lib-$(CONFIG_IPCALC) += ipcalc.o
-lib-$(CONFIG_NAMEIF) += nameif.o
-lib-$(CONFIG_NC) += nc.o
-lib-$(CONFIG_NETSTAT) += netstat.o
-lib-$(CONFIG_NSLOOKUP) += nslookup.o
-lib-$(CONFIG_NTPD) += ntpd.o
-lib-$(CONFIG_PSCAN) += pscan.o
-lib-$(CONFIG_ROUTE) += route.o
-lib-$(CONFIG_SLATTACH) += slattach.o
-lib-$(CONFIG_TC) += tc.o
-lib-$(CONFIG_TELNET) += telnet.o
-lib-$(CONFIG_TELNETD) += telnetd.o
-lib-$(CONFIG_TFTP) += tftp.o
-lib-$(CONFIG_TFTPD) += tftp.o
-lib-$(CONFIG_TRACEROUTE) += traceroute.o
-lib-$(CONFIG_TUNCTL) += tunctl.o
-lib-$(CONFIG_VCONFIG) += vconfig.o
-lib-$(CONFIG_WGET) += wget.o
-lib-$(CONFIG_ZCIP) += zcip.o
-
-lib-$(CONFIG_TCPSVD) += tcpudp.o tcpudp_perhost.o
-lib-$(CONFIG_UDPSVD) += tcpudp.o tcpudp_perhost.o
diff --git a/networking/arp.c b/networking/arp.c
index e79b1b6..a62a376 100644
--- a/networking/arp.c
+++ b/networking/arp.c
@@ -12,6 +12,16 @@
*
* modified for getopt32 by Arne Bernin <arne [at] alamut.de>
*/
+//config:config ARP
+//config: bool "arp"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Manipulate the system ARP cache.
+
+//applet:IF_ARP(APPLET(arp, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ARP) += arp.o interface.o
//usage:#define arp_trivial_usage
//usage: "\n[-vn] [-H HWTYPE] [-i IF] -a [HOSTNAME]"
@@ -32,6 +42,7 @@
//usage: "\n -H HWTYPE Hardware address type"
#include "libbb.h"
+#include "common_bufsiz.h"
#include "inet_common.h"
#include <arpa/inet.h>
@@ -68,14 +79,14 @@ struct globals {
const struct hwtype *hw; /* current hardware type */
const char *device; /* current device */
smallint hw_set; /* flag if hw-type was set (-H) */
-
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define ap (G.ap )
#define hw (G.hw )
#define device (G.device )
#define hw_set (G.hw_set )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
device = ""; \
} while (0)
@@ -177,7 +188,7 @@ static int arp_del(char **args)
if (flags == 0)
flags = 3;
- strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+ strncpy_IFNAMSIZ(req.arp_dev, device);
err = -1;
@@ -218,7 +229,7 @@ static void arp_getdevhw(char *ifname, struct sockaddr *sa)
struct ifreq ifr;
const struct hwtype *xhw;
- strcpy(ifr.ifr_name, ifname);
+ strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
ioctl_or_perror_and_die(sockfd, SIOCGIFHWADDR, &ifr,
"can't get HW-Address for '%s'", ifname);
if (hw_set && (ifr.ifr_hwaddr.sa_family != hw->type)) {
@@ -331,7 +342,7 @@ static int arp_set(char **args)
/* Fill in the remainder of the request. */
req.arp_flags = flags;
- strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+ strncpy_IFNAMSIZ(req.arp_dev, device);
/* Call the kernel. */
if (option_mask32 & ARP_OPT_v)
diff --git a/networking/arping.c b/networking/arping.c
index a4421ed..5bfeb1b 100644
--- a/networking/arping.c
+++ b/networking/arping.c
@@ -5,6 +5,17 @@
* Author: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
* Busybox port: Nick Fedchik <nick@fedchik.org.ua>
*/
+//config:config ARPING
+//config: bool "arping"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Ping hosts by ARP packets.
+//config:
+
+//applet:IF_ARPING(APPLET(arping, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ARPING) += arping.o
//usage:#define arping_trivial_usage
//usage: "[-fqbDUA] [-c CNT] [-w TIMEOUT] [-I IFACE] [-s SRC_IP] DST_IP"
@@ -13,11 +24,11 @@
//usage: "\n -f Quit on first ARP reply"
//usage: "\n -q Quiet"
//usage: "\n -b Keep broadcasting, don't go unicast"
-//usage: "\n -D Duplicated address detection mode"
+//usage: "\n -D Exit with 1 if DST_IP replies"
//usage: "\n -U Unsolicited ARP mode, update your neighbors"
//usage: "\n -A ARP answer mode, update your neighbors"
//usage: "\n -c N Stop after sending N ARP requests"
-//usage: "\n -w TIMEOUT Time to wait for ARP reply, seconds"
+//usage: "\n -w TIMEOUT Seconds to wait for ARP reply"
//usage: "\n -I IFACE Interface to use (default eth0)"
//usage: "\n -s SRC_IP Sender IP address"
//usage: "\n DST_IP Target IP address"
@@ -28,6 +39,7 @@
#include <netpacket/packet.h>
#include "libbb.h"
+#include "common_bufsiz.h"
/* We don't expect to see 1000+ seconds delay, unsigned is enough */
#define MONOTONIC_US() ((unsigned)monotonic_us())
@@ -60,7 +72,7 @@ struct globals {
unsigned brd_recv;
unsigned req_recv;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define src (G.src )
#define dst (G.dst )
#define me (G.me )
@@ -76,6 +88,7 @@ struct globals {
#define brd_recv (G.brd_recv )
#define req_recv (G.req_recv )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
count = -1; \
} while (0)
@@ -162,7 +175,7 @@ static void catcher(void)
alarm(1);
}
-static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+static void recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
{
struct arphdr *ah = (struct arphdr *) buf;
unsigned char *p = (unsigned char *) (ah + 1);
@@ -181,33 +194,33 @@ static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
if (FROM->sll_pkttype != PACKET_HOST
&& FROM->sll_pkttype != PACKET_BROADCAST
&& FROM->sll_pkttype != PACKET_MULTICAST)
- return false;
+ return;
/* Only these types are recognized */
if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
- return false;
+ return;
/* ARPHRD check and this darned FDDI hack here :-( */
if (ah->ar_hrd != htons(FROM->sll_hatype)
&& (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
- return false;
+ return;
/* Protocol must be IP. */
if (ah->ar_pro != htons(ETH_P_IP)
|| (ah->ar_pln != 4)
|| (ah->ar_hln != me.sll_halen)
|| (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
- return false;
+ return;
move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
if (dst.s_addr != src_ip.s_addr)
- return false;
+ return;
if (!(option_mask32 & DAD)) {
if ((src.s_addr != dst_ip.s_addr)
- || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
- return false;
+ || (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
+ return;
} else {
/* DAD packet was:
src_ip = 0 (or some src)
@@ -224,32 +237,35 @@ static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
*/
if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
|| (src.s_addr && src.s_addr != dst_ip.s_addr))
- return false;
+ return;
}
if (!(option_mask32 & QUIET)) {
int s_printed = 0;
- printf("%scast re%s from %s [%s]",
+ printf("%scast re%s from %s [%02x:%02x:%02x:%02x:%02x:%02x]",
FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
inet_ntoa(src_ip),
- ether_ntoa((struct ether_addr *) p));
+ p[0], p[1], p[2], p[3], p[4], p[5]
+ );
if (dst_ip.s_addr != src.s_addr) {
printf("for %s ", inet_ntoa(dst_ip));
s_printed = 1;
}
if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
+ unsigned char *pp = p + ah->ar_hln + 4;
if (!s_printed)
printf("for ");
- printf("[%s]",
- ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+ printf("[%02x:%02x:%02x:%02x:%02x:%02x]",
+ pp[0], pp[1], pp[2], pp[3], pp[4], pp[5]
+ );
}
if (last) {
unsigned diff = MONOTONIC_US() - last;
printf(" %u.%03ums\n", diff / 1000, diff % 1000);
} else {
- printf(" UNSOLICITED?\n");
+ puts(" UNSOLICITED?");
}
fflush_all();
}
@@ -264,7 +280,6 @@ static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
memcpy(he.sll_addr, p, me.sll_halen);
option_mask32 |= UNICASTING;
}
- return true;
}
int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -284,7 +299,6 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
// Need to remove SUID_NEVER from applets.h for this to work
//xsetuid(getuid());
- err_str = xasprintf("interface %s %%s", device);
{
unsigned opt;
char *str_timeout;
@@ -292,8 +306,8 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
/* Dad also sets quit_on_reply.
* Advert also sets unsolicited.
*/
- opt_complementary = "=1:Df:AU:c+";
- opt = getopt32(argv, "DUAqfbc:w:I:s:",
+ opt_complementary = "=1:Df:AU";
+ opt = getopt32(argv, "DUAqfbc:+w:I:s:",
&count, &str_timeout, &device, &source);
if (opt & 0x80) /* -w: timeout */
timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
@@ -302,7 +316,7 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
}
target = argv[optind];
-
+ err_str = xasprintf("interface %s %%s", device);
xfunc_error_retval = 2;
{
@@ -359,8 +373,8 @@ int arping_main(int argc UNUSED_PARAM, char **argv)
saddr.sin_port = htons(1025);
saddr.sin_addr = dst;
- if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
- bb_perror_msg("setsockopt(SO_DONTROUTE)");
+ if (setsockopt_SOL_SOCKET_1(probe_fd, SO_DONTROUTE) != 0)
+ bb_perror_msg("setsockopt(%s)", "SO_DONTROUTE");
xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
getsockname(probe_fd, (struct sockaddr *) &saddr, &alen);
//never happens:
diff --git a/networking/brctl.c b/networking/brctl.c
index 207b069..3587689 100644
--- a/networking/brctl.c
+++ b/networking/brctl.c
@@ -12,6 +12,36 @@
/* This applet currently uses only the ioctl interface and no sysfs at all.
* At the time of this writing this was considered a feature.
*/
+//config:config BRCTL
+//config: bool "brctl"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Manage ethernet bridges.
+//config: Supports addbr/delbr and addif/delif.
+//config:
+//config:config FEATURE_BRCTL_FANCY
+//config: bool "Fancy options"
+//config: default y
+//config: depends on BRCTL
+//config: help
+//config: Add support for extended option like:
+//config: setageing, setfd, sethello, setmaxage,
+//config: setpathcost, setportprio, setbridgeprio,
+//config: stp
+//config: This adds about 600 bytes.
+//config:
+//config:config FEATURE_BRCTL_SHOW
+//config: bool "Support show"
+//config: default y
+//config: depends on BRCTL && FEATURE_BRCTL_FANCY
+//config: help
+//config: Add support for option which prints the current config:
+//config: show
+
+//applet:IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BRCTL) += brctl.o
//usage:#define brctl_trivial_usage
//usage: "COMMAND [BRIDGE [INTERFACE]]"
@@ -64,7 +94,57 @@
#define BRCTL_USE_INTERNAL 1
#if ENABLE_FEATURE_BRCTL_FANCY
-# include <linux/if_bridge.h>
+/* #include <linux/if_bridge.h>
+ * breaks on musl: we already included netinet/in.h in libbb.h,
+ * if we include <linux/if_bridge.h> here, we get this:
+ * In file included from /usr/include/linux/if_bridge.h:18,
+ * from networking/brctl.c:67:
+ * /usr/include/linux/in6.h:32: error: redefinition of 'struct in6_addr'
+ * /usr/include/linux/in6.h:49: error: redefinition of 'struct sockaddr_in6'
+ * /usr/include/linux/in6.h:59: error: redefinition of 'struct ipv6_mreq'
+ */
+/* From <linux/if_bridge.h> */
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+#define BRCTL_SET_BRIDGE_HELLO_TIME 9
+#define BRCTL_SET_BRIDGE_MAX_AGE 10
+#define BRCTL_SET_AGEING_TIME 11
+#define BRCTL_SET_GC_INTERVAL 12
+#define BRCTL_GET_PORT_INFO 13
+#define BRCTL_SET_BRIDGE_STP_STATE 14
+#define BRCTL_SET_BRIDGE_PRIORITY 15
+#define BRCTL_SET_PORT_PRIORITY 16
+#define BRCTL_SET_PATH_COST 17
+#define BRCTL_GET_FDB_ENTRIES 18
+struct __bridge_info {
+ uint64_t designated_root;
+ uint64_t bridge_id;
+ uint32_t root_path_cost;
+ uint32_t max_age;
+ uint32_t hello_time;
+ uint32_t forward_delay;
+ uint32_t bridge_max_age;
+ uint32_t bridge_hello_time;
+ uint32_t bridge_forward_delay;
+ uint8_t topology_change;
+ uint8_t topology_change_detected;
+ uint8_t root_port;
+ uint8_t stp_enabled;
+ uint32_t ageing_time;
+ uint32_t gc_interval;
+ uint32_t hello_timer_value;
+ uint32_t tcn_timer_value;
+ uint32_t topology_change_timer_value;
+ uint32_t gc_timer_value;
+};
+/* end <linux/if_bridge.h> */
/* FIXME: These 4 funcs are not really clean and could be improved */
static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
@@ -78,7 +158,7 @@ static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
# else
if (sscanf(time_str, "%lf", &secs) != 1)
# endif
- bb_error_msg_and_die(bb_msg_invalid_arg, time_str, "timespec");
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
tv->tv_sec = secs;
tv->tv_usec = 1000000 * (secs - tv->tv_sec);
}
@@ -155,7 +235,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
key = index_in_strings(keywords, *argv);
if (key == -1) /* no match found in keywords array, bail out. */
- bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
argv++;
fd = xsocket(AF_INET, SOCK_STREAM, 0);
@@ -167,7 +247,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
arm_ioctl(args, BRCTL_GET_BRIDGES,
(unsigned long) bridx, MAX_PORTS);
num = xioctl(fd, SIOCGIFBR, args);
- printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
+ puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
for (i = 0; i < num; i++) {
char ifname[IFNAMSIZ];
int j, tabs;
@@ -186,7 +266,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
/* print bridge id */
x = (unsigned char *) &bi.bridge_id;
for (j = 0; j < 8; j++) {
- printf("%.2x", x[j]);
+ printf("%02x", x[j]);
if (j == 1)
bb_putchar('.');
}
@@ -249,7 +329,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
"1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
int onoff = index_in_strings(no_yes, *argv);
if (onoff < 0)
- bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
onoff = (unsigned)onoff / 4;
arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
goto fire;
@@ -282,7 +362,7 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
port = if_nametoindex(*argv++);
if (!port)
- bb_error_msg_and_die(bb_msg_invalid_arg, *argv, "port");
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port");
memset(ifidx, 0, sizeof ifidx);
arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
MAX_PORTS);
diff --git a/networking/dnsd.c b/networking/dnsd.c
index fe98400..7be9001 100644
--- a/networking/dnsd.c
+++ b/networking/dnsd.c
@@ -16,6 +16,15 @@
* Some bugfix and minor changes was applied by Roberto A. Foglietta who made
* the first porting of oao' scdns to busybox also.
*/
+//config:config DNSD
+//config: bool "dnsd"
+//config: default y
+//config: help
+//config: Small and static DNS server daemon.
+
+//applet:IF_DNSD(APPLET(dnsd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DNSD) += dnsd.o
//usage:#define dnsd_trivial_usage
//usage: "[-dvs] [-c CONFFILE] [-t TTL_SEC] [-p PORT] [-i ADDR]"
@@ -194,7 +203,7 @@ static char *table_lookup(struct dns_entry *d,
if ((len != 1 || d->name[1] != '*')
/* we assume (do not check) that query_string
* ends in ".in-addr.arpa" */
- && strncmp(d->rip, query_string, strlen(d->rip)) == 0
+ && is_prefixed_with(query_string, d->rip)
) {
#if DEBUG
fprintf(stderr, "Found name:%s\n", d->name);
diff --git a/networking/ether-wake.c b/networking/ether-wake.c
index c38547d..d7d6917 100644
--- a/networking/ether-wake.c
+++ b/networking/ether-wake.c
@@ -63,6 +63,16 @@
* doing so only works with adapters configured for unicast+broadcast Rx
* filter. That configuration consumes more power.
*/
+//config:config ETHER_WAKE
+//config: bool "ether-wake"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Send a magic packet to wake up sleeping machines.
+
+//applet:IF_ETHER_WAKE(APPLET_ODDNAME(ether-wake, ether_wake, BB_DIR_USR_SBIN, BB_SUID_DROP, ether_wake))
+
+//kbuild:lib-$(CONFIG_ETHER_WAKE) += ether-wake.o
//usage:#define ether_wake_trivial_usage
//usage: "[-b] [-i IFACE] [-p aa:bb:cc:dd[:ee:ff]/a.b.c.d] MAC"
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 33db964..bcd60a2 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -11,6 +11,40 @@
*
* You have to run this daemon via inetd.
*/
+//config:config FTPD
+//config: bool "ftpd"
+//config: default y
+//config: help
+//config: simple FTP daemon. You have to run it via inetd.
+//config:
+//config:config FEATURE_FTPD_WRITE
+//config: bool "Enable upload commands"
+//config: default y
+//config: depends on FTPD
+//config: help
+//config: Enable all kinds of FTP upload commands (-w option)
+//config:
+//config:config FEATURE_FTPD_ACCEPT_BROKEN_LIST
+//config: bool "Enable workaround for RFC-violating clients"
+//config: default y
+//config: depends on FTPD
+//config: help
+//config: Some ftp clients (among them KDE's Konqueror) issue illegal
+//config: "LIST -l" requests. This option works around such problems.
+//config: It might prevent you from listing files starting with "-" and
+//config: it increases the code size by ~40 bytes.
+//config: Most other ftp servers seem to behave similar to this.
+//config:
+//config:config FEATURE_FTPD_AUTHENTICATION
+//config: bool "Enable authentication"
+//config: default y
+//config: depends on FTPD
+//config: help
+//config: Enable basic system login as seen in telnet etc.
+
+//applet:IF_FTPD(APPLET(ftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FTPD) += ftpd.o
//usage:#define ftpd_trivial_usage
//usage: "[-wvS] [-t N] [-T N] [DIR]"
@@ -29,6 +63,7 @@
//usage: "\n DIR Change root to this directory"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
#include <netinet/tcp.h>
@@ -116,15 +151,16 @@ struct globals {
len_and_sockaddr *port_addr;
char *ftp_cmd;
char *ftp_arg;
-#if ENABLE_FEATURE_FTP_WRITE
+#if ENABLE_FEATURE_FTPD_WRITE
char *rnfr_filename;
#endif
/* We need these aligned to uint32_t */
char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
/* Moved to main */ \
/*strcpy(G.msg_ok + 4, MSG_OK );*/ \
/*strcpy(G.msg_err + 4, MSG_ERR);*/ \
@@ -377,7 +413,7 @@ ftpdataio_get_pasv_fd(void)
return remote_fd;
}
- setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_keepalive(remote_fd);
return remote_fd;
}
@@ -622,14 +658,8 @@ popen_ls(const char *opt)
pid_t pid;
argv[0] = "ftpd";
- argv[1] = opt; /* "-l" or "-1" */
-#if BB_MMU
+ argv[1] = opt; /* "-lA" or "-1A" */
argv[2] = "--";
-#else
- /* NOMMU ftpd ls helper chdirs to argv[2],
- * preventing peer from seeing real root. */
- argv[2] = xrealloc_getcwd_or_warn(NULL);
-#endif
argv[3] = G.ftp_arg;
argv[4] = NULL;
@@ -650,17 +680,10 @@ popen_ls(const char *opt)
/*fflush_all(); - so far we dont use stdio on output */
pid = BB_MMU ? xfork() : xvfork();
if (pid == 0) {
- /* child */
#if !BB_MMU
- /* On NOMMU, we want to execute a child - copy of ourself.
- * In chroot we usually can't do it. Thus we chdir
- * out of the chroot back to original root,
- * and (see later below) execute bb_busybox_exec_path
- * relative to current directory */
- if (fchdir(G.root_fd) != 0)
- _exit(127);
- /*close(G.root_fd); - close_on_exec_on() took care of this */
+ int cur_fd;
#endif
+ /* child */
/* NB: close _first_, then move fd! */
close(outfd.rd);
xmove_fd(outfd.wr, STDOUT_FILENO);
@@ -674,19 +697,26 @@ popen_ls(const char *opt)
/* memset(&G, 0, sizeof(G)); - ls_main does it */
exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
#else
- /* + 1: we must use relative path here if in chroot.
- * For example, execv("/proc/self/exe") will fail, since
- * it looks for "/proc/self/exe" _relative to chroot!_ */
- execv(bb_busybox_exec_path + 1, (char**) argv);
+ cur_fd = xopen(".", O_RDONLY | O_DIRECTORY);
+ /* On NOMMU, we want to execute a child - copy of ourself
+ * in order to unblock parent after vfork.
+ * In chroot we usually can't re-exec. Thus we escape
+ * out of the chroot back to original root.
+ */
+ if (G.root_fd >= 0) {
+ if (fchdir(G.root_fd) != 0 || chroot(".") != 0)
+ _exit(127);
+ /*close(G.root_fd); - close_on_exec_on() took care of this */
+ }
+ /* Child expects directory to list on fd #3 */
+ xmove_fd(cur_fd, 3);
+ execv(bb_busybox_exec_path, (char**) argv);
_exit(127);
#endif
}
/* parent */
close(outfd.wr);
-#if !BB_MMU
- free((char*)argv[2]);
-#endif
return outfd.rd;
}
@@ -705,10 +735,9 @@ handle_dir_common(int opts)
if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
return; /* port_or_pasv_was_seen emitted error response */
- /* -n prevents user/groupname display,
- * which can be problematic in chroot */
- ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
+ ls_fd = popen_ls((opts & LONG_LISTING) ? "-lA" : "-1A");
ls_fp = xfdopen_for_read(ls_fd);
+/* FIXME: filenames with embedded newlines are mishandled */
if (opts & USE_CTRL_CONN) {
/* STAT <filename> */
@@ -729,16 +758,20 @@ handle_dir_common(int opts)
int remote_fd = get_remote_transfer_fd(" Directory listing");
if (remote_fd >= 0) {
while (1) {
- line = xmalloc_fgetline(ls_fp);
+ unsigned len;
+
+ line = xmalloc_fgets(ls_fp);
if (!line)
break;
/* I've seen clients complaining when they
* are fed with ls output with bare '\n'.
- * Pity... that would be much simpler.
+ * Replace trailing "\n\0" with "\r\n".
*/
-/* TODO: need to s/LF/NUL/g here */
- xwrite_str(remote_fd, line);
- xwrite(remote_fd, "\r\n", 2);
+ len = strlen(line);
+ if (len != 0) /* paranoia check */
+ line[len - 1] = '\r';
+ line[len] = '\n';
+ xwrite(remote_fd, line, len + 1);
free(line);
}
}
@@ -832,7 +865,7 @@ handle_size_or_mdtm(int need_size)
/* Upload commands */
-#if ENABLE_FEATURE_FTP_WRITE
+#if ENABLE_FEATURE_FTPD_WRITE
static void
handle_mkd(void)
{
@@ -975,7 +1008,7 @@ handle_stou(void)
G.restart_pos = 0;
handle_upload_common(0, 1);
}
-#endif /* ENABLE_FEATURE_FTP_WRITE */
+#endif /* ENABLE_FEATURE_FTPD_WRITE */
static uint32_t
cmdio_get_cmd_and_arg(void)
@@ -1085,6 +1118,8 @@ enum {
const_PASV = mk_const4('P', 'A', 'S', 'V'),
const_PORT = mk_const4('P', 'O', 'R', 'T'),
const_PWD = mk_const3('P', 'W', 'D'),
+ /* Same as PWD. Reportedly used by windows ftp client */
+ const_XPWD = mk_const4('X', 'P', 'W', 'D'),
const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
const_REST = mk_const4('R', 'E', 'S', 'T'),
const_RETR = mk_const4('R', 'E', 'T', 'R'),
@@ -1103,10 +1138,11 @@ enum {
#if !BB_MMU
OPT_l = (1 << 0),
OPT_1 = (1 << 1),
+ OPT_A = (1 << 2),
#endif
- OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
- OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
- OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
+ OPT_v = (1 << ((!BB_MMU) * 3 + 0)),
+ OPT_S = (1 << ((!BB_MMU) * 3 + 1)),
+ OPT_w = (1 << ((!BB_MMU) * 3 + 2)) * ENABLE_FEATURE_FTPD_WRITE,
};
int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -1116,6 +1152,9 @@ int ftpd_main(int argc, char **argv)
int ftpd_main(int argc UNUSED_PARAM, char **argv)
#endif
{
+#if ENABLE_FEATURE_FTPD_AUTHENTICATION
+ struct passwd *pw = NULL;
+#endif
unsigned abs_timeout;
unsigned verbose_S;
smallint opts;
@@ -1125,18 +1164,16 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
abs_timeout = 1 * 60 * 60;
verbose_S = 0;
G.timeout = 2 * 60;
- opt_complementary = "t+:T+:vv:SS";
+ opt_complementary = "vv:SS";
#if BB_MMU
- opts = getopt32(argv, "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+ opts = getopt32(argv, "vS" IF_FEATURE_FTPD_WRITE("w") "t:+T:+", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
#else
- opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+ opts = getopt32(argv, "l1AvS" IF_FEATURE_FTPD_WRITE("w") "t:+T:+", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
if (opts & (OPT_l|OPT_1)) {
/* Our secret backdoor to ls */
-/* TODO: pass -n? It prevents user/group resolution, which may not work in chroot anyway */
-/* TODO: pass -A? It shows dot files */
/* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */
- xchdir(argv[2]);
- argv[2] = (char*)"--";
+ if (fchdir(3) != 0)
+ _exit(127);
/* memset(&G, 0, sizeof(G)); - ls_main does it */
return ls_main(argc, argv);
}
@@ -1174,58 +1211,80 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
if (logmode)
applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
-#if !BB_MMU
- G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
- close_on_exec_on(G.root_fd);
-#endif
-
- if (argv[optind]) {
- xchroot(argv[optind]);
- }
-
//umask(077); - admin can set umask before starting us
- /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
- signal(SIGPIPE, SIG_IGN);
+ /* Signals */
+ bb_signals(0
+ /* We'll always take EPIPE rather than a rude signal, thanks */
+ + (1 << SIGPIPE)
+ /* LIST command spawns chilren. Prevent zombies */
+ + (1 << SIGCHLD)
+ , SIG_IGN);
/* Set up options on the command socket (do we need these all? why?) */
- setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
- setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_1(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY);
+ setsockopt_keepalive(STDIN_FILENO);
/* Telnet protocol over command link may send "urgent" data,
* we prefer it to be received in the "normal" data stream: */
- setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
+ setsockopt_1(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE);
WRITE_OK(FTP_GREET);
signal(SIGALRM, timeout_handler);
-#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
- {
- smallint user_was_specified = 0;
- while (1) {
- uint32_t cmdval = cmdio_get_cmd_and_arg();
-
+#if ENABLE_FEATURE_FTPD_AUTHENTICATION
+ while (1) {
+ uint32_t cmdval = cmdio_get_cmd_and_arg();
if (cmdval == const_USER) {
- if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
- cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
- else {
- user_was_specified = 1;
- cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
- }
- } else if (cmdval == const_PASS) {
- if (user_was_specified)
- break;
- cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
- } else if (cmdval == const_QUIT) {
- WRITE_OK(FTP_GOODBYE);
- return 0;
- } else {
- cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
+ pw = getpwnam(G.ftp_arg);
+ cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify password\r\n");
+ } else if (cmdval == const_PASS) {
+ if (check_password(pw, G.ftp_arg) > 0) {
+ break; /* login success */
}
+ cmdio_write_raw(STR(FTP_LOGINERR)" Login failed\r\n");
+ pw = NULL;
+ } else if (cmdval == const_QUIT) {
+ WRITE_OK(FTP_GOODBYE);
+ return 0;
+ } else {
+ cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
}
}
WRITE_OK(FTP_LOGINOK);
#endif
+ /* Do this after auth, else /etc/passwd is not accessible */
+#if !BB_MMU
+ G.root_fd = -1;
+#endif
+ argv += optind;
+ if (argv[0]) {
+ const char *basedir = argv[0];
+#if !BB_MMU
+ G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
+ close_on_exec_on(G.root_fd);
+#endif
+ if (chroot(basedir) == 0)
+ basedir = "/";
+#if !BB_MMU
+ else {
+ close(G.root_fd);
+ G.root_fd = -1;
+ }
+#endif
+ /*
+ * If chroot failed, assume that we aren't root,
+ * and at least chdir to the specified DIR
+ * (older versions were dying with error message).
+ * If chroot worked, move current dir to new "/":
+ */
+ xchdir(basedir);
+ }
+
+#if ENABLE_FEATURE_FTPD_AUTHENTICATION
+ change_identity(pw);
+#endif
+
/* RFC-959 Section 5.1
* The following commands and options MUST be supported by every
* server-FTP and user-FTP, except in cases where the underlying
@@ -1292,7 +1351,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
WRITE_OK(FTP_ALLOOK);
else if (cmdval == const_SYST)
cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
- else if (cmdval == const_PWD)
+ else if (cmdval == const_PWD || cmdval == const_XPWD)
handle_pwd();
else if (cmdval == const_CWD)
handle_cwd();
@@ -1329,7 +1388,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
handle_port();
else if (cmdval == const_REST)
handle_rest();
-#if ENABLE_FEATURE_FTP_WRITE
+#if ENABLE_FEATURE_FTPD_WRITE
else if (opts & OPT_w) {
if (cmdval == const_STOR)
handle_stor();
@@ -1369,7 +1428,7 @@ int ftpd_main(int argc UNUSED_PARAM, char **argv)
* (doesn't necessarily mean "we must support them")
* foo 1.2.3: XXXX - comment
*/
-#if ENABLE_FEATURE_FTP_WRITE
+#if ENABLE_FEATURE_FTPD_WRITE
bad_cmd:
#endif
cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index 8ab078f..49c5fd4 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -12,6 +12,30 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FTPGET
+//config: bool "ftpget"
+//config: default y
+//config: help
+//config: Retrieve a remote file via FTP.
+//config:
+//config:config FTPPUT
+//config: bool "ftpput"
+//config: default y
+//config: help
+//config: Store a remote file via FTP.
+//config:
+//config:config FEATURE_FTPGETPUT_LONG_OPTIONS
+//config: bool "Enable long options in ftpget/ftpput"
+//config: default y
+//config: depends on LONG_OPTS && (FTPGET || FTPPUT)
+//config: help
+//config: Support long options for the ftpget/ftpput applet.
+
+//applet:IF_FTPGET(APPLET_ODDNAME(ftpget, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpget))
+//applet:IF_FTPPUT(APPLET_ODDNAME(ftpput, ftpgetput, BB_DIR_USR_BIN, BB_SUID_DROP, ftpput))
+
+//kbuild:lib-$(CONFIG_FTPGET) += ftpgetput.o
+//kbuild:lib-$(CONFIG_FTPPUT) += ftpgetput.o
//usage:#define ftpget_trivial_usage
//usage: "[OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE"
@@ -50,6 +74,7 @@
//usage: )
#include "libbb.h"
+#include "common_bufsiz.h"
struct globals {
const char *user;
@@ -60,11 +85,8 @@ struct globals {
int do_continue;
char buf[4]; /* actually [BUFSZ] */
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
#define user (G.user )
#define password (G.password )
#define lsa (G.lsa )
@@ -72,7 +94,10 @@ struct BUG_G_too_big {
#define verbose_flag (G.verbose_flag )
#define do_continue (G.do_continue )
#define buf (G.buf )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+} while (0)
static void ftp_die(const char *msg) NORETURN;
diff --git a/networking/hostname.c b/networking/hostname.c
index 4da5b40..06d91f2 100644
--- a/networking/hostname.c
+++ b/networking/hostname.c
@@ -10,6 +10,24 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config HOSTNAME
+//config: bool "hostname"
+//config: default y
+//config: help
+//config: Show or set the system's host name.
+//config:
+//config:config DNSDOMAINNAME
+//config: bool "dnsdomainname"
+//config: default y
+//config: help
+//config: Alias to "hostname -d".
+
+//applet:IF_DNSDOMAINNAME(APPLET_ODDNAME(dnsdomainname, hostname, BB_DIR_BIN, BB_SUID_DROP, dnsdomainname))
+//applet:IF_HOSTNAME(APPLET(hostname, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild: lib-$(CONFIG_HOSTNAME) += hostname.o
+//kbuild: lib-$(CONFIG_DNSDOMAINNAME) += hostname.o
+
//usage:#define hostname_trivial_usage
//usage: "[OPTIONS] [HOSTNAME | -F FILE]"
//usage:#define hostname_full_usage "\n\n"
@@ -131,8 +149,12 @@ int hostname_main(int argc UNUSED_PARAM, char **argv)
opts = getopt32(argv, "dfisF:v", &hostname_str);
argv += optind;
buf = safe_gethostname();
- if (applet_name[0] == 'd') /* dnsdomainname? */
- opts = OPT_d;
+ if (ENABLE_DNSDOMAINNAME) {
+ if (!ENABLE_HOSTNAME || applet_name[0] == 'd') {
+ /* dnsdomainname */
+ opts = OPT_d;
+ }
+ }
if (opts & OPT_dfi) {
/* Cases when we need full hostname (or its part) */
diff --git a/networking/httpd.c b/networking/httpd.c
index 621d9cd..d301d59 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -98,6 +98,128 @@
*
*/
/* TODO: use TCP_CORK, parse_config() */
+//config:config HTTPD
+//config: bool "httpd"
+//config: default y
+//config: help
+//config: Serve web pages via an HTTP server.
+//config:
+//config:config FEATURE_HTTPD_RANGES
+//config: bool "Support 'Ranges:' header"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: Makes httpd emit "Accept-Ranges: bytes" header and understand
+//config: "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
+//config: downloads, seeking in multimedia players etc.
+//config:
+//config:config FEATURE_HTTPD_SETUID
+//config: bool "Enable -u <user> option"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: This option allows the server to run as a specific user
+//config: rather than defaulting to the user that starts the server.
+//config: Use of this option requires special privileges to change to a
+//config: different user.
+//config:
+//config:config FEATURE_HTTPD_BASIC_AUTH
+//config: bool "Enable Basic http Authentication"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: Utilizes password settings from /etc/httpd.conf for basic
+//config: authentication on a per url basis.
+//config: Example for httpd.conf file:
+//config: /adm:toor:PaSsWd
+//config:
+//config:config FEATURE_HTTPD_AUTH_MD5
+//config: bool "Support MD5 crypted passwords for http Authentication"
+//config: default y
+//config: depends on FEATURE_HTTPD_BASIC_AUTH
+//config: help
+//config: Enables encrypted passwords, and wildcard user/passwords
+//config: in httpd.conf file.
+//config: User '*' means 'any system user name is ok',
+//config: password of '*' means 'use system password for this user'
+//config: Examples:
+//config: /adm:toor:$1$P/eKnWXS$aI1aPGxT.dJD5SzqAKWrF0
+//config: /adm:root:*
+//config: /wiki:*:*
+//config:
+//config:config FEATURE_HTTPD_CGI
+//config: bool "Support Common Gateway Interface (CGI)"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: This option allows scripts and executables to be invoked
+//config: when specific URLs are requested.
+//config:
+//config:config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+//config: bool "Support for running scripts through an interpreter"
+//config: default y
+//config: depends on FEATURE_HTTPD_CGI
+//config: help
+//config: This option enables support for running scripts through an
+//config: interpreter. Turn this on if you want PHP scripts to work
+//config: properly. You need to supply an additional line in your
+//config: httpd.conf file:
+//config: *.php:/path/to/your/php
+//config:
+//config:config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+//config: bool "Set REMOTE_PORT environment variable for CGI"
+//config: default y
+//config: depends on FEATURE_HTTPD_CGI
+//config: help
+//config: Use of this option can assist scripts in generating
+//config: references that contain a unique port number.
+//config:
+//config:config FEATURE_HTTPD_ENCODE_URL_STR
+//config: bool "Enable -e option (useful for CGIs written as shell scripts)"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: This option allows html encoding of arbitrary strings for display
+//config: by the browser. Output goes to stdout.
+//config: For example, httpd -e "<Hello World>" produces
+//config: "&#60Hello&#32World&#62".
+//config:
+//config:config FEATURE_HTTPD_ERROR_PAGES
+//config: bool "Support for custom error pages"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: This option allows you to define custom error pages in
+//config: the configuration file instead of the default HTTP status
+//config: error pages. For instance, if you add the line:
+//config: E404:/path/e404.html
+//config: in the config file, the server will respond the specified
+//config: '/path/e404.html' file instead of the terse '404 NOT FOUND'
+//config: message.
+//config:
+//config:config FEATURE_HTTPD_PROXY
+//config: bool "Support for reverse proxy"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: This option allows you to define URLs that will be forwarded
+//config: to another HTTP server. To setup add the following line to the
+//config: configuration file
+//config: P:/url/:http://hostname[:port]/new/path/
+//config: Then a request to /url/myfile will be forwarded to
+//config: http://hostname[:port]/new/path/myfile.
+//config:
+//config:config FEATURE_HTTPD_GZIP
+//config: bool "Support for GZIP content encoding"
+//config: default y
+//config: depends on HTTPD
+//config: help
+//config: Makes httpd send files using GZIP content encoding if the
+//config: client supports it and a pre-compressed <file>.gz exists.
+
+//applet:IF_HTTPD(APPLET(httpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_HTTPD) += httpd.o
//usage:#define httpd_trivial_usage
//usage: "[-ifv[v]]"
@@ -125,6 +247,7 @@
//usage: "\n -d STRING URL decode STRING"
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_PAM
/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
# undef setlocale
@@ -133,7 +256,7 @@
# include <security/pam_appl.h>
# include <security/pam_misc.h>
#endif
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
# include <sys/sendfile.h>
#endif
/* amount of buffering in a pipe */
@@ -307,7 +430,8 @@ struct globals {
Htaccess *script_i; /* config script interpreters */
#endif
char *iobuf; /* [IOBUF_SIZE] */
-#define hdr_buf bb_common_bufsiz1
+#define hdr_buf bb_common_bufsiz1
+#define sizeof_hdr_buf COMMON_BUFSIZE
char *hdr_ptr;
int hdr_cnt;
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
@@ -368,6 +492,7 @@ enum {
# define content_gzip 0
#endif
#define INIT_G() do { \
+ setup_common_bufsiz(); \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
IF_FEATURE_HTTPD_BASIC_AUTH(g_realm = "Web Server Authentication";) \
IF_FEATURE_HTTPD_RANGES(range_start = -1;) \
@@ -697,7 +822,7 @@ static void parse_conf(const char *path, int flag)
goto config_error;
}
*host_port++ = '\0';
- if (strncmp(host_port, "http://", 7) == 0)
+ if (is_prefixed_with(host_port, "http://"))
host_port += 7;
if (*host_port == '\0') {
goto config_error;
@@ -923,16 +1048,16 @@ static void log_and_exit(void)
static void send_headers(int responseNum)
{
static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
+ /* Fixed size 29-byte string. Example: Sun, 06 Nov 1994 08:49:37 GMT */
+ char date_str[40]; /* using a bit larger buffer to paranoia reasons */
const char *responseString = "";
const char *infoString = NULL;
- const char *mime_type;
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
const char *error_page = NULL;
#endif
unsigned i;
time_t timer = time(NULL);
- char tmp_str[80];
int len;
for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
@@ -945,41 +1070,61 @@ static void send_headers(int responseNum)
break;
}
}
- /* error message is HTML */
- mime_type = responseNum == HTTP_OK ?
- found_mime_type : "text/html";
if (verbose)
bb_error_msg("response:%u", responseNum);
- /* emit the current date */
- strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer));
+ /* We use sprintf, not snprintf (it's less code).
+ * iobuf[] is several kbytes long and all headers we generate
+ * always fit into those kbytes.
+ */
+
+ strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime(&timer));
len = sprintf(iobuf,
- "HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
- "Date: %s\r\nConnection: close\r\n",
- responseNum, responseString, mime_type, tmp_str);
+ "HTTP/1.0 %d %s\r\n"
+ "Content-type: %s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n",
+ responseNum, responseString,
+ /* if it's error message, then it's HTML */
+ (responseNum == HTTP_OK ? found_mime_type : "text/html"),
+ date_str
+ );
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
if (responseNum == HTTP_UNAUTHORIZED) {
len += sprintf(iobuf + len,
- "WWW-Authenticate: Basic realm=\"%s\"\r\n",
- g_realm);
+ "WWW-Authenticate: Basic realm=\"%.999s\"\r\n",
+ g_realm /* %.999s protects from overflowing iobuf[] */
+ );
}
#endif
if (responseNum == HTTP_MOVED_TEMPORARILY) {
- len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
+ /* Responding to "GET /dir" with
+ * "HTTP/1.0 302 Found" "Location: /dir/"
+ * - IOW, asking them to repeat with a slash.
+ * Here, overflow IS possible, can't use sprintf:
+ * mkdir test
+ * python -c 'print("get /test?" + ("x" * 8192))' | busybox httpd -i -h .
+ */
+ len += snprintf(iobuf + len, IOBUF_SIZE-3 - len,
+ "Location: %s/%s%s\r\n",
found_moved_temporarily,
(g_query ? "?" : ""),
- (g_query ? g_query : ""));
+ (g_query ? g_query : "")
+ );
+ if (len > IOBUF_SIZE-3)
+ len = IOBUF_SIZE-3;
}
#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
if (error_page && access(error_page, R_OK) == 0) {
- strcat(iobuf, "\r\n");
- len += 2;
-
- if (DEBUG)
+ iobuf[len++] = '\r';
+ iobuf[len++] = '\n';
+ if (DEBUG) {
+ iobuf[len] = '\0';
fprintf(stderr, "headers: '%s'\n", iobuf);
+ }
full_write(STDOUT_FILENO, iobuf, len);
if (DEBUG)
fprintf(stderr, "writing error page: '%s'\n", error_page);
@@ -988,13 +1133,15 @@ static void send_headers(int responseNum)
#endif
if (file_size != -1) { /* file */
- strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
+ strftime(date_str, sizeof(date_str), RFC1123FMT, gmtime(&last_mod));
#if ENABLE_FEATURE_HTTPD_RANGES
if (responseNum == HTTP_PARTIAL_CONTENT) {
- len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
+ len += sprintf(iobuf + len,
+ "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
range_start,
range_end,
- file_size);
+ file_size
+ );
file_size = range_end - range_start + 1;
}
#endif
@@ -1002,8 +1149,9 @@ static void send_headers(int responseNum)
#if ENABLE_FEATURE_HTTPD_RANGES
"Accept-Ranges: bytes\r\n"
#endif
- "Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
- tmp_str,
+ "Last-Modified: %s\r\n"
+ "%s %"OFF_FMT"u\r\n",
+ date_str,
content_gzip ? "Transfer-length:" : "Content-length:",
file_size
);
@@ -1017,12 +1165,18 @@ static void send_headers(int responseNum)
if (infoString) {
len += sprintf(iobuf + len,
"<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n"
- "<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n",
+ "<BODY><H1>%d %s</H1>\n"
+ "%s\n"
+ "</BODY></HTML>\n",
+ responseNum, responseString,
responseNum, responseString,
- responseNum, responseString, infoString);
+ infoString
+ );
}
- if (DEBUG)
+ if (DEBUG) {
+ iobuf[len] = '\0';
fprintf(stderr, "headers: '%s'\n", iobuf);
+ }
if (full_write(STDOUT_FILENO, iobuf, len) != len) {
if (verbose > 1)
bb_perror_msg("error");
@@ -1053,7 +1207,7 @@ static int get_line(void)
alarm(HEADER_READ_TIMEOUT);
while (1) {
if (hdr_cnt <= 0) {
- hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+ hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
if (hdr_cnt <= 0)
break;
hdr_ptr = hdr_buf;
@@ -1178,9 +1332,9 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
/* We expect data, prev data portion is eaten by CGI
* and there *is* data to read from the peer
* (POSTDATA) */
- //count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len;
+ //count = post_len > (int)sizeof_hdr_buf ? (int)sizeof_hdr_buf : post_len;
//count = safe_read(STDIN_FILENO, hdr_buf, count);
- count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+ count = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
if (count > 0) {
hdr_cnt = count;
hdr_ptr = hdr_buf;
@@ -1222,12 +1376,12 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
out_cnt += count;
count = 0;
/* "Status" header format is: "Status: 302 Redirected\r\n" */
- if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
+ if (out_cnt >= 7 && memcmp(rbuf, "Status:", 7) == 0) {
/* send "HTTP/1.0 " */
if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
break;
- rbuf += 8; /* skip "Status: " */
- count = out_cnt - 8;
+ rbuf += 7; /* skip "Status:" */
+ count = out_cnt - 7;
out_cnt = -1; /* buffering off */
} else if (out_cnt >= 4) {
/* Did CGI add "HTTP"? */
@@ -1624,7 +1778,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
#endif
if (what & SEND_HEADERS)
send_headers(HTTP_OK);
-#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+#if ENABLE_FEATURE_USE_SENDFILE
{
off_t offset = range_start;
while (1) {
@@ -1654,7 +1808,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
break;
}
if (count < 0) {
- IF_FEATURE_HTTPD_USE_SENDFILE(fin:)
+ IF_FEATURE_USE_SENDFILE(fin:)
if (verbose > 1)
bb_perror_msg("error");
}
@@ -1894,7 +2048,7 @@ static Htaccess_Proxy *find_proxy_entry(const char *url)
{
Htaccess_Proxy *p;
for (p = proxy; p; p = p->next) {
- if (strncmp(url, p->url_from, strlen(p->url_from)) == 0)
+ if (is_prefixed_with(url, p->url_from))
return p;
}
return NULL;
@@ -2183,7 +2337,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
if (STRNCASECMP(iobuf, "Range:") == 0) {
/* We know only bytes=NNN-[MMM] */
char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
- if (strncmp(s, "bytes=", 6) == 0) {
+ if (is_prefixed_with(s, "bytes=") == 0) {
s += sizeof("bytes=")-1;
range_start = BB_STRTOOFF(s, &s, 10);
if (s[0] != '-' || range_start < 0) {
@@ -2269,7 +2423,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
tptr = urlcopy + 1; /* skip first '/' */
#if ENABLE_FEATURE_HTTPD_CGI
- if (strncmp(tptr, "cgi-bin/", 8) == 0) {
+ if (is_prefixed_with(tptr, "cgi-bin/")) {
if (tptr[8] == '\0') {
/* protect listing "cgi-bin/" */
send_headers_and_exit(HTTP_FORBIDDEN);
@@ -2352,7 +2506,7 @@ static void mini_httpd(int server_socket)
continue;
/* set the KEEPALIVE option to cull dead connections */
- setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_keepalive(n);
if (fork() == 0) {
/* child */
@@ -2395,7 +2549,7 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
continue;
/* set the KEEPALIVE option to cull dead connections */
- setsockopt(n, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_keepalive(n);
if (vfork() == 0) {
/* child */
diff --git a/networking/ifconfig.c b/networking/ifconfig.c
index dcbb552..5fed60a 100644
--- a/networking/ifconfig.c
+++ b/networking/ifconfig.c
@@ -25,6 +25,57 @@
* 2002-04-20
* IPV6 support added by Bart Visscher <magick@linux-fan.com>
*/
+//config:config IFCONFIG
+//config: bool "ifconfig"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Ifconfig is used to configure the kernel-resident network interfaces.
+//config:
+//config:config FEATURE_IFCONFIG_STATUS
+//config: bool "Enable status reporting output (+7k)"
+//config: default y
+//config: depends on IFCONFIG
+//config: help
+//config: If ifconfig is called with no arguments it will display the status
+//config: of the currently active interfaces.
+//config:
+//config:config FEATURE_IFCONFIG_SLIP
+//config: bool "Enable slip-specific options \"keepalive\" and \"outfill\""
+//config: default y
+//config: depends on IFCONFIG
+//config: help
+//config: Allow "keepalive" and "outfill" support for SLIP. If you're not
+//config: planning on using serial lines, leave this unchecked.
+//config:
+//config:config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+//config: bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
+//config: default y
+//config: depends on IFCONFIG
+//config: help
+//config: Allow the start address for shared memory, start address for I/O,
+//config: and/or the interrupt line used by the specified device.
+//config:
+//config:config FEATURE_IFCONFIG_HW
+//config: bool "Enable option \"hw\" (ether only)"
+//config: default y
+//config: depends on IFCONFIG
+//config: help
+//config: Set the hardware address of this interface, if the device driver
+//config: supports this operation. Currently, we only support the 'ether'
+//config: class.
+//config:
+//config:config FEATURE_IFCONFIG_BROADCAST_PLUS
+//config: bool "Set the broadcast automatically"
+//config: default y
+//config: depends on IFCONFIG
+//config: help
+//config: Setting this will make ifconfig attempt to find the broadcast
+//config: automatically if the value '+' is used.
+
+//applet:IF_IFCONFIG(APPLET(ifconfig, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IFCONFIG) += ifconfig.o interface.o
//usage:#define ifconfig_trivial_usage
//usage: IF_FEATURE_IFCONFIG_STATUS("[-a]") " interface [address]"
diff --git a/networking/ifenslave.c b/networking/ifenslave.c
index c3be818..1cb765e 100644
--- a/networking/ifenslave.c
+++ b/networking/ifenslave.c
@@ -97,6 +97,17 @@
* - Code cleanup and style changes
* set version to 1.1.0
*/
+//config:config IFENSLAVE
+//config: bool "ifenslave"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Userspace application to bind several interfaces
+//config: to a logical interface (use with kernel bonding driver).
+
+//applet:IF_IFENSLAVE(APPLET(ifenslave, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IFENSLAVE) += ifenslave.o interface.o
//usage:#define ifenslave_trivial_usage
//usage: "[-cdf] MASTER_IFACE SLAVE_IFACE..."
@@ -577,8 +588,8 @@ int ifenslave_main(int argc UNUSED_PARAM, char **argv)
/* Can't work with this slave, */
/* remember the error and skip it */
bb_perror_msg(
- "skipping %s: can't get flags",
- slave_ifname);
+ "skipping %s: can't get %s",
+ slave_ifname, "flags");
res = rv;
continue;
}
@@ -595,8 +606,8 @@ int ifenslave_main(int argc UNUSED_PARAM, char **argv)
/* Can't work with this slave, */
/* remember the error and skip it */
bb_perror_msg(
- "skipping %s: can't get settings",
- slave_ifname);
+ "skipping %s: can't get %s",
+ slave_ifname, "settings");
res = rv;
continue;
}
diff --git a/networking/ifplugd.c b/networking/ifplugd.c
index b578f4c..4f8a274 100644
--- a/networking/ifplugd.c
+++ b/networking/ifplugd.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IFPLUGD
+//config: bool "ifplugd"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Network interface plug detection daemon.
+
+//applet:IF_IFPLUGD(APPLET(ifplugd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IFPLUGD) += ifplugd.o
//usage:#define ifplugd_trivial_usage
//usage: "[OPTIONS]"
@@ -38,7 +48,17 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#ifdef HAVE_NET_ETHERNET_H
-# include <net/ethernet.h>
+/* musl breakage:
+ * In file included from /usr/include/net/ethernet.h:10,
+ * from networking/ifplugd.c:41:
+ * /usr/include/netinet/if_ether.h:96: error: redefinition of 'struct ethhdr'
+ *
+ * Build succeeds without it on musl. Commented it out.
+ * If on your system you need it, consider removing <linux/ethtool.h>
+ * and copy-pasting its definitions here (<linux/ethtool.h> is what pulls in
+ * conflicting definition of struct ethhdr on musl).
+ */
+/* # include <net/ethernet.h> */
#endif
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
@@ -48,6 +68,10 @@
#define __user
#include <linux/wireless.h>
+#ifndef ETH_ALEN
+# define ETH_ALEN 6
+#endif
+
/*
From initial port to busybox, removed most of the redundancy by
converting implementation of a polymorphic interface to the strict
@@ -93,9 +117,9 @@ enum {
#endif
};
#if ENABLE_FEATURE_PIDFILE
-# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
+# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk"
#else
-# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
+# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M"
#endif
enum { // interface status
@@ -289,8 +313,6 @@ static const struct {
{ "IFF_RUNNING" , &detect_link_iff },
};
-
-
static const char *strstatus(int status)
{
if (status == IFSTATUS_ERR)
@@ -346,8 +368,12 @@ static void up_iface(void)
ifrequest.ifr_flags |= IFF_UP;
/* Let user know we mess up with interface */
bb_error_msg("upping interface");
- if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0)
- xfunc_die();
+ if (network_ioctl(SIOCSIFFLAGS, &ifrequest, "setting interface flags") < 0) {
+ if (errno != ENODEV)
+ xfunc_die();
+ G.iface_exists = 0;
+ return;
+ }
}
#if 0 /* why do we mess with IP addr? It's not our business */
@@ -548,7 +574,6 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
- opt_complementary = "t+:u+:d+";
opts = getopt32(argv, OPTION_STR,
&G.iface, &G.script_name, &G.poll_time, &G.delay_up,
&G.delay_down, &G.api_mode, &G.extra_arg);
@@ -652,7 +677,6 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
delay_time = 0;
while (1) {
int iface_status_old;
- int iface_exists_old;
switch (bb_got_signal) {
case SIGINT:
@@ -678,12 +702,12 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
goto exiting;
}
- iface_status_old = iface_status;
- iface_exists_old = G.iface_exists;
-
if ((opts & FLAG_MONITOR)
&& (netlink_pollfd[0].revents & POLLIN)
) {
+ int iface_exists_old;
+
+ iface_exists_old = G.iface_exists;
G.iface_exists = check_existence_through_netlink();
if (G.iface_exists < 0) /* error */
goto exiting;
@@ -696,6 +720,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
}
/* note: if !G.iface_exists, returns DOWN */
+ iface_status_old = iface_status;
iface_status = detect_link();
if (iface_status == IFSTATUS_ERR) {
if (!(opts & FLAG_MONITOR))
@@ -709,7 +734,7 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
if (delay_time) {
/* link restored its old status before
- * we run script. don't run the script: */
+ * we ran script. don't run the script: */
delay_time = 0;
} else {
delay_time = monotonic_sec();
@@ -717,15 +742,19 @@ int ifplugd_main(int argc UNUSED_PARAM, char **argv)
delay_time += G.delay_up;
if (iface_status == IFSTATUS_DOWN)
delay_time += G.delay_down;
- if (delay_time == 0)
- delay_time++;
+#if 0 /* if you are back in 1970... */
+ if (delay_time == 0) {
+ sleep(1);
+ delay_time = 1;
+ }
+#endif
}
}
if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
- delay_time = 0;
if (run_script(iface_status_str) != 0)
goto exiting;
+ delay_time = 0;
}
} /* while (1) */
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index 0f0857c..1806a6c 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -1,6 +1,6 @@
/* vi: set sw=4 ts=4: */
/*
- * ifupdown for busybox
+ * ifup/ifdown for busybox
* Copyright (c) 2002 Glenn McGrath
* Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
*
@@ -17,23 +17,118 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IFUP
+//config: bool "ifup"
+//config: default y
+//config: help
+//config: Activate the specified interfaces. This applet makes use
+//config: of either "ifconfig" and "route" or the "ip" command to actually
+//config: configure network interfaces. Therefore, you will probably also want
+//config: to enable either IFCONFIG and ROUTE, or enable
+//config: FEATURE_IFUPDOWN_IP and the various IP options. Of
+//config: course you could use non-busybox versions of these programs, so
+//config: against my better judgement (since this will surely result in plenty
+//config: of support questions on the mailing list), I do not force you to
+//config: enable these additional options. It is up to you to supply either
+//config: "ifconfig", "route" and "run-parts" or the "ip" command, either
+//config: via busybox or via standalone utilities.
+//config:
+//config:config IFDOWN
+//config: bool "ifdown"
+//config: default y
+//config: help
+//config: Deactivate the specified interfaces.
+//config:
+//config:config IFUPDOWN_IFSTATE_PATH
+//config: string "Absolute path to ifstate file"
+//config: default "/var/run/ifstate"
+//config: depends on IFUP || IFDOWN
+//config: help
+//config: ifupdown keeps state information in a file called ifstate.
+//config: Typically it is located in /var/run/ifstate, however
+//config: some distributions tend to put it in other places
+//config: (debian, for example, uses /etc/network/run/ifstate).
+//config: This config option defines location of ifstate.
+//config:
+//config:config FEATURE_IFUPDOWN_IP
+//config: bool "Use ip tool (else ifconfig/route is used)"
+//config: default y
+//config: depends on IFUP || IFDOWN
+//config: help
+//config: Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+//config: than the default of using the older "ifconfig" and "route" utilities.
+//config:
+//config: If Y: you must install either the full-blown iproute2 package
+//config: or enable "ip" applet in Busybox, or the "ifup" and "ifdown" applets
+//config: will not work.
+//config:
+//config: If N: you must install either the full-blown ifconfig and route
+//config: utilities, or enable these applets in Busybox.
+//config:
+//config:config FEATURE_IFUPDOWN_IPV4
+//config: bool "Support for IPv4"
+//config: default y
+//config: depends on IFUP || IFDOWN
+//config: help
+//config: If you want ifup/ifdown to talk IPv4, leave this on.
+//config:
+//config:config FEATURE_IFUPDOWN_IPV6
+//config: bool "Support for IPv6"
+//config: default y
+//config: depends on (IFUP || IFDOWN) && FEATURE_IPV6
+//config: help
+//config: If you need support for IPv6, turn this option on.
+//config:
+//UNUSED:
+////////:config FEATURE_IFUPDOWN_IPX
+////////: bool "Support for IPX"
+////////: default y
+////////: depends on IFUP || IFDOWN
+////////: help
+////////: If this option is selected you can use busybox to work with IPX
+////////: networks.
+//config:
+//config:config FEATURE_IFUPDOWN_MAPPING
+//config: bool "Enable mapping support"
+//config: default y
+//config: depends on IFUP || IFDOWN
+//config: help
+//config: This enables support for the "mapping" stanza, unless you have
+//config: a weird network setup you don't need it.
+//config:
+//config:config FEATURE_IFUPDOWN_EXTERNAL_DHCP
+//config: bool "Support for external dhcp clients"
+//config: default n
+//config: depends on IFUP || IFDOWN
+//config: help
+//config: This enables support for the external dhcp clients. Clients are
+//config: tried in the following order: dhcpcd, dhclient, pump and udhcpc.
+//config: Otherwise, if udhcpc applet is enabled, it is used.
+//config: Otherwise, ifup/ifdown will have no support for DHCP.
+
+//applet:IF_IFUP(APPLET_ODDNAME(ifup, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifup))
+//applet:IF_IFDOWN(APPLET_ODDNAME(ifdown, ifupdown, BB_DIR_SBIN, BB_SUID_DROP, ifdown))
+
+//kbuild:lib-$(CONFIG_IFUP) += ifupdown.o
+//kbuild:lib-$(CONFIG_IFDOWN) += ifupdown.o
+
//usage:#define ifup_trivial_usage
//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
//usage:#define ifup_full_usage "\n\n"
-//usage: " -a De/configure all interfaces automatically"
-//usage: "\n -i FILE Use FILE for interface definitions"
+//usage: " -a Configure all interfaces"
+//usage: "\n -i FILE Use FILE instead of /etc/network/interfaces"
//usage: "\n -n Print out what would happen, but don't do it"
//usage: IF_FEATURE_IFUPDOWN_MAPPING(
//usage: "\n (note: doesn't disable mappings)"
//usage: "\n -m Don't run any mappings"
//usage: )
//usage: "\n -v Print out what would happen before doing it"
-//usage: "\n -f Force de/configuration"
+//usage: "\n -f Force configuration"
//usage:
//usage:#define ifdown_trivial_usage
//usage: "[-an"IF_FEATURE_IFUPDOWN_MAPPING("m")"vf] [-i FILE] IFACE..."
//usage:#define ifdown_full_usage "\n\n"
-//usage: " -a De/configure all interfaces automatically"
+//usage: " -a Deconfigure all interfaces"
//usage: "\n -i FILE Use FILE for interface definitions"
//usage: "\n -n Print out what would happen, but don't do it"
//usage: IF_FEATURE_IFUPDOWN_MAPPING(
@@ -41,9 +136,10 @@
//usage: "\n -m Don't run any mappings"
//usage: )
//usage: "\n -v Print out what would happen before doing it"
-//usage: "\n -f Force de/configuration"
+//usage: "\n -f Force deconfiguration"
#include "libbb.h"
+#include "common_bufsiz.h"
/* After libbb.h, since it needs sys/types.h on some systems */
#include <sys/utsname.h>
#include <fnmatch.h>
@@ -55,6 +151,7 @@
#endif
#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
+#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH
#define debug_noise(args...) /*fprintf(stderr, args)*/
@@ -129,8 +226,8 @@ struct globals {
const char *startup_PATH;
char *shell;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static const char keywords_up_down[] ALIGN1 =
@@ -289,7 +386,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
/* "hwaddress <class> <address>":
* unlike ifconfig, ip doesnt want <class>
* (usually "ether" keyword). Skip it. */
- if (strncmp(command, "hwaddress", 9) == 0) {
+ if (is_prefixed_with(command, "hwaddress")) {
varvalue = skip_whitespace(skip_non_whitespace(varvalue));
}
# endif
@@ -298,7 +395,7 @@ static char *parse(const char *command, struct interface_defn_t *ifd)
# if ENABLE_FEATURE_IFUPDOWN_IP
/* Sigh... Add a special case for 'ip' to convert from
* dotted quad to bit count style netmasks. */
- if (strncmp(command, "bnmask", 6) == 0) {
+ if (is_prefixed_with(command, "bnmask")) {
unsigned res;
varvalue = get_var("netmask", 7, ifd);
if (varvalue) {
@@ -394,8 +491,8 @@ static int FAST_FUNC static_up6(struct interface_defn_t *ifd, execfn *exec)
# if ENABLE_FEATURE_IFUPDOWN_IP
result = execute("ip addr add %address%/%netmask% dev %iface%[[ label %label%]]", ifd, exec);
result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
- /* Was: "[[ ip ....%gateway% ]]". Removed extra spaces w/o checking */
- result += execute("[[ip route add ::/0 via %gateway%]][[ prio %metric%]]", ifd, exec);
+ /* Reportedly, IPv6 needs "dev %iface%", but IPv4 does not: */
+ result += execute("[[ip route add ::/0 via %gateway% dev %iface%]][[ metric %metric%]]", ifd, exec);
# else
result = execute("ifconfig %iface%[[ media %media%]][[ hw %hwaddress%]][[ mtu %mtu%]] up", ifd, exec);
result += execute("ifconfig %iface% add %address%/%netmask%", ifd, exec);
@@ -421,7 +518,8 @@ static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
"%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
result += execute("ip link set %iface% up", ifd, exec);
result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
- result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+ /* Reportedly, IPv6 needs "dev %iface%", but IPv4 does not: */
+ result += execute("[[ip route add ::/0 via %gateway% dev %iface%]]", ifd, exec);
return ((result == 4) ? 4 : 0);
}
@@ -482,7 +580,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
result = execute("ip addr add %address%/%bnmask%[[ broadcast %broadcast%]] "
"dev %iface%[[ peer %pointopoint%]][[ label %label%]]", ifd, exec);
result += execute("ip link set[[ mtu %mtu%]][[ addr %hwaddress%]] %iface% up", ifd, exec);
- result += execute("[[ip route add default via %gateway% dev %iface%[[ prio %metric%]]]]", ifd, exec);
+ result += execute("[[ip route add default via %gateway% dev %iface%[[ metric %metric%]]]]", ifd, exec);
return ((result == 3) ? 3 : 0);
# else
/* ifconfig said to set iface up before it processes hw %hwaddress%,
@@ -490,7 +588,7 @@ static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
result = execute("ifconfig %iface%[[ hw %hwaddress%]][[ media %media%]][[ mtu %mtu%]] up",
ifd, exec);
result += execute("ifconfig %iface% %address% netmask %netmask%"
- "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
+ "[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]]",
ifd, exec);
result += execute("[[route add default gw %gateway%[[ metric %metric%]] %iface%]]", ifd, exec);
return ((result == 3) ? 3 : 0);
@@ -501,7 +599,10 @@ static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
{
int result;
# if ENABLE_FEATURE_IFUPDOWN_IP
- result = execute("ip addr flush dev %iface%", ifd, exec);
+ /* Optional "label LBL" is necessary if interface is an alias (eth0:0),
+ * otherwise "ip addr flush dev eth0:0" flushes all addresses on eth0.
+ */
+ result = execute("ip addr flush dev %iface%[[ label %label%]]", ifd, exec);
result += execute("ip link set %iface% down", ifd, exec);
# else
/* result = execute("[[route del default gw %gateway% %iface%]]", ifd, exec); */
@@ -534,7 +635,7 @@ static const struct dhcp_client_t ext_dhcp_clients[] = {
"pump -i %iface% -k",
},
{ "udhcpc",
- "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -H %hostname%]][[ -c %client%]]"
+ "udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid -i %iface%[[ -x hostname:%hostname%]][[ -c %client%]]"
"[[ -s %script%]][[ %udhcpc_opts%]]",
"kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
},
@@ -555,7 +656,7 @@ static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
return 0;
# endif
for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
- if (exists_execable(ext_dhcp_clients[i].name))
+ if (executable_exists(ext_dhcp_clients[i].name))
return execute(ext_dhcp_clients[i].startcmd, ifd, exec);
}
bb_error_msg("no dhcp clients found");
@@ -574,7 +675,7 @@ static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
return 0;
# endif
return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
- "-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
+ "-i %iface%[[ -x hostname:%hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
ifd, exec);
}
# else
@@ -592,7 +693,7 @@ static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
unsigned i;
for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
- if (exists_execable(ext_dhcp_clients[i].name)) {
+ if (executable_exists(ext_dhcp_clients[i].name)) {
result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
if (result)
break;
@@ -685,6 +786,18 @@ static const struct address_family_t addr_inet = {
#endif /* FEATURE_IFUPDOWN_IPV4 */
+static int FAST_FUNC link_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
+{
+ return 1;
+}
+
+static const struct method_t link_methods[] = {
+ { "none", link_up_down, link_up_down }
+};
+
+static const struct address_family_t addr_link = {
+ "link", ARRAY_SIZE(link_methods), link_methods
+};
/* Returns pointer to the next word, or NULL.
* In 1st case, advances *buf to the word after this one.
@@ -831,12 +944,12 @@ static struct interfaces_file_t *read_interfaces(const char *filename, struct in
#if ENABLE_FEATURE_IFUPDOWN_IPV6
&addr_inet6,
#endif
+ &addr_link,
NULL
};
char *iface_name;
char *address_family_name;
char *method_name;
- llist_t *iface_list;
currif = xzalloc(sizeof(*currif));
iface_name = next_word(&rest_of_line);
@@ -861,7 +974,20 @@ static struct interfaces_file_t *read_interfaces(const char *filename, struct in
currif->method = get_method(currif->address_family, method_name);
if (!currif->method)
bb_error_msg_and_die("unknown method \"%s\"", method_name);
-
+#if 0
+// Allegedly, Debian allows a duplicate definition:
+// iface eth0 inet static
+// address 192.168.0.15
+// netmask 255.255.0.0
+// gateway 192.168.0.1
+//
+// iface eth0 inet static
+// address 10.0.0.1
+// netmask 255.255.255.0
+//
+// This adds *two* addresses to eth0 (probably requires use of "ip", not "ifconfig"
+//
+ llist_t *iface_list;
for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
if ((strcmp(tmp->iface, currif->iface) == 0)
@@ -870,6 +996,7 @@ static struct interfaces_file_t *read_interfaces(const char *filename, struct in
bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
}
}
+#endif
llist_add_to_end(&(defn->ifaces), (char*)currif);
debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
@@ -1038,6 +1165,11 @@ static int execute_all(struct interface_defn_t *ifd, const char *opt)
}
}
+ /* Tested on Debian Squeeze: "standard" ifup runs this without
+ * checking that directory exists. If it doesn't, run-parts
+ * complains, and this message _is_ annoyingly visible.
+ * Don't "fix" this (unless newer Debian does).
+ */
buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
/* heh, we don't bother free'ing it */
return doit(buf);
@@ -1146,12 +1278,12 @@ static char *run_mapping(char *physical, struct mapping_defn_t *map)
static llist_t *find_iface_state(llist_t *state_list, const char *iface)
{
- unsigned iface_len = strlen(iface);
llist_t *search = state_list;
while (search) {
- if ((strncmp(search->data, iface, iface_len) == 0)
- && (search->data[iface_len] == '=')
+ char *after_iface = is_prefixed_with(search->data, iface);
+ if (after_iface
+ && *after_iface == '='
) {
return search;
}
@@ -1164,7 +1296,7 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface)
static llist_t *read_iface_state(void)
{
llist_t *state_list = NULL;
- FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
+ FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH);
if (state_fp) {
char *start, *end_ptr;
@@ -1179,6 +1311,37 @@ static llist_t *read_iface_state(void)
return state_list;
}
+/* read the previous state from the state file */
+static FILE *open_new_state_file(void)
+{
+ int fd, flags, cnt;
+
+ cnt = 0;
+ flags = (O_WRONLY | O_CREAT | O_EXCL);
+ for (;;) {
+ fd = open(IFSTATE_FILE_PATH".new", flags, 0666);
+ if (fd >= 0)
+ break;
+ if (errno != EEXIST
+ || flags == (O_WRONLY | O_CREAT | O_TRUNC)
+ ) {
+ bb_perror_msg_and_die("can't open '%s'",
+ IFSTATE_FILE_PATH".new");
+ }
+ /* Someone else created the .new file */
+ if (cnt > 30 * 1000) {
+ /* Waited for 30*30/2 = 450 milliseconds, still EEXIST.
+ * Assuming a stale file, rewriting it.
+ */
+ flags = (O_WRONLY | O_CREAT | O_TRUNC);
+ continue;
+ }
+ usleep(cnt);
+ cnt += 1000;
+ }
+
+ return xfdopen_for_write(fd);
+}
int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ifupdown_main(int argc UNUSED_PARAM, char **argv)
@@ -1194,10 +1357,13 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
G.startup_PATH = getenv("PATH");
G.shell = xstrdup(get_shell_name());
- cmds = iface_down;
- if (applet_name[2] == 'u') {
+ if (ENABLE_IFUP
+ && (!ENABLE_IFDOWN || applet_name[2] == 'u')
+ ) {
/* ifup command */
cmds = iface_up;
+ } else {
+ cmds = iface_down;
}
getopt32(argv, OPTION_STR, &interfaces);
@@ -1226,6 +1392,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
char *pch;
bool okay = 0;
int cmds_ret;
+ bool curr_failure = 0;
iface = xstrdup(target_list->data);
target_list = target_list->link;
@@ -1291,11 +1458,11 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
/* Call the cmds function pointer, does either iface_up() or iface_down() */
cmds_ret = cmds(currif);
if (cmds_ret == -1) {
- bb_error_msg("don't seem to have all the variables for %s/%s",
+ bb_error_msg("don't have all variables for %s/%s",
liface, currif->address_family->name);
- any_failures = 1;
+ any_failures = curr_failure = 1;
} else if (cmds_ret == 0) {
- any_failures = 1;
+ any_failures = curr_failure = 1;
}
currif->iface = oldiface;
@@ -1311,12 +1478,12 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
any_failures = 1;
} else if (!NO_ACT) {
/* update the state file */
- FILE *state_fp;
+ FILE *new_state_fp = open_new_state_file();
llist_t *state;
llist_t *state_list = read_iface_state();
llist_t *iface_state = find_iface_state(state_list, iface);
- if (cmds == iface_up && !any_failures) {
+ if (cmds == iface_up && !curr_failure) {
char *newiface = xasprintf("%s=%s", iface, liface);
if (!iface_state) {
llist_add_to_end(&state_list, newiface);
@@ -1331,15 +1498,15 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
}
/* Actually write the new state */
- state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
state = state_list;
while (state) {
if (state->data) {
- fprintf(state_fp, "%s\n", state->data);
+ fprintf(new_state_fp, "%s\n", state->data);
}
state = state->link;
}
- fclose(state_fp);
+ fclose(new_state_fp);
+ xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
llist_free(state_list, free);
}
next:
diff --git a/networking/inetd.c b/networking/inetd.c
index aebb917..d03a305 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -153,6 +153,59 @@
* setgid(specified group)
* setuid()
*/
+//config:config INETD
+//config: bool "inetd"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: Internet superserver daemon
+//config:
+//config:config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+//config: bool "Support echo service"
+//config: default y
+//config: depends on INETD
+//config: help
+//config: Echo received data internal inetd service
+//config:
+//config:config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+//config: bool "Support discard service"
+//config: default y
+//config: depends on INETD
+//config: help
+//config: Internet /dev/null internal inetd service
+//config:
+//config:config FEATURE_INETD_SUPPORT_BUILTIN_TIME
+//config: bool "Support time service"
+//config: default y
+//config: depends on INETD
+//config: help
+//config: Return 32 bit time since 1900 internal inetd service
+//config:
+//config:config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+//config: bool "Support daytime service"
+//config: default y
+//config: depends on INETD
+//config: help
+//config: Return human-readable time internal inetd service
+//config:
+//config:config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+//config: bool "Support chargen service"
+//config: default y
+//config: depends on INETD
+//config: help
+//config: Familiar character generator internal inetd service
+//config:
+//config:config FEATURE_INETD_RPC
+//config: bool "Support RPC services"
+//config: default n # very rarely used, and needs Sun RPC support in libc
+//config: depends on INETD
+//config: select FEATURE_HAVE_RPC
+//config: help
+//config: Support Sun-RPC based services
+
+//applet:IF_INETD(APPLET(inetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_INETD) += inetd.o
//usage:#define inetd_trivial_usage
//usage: "[-fe] [-q N] [-R N] [CONFFILE]"
@@ -170,6 +223,7 @@
#include <sys/un.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_INETD_RPC
# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__)
@@ -327,11 +381,8 @@ struct globals {
/* Used in next_line(), and as scratch read buffer */
char line[256]; /* _at least_ 256, see LINE_SIZE */
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
#define rlim_ofile_cur (G.rlim_ofile_cur )
#define rlim_ofile (G.rlim_ofile )
#define serv_list (G.serv_list )
@@ -352,6 +403,8 @@ struct BUG_G_too_big {
#define allsock (G.allsock )
#define line (G.line )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
rlim_ofile_cur = OPEN_MAX; \
global_queuelen = 128; \
config_filename = "/etc/inetd.conf"; \
@@ -645,7 +698,7 @@ static servtab_t *dup_servtab(servtab_t *sep)
}
/* gcc generates much more code if this is inlined */
-static servtab_t *parse_one_line(void)
+static NOINLINE servtab_t *parse_one_line(void)
{
int argc;
char *token[6+MAXARGV];
@@ -675,6 +728,8 @@ static servtab_t *parse_one_line(void)
* default host for the following lines. */
free(default_local_hostname);
default_local_hostname = sep->se_local_hostname;
+ /*sep->se_local_hostname = NULL; - redundant */
+ /* (we'll overwrite this field anyway) */
goto more;
}
} else
@@ -688,10 +743,10 @@ static servtab_t *parse_one_line(void)
parse_err:
bb_error_msg("parse error on line %u, line is ignored",
parser->lineno);
- free_servtab_strings(sep);
/* Just "goto more" can make sep to carry over e.g.
* "rpc"-ness (by having se_rpcver_lo != 0).
* We will be more paranoid: */
+ free_servtab_strings(sep);
free(sep);
goto new;
}
@@ -725,7 +780,7 @@ static servtab_t *parse_one_line(void)
goto parse_err;
#endif
}
- if (strncmp(arg, "rpc/", 4) == 0) {
+ if (is_prefixed_with(arg, "rpc/")) {
#if ENABLE_FEATURE_INETD_RPC
unsigned n;
arg += 4;
@@ -815,7 +870,7 @@ static servtab_t *parse_one_line(void)
}
#endif
argc = 0;
- while ((arg = token[6+argc]) != NULL && argc < MAXARGV)
+ while (argc < MAXARGV && (arg = token[6+argc]) != NULL)
sep->se_argv[argc++] = xstrdup(arg);
/* Some inetd.conf files have no argv's, not even argv[0].
* Fix them up.
@@ -834,10 +889,10 @@ static servtab_t *parse_one_line(void)
goto parse_err;
}
-// bb_info_msg(
-// "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
-// sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
-// sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
+ //bb_error_msg(
+ // "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
+ // sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
+ // sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
/* check if the hostname specifier is a comma separated list
* of hostnames. we'll make new entries for each address. */
@@ -1151,8 +1206,8 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
if (real_uid != 0) /* run by non-root user */
config_filename = NULL;
- opt_complementary = "R+:q+"; /* -q N, -R N */
- opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
+ /* -q N, -R N */
+ opt = getopt32(argv, "R:+feq:+", &max_concurrency, &global_queuelen);
argv += optind;
//argc -= optind;
if (argv[0])
@@ -1653,7 +1708,7 @@ static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
{
time_t t;
- t = time(NULL);
+ time(&t);
fdprintf(s, "%.24s\r\n", ctime(&t));
}
static void FAST_FUNC daytime_dg(int s, servtab_t *sep)
diff --git a/networking/interface.c b/networking/interface.c
index 8e95d94..9246132 100644
--- a/networking/interface.c
+++ b/networking/interface.c
@@ -89,13 +89,9 @@ struct in6_ifreq {
/* Display an Internet socket address. */
static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric)
{
- static char *buff; /* defaults to NULL */
-
- free(buff);
if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
return "[NONE SET]";
- buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00);
- return buff;
+ return auto_string(INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00));
}
#ifdef UNUSED_AND_BUGGY
@@ -171,13 +167,9 @@ static const struct aftype inet_aftype = {
/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric)
{
- static char *buff;
-
- free(buff);
if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
return "[NONE SET]";
- buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric);
- return buff;
+ return auto_string(INET6_rresolve((struct sockaddr_in6 *) sap, numeric));
}
#ifdef UNUSED
@@ -223,13 +215,11 @@ static const struct aftype inet6_aftype = {
/* Display an UNSPEC address. */
static char* FAST_FUNC UNSPEC_print(unsigned char *ptr)
{
- static char *buff;
-
+ char *buff;
char *pos;
unsigned int i;
- if (!buff)
- buff = xmalloc(sizeof(struct sockaddr) * 3 + 1);
+ buff = auto_string(xmalloc(sizeof(struct sockaddr) * 3 + 1));
pos = buff;
for (i = 0; i < sizeof(struct sockaddr); i++) {
/* careful -- not every libc's sprintf returns # bytes written */
@@ -274,7 +264,7 @@ const struct aftype* FAST_FUNC get_aftype(const char *name)
afp = aftypes;
while (*afp != NULL) {
- if (!strcmp((*afp)->name, name))
+ if (strcmp((*afp)->name, name) == 0)
return (*afp);
afp++;
}
@@ -582,7 +572,7 @@ static int if_readlist_proc(char *target)
ife = add_interface(name);
get_dev_fields(s, ife, procnetdev_vsn);
ife->statistics_valid = 1;
- if (target && !strcmp(target, name))
+ if (target && strcmp(target, name) == 0)
break;
}
if (ferror(fh)) {
@@ -712,14 +702,12 @@ static const struct hwtype loop_hwtype = {
/* Display an Ethernet address in readable format. */
static char* FAST_FUNC ether_print(unsigned char *ptr)
{
- static char *buff;
-
- free(buff);
+ char *buff;
buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
(ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
(ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
);
- return buff;
+ return auto_string(buff);
}
static const struct hwtype ether_hwtype = {
@@ -793,7 +781,7 @@ const struct hwtype* FAST_FUNC get_hwtype(const char *name)
hwp = hwtypes;
while (*hwp != NULL) {
- if (!strcmp((*hwp)->name, name))
+ if (strcmp((*hwp)->name, name) == 0)
return (*hwp);
hwp++;
}
@@ -889,10 +877,11 @@ static void ife_print6(struct interface *ptr)
addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
&dad_status, devname) != EOF
) {
- if (!strcmp(devname, ptr->name)) {
+ if (strcmp(devname, ptr->name) == 0) {
sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
addr6p[0], addr6p[1], addr6p[2], addr6p[3],
addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ memset(&sap, 0, sizeof(sap));
inet_pton(AF_INET6, addr6,
(struct sockaddr *) &sap.sin6_addr);
sap.sin6_family = AF_INET6;
diff --git a/networking/ip.c b/networking/ip.c
index 98fe621..939721e 100644
--- a/networking/ip.c
+++ b/networking/ip.c
@@ -8,6 +8,137 @@
* Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
* Bernhard Reutner-Fischer rewrote to use index_in_substr_array
*/
+//config:config IP
+//config: bool "ip"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The "ip" applet is a TCP/IP interface configuration and routing
+//config: utility. You generally don't need "ip" to use busybox with
+//config: TCP/IP.
+//config:
+//config:config IPADDR
+//config: bool "ipaddr"
+//config: default y
+//config: select FEATURE_IP_ADDRESS
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip addr: ipaddr
+//config:
+//config:config IPLINK
+//config: bool "iplink"
+//config: default y
+//config: select FEATURE_IP_LINK
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip link: iplink
+//config:
+//config:config IPROUTE
+//config: bool "iproute"
+//config: default y
+//config: select FEATURE_IP_ROUTE
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip route: iproute
+//config:
+//config:config IPTUNNEL
+//config: bool "iptunnel"
+//config: default y
+//config: select FEATURE_IP_TUNNEL
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip tunnel: iptunnel
+//config:
+//config:config IPRULE
+//config: bool "iprule"
+//config: default y
+//config: select FEATURE_IP_RULE
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip rule: iprule
+//config:
+//config:config IPNEIGH
+//config: bool "ipneigh"
+//config: default y
+//config: select FEATURE_IP_NEIGH
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support short form of ip neigh: ipneigh
+//config:
+//config:config FEATURE_IP_ADDRESS
+//config: bool "ip address"
+//config: default y
+//config: depends on IP || IPADDR
+//config: help
+//config: Address manipulation support for the "ip" applet.
+//config:
+//config:config FEATURE_IP_LINK
+//config: bool "ip link"
+//config: default y
+//config: depends on IP || IPLINK
+//config: help
+//config: Configure network devices with "ip".
+//config:
+//config:config FEATURE_IP_ROUTE
+//config: bool "ip route"
+//config: default y
+//config: depends on IP || IPROUTE
+//config: help
+//config: Add support for routing table management to "ip".
+//config:
+//config:config FEATURE_IP_ROUTE_DIR
+//config: string "ip route configuration directory"
+//config: default "/etc/iproute2"
+//config: depends on FEATURE_IP_ROUTE
+//config: help
+//config: Location of the "ip" applet routing configuration.
+//config:
+//config:config FEATURE_IP_TUNNEL
+//config: bool "ip tunnel"
+//config: default y
+//config: depends on IP || IPTUNNEL
+//config: help
+//config: Add support for tunneling commands to "ip".
+//config:
+//config:config FEATURE_IP_RULE
+//config: bool "ip rule"
+//config: default y
+//config: depends on IP || IPRULE
+//config: help
+//config: Add support for rule commands to "ip".
+//config:
+//config:config FEATURE_IP_NEIGH
+//config: bool "ip neighbor"
+//config: default y
+//config: depends on IP || IPNEIGH
+//config: help
+//config: Add support for neighbor commands to "ip".
+//config:
+//config:config FEATURE_IP_RARE_PROTOCOLS
+//config: bool "Support displaying rarely used link types"
+//config: default n
+//config: depends on IP || IPADDR || IPLINK || IPROUTE || IPTUNNEL || IPRULE || IPNEIGH
+//config: help
+//config: If you are not going to use links of type "frad", "econet",
+//config: "bif" etc, you probably don't need to enable this.
+//config: Ethernet, wireless, infrared, ppp/slip, ip tunnelling
+//config: link types are supported without this option selected.
+
+//applet:IF_IP(APPLET(ip, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPADDR(APPLET(ipaddr, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPLINK(APPLET(iplink, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPROUTE(APPLET(iproute, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPRULE(APPLET(iprule, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPTUNNEL(APPLET(iptunnel, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_IPNEIGH(APPLET(ipneigh, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IP) += ip.o
+//kbuild:lib-$(CONFIG_IPADDR) += ip.o
+//kbuild:lib-$(CONFIG_IPLINK) += ip.o
+//kbuild:lib-$(CONFIG_IPROUTE) += ip.o
+//kbuild:lib-$(CONFIG_IPRULE) += ip.o
+//kbuild:lib-$(CONFIG_IPTUNNEL) += ip.o
+//kbuild:lib-$(CONFIG_IPNEIGH) += ip.o
/* would need to make the " | " optional depending on more than one selected: */
//usage:#define ip_trivial_usage
@@ -16,6 +147,7 @@
//usage: IF_FEATURE_IP_ROUTE("route | ")
//usage: IF_FEATURE_IP_LINK("link | ")
//usage: IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage: IF_FEATURE_IP_NEIGH("neigh | ")
//usage: IF_FEATURE_IP_RULE("rule")
//usage: "} {COMMAND}"
//usage:#define ip_full_usage "\n\n"
@@ -25,6 +157,7 @@
//usage: IF_FEATURE_IP_ROUTE("route | ")
//usage: IF_FEATURE_IP_LINK("link | ")
//usage: IF_FEATURE_IP_TUNNEL("tunnel | ")
+//usage: IF_FEATURE_IP_NEIGH("neigh | ")
//usage: IF_FEATURE_IP_RULE("rule")
//usage: "}\n"
//usage: "OPTIONS := { -f[amily] { inet | inet6 | link } | -o[neline] }"
@@ -33,7 +166,7 @@
//usage: "{ {add|del} IFADDR dev STRING | {show|flush}\n"
//usage: " [dev STRING] [to PREFIX] }"
//usage:#define ipaddr_full_usage "\n\n"
-//usage: "ipaddr {add|delete} IFADDR dev STRING\n"
+//usage: "ipaddr {add|change|replace|delete} IFADDR dev STRING\n"
//usage: "ipaddr {show|flush} [dev STRING] [scope SCOPE-ID]\n"
//usage: " [to PREFIX] [label PATTERN]\n"
//usage: " IFADDR := PREFIX | ADDR peer PREFIX\n"
@@ -80,72 +213,85 @@
//usage: " [mode { ipip | gre | sit }] [remote ADDR] [local ADDR]\n"
//usage: " [[i|o]seq] [[i|o]key KEY] [[i|o]csum]\n"
//usage: " [ttl TTL] [tos TOS] [[no]pmtudisc] [dev PHYS_DEV]"
+//usage:
+//usage:#define ipneigh_trivial_usage
+//usage: "{ show | flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]"
+//usage:#define ipneigh_full_usage "\n\n"
+//usage: "ipneigh { show | flush} [ to PREFIX ] [ dev DEV ] [ nud STATE ]"
#include "libbb.h"
#include "libiproute/utils.h"
#include "libiproute/ip_common.h"
-#if ENABLE_FEATURE_IP_ADDRESS \
- || ENABLE_FEATURE_IP_ROUTE \
- || ENABLE_FEATURE_IP_LINK \
- || ENABLE_FEATURE_IP_TUNNEL \
- || ENABLE_FEATURE_IP_RULE
-
-static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM)
-{
- bb_show_usage();
-}
-
typedef int FAST_FUNC (*ip_func_ptr_t)(char**);
+#if ENABLE_IPADDR \
+ || ENABLE_IPLINK \
+ || ENABLE_IPROUTE \
+ || ENABLE_IPRULE \
+ || ENABLE_IPTUNNEL \
+ || ENABLE_IPNEIGH
static int ip_do(ip_func_ptr_t ip_func, char **argv)
{
argv = ip_parse_common_args(argv + 1);
return ip_func(argv);
}
+#endif
-#if ENABLE_FEATURE_IP_ADDRESS
+#if ENABLE_IPADDR
int ipaddr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ipaddr_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_ipaddr, argv);
}
#endif
-#if ENABLE_FEATURE_IP_LINK
+#if ENABLE_IPLINK
int iplink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int iplink_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_iplink, argv);
}
#endif
-#if ENABLE_FEATURE_IP_ROUTE
+#if ENABLE_IPROUTE
int iproute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int iproute_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_iproute, argv);
}
#endif
-#if ENABLE_FEATURE_IP_RULE
+#if ENABLE_IPRULE
int iprule_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int iprule_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_iprule, argv);
}
#endif
-#if ENABLE_FEATURE_IP_TUNNEL
+#if ENABLE_IPTUNNEL
int iptunnel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int iptunnel_main(int argc UNUSED_PARAM, char **argv)
{
return ip_do(do_iptunnel, argv);
}
#endif
+#if ENABLE_IPNEIGH
+int ipneigh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipneigh_main(int argc UNUSED_PARAM, char **argv)
+{
+ return ip_do(do_ipneigh, argv);
+}
+#endif
+#if ENABLE_IP
+static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM)
+{
+ bb_show_usage();
+}
int ip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ip_main(int argc UNUSED_PARAM, char **argv)
{
- static const char keywords[] ALIGN1 =
+ static const char keywords[] ALIGN1 = ""
IF_FEATURE_IP_ADDRESS("address\0")
IF_FEATURE_IP_ROUTE("route\0")
IF_FEATURE_IP_ROUTE("r\0")
@@ -153,6 +299,7 @@ int ip_main(int argc UNUSED_PARAM, char **argv)
IF_FEATURE_IP_TUNNEL("tunnel\0")
IF_FEATURE_IP_TUNNEL("tunl\0")
IF_FEATURE_IP_RULE("rule\0")
+ IF_FEATURE_IP_NEIGH("neigh\0")
;
static const ip_func_ptr_t ip_func_ptrs[] = {
ip_print_help,
@@ -163,15 +310,18 @@ int ip_main(int argc UNUSED_PARAM, char **argv)
IF_FEATURE_IP_TUNNEL(do_iptunnel,)
IF_FEATURE_IP_TUNNEL(do_iptunnel,)
IF_FEATURE_IP_RULE(do_iprule,)
+ IF_FEATURE_IP_NEIGH(do_ipneigh,)
};
ip_func_ptr_t ip_func;
int key;
argv = ip_parse_common_args(argv + 1);
- key = *argv ? index_in_substrings(keywords, *argv++) : -1;
+ if (ARRAY_SIZE(ip_func_ptrs) > 1 && *argv)
+ key = index_in_substrings(keywords, *argv++);
+ else
+ key = -1;
ip_func = ip_func_ptrs[key + 1];
return ip_func(argv);
}
-
-#endif /* any of ENABLE_FEATURE_IP_xxx is 1 */
+#endif
diff --git a/networking/ipcalc.c b/networking/ipcalc.c
index 3c8b8bf..2121942 100644
--- a/networking/ipcalc.c
+++ b/networking/ipcalc.c
@@ -11,9 +11,35 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IPCALC
+//config: bool "ipcalc"
+//config: default y
+//config: help
+//config: ipcalc takes an IP address and netmask and calculates the
+//config: resulting broadcast, network, and host range.
+//config:
+//config:config FEATURE_IPCALC_FANCY
+//config: bool "Fancy IPCALC, more options, adds 1 kbyte"
+//config: default y
+//config: depends on IPCALC
+//config: help
+//config: Adds the options hostname, prefix and silent to the output of
+//config: "ipcalc".
+//config:
+//config:config FEATURE_IPCALC_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on IPCALC && LONG_OPTS
+//config: help
+//config: Support long options for the ipcalc applet.
+
+//applet:IF_IPCALC(APPLET(ipcalc, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IPCALC) += ipcalc.o
//usage:#define ipcalc_trivial_usage
-//usage: "[OPTIONS] ADDRESS[[/]NETMASK] [NETMASK]"
+//usage: "[OPTIONS] ADDRESS"
+//usage: IF_FEATURE_IPCALC_FANCY("[/PREFIX]") " [NETMASK]"
//usage:#define ipcalc_full_usage "\n\n"
//usage: "Calculate IP network settings from a IP address\n"
//usage: IF_FEATURE_IPCALC_LONG_OPTIONS(
diff --git a/networking/isrv.c b/networking/isrv.c
index 1c6491e..3673db7 100644
--- a/networking/isrv.c
+++ b/networking/isrv.c
@@ -194,7 +194,6 @@ static void handle_accept(isrv_state_t *state, int fd)
remove_peer(state, n); /* unsuccesful peer start */
}
-void BUG_sizeof_fd_set_is_strange(void);
static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
{
enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
@@ -203,8 +202,7 @@ static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void *
/* need to know value at _the beginning_ of this routine */
int fd_cnt = FD_COUNT;
- if (LONG_CNT * sizeof(long) != sizeof(fd_set))
- BUG_sizeof_fd_set_is_strange();
+ BUILD_BUG_ON(LONG_CNT * sizeof(long) != sizeof(fd_set));
fds_pos = 0;
while (1) {
diff --git a/networking/isrv.h b/networking/isrv.h
index 4c7e01d..7b12e0e 100644
--- a/networking/isrv.h
+++ b/networking/isrv.h
@@ -23,7 +23,31 @@ int isrv_register_fd(isrv_state_t *state, int peer, int fd);
void isrv_close_fd(isrv_state_t *state, int fd);
int isrv_register_peer(isrv_state_t *state, void *param);
-/* driver */
+/* Driver:
+ *
+ * Select on listen_fd for <linger_timeout> (or forever if 0).
+ *
+ * If we time out and we have no peers, exit.
+ * If we have peers, call do_timeout(peer_param),
+ * if it returns !0, peer is removed.
+ *
+ * If listen_fd is active, accept new connection ("peer"),
+ * call new_peer() on it, and if it returns 0,
+ * add it to fds to select on.
+ * Now, select will wait for <timeout>, not <linger_timeout>
+ * (as long as we have more than zero peers).
+ *
+ * If a peer's fd is active, we call do_rd() on it if read
+ * bit was set, and then do_wr() if write bit was also set.
+ * If either returns !0, peer is removed.
+ * Reaching this place also resets timeout counter for this peer.
+ *
+ * Note that peer must indicate that he wants to be selected
+ * for read and/or write using isrv_want_rd()/isrv_want_wr()
+ * [can be called in new_peer() or in do_rd()/do_wr()].
+ * If it never wants to be selected for write, do_wr()
+ * will never be called (can be NULL).
+ */
void isrv_run(
int listen_fd,
int (*new_peer)(isrv_state_t *state, int fd),
diff --git a/networking/isrv_identd.c b/networking/isrv_identd.c
index a41405c..219c64b 100644
--- a/networking/isrv_identd.c
+++ b/networking/isrv_identd.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FAKEIDENTD
+//config: bool "fakeidentd"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: fakeidentd listens on the ident port and returns a predefined
+//config: fake value on any query.
+
+//applet:IF_FAKEIDENTD(APPLET(fakeidentd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FAKEIDENTD) += isrv_identd.o isrv.o
//usage:#define fakeidentd_trivial_usage
//usage: "[-fiw] [-b ADDR] [STRING]"
@@ -18,6 +29,7 @@
//usage: "\n STRING Ident answer string (default: nobody)"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
#include "isrv.h"
@@ -25,8 +37,7 @@ enum { TIMEOUT = 20 };
typedef struct identd_buf_t {
int pos;
- int fd_flag;
- char buf[64 - 2*sizeof(int)];
+ char buf[64 - sizeof(int)];
} identd_buf_t;
#define bogouser bb_common_bufsiz1
@@ -42,7 +53,7 @@ static int new_peer(isrv_state_t *state, int fd)
if (isrv_register_fd(state, peer, fd) < 0)
return peer; /* failure, unregister peer */
- buf->fd_flag = fcntl(fd, F_GETFL) | O_NONBLOCK;
+ ndelay_on(fd);
isrv_want_rd(state, fd);
return 0;
}
@@ -51,19 +62,16 @@ static int do_rd(int fd, void **paramp)
{
identd_buf_t *buf = *paramp;
char *cur, *p;
- int retval = 0; /* session is ok (so far) */
int sz;
cur = buf->buf + buf->pos;
- if (buf->fd_flag & O_NONBLOCK)
- fcntl(fd, F_SETFL, buf->fd_flag);
- sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+ sz = safe_read(fd, cur, sizeof(buf->buf) - 1 - buf->pos);
if (sz < 0) {
if (errno != EAGAIN)
- goto term; /* terminate this session if !EAGAIN */
- goto ok;
+ goto term;
+ return 0; /* "session is ok" */
}
buf->pos += sz;
@@ -71,19 +79,22 @@ static int do_rd(int fd, void **paramp)
p = strpbrk(cur, "\r\n");
if (p)
*p = '\0';
- if (!p && sz && buf->pos <= (int)sizeof(buf->buf))
- goto ok;
+ if (!p && sz)
+ return 0; /* "session is ok" */
+
/* Terminate session. If we are in server mode, then
* fd is still in nonblocking mode - we never block here */
- if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
+ if (fd == 0)
+ fd++; /* inetd mode? then write to fd 1 */
fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ /*
+ * Why bother if we are going to close fd now anyway?
+ * if (server)
+ * ndelay_off(fd);
+ */
term:
free(buf);
- retval = 1; /* terminate */
- ok:
- if (buf->fd_flag & O_NONBLOCK)
- fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
- return retval;
+ return 1; /* "terminate" */
}
static int do_timeout(void **paramp UNUSED_PARAM)
@@ -95,10 +106,9 @@ static void inetd_mode(void)
{
identd_buf_t *buf = xzalloc(sizeof(*buf));
/* buf->pos = 0; - xzalloc did it */
- /* We do NOT want nonblocking I/O here! */
- /* buf->fd_flag = 0; - xzalloc did it */
do
alarm(TIMEOUT);
+ /* Note: we do NOT want nonblocking I/O here! */
while (do_rd(0, (void*)&buf) == 0);
}
@@ -117,10 +127,12 @@ int fakeidentd_main(int argc UNUSED_PARAM, char **argv)
unsigned opt;
int fd;
+ setup_common_bufsiz();
+
opt = getopt32(argv, "fiwb:", &bind_address);
strcpy(bogouser, "nobody");
if (argv[optind])
- strncpy(bogouser, argv[optind], sizeof(bogouser));
+ strncpy(bogouser, argv[optind], COMMON_BUFSIZE - 1);
/* Daemonize if no -f and no -i and no -w */
if (!(opt & OPT_fiw))
diff --git a/networking/libiproute/Kbuild.src b/networking/libiproute/Kbuild.src
index 7c78f3c..056a585 100644
--- a/networking/libiproute/Kbuild.src
+++ b/networking/libiproute/Kbuild.src
@@ -62,5 +62,16 @@ lib-$(CONFIG_FEATURE_IP_TUNNEL) += \
lib-$(CONFIG_FEATURE_IP_RULE) += \
ip_parse_common_args.o \
iprule.o \
+ libnetlink.o \
+ rt_names.o \
+ rtm_map.o \
+ utils.o
+
+lib-$(CONFIG_FEATURE_IP_NEIGH) += \
+ ip_parse_common_args.o \
+ ipneigh.o \
+ libnetlink.o \
+ ll_addr.o \
+ ll_map.o \
rt_names.o \
utils.o
diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h
index 30c7e59..40171be 100644
--- a/networking/libiproute/ip_common.h
+++ b/networking/libiproute/ip_common.h
@@ -24,7 +24,7 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush);
int FAST_FUNC do_ipaddr(char **argv);
int FAST_FUNC do_iproute(char **argv);
int FAST_FUNC do_iprule(char **argv);
-//int FAST_FUNC do_ipneigh(char **argv);
+int FAST_FUNC do_ipneigh(char **argv);
int FAST_FUNC do_iptunnel(char **argv);
int FAST_FUNC do_iplink(char **argv);
//int FAST_FUNC do_ipmonitor(char **argv);
diff --git a/networking/libiproute/ip_parse_common_args.c b/networking/libiproute/ip_parse_common_args.c
index 59c759b..1a298f7 100644
--- a/networking/libiproute/ip_parse_common_args.c
+++ b/networking/libiproute/ip_parse_common_args.c
@@ -67,7 +67,7 @@ char** FAST_FUNC ip_parse_common_args(char **argv)
bb_show_usage();
arg = index_in_strings(families, *argv);
if (arg < 0)
- invarg(*argv, "protocol family");
+ invarg_1_to_2(*argv, "family");
/* now arg == 0, 1 or 2 */
} else {
arg -= ARG_IPv4;
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
index 1058b5a..21465dc 100644
--- a/networking/libiproute/ipaddress.c
+++ b/networking/libiproute/ipaddress.c
@@ -13,6 +13,7 @@
#include <net/if_arp.h>
#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "common_bufsiz.h"
#include "rt_names.h"
#include "utils.h"
@@ -39,8 +40,8 @@ struct filter_t {
} FIX_ALIASING;
typedef struct filter_t filter_t;
-#define G_filter (*(filter_t*)&bb_common_bufsiz1)
-
+#define G_filter (*(filter_t*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void print_link_flags(unsigned flags, unsigned mdown)
{
@@ -137,12 +138,11 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
{
unsigned m_flag = 0;
if (tb[IFLA_LINK]) {
- SPRINT_BUF(b1);
int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
if (iflink == 0)
printf("@NONE: ");
else {
- printf("@%s: ", ll_idx_n2a(iflink, b1));
+ printf("@%s: ", ll_index_to_name(iflink));
m_flag = ll_index_to_flags(iflink);
m_flag = !(m_flag & IFF_UP);
}
@@ -158,8 +158,7 @@ static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
printf("qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
#ifdef IFLA_MASTER
if (tb[IFLA_MASTER]) {
- SPRINT_BUF(b1);
- printf("master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
+ printf("master %s ", ll_index_to_name(*(int*)RTA_DATA(tb[IFLA_MASTER])));
}
#endif
/* IFLA_OPERSTATE was added to kernel with the same commit as IFF_DORMANT */
@@ -216,9 +215,7 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
{
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
- struct rtattr * rta_tb[IFA_MAX+1];
- char abuf[256];
- SPRINT_BUF(b1);
+ struct rtattr *rta_tb[IFA_MAX+1];
if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
return 0;
@@ -250,7 +247,7 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
if (rta_tb[IFA_LABEL])
label = RTA_DATA(rta_tb[IFA_LABEL]);
else
- label = ll_idx_n2a(ifa->ifa_index, b1);
+ label = ll_index_to_name(ifa->ifa_index);
if (fnmatch(G_filter.label, label, 0) != 0)
return 0;
}
@@ -294,9 +291,9 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
printf(" family %d ", ifa->ifa_family);
if (rta_tb[IFA_LOCAL]) {
- fputs(rt_addr_n2a(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_LOCAL]),
- abuf, sizeof(abuf)), stdout);
+ fputs(rt_addr_n2a(ifa->ifa_family, RTA_DATA(rta_tb[IFA_LOCAL])),
+ stdout
+ );
if (rta_tb[IFA_ADDRESS] == NULL
|| memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0
@@ -304,28 +301,25 @@ static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
printf("/%d ", ifa->ifa_prefixlen);
} else {
printf(" peer %s/%d ",
- rt_addr_n2a(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_ADDRESS]),
- abuf, sizeof(abuf)),
- ifa->ifa_prefixlen);
+ rt_addr_n2a(ifa->ifa_family, RTA_DATA(rta_tb[IFA_ADDRESS])),
+ ifa->ifa_prefixlen
+ );
}
}
if (rta_tb[IFA_BROADCAST]) {
printf("brd %s ",
rt_addr_n2a(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_BROADCAST]),
- abuf, sizeof(abuf))
+ RTA_DATA(rta_tb[IFA_BROADCAST]))
);
}
if (rta_tb[IFA_ANYCAST]) {
printf("any %s ",
rt_addr_n2a(ifa->ifa_family,
- RTA_DATA(rta_tb[IFA_ANYCAST]),
- abuf, sizeof(abuf))
+ RTA_DATA(rta_tb[IFA_ANYCAST]))
);
}
- printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1));
+ printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope));
if (ifa->ifa_flags & IFA_F_SECONDARY) {
ifa->ifa_flags &= ~IFA_F_SECONDARY;
printf("secondary ");
@@ -458,7 +452,7 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush)
G_filter.scopemask = -1;
if (rtnl_rtscope_a2n(&scope, *argv)) {
if (strcmp(*argv, "all") != 0) {
- invarg(*argv, "scope");
+ invarg_1_to_2(*argv, "scope");
}
scope = RT_SCOPE_NOWHERE;
G_filter.scopemask = 0;
@@ -556,12 +550,11 @@ int FAST_FUNC ipaddr_list_or_flush(char **argv, int flush)
continue;
}
if (G_filter.label) {
- SPRINT_BUF(b1);
const char *label;
if (tb[IFA_LABEL])
label = RTA_DATA(tb[IFA_LABEL]);
else
- label = ll_idx_n2a(ifa->ifa_index, b1);
+ label = ll_index_to_name(ifa->ifa_index);
if (fnmatch(G_filter.label, label, 0) != 0)
continue;
}
@@ -598,7 +591,7 @@ static int default_scope(inet_prefix *lcl)
}
/* Return value becomes exitcode. It's okay to not return at all */
-static int ipaddr_modify(int cmd, char **argv)
+static int ipaddr_modify(int cmd, int flags, char **argv)
{
static const char option[] ALIGN1 =
"peer\0""remote\0""broadcast\0""brd\0"
@@ -622,7 +615,7 @@ static int ipaddr_modify(int cmd, char **argv)
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.ifa.ifa_family = preferred_family;
@@ -673,7 +666,7 @@ static int ipaddr_modify(int cmd, char **argv)
} else if (arg == 5) { /* scope */
uint32_t scope = 0;
if (rtnl_rtscope_a2n(&scope, *argv)) {
- invarg(*argv, "scope");
+ invarg_1_to_2(*argv, "scope");
}
req.ifa.ifa_scope = scope;
scoped = 1;
@@ -701,7 +694,7 @@ static int ipaddr_modify(int cmd, char **argv)
/* There was no "dev IFACE", but we need that */
bb_error_msg_and_die("need \"dev IFACE\"");
}
- if (l && strncmp(d, l, strlen(d)) != 0) {
+ if (l && !is_prefixed_with(l, d)) {
bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
}
@@ -749,16 +742,27 @@ static int ipaddr_modify(int cmd, char **argv)
int FAST_FUNC do_ipaddr(char **argv)
{
static const char commands[] ALIGN1 =
- "add\0""delete\0""list\0""show\0""lst\0""flush\0";
+ /* 0 1 2 3 4 5 6 7 8 */
+ "add\0""change\0""chg\0""replace\0""delete\0""list\0""show\0""lst\0""flush\0";
int cmd = 2;
+
+ INIT_G();
+
if (*argv) {
cmd = index_in_substrings(commands, *argv);
if (cmd < 0)
- invarg(*argv, applet_name);
+ invarg_1_to_2(*argv, applet_name);
argv++;
- if (cmd <= 1)
- return ipaddr_modify((cmd == 0) ? RTM_NEWADDR : RTM_DELADDR, argv);
+ if (cmd <= 4) {
+ return ipaddr_modify(
+ /*cmd:*/ cmd == 4 ? RTM_DELADDR : RTM_NEWADDR,
+ /*flags:*/
+ cmd == 0 ? NLM_F_CREATE|NLM_F_EXCL : /* add */
+ cmd == 1 || cmd == 2 ? NLM_F_REPLACE : /* change */
+ cmd == 3 ? NLM_F_CREATE|NLM_F_REPLACE : /* replace */
+ 0 /* delete */
+ , argv);
+ }
}
- /* 2 == list, 3 == show, 4 == lst */
- return ipaddr_list_or_flush(argv, cmd == 5);
+ return ipaddr_list_or_flush(argv, cmd == 8);
}
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
index 1b414a2..29cf1ab 100644
--- a/networking/libiproute/iplink.c
+++ b/networking/libiproute/iplink.c
@@ -9,6 +9,7 @@
/*#include <net/if_packet.h> - not needed? */
#include <netpacket/packet.h>
#include <netinet/if_ether.h>
+#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include "ip_common.h" /* #include "libbb.h" is inside */
@@ -46,6 +47,12 @@ struct ifla_vlan_flags {
/* taken from linux/sockios.h */
#define SIOCSIFNAME 0x8923 /* set interface name */
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
+
/* Exits on error */
static int get_ctl_fd(void)
{
@@ -343,7 +350,7 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
while (*argv) {
arg = index_in_substrings(keywords, *argv);
if (arg < 0)
- invarg(*argv, "type vlan");
+ invarg_1_to_2(*argv, "type vlan");
NEXT_ARG();
if (arg == ARG_id) {
@@ -401,12 +408,13 @@ static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
static int do_add_or_delete(char **argv, const unsigned rtm)
{
static const char keywords[] ALIGN1 =
- "link\0""name\0""type\0""dev\0";
+ "link\0""name\0""type\0""dev\0""address\0";
enum {
ARG_link,
ARG_name,
ARG_type,
ARG_dev,
+ ARG_address,
};
struct rtnl_handle rth;
struct {
@@ -415,7 +423,11 @@ static int do_add_or_delete(char **argv, const unsigned rtm)
char buf[1024];
} req;
smalluint arg;
- char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
+ char *name_str = NULL;
+ char *link_str = NULL;
+ char *type_str = NULL;
+ char *dev_str = NULL;
+ char *address_str = NULL;
memset(&req, 0, sizeof(req));
@@ -431,14 +443,21 @@ static int do_add_or_delete(char **argv, const unsigned rtm)
if (arg == ARG_type) {
NEXT_ARG();
type_str = *argv++;
+ dbg("type_str:'%s'", type_str);
break;
}
if (arg == ARG_link) {
NEXT_ARG();
link_str = *argv;
+ dbg("link_str:'%s'", link_str);
} else if (arg == ARG_name) {
NEXT_ARG();
name_str = *argv;
+ dbg("name_str:'%s'", name_str);
+ } else if (arg == ARG_address) {
+ NEXT_ARG();
+ address_str = *argv;
+ dbg("address_str:'%s'", name_str);
} else {
if (arg == ARG_dev) {
if (dev_str)
@@ -446,6 +465,7 @@ static int do_add_or_delete(char **argv, const unsigned rtm)
NEXT_ARG();
}
dev_str = *argv;
+ dbg("dev_str:'%s'", dev_str);
}
argv++;
}
@@ -481,11 +501,19 @@ static int do_add_or_delete(char **argv, const unsigned rtm)
int idx = xll_name_to_index(link_str);
addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
}
+ if (address_str) {
+ unsigned char abuf[32];
+ int len = ll_addr_a2n(abuf, sizeof(abuf), address_str);
+ dbg("address len:%d", len);
+ if (len < 0)
+ return -1;
+ addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len);
+ }
}
if (name_str) {
const size_t name_len = strlen(name_str) + 1;
if (name_len < 2 || name_len > IFNAMSIZ)
- invarg(name_str, "name");
+ invarg_1_to_2(name_str, "name");
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
}
if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
@@ -493,6 +521,164 @@ static int do_add_or_delete(char **argv, const unsigned rtm)
return 0;
}
+/* Other keywords recognized by iproute2-3.12.0: */
+#if 0
+ } else if (matches(*argv, "broadcast") == 0 ||
+ strcmp(*argv, "brd") == 0) {
+ NEXT_ARG();
+ len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
+ if (len < 0)
+ return -1;
+ addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
+ } else if (matches(*argv, "txqueuelen") == 0 ||
+ strcmp(*argv, "qlen") == 0 ||
+ matches(*argv, "txqlen") == 0) {
+ NEXT_ARG();
+ if (qlen != -1)
+ duparg("txqueuelen", *argv);
+ if (get_integer(&qlen, *argv, 0))
+ invarg_1_to_2(*argv, "txqueuelen");
+ addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
+ } else if (strcmp(*argv, "mtu") == 0) {
+ NEXT_ARG();
+ if (mtu != -1)
+ duparg("mtu", *argv);
+ if (get_integer(&mtu, *argv, 0))
+ invarg_1_to_2(*argv, "mtu");
+ addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
+ } else if (strcmp(*argv, "netns") == 0) {
+ NEXT_ARG();
+ if (netns != -1)
+ duparg("netns", *argv);
+ if ((netns = get_netns_fd(*argv)) >= 0)
+ addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
+ else if (get_integer(&netns, *argv, 0) == 0)
+ addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
+ else
+ invarg_1_to_2(*argv, "netns");
+ } else if (strcmp(*argv, "multicast") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_MULTICAST;
+ if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags |= IFF_MULTICAST;
+ } else if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags &= ~IFF_MULTICAST;
+ } else
+ return on_off("multicast", *argv);
+ } else if (strcmp(*argv, "allmulticast") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_ALLMULTI;
+ if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags |= IFF_ALLMULTI;
+ } else if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags &= ~IFF_ALLMULTI;
+ } else
+ return on_off("allmulticast", *argv);
+ } else if (strcmp(*argv, "promisc") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_PROMISC;
+ if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags |= IFF_PROMISC;
+ } else if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags &= ~IFF_PROMISC;
+ } else
+ return on_off("promisc", *argv);
+ } else if (strcmp(*argv, "trailers") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_NOTRAILERS;
+ if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags |= IFF_NOTRAILERS;
+ } else if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags &= ~IFF_NOTRAILERS;
+ } else
+ return on_off("trailers", *argv);
+ } else if (strcmp(*argv, "arp") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_NOARP;
+ if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags &= ~IFF_NOARP;
+ } else if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags |= IFF_NOARP;
+ } else
+ return on_off("noarp", *argv);
+ } else if (strcmp(*argv, "vf") == 0) {
+ struct rtattr *vflist;
+ NEXT_ARG();
+ if (get_integer(&vf, *argv, 0)) {
+ invarg_1_to_2(*argv, "vf");
+ }
+ vflist = addattr_nest(&req->n, sizeof(*req),
+ IFLA_VFINFO_LIST);
+ len = iplink_parse_vf(vf, &argc, &argv, req);
+ if (len < 0)
+ return -1;
+ addattr_nest_end(&req->n, vflist);
+ } else if (matches(*argv, "master") == 0) {
+ int ifindex;
+ NEXT_ARG();
+ ifindex = ll_name_to_index(*argv);
+ if (!ifindex)
+ invarg_1_to_2(*argv, "master");
+ addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+ &ifindex, 4);
+ } else if (matches(*argv, "nomaster") == 0) {
+ int ifindex = 0;
+ addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
+ &ifindex, 4);
+ } else if (matches(*argv, "dynamic") == 0) {
+ NEXT_ARG();
+ req->i.ifi_change |= IFF_DYNAMIC;
+ if (strcmp(*argv, "on") == 0) {
+ req->i.ifi_flags |= IFF_DYNAMIC;
+ } else if (strcmp(*argv, "off") == 0) {
+ req->i.ifi_flags &= ~IFF_DYNAMIC;
+ } else
+ return on_off("dynamic", *argv);
+ } else if (matches(*argv, "alias") == 0) {
+ NEXT_ARG();
+ addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
+ *argv, strlen(*argv));
+ argc--; argv++;
+ break;
+ } else if (strcmp(*argv, "group") == 0) {
+ NEXT_ARG();
+ if (*group != -1)
+ duparg("group", *argv);
+ if (rtnl_group_a2n(group, *argv))
+ invarg_1_to_2(*argv, "group");
+ } else if (strcmp(*argv, "mode") == 0) {
+ int mode;
+ NEXT_ARG();
+ mode = get_link_mode(*argv);
+ if (mode < 0)
+ invarg_1_to_2(*argv, "mode");
+ addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode);
+ } else if (strcmp(*argv, "state") == 0) {
+ int state;
+ NEXT_ARG();
+ state = get_operstate(*argv);
+ if (state < 0)
+ invarg_1_to_2(*argv, "state");
+ addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state);
+ } else if (matches(*argv, "numtxqueues") == 0) {
+ NEXT_ARG();
+ if (numtxqueues != -1)
+ duparg("numtxqueues", *argv);
+ if (get_integer(&numtxqueues, *argv, 0))
+ invarg_1_to_2(*argv, "numtxqueues");
+ addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES,
+ &numtxqueues, 4);
+ } else if (matches(*argv, "numrxqueues") == 0) {
+ NEXT_ARG();
+ if (numrxqueues != -1)
+ duparg("numrxqueues", *argv);
+ if (get_integer(&numrxqueues, *argv, 0))
+ invarg_1_to_2(*argv, "numrxqueues");
+ addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
+ &numrxqueues, 4);
+ }
+#endif
+
/* Return value becomes exitcode. It's okay to not return at all */
int FAST_FUNC do_iplink(char **argv)
{
@@ -501,7 +687,7 @@ int FAST_FUNC do_iplink(char **argv)
if (*argv) {
int key = index_in_substrings(keywords, *argv);
if (key < 0) /* invalid argument */
- invarg(*argv, applet_name);
+ invarg_1_to_2(*argv, applet_name);
argv++;
if (key <= 1) /* add/delete */
return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK);
diff --git a/networking/libiproute/ipneigh.c b/networking/libiproute/ipneigh.c
new file mode 100644
index 0000000..2a1c20e
--- a/dev/null
+++ b/networking/libiproute/ipneigh.c
@@ -0,0 +1,357 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Ported to Busybox by: Curt Brune <curt@cumulusnetworks.com>
+ */
+
+#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "common_bufsiz.h"
+#include "rt_names.h"
+#include "utils.h"
+#include <linux/neighbour.h>
+#include <net/if_arp.h>
+
+//static int xshow_stats = 3;
+enum { xshow_stats = 3 };
+
+static inline uint32_t rta_getattr_u32(const struct rtattr *rta)
+{
+ return *(uint32_t *)RTA_DATA(rta);
+}
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+struct filter_t {
+ int family;
+ int index;
+ int state;
+ int unused_only;
+ inet_prefix pfx;
+ int flushed;
+ char *flushb;
+ int flushp;
+ int flushe;
+ struct rtnl_handle *rth;
+} FIX_ALIASING;
+typedef struct filter_t filter_t;
+
+#define G_filter (*(filter_t*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
+
+static int flush_update(void)
+{
+ if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
+ bb_perror_msg("can't send flush request");
+ return -1;
+ }
+ G_filter.flushp = 0;
+ return 0;
+}
+
+static unsigned nud_state_a2n(char *arg)
+{
+ static const char keywords[] ALIGN1 =
+ /* "ip neigh show/flush" parameters: */
+ "permanent\0" "reachable\0" "noarp\0" "none\0"
+ "stale\0" "incomplete\0" "delay\0" "probe\0"
+ "failed\0"
+ ;
+ static uint8_t nuds[] ALIGN1 = {
+ NUD_PERMANENT,NUD_REACHABLE, NUD_NOARP,NUD_NONE,
+ NUD_STALE, NUD_INCOMPLETE,NUD_DELAY,NUD_PROBE,
+ NUD_FAILED
+ };
+ int id;
+
+ BUILD_BUG_ON(
+ (NUD_PERMANENT|NUD_REACHABLE| NUD_NOARP|NUD_NONE|
+ NUD_STALE| NUD_INCOMPLETE|NUD_DELAY|NUD_PROBE|
+ NUD_FAILED) > 0xff
+ );
+
+ id = index_in_substrings(keywords, arg);
+ if (id < 0)
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, arg, "nud state");
+ return nuds[id];
+}
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+
+
+static int FAST_FUNC print_neigh(const struct sockaddr_nl *who UNUSED_PARAM,
+ struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+ struct ndmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr *tb[NDA_MAX+1];
+
+ if (n->nlmsg_type != RTM_NEWNEIGH && n->nlmsg_type != RTM_DELNEIGH) {
+ bb_error_msg_and_die("not RTM_NEWNEIGH: %08x %08x %08x",
+ n->nlmsg_len, n->nlmsg_type,
+ n->nlmsg_flags);
+ }
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ bb_error_msg_and_die("BUG: wrong nlmsg len %d", len);
+ }
+
+ if (G_filter.flushb && n->nlmsg_type != RTM_NEWNEIGH)
+ return 0;
+
+ if (G_filter.family && G_filter.family != r->ndm_family)
+ return 0;
+ if (G_filter.index && G_filter.index != r->ndm_ifindex)
+ return 0;
+ if (!(G_filter.state&r->ndm_state) &&
+ !(r->ndm_flags & NTF_PROXY) &&
+ (r->ndm_state || !(G_filter.state & 0x100)) &&
+ (r->ndm_family != AF_DECnet))
+ return 0;
+
+ parse_rtattr(tb, NDA_MAX, NDA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)));
+
+ if (tb[NDA_DST]) {
+ if (G_filter.pfx.family) {
+ inet_prefix dst;
+ memset(&dst, 0, sizeof(dst));
+ dst.family = r->ndm_family;
+ memcpy(&dst.data, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+ if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen))
+ return 0;
+ }
+ }
+ if (G_filter.unused_only && tb[NDA_CACHEINFO]) {
+ struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+ if (ci->ndm_refcnt)
+ return 0;
+ }
+
+ if (G_filter.flushb) {
+ struct nlmsghdr *fn;
+ if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
+ if (flush_update())
+ return -1;
+ }
+ fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
+ memcpy(fn, n, n->nlmsg_len);
+ fn->nlmsg_type = RTM_DELNEIGH;
+ fn->nlmsg_flags = NLM_F_REQUEST;
+ fn->nlmsg_seq = ++(G_filter.rth->seq);
+ G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
+ G_filter.flushed++;
+ if (xshow_stats < 2)
+ return 0;
+ }
+
+ if (tb[NDA_DST]) {
+ printf("%s ",
+ format_host(r->ndm_family,
+ RTA_PAYLOAD(tb[NDA_DST]),
+ RTA_DATA(tb[NDA_DST]))
+ );
+ }
+ if (!G_filter.index && r->ndm_ifindex)
+ printf("dev %s ", ll_index_to_name(r->ndm_ifindex));
+ if (tb[NDA_LLADDR]) {
+ SPRINT_BUF(b1);
+ printf("lladdr %s", ll_addr_n2a(RTA_DATA(tb[NDA_LLADDR]),
+ RTA_PAYLOAD(tb[NDA_LLADDR]),
+ ARPHRD_ETHER,
+ b1, sizeof(b1)));
+ }
+ if (r->ndm_flags & NTF_ROUTER) {
+ printf(" router");
+ }
+ if (r->ndm_flags & NTF_PROXY) {
+ printf(" proxy");
+ }
+ if (tb[NDA_CACHEINFO] && xshow_stats) {
+ struct nda_cacheinfo *ci = RTA_DATA(tb[NDA_CACHEINFO]);
+ int hz = get_hz();
+
+ if (ci->ndm_refcnt)
+ printf(" ref %d", ci->ndm_refcnt);
+ printf(" used %d/%d/%d", ci->ndm_used/hz,
+ ci->ndm_confirmed/hz, ci->ndm_updated/hz);
+ }
+
+ if (tb[NDA_PROBES] && xshow_stats) {
+ uint32_t p = rta_getattr_u32(tb[NDA_PROBES]);
+ printf(" probes %u", p);
+ }
+
+ /*if (r->ndm_state)*/ {
+ int nud = r->ndm_state;
+ char c = ' ';
+#define PRINT_FLAG(f) \
+ if (nud & NUD_##f) { \
+ printf("%c"#f, c); \
+ c = ','; \
+ }
+ PRINT_FLAG(INCOMPLETE);
+ PRINT_FLAG(REACHABLE);
+ PRINT_FLAG(STALE);
+ PRINT_FLAG(DELAY);
+ PRINT_FLAG(PROBE);
+ PRINT_FLAG(FAILED);
+ PRINT_FLAG(NOARP);
+ PRINT_FLAG(PERMANENT);
+#undef PRINT_FLAG
+ }
+ bb_putchar('\n');
+
+ return 0;
+}
+
+static void ipneigh_reset_filter(void)
+{
+ memset(&G_filter, 0, sizeof(G_filter));
+ G_filter.state = ~0;
+}
+
+#define MAX_ROUNDS 10
+/* Return value becomes exitcode. It's okay to not return at all */
+static int FAST_FUNC ipneigh_list_or_flush(char **argv, int flush)
+{
+ static const char keywords[] ALIGN1 =
+ /* "ip neigh show/flush" parameters: */
+ "to\0" "dev\0" "nud\0";
+ enum {
+ KW_to, KW_dev, KW_nud,
+ };
+ struct rtnl_handle rth;
+ struct ndmsg ndm = { 0 };
+ char *filter_dev = NULL;
+ int state_given = 0;
+ int arg;
+
+ ipneigh_reset_filter();
+
+ if (flush && !*argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, "\"ip neigh flush\"");
+
+ if (!G_filter.family)
+ G_filter.family = preferred_family;
+
+ G_filter.state = (flush) ?
+ ~(NUD_PERMANENT|NUD_NOARP) : 0xFF & ~NUD_NOARP;
+
+ while (*argv) {
+ arg = index_in_substrings(keywords, *argv);
+ if (arg == KW_dev) {
+ NEXT_ARG();
+ filter_dev = *argv;
+ } else if (arg == KW_nud) {
+ unsigned state;
+ NEXT_ARG();
+ if (!state_given) {
+ state_given = 1;
+ G_filter.state = 0;
+ }
+ if (strcmp(*argv, "all") == 0) {
+ state = ~0;
+ if (flush)
+ state &= ~NUD_NOARP;
+ } else {
+ state = nud_state_a2n(*argv);
+ }
+ if (state == 0)
+ state = 0x100;
+ G_filter.state |= state;
+ } else {
+ if (arg == KW_to) {
+ NEXT_ARG();
+ }
+ get_prefix(&G_filter.pfx, *argv, G_filter.family);
+ if (G_filter.family == AF_UNSPEC)
+ G_filter.family = G_filter.pfx.family;
+ }
+ argv++;
+ }
+
+ xrtnl_open(&rth);
+ ll_init_map(&rth);
+
+ if (filter_dev) {
+ G_filter.index = xll_name_to_index(filter_dev);
+ if (G_filter.index == 0) {
+ bb_error_msg_and_die("can't find device '%s'", filter_dev);
+ }
+ }
+
+ if (flush) {
+ int round = 0;
+ char flushb[4096-512];
+ G_filter.flushb = flushb;
+ G_filter.flushp = 0;
+ G_filter.flushe = sizeof(flushb);
+ G_filter.state &= ~NUD_FAILED;
+ G_filter.rth = &rth;
+
+ while (round < MAX_ROUNDS) {
+ if (xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETNEIGH) < 0) {
+ bb_perror_msg_and_die("can't send dump request");
+ }
+ G_filter.flushed = 0;
+ if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) {
+ bb_perror_msg_and_die("flush terminated");
+ }
+ if (G_filter.flushed == 0) {
+ if (round == 0)
+ puts("Nothing to flush");
+ else
+ printf("*** Flush is complete after %d round(s) ***\n", round);
+ return 0;
+ }
+ round++;
+ if (flush_update() < 0)
+ xfunc_die();
+ printf("\n*** Round %d, deleting %d entries ***\n", round, G_filter.flushed);
+ }
+ bb_error_msg_and_die("*** Flush not complete bailing out after %d rounds", MAX_ROUNDS);
+ }
+
+ ndm.ndm_family = G_filter.family;
+
+ if (rtnl_dump_request(&rth, RTM_GETNEIGH, &ndm, sizeof(struct ndmsg)) < 0) {
+ bb_perror_msg_and_die("can't send dump request");
+ }
+
+ if (xrtnl_dump_filter(&rth, print_neigh, NULL) < 0) {
+ bb_error_msg_and_die("dump terminated");
+ }
+
+ return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int FAST_FUNC do_ipneigh(char **argv)
+{
+ static const char ip_neigh_commands[] ALIGN1 =
+ /*0-1*/ "show\0" "flush\0";
+ int command_num;
+
+ INIT_G();
+
+ if (!*argv)
+ return ipneigh_list_or_flush(argv, 0);
+
+ command_num = index_in_substrings(ip_neigh_commands, *argv);
+ switch (command_num) {
+ case 0: /* show */
+ return ipneigh_list_or_flush(argv + 1, 0);
+ case 1: /* flush */
+ return ipneigh_list_or_flush(argv + 1, 1);
+ }
+ invarg_1_to_2(*argv, applet_name);
+ return 1;
+}
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
index 03425fa..e28c67c 100644
--- a/networking/libiproute/iproute.c
+++ b/networking/libiproute/iproute.c
@@ -11,6 +11,7 @@
*/
#include "ip_common.h" /* #include "libbb.h" is inside */
+#include "common_bufsiz.h"
#include "rt_names.h"
#include "utils.h"
@@ -43,7 +44,8 @@ struct filter_t {
} FIX_ALIASING;
typedef struct filter_t filter_t;
-#define G_filter (*(filter_t*)&bb_common_bufsiz1)
+#define G_filter (*(filter_t*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static int flush_update(void)
{
@@ -55,39 +57,16 @@ static int flush_update(void)
return 0;
}
-static unsigned get_hz(void)
-{
- static unsigned hz_internal;
- FILE *fp;
-
- if (hz_internal)
- return hz_internal;
-
- fp = fopen_for_read("/proc/net/psched");
- if (fp) {
- unsigned nom, denom;
-
- if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
- if (nom == 1000000)
- hz_internal = denom;
- fclose(fp);
- }
- if (!hz_internal)
- hz_internal = sysconf(_SC_CLK_TCK);
- return hz_internal;
-}
-
static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
struct nlmsghdr *n, void *arg UNUSED_PARAM)
{
struct rtmsg *r = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *tb[RTA_MAX+1];
- char abuf[256];
inet_prefix dst;
inet_prefix src;
int host_len = -1;
- SPRINT_BUF(b1);
+ uint32_t tid;
if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
fprintf(stderr, "Not a route: %08x %08x %08x\n",
@@ -100,6 +79,14 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
if (len < 0)
bb_error_msg_and_die("wrong nlmsg len %d", len);
+ memset(tb, 0, sizeof(tb));
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+ if (tb[RTA_TABLE])
+ tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
+ else
+ tid = r->rtm_table;
+
if (r->rtm_family == AF_INET6)
host_len = 128;
else if (r->rtm_family == AF_INET)
@@ -129,7 +116,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
}
}
} else {
- if (G_filter.tb > 0 && G_filter.tb != r->rtm_table) {
+ if (G_filter.tb > 0 && G_filter.tb != tid) {
return 0;
}
}
@@ -158,10 +145,8 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
return 0;
}
- memset(tb, 0, sizeof(tb));
memset(&src, 0, sizeof(src));
memset(&dst, 0, sizeof(dst));
- parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
if (tb[RTA_SRC]) {
src.bitlen = r->rtm_src_len;
@@ -218,7 +203,7 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
if ((int) (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len) > G_filter.flushe) {
if (flush_update())
- bb_error_msg_and_die("flush");
+ xfunc_die();
}
fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
memcpy(fn, n, n->nlmsg_len);
@@ -236,22 +221,20 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
printf("Deleted ");
}
if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
- printf("%s ", rtnl_rtntype_n2a(r->rtm_type, b1));
+ printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
}
if (tb[RTA_DST]) {
if (r->rtm_dst_len != host_len) {
- printf("%s/%u ", rt_addr_n2a(r->rtm_family,
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf)),
- r->rtm_dst_len
- );
+ printf("%s/%u ",
+ rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
+ r->rtm_dst_len
+ );
} else {
printf("%s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_DST]),
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf))
- );
+ RTA_DATA(tb[RTA_DST]))
+ );
}
} else if (r->rtm_dst_len) {
printf("0/%d ", r->rtm_dst_len);
@@ -260,17 +243,15 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
}
if (tb[RTA_SRC]) {
if (r->rtm_src_len != host_len) {
- printf("from %s/%u ", rt_addr_n2a(r->rtm_family,
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf)),
- r->rtm_src_len
- );
+ printf("from %s/%u ",
+ rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
+ r->rtm_src_len
+ );
} else {
printf("from %s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_SRC]),
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf))
- );
+ RTA_DATA(tb[RTA_SRC]))
+ );
}
} else if (r->rtm_src_len) {
printf("from 0/%u ", r->rtm_src_len);
@@ -278,12 +259,16 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
printf("via %s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_GATEWAY]),
- RTA_DATA(tb[RTA_GATEWAY]),
- abuf, sizeof(abuf)));
+ RTA_DATA(tb[RTA_GATEWAY]))
+ );
}
if (tb[RTA_OIF]) {
printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
}
+#if ENABLE_FEATURE_IP_RULE
+ if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
+ printf("table %s ", rtnl_rttable_n2a(tid));
+#endif
/* Todo: parse & show "proto kernel", "scope link" here */
@@ -292,12 +277,24 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
and symbolic name will not be useful.
*/
printf(" src %s ", rt_addr_n2a(r->rtm_family,
- RTA_DATA(tb[RTA_PREFSRC]),
- abuf, sizeof(abuf)));
+ RTA_DATA(tb[RTA_PREFSRC])));
}
if (tb[RTA_PRIORITY]) {
printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
}
+ if (r->rtm_flags & RTNH_F_DEAD) {
+ printf("dead ");
+ }
+ if (r->rtm_flags & RTNH_F_ONLINK) {
+ printf("onlink ");
+ }
+ if (r->rtm_flags & RTNH_F_PERVASIVE) {
+ printf("pervasive ");
+ }
+ if (r->rtm_flags & RTM_F_NOTIFY) {
+ printf("notify ");
+ }
+
if (r->rtm_family == AF_INET6) {
struct rta_cacheinfo *ci = NULL;
if (tb[RTA_CACHEINFO]) {
@@ -329,18 +326,20 @@ static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
static int iproute_modify(int cmd, unsigned flags, char **argv)
{
static const char keywords[] ALIGN1 =
- "src\0""via\0""mtu\0""lock\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
- "dev\0""oif\0""to\0""metric\0";
+ "src\0""via\0""mtu\0""lock\0""scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
+ "dev\0""oif\0""to\0""metric\0""onlink\0";
enum {
ARG_src,
ARG_via,
ARG_mtu, PARM_lock,
+ ARG_scope,
ARG_protocol,
IF_FEATURE_IP_RULE(ARG_table,)
ARG_dev,
ARG_oif,
ARG_to,
ARG_metric,
+ ARG_onlink,
};
enum {
gw_ok = 1 << 0,
@@ -359,6 +358,7 @@ IF_FEATURE_IP_RULE(ARG_table,)
unsigned mxlock = 0;
char *d = NULL;
smalluint ok = 0;
+ smalluint scope_ok = 0;
int arg;
memset(&req, 0, sizeof(req));
@@ -367,15 +367,17 @@ IF_FEATURE_IP_RULE(ARG_table,)
req.n.nlmsg_flags = NLM_F_REQUEST | flags;
req.n.nlmsg_type = cmd;
req.r.rtm_family = preferred_family;
- if (RT_TABLE_MAIN) /* if it is zero, memset already did it */
+ if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
req.r.rtm_table = RT_TABLE_MAIN;
- if (RT_SCOPE_NOWHERE)
+ if (RT_SCOPE_NOWHERE != 0)
req.r.rtm_scope = RT_SCOPE_NOWHERE;
if (cmd != RTM_DELROUTE) {
- req.r.rtm_protocol = RTPROT_BOOT;
req.r.rtm_scope = RT_SCOPE_UNIVERSE;
- req.r.rtm_type = RTN_UNICAST;
+ if (RTPROT_BOOT != 0)
+ req.r.rtm_protocol = RTPROT_BOOT;
+ if (RTN_UNICAST != 0)
+ req.r.rtm_type = RTN_UNICAST;
}
mxrta->rta_type = RTA_METRICS;
@@ -408,11 +410,18 @@ IF_FEATURE_IP_RULE(ARG_table,)
}
mtu = get_unsigned(*argv, "mtu");
rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+ } else if (arg == ARG_scope) {
+ uint32_t scope;
+ NEXT_ARG();
+ if (rtnl_rtscope_a2n(&scope, *argv))
+ invarg_1_to_2(*argv, "scope");
+ req.r.rtm_scope = scope;
+ scope_ok = 1;
} else if (arg == ARG_protocol) {
uint32_t prot;
NEXT_ARG();
if (rtnl_rtprot_a2n(&prot, *argv))
- invarg(*argv, "protocol");
+ invarg_1_to_2(*argv, "protocol");
req.r.rtm_protocol = prot;
ok |= proto_ok;
#if ENABLE_FEATURE_IP_RULE
@@ -420,8 +429,13 @@ IF_FEATURE_IP_RULE(ARG_table,)
uint32_t tid;
NEXT_ARG();
if (rtnl_rttable_a2n(&tid, *argv))
- invarg(*argv, "table");
- req.r.rtm_table = tid;
+ invarg_1_to_2(*argv, "table");
+ if (tid < 256)
+ req.r.rtm_table = tid;
+ else {
+ req.r.rtm_table = RT_TABLE_UNSPEC;
+ addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
+ }
#endif
} else if (arg == ARG_dev || arg == ARG_oif) {
NEXT_ARG();
@@ -431,6 +445,8 @@ IF_FEATURE_IP_RULE(ARG_table,)
NEXT_ARG();
metric = get_u32(*argv, "metric");
addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
+ } else if (arg == ARG_onlink) {
+ req.r.rtm_flags |= RTNH_F_ONLINK;
} else {
int type;
inet_prefix dst;
@@ -482,20 +498,22 @@ IF_FEATURE_IP_RULE(ARG_table,)
addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
}
- if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
- req.r.rtm_scope = RT_SCOPE_HOST;
- else
- if (req.r.rtm_type == RTN_BROADCAST
- || req.r.rtm_type == RTN_MULTICAST
- || req.r.rtm_type == RTN_ANYCAST
- ) {
- req.r.rtm_scope = RT_SCOPE_LINK;
- }
- else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
- if (cmd == RTM_DELROUTE)
- req.r.rtm_scope = RT_SCOPE_NOWHERE;
- else if (!(ok & gw_ok))
+ if (!scope_ok) {
+ if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
+ req.r.rtm_scope = RT_SCOPE_HOST;
+ else
+ if (req.r.rtm_type == RTN_BROADCAST
+ || req.r.rtm_type == RTN_MULTICAST
+ || req.r.rtm_type == RTN_ANYCAST
+ ) {
req.r.rtm_scope = RT_SCOPE_LINK;
+ }
+ else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
+ if (cmd == RTM_DELROUTE)
+ req.r.rtm_scope = RT_SCOPE_NOWHERE;
+ else if (!(ok & gw_ok))
+ req.r.rtm_scope = RT_SCOPE_LINK;
+ }
}
if (req.r.rtm_family == AF_UNSPEC) {
@@ -604,7 +622,7 @@ static int iproute_list_or_flush(char **argv, int flush)
//G_filter.protocolmask = -1;
if (rtnl_rtprot_a2n(&prot, *argv)) {
if (index_in_strings(keywords, *argv) != KW_all)
- invarg(*argv, "protocol");
+ invarg_1_to_2(*argv, "protocol");
prot = 0;
//G_filter.protocolmask = 0;
}
@@ -629,10 +647,10 @@ static int iproute_list_or_flush(char **argv, int flush)
#if ENABLE_FEATURE_IP_RULE
uint32_t tid;
if (rtnl_rttable_a2n(&tid, *argv))
- invarg(*argv, "table");
+ invarg_1_to_2(*argv, "table");
G_filter.tb = tid;
#else
- invarg(*argv, "table");
+ invarg_1_to_2(*argv, "table");
#endif
}
} else if (arg == KW_cache) {
@@ -901,6 +919,8 @@ int FAST_FUNC do_iproute(char **argv)
unsigned flags = 0;
int cmd = RTM_NEWROUTE;
+ INIT_G();
+
if (!*argv)
return iproute_list_or_flush(argv, 0);
@@ -939,7 +959,7 @@ int FAST_FUNC do_iproute(char **argv)
case 11: /* flush */
return iproute_list_or_flush(argv+1, 1);
default:
- bb_error_msg_and_die("unknown command %s", *argv);
+ invarg_1_to_2(*argv, applet_name);
}
return iproute_modify(cmd, flags, argv+1);
diff --git a/networking/libiproute/iprule.c b/networking/libiproute/iprule.c
index 8dbe6bd..8f3f862 100644
--- a/networking/libiproute/iprule.c
+++ b/networking/libiproute/iprule.c
@@ -44,8 +44,6 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
int len = n->nlmsg_len;
int host_len = -1;
struct rtattr * tb[RTA_MAX+1];
- char abuf[256];
- SPRINT_BUF(b1);
if (n->nlmsg_type != RTM_NEWRULE)
return 0;
@@ -72,16 +70,14 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
printf("from ");
if (tb[RTA_SRC]) {
if (r->rtm_src_len != host_len) {
- printf("%s/%u", rt_addr_n2a(r->rtm_family,
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf)),
+ printf("%s/%u",
+ rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
r->rtm_src_len
);
} else {
fputs(format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_SRC]),
- RTA_DATA(tb[RTA_SRC]),
- abuf, sizeof(abuf)),
+ RTA_DATA(tb[RTA_SRC])),
stdout
);
}
@@ -95,22 +91,20 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
if (tb[RTA_DST]) {
if (r->rtm_dst_len != host_len) {
printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf)),
+ RTA_DATA(tb[RTA_DST])),
r->rtm_dst_len
);
} else {
printf("to %s ", format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_DST]),
- RTA_DATA(tb[RTA_DST]),
- abuf, sizeof(abuf)));
+ RTA_DATA(tb[RTA_DST])));
}
} else if (r->rtm_dst_len) {
printf("to 0/%d ", r->rtm_dst_len);
}
if (r->rtm_tos) {
- printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1));
+ printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos));
}
if (tb[RTA_PROTOINFO]) {
printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
@@ -120,8 +114,10 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
}
- if (r->rtm_table)
- printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1));
+ if (tb[RTA_TABLE])
+ printf("lookup %s ", rtnl_rttable_n2a(*(uint32_t*)RTA_DATA(tb[RTA_TABLE])));
+ else if (r->rtm_table)
+ printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table));
if (tb[RTA_FLOW]) {
uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
@@ -129,10 +125,10 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
to &= 0xFFFF;
if (from) {
printf("realms %s/",
- rtnl_rtrealm_n2a(from, b1));
+ rtnl_rtrealm_n2a(from));
}
printf("%s ",
- rtnl_rtrealm_n2a(to, b1));
+ rtnl_rtrealm_n2a(to));
}
if (r->rtm_type == RTN_NAT) {
@@ -140,12 +136,12 @@ static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
printf("map-to %s ",
format_host(r->rtm_family,
RTA_PAYLOAD(tb[RTA_GATEWAY]),
- RTA_DATA(tb[RTA_GATEWAY]),
- abuf, sizeof(abuf)));
+ RTA_DATA(tb[RTA_GATEWAY]))
+ );
} else
printf("masquerade");
} else if (r->rtm_type != RTN_UNICAST)
- fputs(rtnl_rtntype_n2a(r->rtm_type, b1), stdout);
+ fputs(rtnl_rtntype_n2a(r->rtm_type), stdout);
bb_putchar('\n');
/*fflush_all();*/
@@ -203,9 +199,11 @@ static int iprule_modify(int cmd, char **argv)
req.n.nlmsg_flags = NLM_F_REQUEST;
req.r.rtm_family = preferred_family;
req.r.rtm_protocol = RTPROT_BOOT;
- req.r.rtm_scope = RT_SCOPE_UNIVERSE;
- req.r.rtm_table = 0;
- req.r.rtm_type = RTN_UNSPEC;
+ if (RT_SCOPE_UNIVERSE != 0)
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+ /*req.r.rtm_table = 0; - already is */
+ if (RTN_UNSPEC != 0)
+ req.r.rtm_type = RTN_UNSPEC;
if (cmd == RTM_NEWRULE) {
req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
@@ -215,7 +213,7 @@ static int iprule_modify(int cmd, char **argv)
while (*argv) {
key = index_in_substrings(keywords, *argv) + 1;
if (key == 0) /* no match found in keywords array, bail out. */
- invarg(*argv, applet_name);
+ invarg_1_to_2(*argv, applet_name);
if (key == ARG_from) {
inet_prefix dst;
NEXT_ARG();
@@ -240,7 +238,7 @@ static int iprule_modify(int cmd, char **argv)
uint32_t tos;
NEXT_ARG();
if (rtnl_dsfield_a2n(&tos, *argv))
- invarg(*argv, "TOS");
+ invarg_1_to_2(*argv, "TOS");
req.r.rtm_tos = tos;
} else if (key == ARG_fwmark) {
uint32_t fwmark;
@@ -251,7 +249,7 @@ static int iprule_modify(int cmd, char **argv)
uint32_t realm;
NEXT_ARG();
if (get_rt_realms(&realm, *argv))
- invarg(*argv, "realms");
+ invarg_1_to_2(*argv, "realms");
addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
} else if (key == ARG_table ||
key == ARG_lookup
@@ -259,8 +257,13 @@ static int iprule_modify(int cmd, char **argv)
uint32_t tid;
NEXT_ARG();
if (rtnl_rttable_a2n(&tid, *argv))
- invarg(*argv, "table ID");
- req.r.rtm_table = tid;
+ invarg_1_to_2(*argv, "table ID");
+ if (tid < 256)
+ req.r.rtm_table = tid;
+ else {
+ req.r.rtm_table = RT_TABLE_UNSPEC;
+ addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
+ }
table_ok = 1;
} else if (key == ARG_dev ||
key == ARG_iif
@@ -282,7 +285,7 @@ static int iprule_modify(int cmd, char **argv)
if (key == ARG_help)
bb_show_usage();
if (rtnl_rtntype_a2n(&type, *argv))
- invarg(*argv, "type");
+ invarg_1_to_2(*argv, "type");
req.r.rtm_type = type;
}
argv++;
@@ -310,7 +313,7 @@ int FAST_FUNC do_iprule(char **argv)
if (*argv) {
int cmd = index_in_substrings(ip_rule_commands, *argv);
if (cmd < 0)
- invarg(*argv, applet_name);
+ invarg_1_to_2(*argv, applet_name);
argv++;
if (cmd < 2)
return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
diff --git a/networking/libiproute/iptunnel.c b/networking/libiproute/iptunnel.c
index b54c3c5..eb136e4 100644
--- a/networking/libiproute/iptunnel.c
+++ b/networking/libiproute/iptunnel.c
@@ -294,7 +294,7 @@ static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
if (key != ARG_inherit) {
uval = get_unsigned(*argv, "TTL");
if (uval > 255)
- invarg(*argv, "TTL must be <=255");
+ invarg_1_to_2(*argv, "TTL");
p->iph.ttl = uval;
}
} else if (key == ARG_tos ||
@@ -305,7 +305,7 @@ static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
key = index_in_strings(keywords, *argv);
if (key != ARG_inherit) {
if (rtnl_dsfield_a2n(&uval, *argv))
- invarg(*argv, "TOS");
+ invarg_1_to_2(*argv, "TOS");
p->iph.tos = uval;
} else
p->iph.tos = 1;
@@ -404,22 +404,18 @@ static int do_del(char **argv)
static void print_tunnel(struct ip_tunnel_parm *p)
{
- char s1[256];
- char s2[256];
- char s3[64];
- char s4[64];
-
- format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
- format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
- inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
- inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+ char s3[INET_ADDRSTRLEN];
+ char s4[INET_ADDRSTRLEN];
printf("%s: %s/ip remote %s local %s ",
- p->name,
- p->iph.protocol == IPPROTO_IPIP ? "ip" :
- (p->iph.protocol == IPPROTO_GRE ? "gre" :
- (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
- p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+ p->name,
+ p->iph.protocol == IPPROTO_IPIP ? "ip" :
+ p->iph.protocol == IPPROTO_GRE ? "gre" :
+ p->iph.protocol == IPPROTO_IPV6 ? "ipv6" :
+ "unknown",
+ p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr) : "any",
+ p->iph.saddr ? format_host(AF_INET, 4, &p->iph.saddr) : "any"
+ );
if (p->link) {
char *n = do_ioctl_get_ifname(p->link);
if (n) {
@@ -432,20 +428,21 @@ static void print_tunnel(struct ip_tunnel_parm *p)
else
printf(" ttl inherit ");
if (p->iph.tos) {
- SPRINT_BUF(b1);
printf(" tos");
if (p->iph.tos & 1)
printf(" inherit");
if (p->iph.tos & ~1)
printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
- rtnl_dsfield_n2a(p->iph.tos & ~1, b1));
+ rtnl_dsfield_n2a(p->iph.tos & ~1));
}
if (!(p->iph.frag_off & htons(IP_DF)))
printf(" nopmtudisc");
+ inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+ inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
printf(" key %s", s3);
- else if ((p->i_flags | p->o_flags) & GRE_KEY) {
+ else {
if (p->i_flags & GRE_KEY)
printf(" ikey %s ", s3);
if (p->o_flags & GRE_KEY)
@@ -563,7 +560,7 @@ int FAST_FUNC do_iptunnel(char **argv)
if (*argv) {
int key = index_in_substrings(keywords, *argv);
if (key < 0)
- invarg(*argv, applet_name);
+ invarg_1_to_2(*argv, applet_name);
argv++;
if (key == ARG_add)
return do_add(SIOCADDTUNNEL, argv);
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
index 7c80072..a1a9fc1 100644
--- a/networking/libiproute/libnetlink.c
+++ b/networking/libiproute/libnetlink.c
@@ -68,26 +68,32 @@ int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
{
- struct nlmsghdr nlh;
- struct sockaddr_nl nladdr;
- struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
- struct msghdr msg = {
- (void*)&nladdr, sizeof(nladdr),
- iov, 2,
- NULL, 0,
- 0
- };
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
-
- nlh.nlmsg_len = NLMSG_LENGTH(len);
- nlh.nlmsg_type = type;
- nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
- nlh.nlmsg_pid = 0;
- nlh.nlmsg_seq = rth->dump = ++rth->seq;
-
- return sendmsg(rth->fd, &msg, 0);
+ struct {
+ struct nlmsghdr nlh;
+ struct msghdr msg;
+ struct sockaddr_nl nladdr;
+ } s;
+ struct iovec iov[2] = { { &s.nlh, sizeof(s.nlh) }, { req, len } };
+
+ memset(&s, 0, sizeof(s));
+
+ s.msg.msg_name = (void*)&s.nladdr;
+ s.msg.msg_namelen = sizeof(s.nladdr);
+ s.msg.msg_iov = iov;
+ s.msg.msg_iovlen = 2;
+ /*s.msg.msg_control = NULL; - already is */
+ /*s.msg.msg_controllen = 0; - already is */
+ /*s.msg.msg_flags = 0; - already is */
+
+ s.nladdr.nl_family = AF_NETLINK;
+
+ s.nlh.nlmsg_len = NLMSG_LENGTH(len);
+ s.nlh.nlmsg_type = type;
+ s.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ /*s.nlh.nlmsg_pid = 0; - already is */
+ s.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &s.msg, 0);
}
static int rtnl_dump_filter(struct rtnl_handle *rth,
@@ -104,12 +110,15 @@ static int rtnl_dump_filter(struct rtnl_handle *rth,
while (1) {
int status;
struct nlmsghdr *h;
-
+ /* Use designated initializers, struct layout is non-portable */
struct msghdr msg = {
- (void*)&nladdr, sizeof(nladdr),
- &iov, 1,
- NULL, 0,
- 0
+ .msg_name = (void*)&nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0
};
status = recvmsg(rth->fd, &msg, 0);
@@ -211,11 +220,15 @@ int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
struct sockaddr_nl nladdr;
struct iovec iov = { (void*)n, n->nlmsg_len };
char *buf = xmalloc(8*1024); /* avoid big stack buffer */
+ /* Use designated initializers, struct layout is non-portable */
struct msghdr msg = {
- (void*)&nladdr, sizeof(nladdr),
- &iov, 1,
- NULL, 0,
- 0
+ .msg_name = (void*)&nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0
};
memset(&nladdr, 0, sizeof(nladdr));
@@ -327,14 +340,14 @@ int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
int len = RTA_LENGTH(4);
struct rtattr *rta;
- if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) {
return -1;
}
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
move_to_unaligned32(RTA_DATA(rta), data);
- n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len);
return 0;
}
@@ -343,14 +356,14 @@ int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, in
int len = RTA_LENGTH(alen);
struct rtattr *rta;
- if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len + len)) > maxlen) {
return -1;
}
rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, alen);
- n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len + len);
return 0;
}
@@ -359,14 +372,14 @@ int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t d
int len = RTA_LENGTH(4);
struct rtattr *subrta;
- if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ if (RTA_ALIGN(rta->rta_len + len) > maxlen) {
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
move_to_unaligned32(RTA_DATA(subrta), data);
- rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len + len);
return 0;
}
@@ -375,14 +388,14 @@ int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data
struct rtattr *subrta;
int len = RTA_LENGTH(alen);
- if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ if (RTA_ALIGN(rta->rta_len + len) > maxlen) {
return -1;
}
subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
subrta->rta_type = type;
subrta->rta_len = len;
memcpy(RTA_DATA(subrta), data, alen);
- rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len + len);
return 0;
}
diff --git a/networking/libiproute/ll_map.c b/networking/libiproute/ll_map.c
index 27cd90f..af9eb46 100644
--- a/networking/libiproute/ll_map.c
+++ b/networking/libiproute/ll_map.c
@@ -86,7 +86,8 @@ int FAST_FUNC ll_remember_index(const struct sockaddr_nl *who UNUSED_PARAM,
return 0;
}
-const char FAST_FUNC *ll_idx_n2a(int idx, char *buf)
+static
+const char FAST_FUNC *ll_idx_n2a(int idx/*, char *buf*/)
{
struct idxmap *im;
@@ -95,16 +96,15 @@ const char FAST_FUNC *ll_idx_n2a(int idx, char *buf)
im = find_by_index(idx);
if (im)
return im->name;
- snprintf(buf, 16, "if%d", idx);
- return buf;
+ //snprintf(buf, 16, "if%d", idx);
+ //return buf;
+ return auto_string(xasprintf("if%d", idx));
}
-
const char FAST_FUNC *ll_index_to_name(int idx)
{
- static char nbuf[16];
-
- return ll_idx_n2a(idx, nbuf);
+ //static char nbuf[16];
+ return ll_idx_n2a(idx/*, nbuf*/);
}
#ifdef UNUSED
@@ -136,7 +136,6 @@ unsigned FAST_FUNC ll_index_to_flags(int idx)
int FAST_FUNC xll_name_to_index(const char *name)
{
int ret = 0;
- int sock_fd;
/* caching is not warranted - no users which repeatedly call it */
#ifdef UNUSED
@@ -164,30 +163,8 @@ int FAST_FUNC xll_name_to_index(const char *name)
}
}
}
- /* We have not found the interface in our cache, but the kernel
- * may still know about it. One reason is that we may be using
- * module on-demand loading, which means that the kernel will
- * load the module and make the interface exist only when
- * we explicitely request it (check for dev_load() in net/core/dev.c).
- * I can think of other similar scenario, but they are less common...
- * Jean II */
#endif
-
- sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sock_fd >= 0) {
- struct ifreq ifr;
- int tmp;
-
- strncpy_IFNAMSIZ(ifr.ifr_name, name);
- ifr.ifr_ifindex = -1;
- tmp = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
- close(sock_fd);
- if (tmp >= 0)
- /* In theory, we should redump the interface list
- * to update our cache, this is left as an exercise
- * to the reader... Jean II */
- ret = ifr.ifr_ifindex;
- }
+ ret = if_nametoindex(name);
/* out:*/
if (ret <= 0)
bb_error_msg_and_die("can't find device '%s'", name);
diff --git a/networking/libiproute/ll_map.h b/networking/libiproute/ll_map.h
index c5d3834..7ea383c 100644
--- a/networking/libiproute/ll_map.h
+++ b/networking/libiproute/ll_map.h
@@ -7,8 +7,8 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
int ll_remember_index(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) FAST_FUNC;
int ll_init_map(struct rtnl_handle *rth) FAST_FUNC;
int xll_name_to_index(const char *name) FAST_FUNC;
+//static: const char *ll_idx_n2a(int idx, char *buf) FAST_FUNC;
const char *ll_index_to_name(int idx) FAST_FUNC;
-const char *ll_idx_n2a(int idx, char *buf) FAST_FUNC;
/* int ll_index_to_type(int idx); */
unsigned ll_index_to_flags(int idx) FAST_FUNC;
diff --git a/networking/libiproute/ll_proto.c b/networking/libiproute/ll_proto.c
index da2b53c..263ac78 100644
--- a/networking/libiproute/ll_proto.c
+++ b/networking/libiproute/ll_proto.c
@@ -13,6 +13,8 @@
#include "utils.h"
#include <netinet/if_ether.h>
+#include <linux/if_ether.h>
+
/* Please conditionalize exotic protocols on CONFIG_something */
@@ -84,7 +86,7 @@ ETH_P_IP
/* Keep declarations above and below in sync! */
-static const char llproto_names[] =
+static const char llproto_names[] ALIGN1 =
#define __PF(f,n) #n "\0"
__PF(LOOP,loop)
__PF(PUP,pup)
diff --git a/networking/libiproute/ll_types.c b/networking/libiproute/ll_types.c
index bb42e26..62ee0cc 100644
--- a/networking/libiproute/ll_types.c
+++ b/networking/libiproute/ll_types.c
@@ -16,7 +16,7 @@
const char* FAST_FUNC ll_type_n2a(int type, char *buf)
{
- static const char arphrd_name[] =
+ static const char arphrd_name[] ALIGN1 =
/* 0, */ "generic" "\0"
/* ARPHRD_LOOPBACK, */ "loopback" "\0"
/* ARPHRD_ETHER, */ "ether" "\0"
@@ -105,7 +105,7 @@ const char* FAST_FUNC ll_type_n2a(int type, char *buf)
/* Keep these arrays in sync! */
- static const uint16_t arphrd_type[] = {
+ static const uint16_t arphrd_type[] ALIGN2 = {
0, /* "generic" "\0" */
ARPHRD_LOOPBACK, /* "loopback" "\0" */
ARPHRD_ETHER, /* "ether" "\0" */
diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c
index c474ab9..51f2e9b 100644
--- a/networking/libiproute/rt_names.c
+++ b/networking/libiproute/rt_names.c
@@ -10,20 +10,32 @@
#include "libbb.h"
#include "rt_names.h"
+#define CONFDIR CONFIG_FEATURE_IP_ROUTE_DIR
+
typedef struct rtnl_tab_t {
const char *cached_str;
unsigned cached_result;
- const char *tab[256];
+ /* upstream version switched to a hash table and removed
+ * id < 256 limit. For now bbox bumps this array size from 256
+ * to 1024. If you plan to change this to a hash table,
+ * consider merging several hash tables we have (for example,
+ * awk has resizable one!
+ */
+#define RT_TABLE_MAX 1023
+ const char *tab[RT_TABLE_MAX+1];
} rtnl_tab_t;
static void rtnl_tab_initialize(const char *file, const char **tab)
{
char *token[2];
- parser_t *parser = config_open2(file, fopen_for_read);
+ char fullname[sizeof(CONFDIR"/rt_dsfield") + 8];
+ parser_t *parser;
+ sprintf(fullname, CONFDIR"/rt_%s", file);
+ parser = config_open2(fullname, fopen_for_read);
while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
unsigned id = bb_strtou(token[0], NULL, 0);
- if (id > 256) {
+ if (id > RT_TABLE_MAX) {
bb_error_msg("database %s is corrupted at line %d",
file, parser->lineno);
break;
@@ -42,7 +54,7 @@ static int rtnl_a2n(rtnl_tab_t *tab, uint32_t *id, const char *arg, int base)
return 0;
}
- for (i = 0; i < 256; i++) {
+ for (i = 0; i <= RT_TABLE_MAX; i++) {
if (tab->tab[i]
&& strcmp(tab->tab[i], arg) == 0
) {
@@ -54,7 +66,7 @@ static int rtnl_a2n(rtnl_tab_t *tab, uint32_t *id, const char *arg, int base)
}
i = bb_strtou(arg, NULL, base);
- if (i > 255)
+ if (i > RT_TABLE_MAX)
return -1;
*id = i;
return 0;
@@ -85,24 +97,23 @@ static void rtnl_rtprot_initialize(void)
return;
rtnl_rtprot_tab = xzalloc(sizeof(*rtnl_rtprot_tab));
memcpy(rtnl_rtprot_tab->tab, init_tab, sizeof(init_tab));
- rtnl_tab_initialize("/etc/iproute2/rt_protos", rtnl_rtprot_tab->tab);
+ rtnl_tab_initialize("protos", rtnl_rtprot_tab->tab);
}
-const char* FAST_FUNC rtnl_rtprot_n2a(int id, char *buf)
+#if 0 /* UNUSED */
+const char* FAST_FUNC rtnl_rtprot_n2a(int id)
{
- if (id < 0 || id >= 256) {
- sprintf(buf, "%d", id);
- return buf;
+ if (id < 0 || id > RT_TABLE_MAX) {
+ return itoa(id);
}
rtnl_rtprot_initialize();
if (rtnl_rtprot_tab->tab[id])
return rtnl_rtprot_tab->tab[id];
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "%d", id);
- return buf;
+ return itoa(id);
}
+#endif
int FAST_FUNC rtnl_rtprot_a2n(uint32_t *id, char *arg)
{
@@ -123,23 +134,20 @@ static void rtnl_rtscope_initialize(void)
rtnl_rtscope_tab->tab[254] = "host";
rtnl_rtscope_tab->tab[253] = "link";
rtnl_rtscope_tab->tab[200] = "site";
- rtnl_tab_initialize("/etc/iproute2/rt_scopes", rtnl_rtscope_tab->tab);
+ rtnl_tab_initialize("scopes", rtnl_rtscope_tab->tab);
}
-const char* FAST_FUNC rtnl_rtscope_n2a(int id, char *buf)
+const char* FAST_FUNC rtnl_rtscope_n2a(int id)
{
- if (id < 0 || id >= 256) {
- sprintf(buf, "%d", id);
- return buf;
+ if (id < 0 || id > RT_TABLE_MAX) {
+ return itoa(id);
}
rtnl_rtscope_initialize();
if (rtnl_rtscope_tab->tab[id])
return rtnl_rtscope_tab->tab[id];
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "%d", id);
- return buf;
+ return itoa(id);
}
int FAST_FUNC rtnl_rtscope_a2n(uint32_t *id, char *arg)
@@ -156,7 +164,7 @@ static void rtnl_rtrealm_initialize(void)
if (rtnl_rtrealm_tab) return;
rtnl_rtrealm_tab = xzalloc(sizeof(*rtnl_rtrealm_tab));
rtnl_rtrealm_tab->tab[0] = "unknown";
- rtnl_tab_initialize("/etc/iproute2/rt_realms", rtnl_rtrealm_tab->tab);
+ rtnl_tab_initialize("realms", rtnl_rtrealm_tab->tab);
}
int FAST_FUNC rtnl_rtrealm_a2n(uint32_t *id, char *arg)
@@ -166,20 +174,17 @@ int FAST_FUNC rtnl_rtrealm_a2n(uint32_t *id, char *arg)
}
#if ENABLE_FEATURE_IP_RULE
-const char* FAST_FUNC rtnl_rtrealm_n2a(int id, char *buf)
+const char* FAST_FUNC rtnl_rtrealm_n2a(int id)
{
- if (id < 0 || id >= 256) {
- sprintf(buf, "%d", id);
- return buf;
+ if (id < 0 || id > RT_TABLE_MAX) {
+ return itoa(id);
}
rtnl_rtrealm_initialize();
if (rtnl_rtrealm_tab->tab[id])
return rtnl_rtrealm_tab->tab[id];
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "%d", id);
- return buf;
+ return itoa(id);
}
#endif
@@ -191,23 +196,20 @@ static void rtnl_rtdsfield_initialize(void)
if (rtnl_rtdsfield_tab) return;
rtnl_rtdsfield_tab = xzalloc(sizeof(*rtnl_rtdsfield_tab));
rtnl_rtdsfield_tab->tab[0] = "0";
- rtnl_tab_initialize("/etc/iproute2/rt_dsfield", rtnl_rtdsfield_tab->tab);
+ rtnl_tab_initialize("dsfield", rtnl_rtdsfield_tab->tab);
}
-const char* FAST_FUNC rtnl_dsfield_n2a(int id, char *buf)
+const char* FAST_FUNC rtnl_dsfield_n2a(int id)
{
- if (id < 0 || id >= 256) {
- sprintf(buf, "%d", id);
- return buf;
+ if (id < 0 || id > RT_TABLE_MAX) {
+ return itoa(id);
}
rtnl_rtdsfield_initialize();
if (rtnl_rtdsfield_tab->tab[id])
return rtnl_rtdsfield_tab->tab[id];
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "0x%02x", id);
- return buf;
+ return itoa(id);
}
int FAST_FUNC rtnl_dsfield_a2n(uint32_t *id, char *arg)
@@ -222,29 +224,28 @@ static rtnl_tab_t *rtnl_rttable_tab;
static void rtnl_rttable_initialize(void)
{
- if (rtnl_rtdsfield_tab) return;
+ if (rtnl_rttable_tab)
+ return;
+
rtnl_rttable_tab = xzalloc(sizeof(*rtnl_rttable_tab));
rtnl_rttable_tab->tab[0] = "unspec";
rtnl_rttable_tab->tab[255] = "local";
rtnl_rttable_tab->tab[254] = "main";
rtnl_rttable_tab->tab[253] = "default";
- rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab->tab);
+ rtnl_tab_initialize("tables", rtnl_rttable_tab->tab);
}
-const char* FAST_FUNC rtnl_rttable_n2a(int id, char *buf)
+const char* FAST_FUNC rtnl_rttable_n2a(int id)
{
- if (id < 0 || id >= 256) {
- sprintf(buf, "%d", id);
- return buf;
+ if (id < 0 || id > RT_TABLE_MAX) {
+ return itoa(id);
}
rtnl_rttable_initialize();
if (rtnl_rttable_tab->tab[id])
return rtnl_rttable_tab->tab[id];
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "%d", id);
- return buf;
+ return itoa(id);
}
int FAST_FUNC rtnl_rttable_a2n(uint32_t *id, char *arg)
diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h
index e73aa85..29932d6 100644
--- a/networking/libiproute/rt_names.h
+++ b/networking/libiproute/rt_names.h
@@ -4,12 +4,11 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-/* buf is SPRINT_BSIZE big */
-extern const char* rtnl_rtprot_n2a(int id, char *buf) FAST_FUNC;
-extern const char* rtnl_rtscope_n2a(int id, char *buf) FAST_FUNC;
-extern const char* rtnl_rtrealm_n2a(int id, char *buf) FAST_FUNC;
-extern const char* rtnl_dsfield_n2a(int id, char *buf) FAST_FUNC;
-extern const char* rtnl_rttable_n2a(int id, char *buf) FAST_FUNC;
+extern const char* rtnl_rtprot_n2a(int id) FAST_FUNC;
+extern const char* rtnl_rtscope_n2a(int id) FAST_FUNC;
+extern const char* rtnl_rtrealm_n2a(int id) FAST_FUNC;
+extern const char* rtnl_dsfield_n2a(int id) FAST_FUNC;
+extern const char* rtnl_rttable_n2a(int id) FAST_FUNC;
extern int rtnl_rtprot_a2n(uint32_t *id, char *arg) FAST_FUNC;
extern int rtnl_rtscope_a2n(uint32_t *id, char *arg) FAST_FUNC;
extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg) FAST_FUNC;
diff --git a/networking/libiproute/rtm_map.c b/networking/libiproute/rtm_map.c
index 3bab53b..c763da0 100644
--- a/networking/libiproute/rtm_map.c
+++ b/networking/libiproute/rtm_map.c
@@ -12,7 +12,7 @@
#include "rt_names.h"
#include "utils.h"
-const char* FAST_FUNC rtnl_rtntype_n2a(int id, char *buf)
+const char* FAST_FUNC rtnl_rtntype_n2a(int id)
{
switch (id) {
case RTN_UNSPEC:
@@ -40,9 +40,7 @@ const char* FAST_FUNC rtnl_rtntype_n2a(int id, char *buf)
case RTN_XRESOLVE:
return "xresolve";
default:
- /* buf is SPRINT_BSIZE big */
- sprintf(buf, "%d", id);
- return buf;
+ return itoa(id);
}
}
diff --git a/networking/libiproute/rtm_map.h b/networking/libiproute/rtm_map.h
index 4377bd5..63b6651 100644
--- a/networking/libiproute/rtm_map.h
+++ b/networking/libiproute/rtm_map.h
@@ -4,7 +4,7 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-const char *rtnl_rtntype_n2a(int id, char *buf) FAST_FUNC;
+const char *rtnl_rtntype_n2a(int id) FAST_FUNC;
int rtnl_rtntype_a2n(int *id, char *arg) FAST_FUNC;
int get_rt_realms(uint32_t *realms, char *arg) FAST_FUNC;
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
index 37c8f43..779c6a3 100644
--- a/networking/libiproute/utils.c
+++ b/networking/libiproute/utils.c
@@ -13,6 +13,28 @@
#include "utils.h"
#include "inet_common.h"
+unsigned get_hz(void)
+{
+ static unsigned hz_internal;
+ FILE *fp;
+
+ if (hz_internal)
+ return hz_internal;
+
+ fp = fopen_for_read("/proc/net/psched");
+ if (fp) {
+ unsigned nom, denom;
+
+ if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+ if (nom == 1000000)
+ hz_internal = denom;
+ fclose(fp);
+ }
+ if (!hz_internal)
+ hz_internal = bb_clk_tck();
+ return hz_internal;
+}
+
unsigned get_unsigned(char *arg, const char *errmsg)
{
unsigned long res;
@@ -25,7 +47,7 @@ unsigned get_unsigned(char *arg, const char *errmsg)
return res;
}
}
- invarg(arg, errmsg); /* does not return */
+ invarg_1_to_2(arg, errmsg); /* does not return */
}
uint32_t get_u32(char *arg, const char *errmsg)
@@ -40,7 +62,7 @@ uint32_t get_u32(char *arg, const char *errmsg)
return res;
}
}
- invarg(arg, errmsg); /* does not return */
+ invarg_1_to_2(arg, errmsg); /* does not return */
}
uint16_t get_u16(char *arg, const char *errmsg)
@@ -55,7 +77,7 @@ uint16_t get_u16(char *arg, const char *errmsg)
return res;
}
}
- invarg(arg, errmsg); /* does not return */
+ invarg_1_to_2(arg, errmsg); /* does not return */
}
int get_addr_1(inet_prefix *addr, char *name, int family)
@@ -208,12 +230,12 @@ uint32_t get_addr32(char *name)
void incomplete_command(void)
{
- bb_error_msg_and_die("command line is not complete, try option \"help\"");
+ bb_error_msg_and_die("command line is not complete, try \"help\"");
}
-void invarg(const char *arg, const char *opt)
+void invarg_1_to_2(const char *arg, const char *opt)
{
- bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
+ bb_error_msg_and_die(bb_msg_invalid_arg_to, arg, opt);
}
void duparg(const char *key, const char *arg)
@@ -254,20 +276,21 @@ int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits)
return 0;
}
-const char *rt_addr_n2a(int af,
- void *addr, char *buf, int buflen)
+const char *rt_addr_n2a(int af, void *addr)
{
switch (af) {
case AF_INET:
case AF_INET6:
- return inet_ntop(af, addr, buf, buflen);
+ return inet_ntop(af, addr,
+ auto_string(xzalloc(INET6_ADDRSTRLEN)), INET6_ADDRSTRLEN
+ );
default:
return "???";
}
}
#ifdef RESOLVE_HOSTNAMES
-const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+const char *format_host(int af, int len, void *addr)
{
if (resolve_hosts) {
struct hostent *h_ent;
@@ -286,11 +309,10 @@ const char *format_host(int af, int len, void *addr, char *buf, int buflen)
if (len > 0) {
h_ent = gethostbyaddr(addr, len, af);
if (h_ent != NULL) {
- safe_strncpy(buf, h_ent->h_name, buflen);
- return buf;
+ return auto_string(xstrdup(h_ent->h_name));
}
}
}
- return rt_addr_n2a(af, addr, buf, buflen);
+ return rt_addr_n2a(af, addr);
}
#endif
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
index 5fb4a86..408d5f6 100644
--- a/networking/libiproute/utils.h
+++ b/networking/libiproute/utils.h
@@ -66,15 +66,15 @@ extern unsigned get_unsigned(char *arg, const char *errmsg);
extern uint32_t get_u32(char *arg, const char *errmsg);
extern uint16_t get_u16(char *arg, const char *errmsg);
-extern const char *rt_addr_n2a(int af, void *addr, char *buf, int buflen);
+extern const char *rt_addr_n2a(int af, void *addr);
#ifdef RESOLVE_HOSTNAMES
-extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+extern const char *format_host(int af, int len, void *addr);
#else
-#define format_host(af, len, addr, buf, buflen) \
- rt_addr_n2a(af, addr, buf, buflen)
+#define format_host(af, len, addr) \
+ rt_addr_n2a(af, addr)
#endif
-void invarg(const char *, const char *) NORETURN;
+void invarg_1_to_2(const char *, const char *) NORETURN;
void duparg(const char *, const char *) NORETURN;
void duparg2(const char *, const char *) NORETURN;
int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
@@ -85,6 +85,8 @@ int dnet_pton(int af, const char *src, void *addr);
const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
int ipx_pton(int af, const char *src, void *addr);
+unsigned get_hz(void);
+
POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/networking/nameif.c b/networking/nameif.c
index 9a8846d..cffd5bf 100644
--- a/networking/nameif.c
+++ b/networking/nameif.c
@@ -40,6 +40,10 @@
//config: new_interface_name mac=00:80:C8:38:91:B5
//config: new_interface_name 00:80:C8:38:91:B5
+//applet:IF_NAMEIF(APPLET(nameif, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NAMEIF) += nameif.o
+
//usage:#define nameif_trivial_usage
//usage: IF_NOT_FEATURE_NAMEIF_EXTENDED(
//usage: "[-s] [-c FILE] [IFNAME HWADDR]..."
@@ -161,19 +165,19 @@ static void nameif_parse_selector(ethtable_t *ch, char *selector)
if (*next)
*next++ = '\0';
/* Check for selectors, mac= is assumed */
- if (strncmp(selector, "bus=", 4) == 0) {
+ if (is_prefixed_with(selector, "bus=")) {
ch->bus_info = xstrdup(selector + 4);
found_selector++;
- } else if (strncmp(selector, "driver=", 7) == 0) {
+ } else if (is_prefixed_with(selector, "driver=")) {
ch->driver = xstrdup(selector + 7);
found_selector++;
- } else if (strncmp(selector, "phyaddr=", 8) == 0) {
+ } else if (is_prefixed_with(selector, "phyaddr=")) {
ch->phy_address = xatoi_positive(selector + 8);
found_selector++;
} else {
#endif
lmac = xmalloc(ETH_ALEN);
- ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) != 0 ? 0 : 4), lmac);
+ ch->mac = ether_aton_r(selector + (is_prefixed_with(selector, "mac=") ? 4 : 0), lmac);
if (ch->mac == NULL)
bb_error_msg_and_die("can't parse %s", selector);
#if ENABLE_FEATURE_NAMEIF_EXTENDED
diff --git a/networking/nbd-client.c b/networking/nbd-client.c
index cadda52..70869d6 100644
--- a/networking/nbd-client.c
+++ b/networking/nbd-client.c
@@ -57,9 +57,8 @@ int nbdclient_main(int argc, char **argv)
uint32_t flags;
char data[124];
} nbd_header;
- struct bug_check {
- char c[offsetof(struct nbd_header_t, data) == 8+8+8+4 ? 1 : -1];
- };
+
+ BUILD_BUG_ON(offsetof(struct nbd_header_t, data) != 8+8+8+4);
// Parse command line stuff (just a stub now)
if (argc != 4)
@@ -83,7 +82,7 @@ int nbdclient_main(int argc, char **argv)
// Find and connect to server
sock = create_and_connect_stream_or_die(host, xatou16(port));
- setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
+ setsockopt_1(sock, IPPROTO_TCP, TCP_NODELAY);
// Log on to the server
xread(sock, &nbd_header, 8+8+8+4 + 124);
diff --git a/networking/nc.c b/networking/nc.c
index 6105db1..50302d8 100644
--- a/networking/nc.c
+++ b/networking/nc.c
@@ -6,9 +6,6 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-
-#include "libbb.h"
-
//config:config NC
//config: bool "nc"
//config: default y
@@ -42,6 +39,12 @@
//config: -s ADDR, -n, -u, -v, -o FILE, -z options, but loses
//config: busybox-specific extensions: -f FILE.
+//applet:IF_NC(APPLET(nc, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NC) += nc.o
+
+#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_NC_110_COMPAT
# include "nc_bloaty.c"
#else
@@ -238,6 +241,8 @@ int nc_main(int argc, char **argv)
FD_SET(cfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
+#define iobuf bb_common_bufsiz1
+ setup_common_bufsiz();
for (;;) {
int fd;
int ofd;
@@ -248,11 +253,10 @@ int nc_main(int argc, char **argv)
if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0)
bb_perror_msg_and_die("select");
-#define iobuf bb_common_bufsiz1
fd = STDIN_FILENO;
while (1) {
if (FD_ISSET(fd, &testfds)) {
- nread = safe_read(fd, iobuf, sizeof(iobuf));
+ nread = safe_read(fd, iobuf, COMMON_BUFSIZE);
if (fd == cfd) {
if (nread < 1)
exit(EXIT_SUCCESS);
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index b28d05f..192e42f 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -794,8 +794,8 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
e_found:
// -g -G -t -r deleted, unimplemented -a deleted too
- opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */
- getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk")
+ opt_complementary = "?2:vv:ll"; /* max 2 params; -v and -l are counters; -w N */
+ getopt32(argv, "np:s:uvw:+" IF_NC_SERVER("lk")
IF_NC_EXTRA("i:o:z"),
&str_p, &str_s, &o_wait
IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
@@ -863,8 +863,8 @@ int nc_main(int argc UNUSED_PARAM, char **argv)
xbind(netfd, &ouraddr->u.sa, ouraddr->len);
}
#if 0
- setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
- setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
+ setsockopt_SOL_SOCKET_int(netfd, SO_RCVBUF, o_rcvbuf);
+ setsockopt_SOL_SOCKET_int(netfd, SO_SNDBUF, o_sndbuf);
#endif
#ifdef BLOAT
diff --git a/networking/netstat.c b/networking/netstat.c
index f80b845..90da6cd 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -13,6 +13,32 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config NETSTAT
+//config: bool "netstat"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: netstat prints information about the Linux networking subsystem.
+//config:
+//config:config FEATURE_NETSTAT_WIDE
+//config: bool "Enable wide netstat output"
+//config: default y
+//config: depends on NETSTAT
+//config: help
+//config: Add support for wide columns. Useful when displaying IPv6 addresses
+//config: (-W option).
+//config:
+//config:config FEATURE_NETSTAT_PRG
+//config: bool "Enable PID/Program name output"
+//config: default y
+//config: depends on NETSTAT
+//config: help
+//config: Add support for -p flag to print out PID and program name.
+//config: +700 bytes of code.
+
+//applet:IF_NETSTAT(APPLET(netstat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NETSTAT) += netstat.o
#include "libbb.h"
#include "inet_common.h"
@@ -119,7 +145,7 @@ typedef enum {
#define ADDR_NORMAL_WIDTH 23
/* When there are IPv6 connections the IPv6 addresses will be
* truncated to none-recognition. The '-W' option makes the
- * address columns wide enough to accomodate for longest possible
+ * address columns wide enough to accommodate for longest possible
* IPv6 addresses, i.e. addresses of the form
* xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
*/
@@ -228,12 +254,12 @@ static long extract_socket_inode(const char *lname)
{
long inode = -1;
- if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
+ if (is_prefixed_with(lname, "socket:[")) {
/* "socket:[12345]", extract the "12345" as inode */
inode = bb_strtoul(lname + sizeof("socket:[")-1, (char**)&lname, 0);
if (*lname != ']')
inode = -1;
- } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
+ } else if (is_prefixed_with(lname, "[0000]:")) {
/* "[0000]:12345", extract the "12345" as inode */
inode = bb_strtoul(lname + sizeof("[0000]:")-1, NULL, 0);
if (errno) /* not NUL terminated? */
@@ -622,7 +648,7 @@ static int FAST_FUNC unix_do_one(char *line)
/* TODO: currently we stop at first NUL byte. Is it a problem? */
line += path_ofs;
- *strchrnul(line, '\n') = '\0';
+ chomp(line);
while (*line)
fputc_printable(*line++, stdout);
bb_putchar('\n');
diff --git a/networking/nslookup.c b/networking/nslookup.c
index 202356b..8be5f34 100644
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
@@ -10,6 +10,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config NSLOOKUP
+//config: bool "nslookup"
+//config: default y
+//config: help
+//config: nslookup is a tool to query Internet name servers.
+
+//applet:IF_NSLOOKUP(APPLET(nslookup, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NSLOOKUP) += nslookup.o
//usage:#define nslookup_trivial_usage
//usage: "[HOST] [SERVER]"
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 1c1c38f..1532669 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -1,35 +1,74 @@
/*
* NTP client/server, based on OpenNTPD 3.9p1
*
- * Author: Adam Tkac <vonsch@gmail.com>
+ * Busybox port author: Adam Tkac (C) 2009 <vonsch@gmail.com>
*
- * Licensed under GPLv2, see file LICENSE in this source tree.
+ * OpenNTPd 3.9p1 copyright holders:
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2004 Alexander Guy <alexander.guy@andern.org>
+ *
+ * OpenNTPd code is licensed under ISC-style licence:
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ ***********************************************************************
*
* Parts of OpenNTPD clock syncronization code is replaced by
- * code which is based on ntp-4.2.6, whuch carries the following
+ * code which is based on ntp-4.2.6, which carries the following
* copyright notice:
*
- ***********************************************************************
- * *
- * Copyright (c) University of Delaware 1992-2009 *
- * *
- * Permission to use, copy, modify, and distribute this software and *
- * its documentation for any purpose with or without fee is hereby *
- * granted, provided that the above copyright notice appears in all *
- * copies and that both the copyright notice and this permission *
- * notice appear in supporting documentation, and that the name *
- * University of Delaware not be used in advertising or publicity *
- * pertaining to distribution of the software without specific, *
- * written prior permission. The University of Delaware makes no *
- * representations about the suitability this software for any *
- * purpose. It is provided "as is" without express or implied *
- * warranty. *
- * *
+ * Copyright (c) University of Delaware 1992-2009
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose with or without fee is hereby
+ * granted, provided that the above copyright notice appears in all
+ * copies and that both the copyright notice and this permission
+ * notice appear in supporting documentation, and that the name
+ * University of Delaware not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The University of Delaware makes no
+ * representations about the suitability this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
***********************************************************************
*/
+//config:config NTPD
+//config: bool "ntpd"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The NTP client/server daemon.
+//config:
+//config:config FEATURE_NTPD_SERVER
+//config: bool "Make ntpd usable as a NTP server"
+//config: default y
+//config: depends on NTPD
+//config: help
+//config: Make ntpd usable as a NTP server. If you disable this option
+//config: ntpd will be usable only as a NTP client.
+//config:
+//config:config FEATURE_NTPD_CONF
+//config: bool "Make ntpd understand /etc/ntp.conf"
+//config: default y
+//config: depends on NTPD
+//config: help
+//config: Make ntpd look in /etc/ntp.conf for peers. Only "server address"
+//config: is supported.
+
+//applet:IF_NTPD(APPLET(ntpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NTPD) += ntpd.o
//usage:#define ntpd_trivial_usage
-//usage: "[-dnqNw"IF_FEATURE_NTPD_SERVER("l")"] [-S PROG] [-p PEER]..."
+//usage: "[-dnqNw"IF_FEATURE_NTPD_SERVER("l -I IFACE")"] [-S PROG] [-p PEER]..."
//usage:#define ntpd_full_usage "\n\n"
//usage: "NTP client/server\n"
//usage: "\n -d Verbose"
@@ -37,11 +76,20 @@
//usage: "\n -q Quit after clock is set"
//usage: "\n -N Run at high priority"
//usage: "\n -w Do not set time (only query peers), implies -n"
-//usage: IF_FEATURE_NTPD_SERVER(
-//usage: "\n -l Run as server on port 123"
-//usage: )
//usage: "\n -S PROG Run PROG after stepping time, stratum change, and every 11 mins"
//usage: "\n -p PEER Obtain time from PEER (may be repeated)"
+//usage: IF_FEATURE_NTPD_CONF(
+//usage: "\n If -p is not given, 'server HOST' lines"
+//usage: "\n from /etc/ntp.conf are used"
+//usage: )
+//usage: IF_FEATURE_NTPD_SERVER(
+//usage: "\n -l Also run as server on port 123"
+//usage: "\n -I IFACE Bind server to IFACE, implies -l"
+//usage: )
+
+// -l and -p options are not compatible with "standard" ntpd:
+// it has them as "-l logfile" and "-p pidfile".
+// -S and -w are not compat either, "standard" ntpd has no such opts.
#include "libbb.h"
#include <math.h>
@@ -57,9 +105,6 @@ extern int adjtimex (struct timex *);
#ifndef IPTOS_LOWDELAY
# define IPTOS_LOWDELAY 0x10
#endif
-#ifndef IP_PKTINFO
-# error "Sorry, your kernel has to support IP_PKTINFO"
-#endif
/* Verbosity control (max level of -dddd options accepted).
@@ -99,7 +144,7 @@ extern int adjtimex (struct timex *);
*
* Made some changes to speed up re-syncing after our clock goes bad
* (tested with suspending my laptop):
- * - if largish offset (>= STEP_THRESHOLD * 8 == 1 sec) is seen
+ * - if largish offset (>= STEP_THRESHOLD == 1 sec) is seen
* from a peer, schedule next query for this peer soon
* without drastically lowering poll interval for everybody.
* This makes us collect enough data for step much faster:
@@ -110,36 +155,47 @@ extern int adjtimex (struct timex *);
* datapoints after the step.
*/
-#define RETRY_INTERVAL 5 /* on error, retry in N secs */
-#define RESPONSE_INTERVAL 15 /* wait for reply up to N secs */
#define INITIAL_SAMPLES 4 /* how many samples do we want for init */
#define BAD_DELAY_GROWTH 4 /* drop packet if its delay grew by more than this */
-/* Clock discipline parameters and constants */
+#define RETRY_INTERVAL 32 /* on send/recv error, retry in N secs (need to be power of 2) */
+#define NOREPLY_INTERVAL 512 /* sent, but got no reply: cap next query by this many seconds */
+#define RESPONSE_INTERVAL 16 /* wait for reply up to N secs */
/* Step threshold (sec). std ntpd uses 0.128.
- * Using exact power of 2 (1/8) results in smaller code */
-#define STEP_THRESHOLD 0.125
-#define WATCH_THRESHOLD 128 /* stepout threshold (sec). std ntpd uses 900 (11 mins (!)) */
+ */
+#define STEP_THRESHOLD 1
+/* Slew threshold (sec): adjtimex() won't accept offsets larger than this.
+ * Using exact power of 2 (1/8) results in smaller code
+ */
+#define SLEW_THRESHOLD 0.125
+/* Stepout threshold (sec). std ntpd uses 900 (11 mins (!)) */
+#define WATCH_THRESHOLD 128
/* NB: set WATCH_THRESHOLD to ~60 when debugging to save time) */
//UNUSED: #define PANIC_THRESHOLD 1000 /* panic threshold (sec) */
+/*
+ * If we got |offset| > BIGOFF from a peer, cap next query interval
+ * for this peer by this many seconds:
+ */
+#define BIGOFF STEP_THRESHOLD
+#define BIGOFF_INTERVAL (1 << 7) /* 128 s */
+
#define FREQ_TOLERANCE 0.000015 /* frequency tolerance (15 PPM) */
#define BURSTPOLL 0 /* initial poll */
#define MINPOLL 5 /* minimum poll interval. std ntpd uses 6 (6: 64 sec) */
-/* If we got largish offset from a peer, cap next query interval
- * for this peer by this many seconds:
- */
-#define BIGOFF_INTERVAL (1 << 6)
-/* If offset > discipline_jitter * POLLADJ_GATE, and poll interval is >= 2^BIGPOLL,
- * then it is decreased _at once_. (If < 2^BIGPOLL, it will be decreased _eventually_).
+/*
+ * If offset > discipline_jitter * POLLADJ_GATE, and poll interval is > 2^BIGPOLL,
+ * then it is decreased _at once_. (If <= 2^BIGPOLL, it will be decreased _eventually_).
*/
-#define BIGPOLL 10 /* 2^10 sec ~= 17 min */
+#define BIGPOLL 9 /* 2^9 sec ~= 8.5 min */
#define MAXPOLL 12 /* maximum poll interval (12: 1.1h, 17: 36.4h). std ntpd uses 17 */
-/* Actively lower poll when we see such big offsets.
- * With STEP_THRESHOLD = 0.125, it means we try to sync more aggressively
- * if offset increases over ~0.04 sec */
-#define POLLDOWN_OFFSET (STEP_THRESHOLD / 3)
+/*
+ * Actively lower poll when we see such big offsets.
+ * With SLEW_THRESHOLD = 0.125, it means we try to sync more aggressively
+ * if offset increases over ~0.04 sec
+ */
+//#define POLLDOWN_OFFSET (SLEW_THRESHOLD / 3)
#define MINDISP 0.01 /* minimum dispersion (sec) */
#define MAXDISP 16 /* maximum dispersion (sec) */
#define MAXSTRAT 16 /* maximum stratum (infinity metric) */
@@ -254,6 +310,9 @@ typedef struct {
* or when receive times out (if p_fd >= 0): */
double next_action_time;
double p_xmttime;
+ double p_raw_delay;
+ /* p_raw_delay is set even by "high delay" packets */
+ /* lastpkt_delay isn't */
double lastpkt_recv_time;
double lastpkt_delay;
double lastpkt_rootdelay;
@@ -265,6 +324,7 @@ typedef struct {
datapoint_t filter_datapoint[NUM_DATAPOINTS];
/* last sent packet: */
msg_t p_xmt_msg;
+ char p_hostname[1];
} peer_t;
@@ -282,6 +342,7 @@ enum {
OPT_p = (1 << 5),
OPT_S = (1 << 6),
OPT_l = (1 << 7) * ENABLE_FEATURE_NTPD_SERVER,
+ OPT_I = (1 << 8) * ENABLE_FEATURE_NTPD_SERVER,
/* We hijack some bits for other purposes */
OPT_qq = (1 << 31),
};
@@ -300,6 +361,7 @@ struct globals {
llist_t *ntp_peers;
#if ENABLE_FEATURE_NTPD_SERVER
int listen_fd;
+ char *if_name;
# define G_listen_fd (G.listen_fd)
#else
# define G_listen_fd (-1)
@@ -351,8 +413,6 @@ struct globals {
*/
#define G_precision_sec 0.002
uint8_t stratum;
- /* Bool. After set to 1, never goes back to 0: */
- smallint initial_poll_complete;
#define STATE_NSET 0 /* initial state, "nothing is set" */
//#define STATE_FSET 1 /* frequency set from file */
@@ -381,8 +441,6 @@ struct globals {
};
#define G (*ptr_to_globals)
-static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
-
#define VERB1 if (MAX_VERBOSE && G.verbose)
#define VERB2 if (MAX_VERBOSE >= 2 && G.verbose >= 2)
@@ -698,11 +756,11 @@ static void
reset_peer_stats(peer_t *p, double offset)
{
int i;
- bool small_ofs = fabs(offset) < 16 * STEP_THRESHOLD;
+ bool small_ofs = fabs(offset) < STEP_THRESHOLD;
/* Used to set p->filter_datapoint[i].d_dispersion = MAXDISP
* and clear reachable bits, but this proved to be too agressive:
- * after step (tested with suspinding laptop for ~30 secs),
+ * after step (tested with suspending laptop for ~30 secs),
* this caused all previous data to be considered invalid,
* making us needing to collect full ~8 datapoins per peer
* after step in order to start trusting them.
@@ -739,17 +797,58 @@ reset_peer_stats(peer_t *p, double offset)
}
static void
-add_peers(char *s)
+resolve_peer_hostname(peer_t *p, int loop_on_fail)
+{
+ len_and_sockaddr *lsa;
+
+ again:
+ lsa = host2sockaddr(p->p_hostname, 123);
+ if (!lsa) {
+ /* error message already emitted by host2sockaddr() */
+ if (!loop_on_fail)
+ return;
+//FIXME: do this to avoid infinite looping on typo in a hostname?
+//well... in which case, what is a good value for loop_on_fail?
+ //if (--loop_on_fail == 0)
+ // xfunc_die();
+ sleep(5);
+ goto again;
+ }
+ free(p->p_lsa);
+ free(p->p_dotted);
+ p->p_lsa = lsa;
+ p->p_dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+}
+
+static void
+add_peers(const char *s)
{
+ llist_t *item;
peer_t *p;
- p = xzalloc(sizeof(*p));
- p->p_lsa = xhost2sockaddr(s, 123);
- p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
+ p = xzalloc(sizeof(*p) + strlen(s));
+ strcpy(p->p_hostname, s);
+ resolve_peer_hostname(p, /*loop_on_fail=*/ 1);
+
+ /* Names like N.<country2chars>.pool.ntp.org are randomly resolved
+ * to a pool of machines. Sometimes different N's resolve to the same IP.
+ * It is not useful to have two peers with same IP. We skip duplicates.
+ */
+ for (item = G.ntp_peers; item != NULL; item = item->link) {
+ peer_t *pp = (peer_t *) item->data;
+ if (strcmp(p->p_dotted, pp->p_dotted) == 0) {
+ bb_error_msg("duplicate peer %s (%s)", s, p->p_dotted);
+ free(p->p_lsa);
+ free(p->p_dotted);
+ free(p);
+ return;
+ }
+ }
+
p->p_fd = -1;
p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
p->next_action_time = G.cur_time; /* = set_next(p, 0); */
- reset_peer_stats(p, 16 * STEP_THRESHOLD);
+ reset_peer_stats(p, STEP_THRESHOLD);
llist_add_to(&G.ntp_peers, p);
G.peer_cnt++;
@@ -813,7 +912,7 @@ send_query_to_peer(peer_t *p)
#if ENABLE_FEATURE_IPV6
if (family == AF_INET)
#endif
- setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+ setsockopt_int(fd, IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY);
free(local_lsa);
}
@@ -836,8 +935,8 @@ send_query_to_peer(peer_t *p)
*
* Save the real transmit timestamp locally.
*/
- p->p_xmt_msg.m_xmttime.int_partl = random();
- p->p_xmt_msg.m_xmttime.fractionl = random();
+ p->p_xmt_msg.m_xmttime.int_partl = rand();
+ p->p_xmt_msg.m_xmttime.fractionl = rand();
p->p_xmttime = gettime1900d();
/* Were doing it only if sendto worked, but
@@ -968,6 +1067,16 @@ step_time(double offset)
}
}
+static void clamp_pollexp_and_set_MAXSTRAT(void)
+{
+ if (G.poll_exp < MINPOLL)
+ G.poll_exp = MINPOLL;
+ if (G.poll_exp > BIGPOLL)
+ G.poll_exp = BIGPOLL;
+ G.polladj_count = 0;
+ G.stratum = MAXSTRAT;
+}
+
/*
* Selection and clustering, and their helpers
@@ -1049,7 +1158,7 @@ select_and_cluster(void)
num_points = 0;
item = G.ntp_peers;
- if (G.initial_poll_complete) while (item != NULL) {
+ while (item != NULL) {
double rd, offset;
p = (peer_t *) item->data;
@@ -1334,7 +1443,9 @@ update_local_clock(peer_t *p)
#if !USING_KERNEL_PLL_LOOP
double freq_drift;
#endif
+#if !USING_KERNEL_PLL_LOOP || USING_INITIAL_FREQ_ESTIMATION
double since_last_update;
+#endif
double etemp, dtemp;
abs_offset = fabs(offset);
@@ -1362,7 +1473,9 @@ update_local_clock(peer_t *p)
* action is and defines how the system reacts to large time
* and frequency errors.
*/
+#if !USING_KERNEL_PLL_LOOP || USING_INITIAL_FREQ_ESTIMATION
since_last_update = recv_time - G.reftime;
+#endif
#if !USING_KERNEL_PLL_LOOP
freq_drift = 0;
#endif
@@ -1445,9 +1558,7 @@ update_local_clock(peer_t *p)
exit(0);
}
- G.polladj_count = 0;
- G.poll_exp = MINPOLL;
- G.stratum = MAXSTRAT;
+ clamp_pollexp_and_set_MAXSTRAT();
run_script("step", offset);
@@ -1461,14 +1572,12 @@ update_local_clock(peer_t *p)
#endif
abs_offset = offset = 0;
set_new_values(STATE_SYNC, offset, recv_time);
-
} else { /* abs_offset <= STEP_THRESHOLD */
- if (G.poll_exp < MINPOLL && G.initial_poll_complete) {
- VERB4 bb_error_msg("small offset:%+f, disabling burst mode", offset);
- G.polladj_count = 0;
- G.poll_exp = MINPOLL;
- }
+ /* The ratio is calculated before jitter is updated to make
+ * poll adjust code more sensitive to large offsets.
+ */
+ G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter;
/* Compute the clock jitter as the RMS of exponentially
* weighted offset differences. Used by the poll adjust code.
@@ -1476,6 +1585,8 @@ update_local_clock(peer_t *p)
etemp = SQUARE(G.discipline_jitter);
dtemp = SQUARE(offset - G.last_update_offset);
G.discipline_jitter = SQRT(etemp + (dtemp - etemp) / AVG);
+ if (G.discipline_jitter < G_precision_sec)
+ G.discipline_jitter = G_precision_sec;
switch (G.discipline_state) {
case STATE_NSET:
@@ -1552,10 +1663,6 @@ update_local_clock(peer_t *p)
}
}
- if (G.discipline_jitter < G_precision_sec)
- G.discipline_jitter = G_precision_sec;
- G.offset_to_jitter_ratio = abs_offset / G.discipline_jitter;
-
G.reftime = G.cur_time;
G.ntp_status = p->lastpkt_status;
G.refid = p->lastpkt_refid;
@@ -1608,14 +1715,7 @@ update_local_clock(peer_t *p)
tmx.freq = G.discipline_freq_drift * 65536e6;
#endif
tmx.modes = ADJ_OFFSET | ADJ_STATUS | ADJ_TIMECONST;// | ADJ_MAXERROR | ADJ_ESTERROR;
- tmx.offset = (offset * 1000000); /* usec */
- tmx.status = STA_PLL;
- if (G.ntp_status & LI_PLUSSEC)
- tmx.status |= STA_INS;
- if (G.ntp_status & LI_MINUSSEC)
- tmx.status |= STA_DEL;
-
- tmx.constant = G.poll_exp - 4;
+ tmx.constant = (int)G.poll_exp - 4;
/* EXPERIMENTAL.
* The below if statement should be unnecessary, but...
* It looks like Linux kernel's PLL is far too gentle in changing
@@ -1626,8 +1726,27 @@ update_local_clock(peer_t *p)
* To be on a safe side, let's do it only if offset is significantly
* larger than jitter.
*/
- if (tmx.constant > 0 && G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
+ if (G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
tmx.constant--;
+ tmx.offset = (long)(offset * 1000000); /* usec */
+ if (SLEW_THRESHOLD < STEP_THRESHOLD) {
+ if (tmx.offset > (long)(SLEW_THRESHOLD * 1000000)) {
+ tmx.offset = (long)(SLEW_THRESHOLD * 1000000);
+ tmx.constant--;
+ }
+ if (tmx.offset < -(long)(SLEW_THRESHOLD * 1000000)) {
+ tmx.offset = -(long)(SLEW_THRESHOLD * 1000000);
+ tmx.constant--;
+ }
+ }
+ if (tmx.constant < 0)
+ tmx.constant = 0;
+
+ tmx.status = STA_PLL;
+ if (G.ntp_status & LI_PLUSSEC)
+ tmx.status |= STA_INS;
+ if (G.ntp_status & LI_MINUSSEC)
+ tmx.status |= STA_DEL;
//tmx.esterror = (uint32_t)(clock_jitter * 1e6);
//tmx.maxerror = (uint32_t)((sys_rootdelay / 2 + sys_rootdisp) * 1e6);
@@ -1640,8 +1759,14 @@ update_local_clock(peer_t *p)
VERB4 bb_error_msg("adjtimex:%d freq:%ld offset:%+ld status:0x%x",
rc, tmx.freq, tmx.offset, tmx.status);
G.kernel_freq_drift = tmx.freq / 65536;
- VERB2 bb_error_msg("update from:%s offset:%+f jitter:%f clock drift:%+.3fppm tc:%d",
- p->p_dotted, offset, G.discipline_jitter, (double)tmx.freq / 65536, (int)tmx.constant);
+ VERB2 bb_error_msg("update from:%s offset:%+f delay:%f jitter:%f clock drift:%+.3fppm tc:%d",
+ p->p_dotted,
+ offset,
+ p->lastpkt_delay,
+ G.discipline_jitter,
+ (double)tmx.freq / 65536,
+ (int)tmx.constant
+ );
return 1; /* "ok to increase poll interval" */
}
@@ -1652,28 +1777,52 @@ update_local_clock(peer_t *p)
* (helpers first)
*/
static unsigned
-retry_interval(void)
+poll_interval(int upper_bound)
{
- /* Local problem, want to retry soon */
- unsigned interval, r;
- interval = RETRY_INTERVAL;
- r = random();
- interval += r % (unsigned)(RETRY_INTERVAL / 4);
- VERB4 bb_error_msg("chose retry interval:%u", interval);
+ unsigned interval, r, mask;
+ interval = 1 << G.poll_exp;
+ if (interval > upper_bound)
+ interval = upper_bound;
+ mask = ((interval-1) >> 4) | 1;
+ r = rand();
+ interval += r & mask; /* ~ random(0..1) * interval/16 */
+ VERB4 bb_error_msg("chose poll interval:%u (poll_exp:%d)", interval, G.poll_exp);
return interval;
}
-static unsigned
-poll_interval(int exponent)
+static void
+adjust_poll(int count)
{
- unsigned interval, r;
- exponent = G.poll_exp + exponent;
- if (exponent < 0)
- exponent = 0;
- interval = 1 << exponent;
- r = random();
- interval += ((r & (interval-1)) >> 4) + ((r >> 8) & 1); /* + 1/16 of interval, max */
- VERB4 bb_error_msg("chose poll interval:%u (poll_exp:%d exp:%d)", interval, G.poll_exp, exponent);
- return interval;
+ G.polladj_count += count;
+ if (G.polladj_count > POLLADJ_LIMIT) {
+ G.polladj_count = 0;
+ if (G.poll_exp < MAXPOLL) {
+ G.poll_exp++;
+ VERB4 bb_error_msg("polladj: discipline_jitter:%f ++poll_exp=%d",
+ G.discipline_jitter, G.poll_exp);
+ }
+ } else if (G.polladj_count < -POLLADJ_LIMIT || (count < 0 && G.poll_exp > BIGPOLL)) {
+ G.polladj_count = 0;
+ if (G.poll_exp > MINPOLL) {
+ llist_t *item;
+
+ G.poll_exp--;
+ /* Correct p->next_action_time in each peer
+ * which waits for sending, so that they send earlier.
+ * Old pp->next_action_time are on the order
+ * of t + (1 << old_poll_exp) + small_random,
+ * we simply need to subtract ~half of that.
+ */
+ for (item = G.ntp_peers; item != NULL; item = item->link) {
+ peer_t *pp = (peer_t *) item->data;
+ if (pp->p_fd < 0)
+ pp->next_action_time -= (1 << G.poll_exp);
+ }
+ VERB4 bb_error_msg("polladj: discipline_jitter:%f --poll_exp=%d",
+ G.discipline_jitter, G.poll_exp);
+ }
+ } else {
+ VERB4 bb_error_msg("polladj: count:%d", G.polladj_count);
+ }
}
static NOINLINE void
recv_and_process_peer_pkt(peer_t *p)
@@ -1682,7 +1831,8 @@ recv_and_process_peer_pkt(peer_t *p)
ssize_t size;
msg_t msg;
double T1, T2, T3, T4;
- double dv, offset;
+ double offset;
+ double prev_delay, delay;
unsigned interval;
datapoint_t *datapoint;
peer_t *q;
@@ -1693,19 +1843,23 @@ recv_and_process_peer_pkt(peer_t *p)
* ntp servers reply from their *other IP*.
* TODO: maybe we should check at least what we can: from.port == 123?
*/
+ recv_again:
size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT);
- if (size == -1) {
- bb_perror_msg("recv(%s) error", p->p_dotted);
- if (errno == EHOSTUNREACH || errno == EHOSTDOWN
- || errno == ENETUNREACH || errno == ENETDOWN
- || errno == ECONNREFUSED || errno == EADDRNOTAVAIL
- || errno == EAGAIN
- ) {
-//TODO: always do this?
- interval = retry_interval();
- goto set_next_and_ret;
- }
- xfunc_die();
+ if (size < 0) {
+ if (errno == EINTR)
+ /* Signal caught */
+ goto recv_again;
+ if (errno == EAGAIN)
+ /* There was no packet after all
+ * (poll() returning POLLIN for a fd
+ * is not a ironclad guarantee that data is there)
+ */
+ return;
+ /*
+ * If you need a different handling for a specific
+ * errno, always explain it in comment.
+ */
+ bb_perror_msg_and_die("recv(%s) error", p->p_dotted);
}
if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
@@ -1731,10 +1885,15 @@ recv_and_process_peer_pkt(peer_t *p)
|| msg.m_stratum == 0
|| msg.m_stratum > NTP_MAXSTRATUM
) {
-// TODO: stratum 0 responses may have commands in 32-bit m_refid field:
-// "DENY", "RSTR" - peer does not like us at all
-// "RATE" - peer is overloaded, reduce polling freq
bb_error_msg("reply from %s: peer is unsynced", p->p_dotted);
+ /*
+ * Stratum 0 responses may have commands in 32-bit m_refid field:
+ * "DENY", "RSTR" - peer does not like us at all,
+ * "RATE" - peer is overloaded, reduce polling freq.
+ * If poll interval is small, increase it.
+ */
+ if (G.poll_exp < BIGPOLL)
+ goto increase_interval;
goto pick_normal_interval;
}
@@ -1742,12 +1901,6 @@ recv_and_process_peer_pkt(peer_t *p)
// if (msg.m_rootdelay / 2 + msg.m_rootdisp >= MAXDISP || p->lastpkt_reftime > msg.m_xmt)
// return; /* invalid header values */
- p->lastpkt_status = msg.m_status;
- p->lastpkt_stratum = msg.m_stratum;
- p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay);
- p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp);
- p->lastpkt_refid = msg.m_refid;
-
/*
* From RFC 2030 (with a correction to the delay math):
*
@@ -1767,28 +1920,35 @@ recv_and_process_peer_pkt(peer_t *p)
T3 = lfp_to_d(msg.m_xmttime);
T4 = G.cur_time;
- p->lastpkt_recv_time = T4;
- VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
-
/* The delay calculation is a special case. In cases where the
* server and client clocks are running at different rates and
* with very fast networks, the delay can appear negative. In
* order to avoid violating the Principle of Least Astonishment,
* the delay is clamped not less than the system precision.
*/
- dv = p->lastpkt_delay;
- p->lastpkt_delay = (T4 - T1) - (T3 - T2);
- if (p->lastpkt_delay < G_precision_sec)
- p->lastpkt_delay = G_precision_sec;
+ delay = (T4 - T1) - (T3 - T2);
+ if (delay < G_precision_sec)
+ delay = G_precision_sec;
/*
* If this packet's delay is much bigger than the last one,
* it's better to just ignore it than use its much less precise value.
*/
- if (p->reachable_bits && p->lastpkt_delay > dv * BAD_DELAY_GROWTH) {
- bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, p->lastpkt_delay);
+ prev_delay = p->p_raw_delay;
+ p->p_raw_delay = delay;
+ if (p->reachable_bits && delay > prev_delay * BAD_DELAY_GROWTH) {
+ bb_error_msg("reply from %s: delay %f is too high, ignoring", p->p_dotted, delay);
goto pick_normal_interval;
}
+ p->lastpkt_delay = delay;
+ p->lastpkt_recv_time = T4;
+ VERB6 bb_error_msg("%s->lastpkt_recv_time=%f", p->p_dotted, p->lastpkt_recv_time);
+ p->lastpkt_status = msg.m_status;
+ p->lastpkt_stratum = msg.m_stratum;
+ p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay);
+ p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp);
+ p->lastpkt_refid = msg.m_refid;
+
p->datapoint_idx = p->reachable_bits ? (p->datapoint_idx + 1) % NUM_DATAPOINTS : 0;
datapoint = &p->filter_datapoint[p->datapoint_idx];
datapoint->d_recv_time = T4;
@@ -1822,21 +1982,36 @@ recv_and_process_peer_pkt(peer_t *p)
/* Muck with statictics and update the clock */
filter_datapoints(p);
q = select_and_cluster();
- rc = -1;
+ rc = 0;
if (q) {
- rc = 0;
if (!(option_mask32 & OPT_w)) {
rc = update_local_clock(q);
+#if 0
+//Disabled this because there is a case where largish offsets
+//are unavoidable: if network round-trip delay is, say, ~0.6s,
+//error in offset estimation would be ~delay/2 ~= 0.3s.
+//Thus, offsets will be usually in -0.3...0.3s range.
+//In this case, this code would keep poll interval small,
+//but it won't be helping.
+//BIGOFF check below deals with a case of seeing multi-second offsets.
+
/* If drift is dangerously large, immediately
* drop poll interval one step down.
*/
if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) {
VERB4 bb_error_msg("offset:%+f > POLLDOWN_OFFSET", q->filter_offset);
- goto poll_down;
+ adjust_poll(-POLLADJ_LIMIT * 3);
+ rc = 0;
}
+#endif
}
+ } else {
+ /* No peer selected.
+ * If poll interval is small, increase it.
+ */
+ if (G.poll_exp < BIGPOLL)
+ goto increase_interval;
}
- /* else: no peer selected, rc = -1: we want to poll more often */
if (rc != 0) {
/* Adjust the poll interval by comparing the current offset
@@ -1848,51 +2023,21 @@ recv_and_process_peer_pkt(peer_t *p)
if (rc > 0 && G.offset_to_jitter_ratio <= POLLADJ_GATE) {
/* was += G.poll_exp but it is a bit
* too optimistic for my taste at high poll_exp's */
- G.polladj_count += MINPOLL;
- if (G.polladj_count > POLLADJ_LIMIT) {
- G.polladj_count = 0;
- if (G.poll_exp < MAXPOLL) {
- G.poll_exp++;
- VERB4 bb_error_msg("polladj: discipline_jitter:%f ++poll_exp=%d",
- G.discipline_jitter, G.poll_exp);
- }
- } else {
- VERB4 bb_error_msg("polladj: incr:%d", G.polladj_count);
- }
+ increase_interval:
+ adjust_poll(MINPOLL);
} else {
- G.polladj_count -= G.poll_exp * 2;
- if (G.polladj_count < -POLLADJ_LIMIT || G.poll_exp >= BIGPOLL) {
- poll_down:
- G.polladj_count = 0;
- if (G.poll_exp > MINPOLL) {
- llist_t *item;
-
- G.poll_exp--;
- /* Correct p->next_action_time in each peer
- * which waits for sending, so that they send earlier.
- * Old pp->next_action_time are on the order
- * of t + (1 << old_poll_exp) + small_random,
- * we simply need to subtract ~half of that.
- */
- for (item = G.ntp_peers; item != NULL; item = item->link) {
- peer_t *pp = (peer_t *) item->data;
- if (pp->p_fd < 0)
- pp->next_action_time -= (1 << G.poll_exp);
- }
- VERB4 bb_error_msg("polladj: discipline_jitter:%f --poll_exp=%d",
- G.discipline_jitter, G.poll_exp);
- }
- } else {
- VERB4 bb_error_msg("polladj: decr:%d", G.polladj_count);
- }
+ VERB3 if (rc > 0)
+ bb_error_msg("want smaller interval: offset/jitter = %u",
+ G.offset_to_jitter_ratio);
+ adjust_poll(-G.poll_exp * 2);
}
}
/* Decide when to send new query for this peer */
pick_normal_interval:
- interval = poll_interval(0);
- if (fabs(offset) >= STEP_THRESHOLD * 8 && interval > BIGOFF_INTERVAL) {
- /* If we are synced, offsets are less than STEP_THRESHOLD,
+ interval = poll_interval(INT_MAX);
+ if (fabs(offset) >= BIGOFF && interval > BIGOFF_INTERVAL) {
+ /* If we are synced, offsets are less than SLEW_THRESHOLD,
* or at the very least not much larger than it.
* Now we see a largish one.
* Either this peer is feeling bad, or packet got corrupted,
@@ -1906,7 +2051,6 @@ recv_and_process_peer_pkt(peer_t *p)
interval = BIGOFF_INTERVAL;
}
- set_next_and_ret:
set_next(p, interval);
}
@@ -1939,6 +2083,13 @@ recv_and_process_client_pkt(void /*int fd*/)
goto bail;
}
+ /* Respond only to client and symmetric active packets */
+ if ((msg.m_status & MODE_MASK) != MODE_CLIENT
+ && (msg.m_status & MODE_MASK) != MODE_SYM_ACT
+ ) {
+ goto bail;
+ }
+
query_status = msg.m_status;
query_xmttime = msg.m_xmttime;
@@ -2071,12 +2222,13 @@ static NOINLINE void ntp_init(char **argv)
unsigned opts;
llist_t *peers;
- srandom(getpid());
+ srand(getpid());
if (getuid())
bb_error_msg_and_die("%s", bb_msg_you_must_be_root);
/* Set some globals */
+ G.discipline_jitter = G_precision_sec;
G.stratum = MAXSTRAT;
if (BURSTPOLL != 0)
G.poll_exp = BURSTPOLL; /* speeds up initial sync */
@@ -2084,40 +2236,75 @@ static NOINLINE void ntp_init(char **argv)
/* Parse options */
peers = NULL;
- opt_complementary = "dd:p::wn"; /* d: counter; p: list; -w implies -n */
+ opt_complementary = "dd:wn" /* -d: counter; -p: list; -w implies -n */
+ IF_FEATURE_NTPD_SERVER(":Il"); /* -I implies -l */
opts = getopt32(argv,
"nqNx" /* compat */
- "wp:S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
+ "wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
+ IF_FEATURE_NTPD_SERVER("I:") /* compat */
"d" /* compat */
"46aAbgL", /* compat, ignored */
- &peers, &G.script_name, &G.verbose);
- if (!(opts & (OPT_p|OPT_l)))
- bb_show_usage();
+ &peers,&G.script_name,
+#if ENABLE_FEATURE_NTPD_SERVER
+ &G.if_name,
+#endif
+ &G.verbose);
+
// if (opts & OPT_x) /* disable stepping, only slew is allowed */
// G.time_was_stepped = 1;
- if (peers) {
- while (peers)
- add_peers(llist_pop(&peers));
- } else {
- /* -l but no peers: "stratum 1 server" mode */
- G.stratum = 1;
- }
- if (!(opts & OPT_n)) {
- bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
- logmode = LOGMODE_NONE;
- }
+
#if ENABLE_FEATURE_NTPD_SERVER
G_listen_fd = -1;
if (opts & OPT_l) {
G_listen_fd = create_and_bind_dgram_or_die(NULL, 123);
+ if (G.if_name) {
+ if (setsockopt_bindtodevice(G_listen_fd, G.if_name))
+ xfunc_die();
+ }
socket_want_pktinfo(G_listen_fd);
- setsockopt(G_listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+ setsockopt_int(G_listen_fd, IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY);
}
#endif
/* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
if (opts & OPT_N)
setpriority(PRIO_PROCESS, 0, -15);
+ /* add_peers() calls can retry DNS resolution (possibly forever).
+ * Daemonize before them, or else boot can stall forever.
+ */
+ if (!(opts & OPT_n)) {
+ bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
+ logmode = LOGMODE_NONE;
+ }
+
+ if (peers) {
+ while (peers)
+ add_peers(llist_pop(&peers));
+ }
+#if ENABLE_FEATURE_NTPD_CONF
+ else {
+ parser_t *parser;
+ char *token[3];
+
+ parser = config_open("/etc/ntp.conf");
+ while (config_read(parser, token, 3, 1, "# \t", PARSE_NORMAL)) {
+ if (strcmp(token[0], "server") == 0 && token[1]) {
+ add_peers(token[1]);
+ continue;
+ }
+ bb_error_msg("skipping %s:%u: unimplemented command '%s'",
+ "/etc/ntp.conf", parser->lineno, token[0]
+ );
+ }
+ config_close(parser);
+ }
+#endif
+ if (G.peer_cnt == 0) {
+ if (!(opts & OPT_l))
+ bb_show_usage();
+ /* -l but no peers: "stratum 1 server" mode */
+ G.stratum = 1;
+ }
/* If network is up, syncronization occurs in ~10 seconds.
* We give "ntpd -q" 10 seconds to get first reply,
* then another 50 seconds to finish syncing.
@@ -2201,16 +2388,26 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
if (p->p_fd == -1) {
/* Time to send new req */
if (--cnt == 0) {
- G.initial_poll_complete = 1;
+ VERB4 bb_error_msg("disabling burst mode");
+ G.polladj_count = 0;
+ G.poll_exp = MINPOLL;
}
send_query_to_peer(p);
} else {
/* Timed out waiting for reply */
close(p->p_fd);
p->p_fd = -1;
- timeout = poll_interval(-2); /* -2: try a bit sooner */
+ /* If poll interval is small, increase it */
+ if (G.poll_exp < BIGPOLL)
+ adjust_poll(MINPOLL);
+ timeout = poll_interval(NOREPLY_INTERVAL);
bb_error_msg("timed out waiting for %s, reach 0x%02x, next query in %us",
p->p_dotted, p->reachable_bits, timeout);
+
+ /* What if don't see it because it changed its IP? */
+ if (p->reachable_bits == 0)
+ resolve_peer_hostname(p, /*loop_on_fail=*/ 0);
+
set_next(p, timeout);
}
}
@@ -2299,9 +2496,7 @@ int ntpd_main(int argc UNUSED_PARAM, char **argv)
goto have_reachable_peer;
}
/* No peer responded for last 8 packets, panic */
- G.polladj_count = 0;
- G.poll_exp = MINPOLL;
- G.stratum = MAXSTRAT;
+ clamp_pollexp_and_set_MAXSTRAT();
run_script("unsync", 0.0);
have_reachable_peer: ;
}
diff --git a/networking/ntpd.diff b/networking/ntpd.diff
new file mode 100644
index 0000000..4afd7e1
--- a/dev/null
+++ b/networking/ntpd.diff
@@ -0,0 +1,24 @@
+This patch scales down small offsets quadratically. Reduces sensitivity to jitter
+
+diff --git a/networking/ntpd.c b/networking/ntpd.c
+index 4695c33..ac05815 100644
+--- a/networking/ntpd.c
++++ b/networking/ntpd.c
+@@ -1654,6 +1654,17 @@ update_local_clock(peer_t *p)
+ */
+ if (G.offset_to_jitter_ratio >= TIMECONST_HACK_GATE)
+ tmx.constant--;
++
++{
++ double d = p->lastpkt_delay;
++ if (d > SLEW_THRESHOLD)
++ d = SLEW_THRESHOLD;
++ d /= 2;
++ if ((abs_offset / d) < 1) {
++ offset *= (abs_offset / d);
++ }
++}
++
+ tmx.offset = (long)(offset * 1000000); /* usec */
+ if (SLEW_THRESHOLD < STEP_THRESHOLD) {
+ if (tmx.offset > (long)(SLEW_THRESHOLD * 1000000)) {
diff --git a/networking/ntpd_simple.c b/networking/ntpd_simple.c
deleted file mode 100644
index 3e7fc47..0000000
--- a/networking/ntpd_simple.c
+++ b/dev/null
@@ -1,1008 +0,0 @@
-/*
- * NTP client/server, based on OpenNTPD 3.9p1
- *
- * Author: Adam Tkac <vonsch@gmail.com>
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-#include "libbb.h"
-#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
-#include <sys/resource.h> /* setpriority */
-#ifndef IPTOS_LOWDELAY
-# define IPTOS_LOWDELAY 0x10
-#endif
-#ifndef IP_PKTINFO
-# error "Sorry, your kernel has to support IP_PKTINFO"
-#endif
-
-
-/* Sync to peers every N secs */
-#define INTERVAL_QUERY_NORMAL 30
-#define INTERVAL_QUERY_PATHETIC 60
-#define INTERVAL_QUERY_AGRESSIVE 5
-
-/* Bad if *less than* TRUSTLEVEL_BADPEER */
-#define TRUSTLEVEL_BADPEER 6
-#define TRUSTLEVEL_PATHETIC 2
-#define TRUSTLEVEL_AGRESSIVE 8
-#define TRUSTLEVEL_MAX 10
-
-#define QSCALE_OFF_MIN 0.05
-#define QSCALE_OFF_MAX 0.50
-
-/* Single query might take N secs max */
-#define QUERYTIME_MAX 15
-/* Min offset for settime at start. "man ntpd" says it's 128 ms */
-#define STEPTIME_MIN_OFFSET 0.128
-
-typedef struct {
- uint32_t int_partl;
- uint32_t fractionl;
-} l_fixedpt_t;
-
-typedef struct {
- uint16_t int_parts;
- uint16_t fractions;
-} s_fixedpt_t;
-
-enum {
- NTP_DIGESTSIZE = 16,
- NTP_MSGSIZE_NOAUTH = 48,
- NTP_MSGSIZE = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE),
-};
-
-typedef struct {
- uint8_t m_status; /* status of local clock and leap info */
- uint8_t m_stratum; /* stratum level */
- uint8_t m_ppoll; /* poll value */
- int8_t m_precision_exp;
- s_fixedpt_t m_rootdelay;
- s_fixedpt_t m_dispersion;
- uint32_t m_refid;
- l_fixedpt_t m_reftime;
- l_fixedpt_t m_orgtime;
- l_fixedpt_t m_rectime;
- l_fixedpt_t m_xmttime;
- uint32_t m_keyid;
- uint8_t m_digest[NTP_DIGESTSIZE];
-} msg_t;
-
-enum {
- NTP_VERSION = 4,
- NTP_MAXSTRATUM = 15,
-
- /* Status Masks */
- MODE_MASK = (7 << 0),
- VERSION_MASK = (7 << 3),
- VERSION_SHIFT = 3,
- LI_MASK = (3 << 6),
-
- /* Leap Second Codes (high order two bits of m_status) */
- LI_NOWARNING = (0 << 6), /* no warning */
- LI_PLUSSEC = (1 << 6), /* add a second (61 seconds) */
- LI_MINUSSEC = (2 << 6), /* minus a second (59 seconds) */
- LI_ALARM = (3 << 6), /* alarm condition */
-
- /* Mode values */
- MODE_RES0 = 0, /* reserved */
- MODE_SYM_ACT = 1, /* symmetric active */
- MODE_SYM_PAS = 2, /* symmetric passive */
- MODE_CLIENT = 3, /* client */
- MODE_SERVER = 4, /* server */
- MODE_BROADCAST = 5, /* broadcast */
- MODE_RES1 = 6, /* reserved for NTP control message */
- MODE_RES2 = 7, /* reserved for private use */
-};
-
-#define OFFSET_1900_1970 2208988800UL /* 1970 - 1900 in seconds */
-
-typedef struct {
- double d_offset;
- double d_delay;
- //UNUSED: double d_error;
- time_t d_rcv_time;
- uint32_t d_refid4;
- uint8_t d_leap;
- uint8_t d_stratum;
- uint8_t d_good;
-} datapoint_t;
-
-#define NUM_DATAPOINTS 8
-typedef struct {
- len_and_sockaddr *p_lsa;
- char *p_dotted;
- /* When to send new query (if p_fd == -1)
- * or when receive times out (if p_fd >= 0): */
- time_t next_action_time;
- int p_fd;
- uint8_t p_datapoint_idx;
- uint8_t p_trustlevel;
- double p_xmttime;
- datapoint_t update;
- datapoint_t p_datapoint[NUM_DATAPOINTS];
- msg_t p_xmt_msg;
-} peer_t;
-
-enum {
- OPT_n = (1 << 0),
- OPT_q = (1 << 1),
- OPT_N = (1 << 2),
- OPT_x = (1 << 3),
- /* Insert new options above this line. */
- /* Non-compat options: */
- OPT_p = (1 << 4),
- OPT_l = (1 << 5) * ENABLE_FEATURE_NTPD_SERVER,
-};
-
-
-struct globals {
- /* total round trip delay to currently selected reference clock */
- double rootdelay;
- /* reference timestamp: time when the system clock was last set or corrected */
- double reftime;
- llist_t *ntp_peers;
-#if ENABLE_FEATURE_NTPD_SERVER
- int listen_fd;
-#endif
- unsigned verbose;
- unsigned peer_cnt;
- unsigned scale;
- uint32_t refid;
- uint32_t refid4;
- uint8_t synced;
- uint8_t leap;
-#define G_precision_exp -6
-// int8_t precision_exp;
- uint8_t stratum;
- uint8_t time_was_stepped;
- uint8_t first_adj_done;
-};
-#define G (*ptr_to_globals)
-
-static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
-
-
-static void
-set_next(peer_t *p, unsigned t)
-{
- p->next_action_time = time(NULL) + t;
-}
-
-static void
-add_peers(char *s)
-{
- peer_t *p;
-
- p = xzalloc(sizeof(*p));
- p->p_lsa = xhost2sockaddr(s, 123);
- p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
- p->p_fd = -1;
- p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
- p->p_trustlevel = TRUSTLEVEL_PATHETIC;
- p->next_action_time = time(NULL); /* = set_next(p, 0); */
-
- llist_add_to(&G.ntp_peers, p);
- G.peer_cnt++;
-}
-
-static double
-gettime1900d(void)
-{
- struct timeval tv;
- gettimeofday(&tv, NULL); /* never fails */
- return (tv.tv_sec + 1.0e-6 * tv.tv_usec + OFFSET_1900_1970);
-}
-
-static void
-d_to_tv(double d, struct timeval *tv)
-{
- tv->tv_sec = (long)d;
- tv->tv_usec = (d - tv->tv_sec) * 1000000;
-}
-
-static double
-lfp_to_d(l_fixedpt_t lfp)
-{
- double ret;
- lfp.int_partl = ntohl(lfp.int_partl);
- lfp.fractionl = ntohl(lfp.fractionl);
- ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX);
- return ret;
-}
-
-#if 0 //UNUSED
-static double
-sfp_to_d(s_fixedpt_t sfp)
-{
- double ret;
- sfp.int_parts = ntohs(sfp.int_parts);
- sfp.fractions = ntohs(sfp.fractions);
- ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX);
- return ret;
-}
-#endif
-
-#if ENABLE_FEATURE_NTPD_SERVER
-static l_fixedpt_t
-d_to_lfp(double d)
-{
- l_fixedpt_t lfp;
- lfp.int_partl = (uint32_t)d;
- lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX);
- lfp.int_partl = htonl(lfp.int_partl);
- lfp.fractionl = htonl(lfp.fractionl);
- return lfp;
-}
-
-static s_fixedpt_t
-d_to_sfp(double d)
-{
- s_fixedpt_t sfp;
- sfp.int_parts = (uint16_t)d;
- sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX);
- sfp.int_parts = htons(sfp.int_parts);
- sfp.fractions = htons(sfp.fractions);
- return sfp;
-}
-#endif
-
-static unsigned
-error_interval(void)
-{
- unsigned interval, r;
- interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN;
- r = (unsigned)random() % (unsigned)(interval / 10);
- return (interval + r);
-}
-
-static int
-do_sendto(int fd,
- const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen,
- msg_t *msg, ssize_t len)
-{
- ssize_t ret;
-
- errno = 0;
- if (!from) {
- ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen);
- } else {
- ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen);
- }
- if (ret != len) {
- bb_perror_msg("send failed");
- return -1;
- }
- return 0;
-}
-
-static int
-send_query_to_peer(peer_t *p)
-{
- // Why do we need to bind()?
- // See what happens when we don't bind:
- //
- // socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
- // setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0
- // gettimeofday({1259071266, 327885}, NULL) = 0
- // sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48
- // ^^^ we sent it from some source port picked by kernel.
- // time(NULL) = 1259071266
- // write(2, "ntpd: entering poll 15 secs\n", 28) = 28
- // poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}])
- // recv(3, "yyy", 68, MSG_DONTWAIT) = 48
- // ^^^ this recv will receive packets to any local port!
- //
- // Uncomment this and use strace to see it in action:
-#define PROBE_LOCAL_ADDR // { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); }
-
- if (p->p_fd == -1) {
- int fd, family;
- len_and_sockaddr *local_lsa;
-
- family = p->p_lsa->u.sa.sa_family;
- p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM);
- /* local_lsa has "null" address and port 0 now.
- * bind() ensures we have a *particular port* selected by kernel
- * and remembered in p->p_fd, thus later recv(p->p_fd)
- * receives only packets sent to this port.
- */
- PROBE_LOCAL_ADDR
- xbind(fd, &local_lsa->u.sa, local_lsa->len);
- PROBE_LOCAL_ADDR
-#if ENABLE_FEATURE_IPV6
- if (family == AF_INET)
-#endif
- setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
- free(local_lsa);
- }
-
- /*
- * Send out a random 64-bit number as our transmit time. The NTP
- * server will copy said number into the originate field on the
- * response that it sends us. This is totally legal per the SNTP spec.
- *
- * The impact of this is two fold: we no longer send out the current
- * system time for the world to see (which may aid an attacker), and
- * it gives us a (not very secure) way of knowing that we're not
- * getting spoofed by an attacker that can't capture our traffic
- * but can spoof packets from the NTP server we're communicating with.
- *
- * Save the real transmit timestamp locally.
- */
- p->p_xmt_msg.m_xmttime.int_partl = random();
- p->p_xmt_msg.m_xmttime.fractionl = random();
- p->p_xmttime = gettime1900d();
-
- if (do_sendto(p->p_fd, /*from:*/ NULL, /*to:*/ &p->p_lsa->u.sa, /*addrlen:*/ p->p_lsa->len,
- &p->p_xmt_msg, NTP_MSGSIZE_NOAUTH) == -1
- ) {
- close(p->p_fd);
- p->p_fd = -1;
- set_next(p, INTERVAL_QUERY_PATHETIC);
- return -1;
- }
-
- if (G.verbose)
- bb_error_msg("sent query to %s", p->p_dotted);
- set_next(p, QUERYTIME_MAX);
-
- return 0;
-}
-
-
-/* Time is stepped only once, when the first packet from a peer is received.
- */
-static void
-step_time_once(double offset)
-{
- double dtime;
- llist_t *item;
- struct timeval tv;
- char buf[80];
- time_t tval;
-
- if (G.time_was_stepped)
- goto bail;
- G.time_was_stepped = 1;
-
- /* if the offset is small, don't step, slew (later) */
- if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET)
- goto bail;
-
- gettimeofday(&tv, NULL); /* never fails */
- dtime = offset + tv.tv_sec;
- dtime += 1.0e-6 * tv.tv_usec;
- d_to_tv(dtime, &tv);
-
- if (settimeofday(&tv, NULL) == -1)
- bb_perror_msg_and_die("settimeofday");
-
- tval = tv.tv_sec;
- strftime_YYYYMMDDHHMMSS(buf, sizeof(buf), &tval);
-
- bb_error_msg("setting clock to %s (offset %fs)", buf, offset);
-
- for (item = G.ntp_peers; item != NULL; item = item->link) {
- peer_t *p = (peer_t *) item->data;
- p->next_action_time -= (time_t)offset;
- }
-
- bail:
- if (option_mask32 & OPT_q)
- exit(0);
-}
-
-
-/* Time is periodically slewed when we collect enough
- * good data points.
- */
-static int
-compare_offsets(const void *aa, const void *bb)
-{
- const peer_t *const *a = aa;
- const peer_t *const *b = bb;
- if ((*a)->update.d_offset < (*b)->update.d_offset)
- return -1;
- return ((*a)->update.d_offset > (*b)->update.d_offset);
-}
-static unsigned
-updated_scale(double offset)
-{
- if (offset < 0)
- offset = -offset;
- if (offset > QSCALE_OFF_MAX)
- return 1;
- if (offset < QSCALE_OFF_MIN)
- return QSCALE_OFF_MAX / QSCALE_OFF_MIN;
- return QSCALE_OFF_MAX / offset;
-}
-static void
-slew_time(void)
-{
- llist_t *item;
- double offset_median;
- struct timeval tv;
-
- {
- peer_t **peers = xzalloc(sizeof(peers[0]) * G.peer_cnt);
- unsigned goodpeer_cnt = 0;
- unsigned middle;
-
- for (item = G.ntp_peers; item != NULL; item = item->link) {
- peer_t *p = (peer_t *) item->data;
- if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
- continue;
- if (!p->update.d_good) {
- free(peers);
- return;
- }
- peers[goodpeer_cnt++] = p;
- }
-
- if (goodpeer_cnt == 0) {
- free(peers);
- goto clear_good;
- }
-
- qsort(peers, goodpeer_cnt, sizeof(peers[0]), compare_offsets);
-
- middle = goodpeer_cnt / 2;
- if (middle != 0 && (goodpeer_cnt & 1) == 0) {
- offset_median = (peers[middle-1]->update.d_offset + peers[middle]->update.d_offset) / 2;
- G.rootdelay = (peers[middle-1]->update.d_delay + peers[middle]->update.d_delay) / 2;
- G.stratum = 1 + MAX(peers[middle-1]->update.d_stratum, peers[middle]->update.d_stratum);
- } else {
- offset_median = peers[middle]->update.d_offset;
- G.rootdelay = peers[middle]->update.d_delay;
- G.stratum = 1 + peers[middle]->update.d_stratum;
- }
- G.leap = peers[middle]->update.d_leap;
- G.refid4 = peers[middle]->update.d_refid4;
- G.refid =
-#if ENABLE_FEATURE_IPV6
- peers[middle]->p_lsa->u.sa.sa_family != AF_INET ?
- G.refid4 :
-#endif
- peers[middle]->p_lsa->u.sin.sin_addr.s_addr;
- free(peers);
- }
-//TODO: if (offset_median > BIG) step_time(offset_median)?
-
- G.scale = updated_scale(offset_median);
-
- bb_error_msg("adjusting clock by %fs, our stratum is %u, time scale %u",
- offset_median, G.stratum, G.scale);
-
- errno = 0;
- d_to_tv(offset_median, &tv);
- if (adjtime(&tv, &tv) == -1)
- bb_perror_msg_and_die("adjtime failed");
- if (G.verbose >= 2)
- bb_error_msg("old adjust: %d.%06u", (int)tv.tv_sec, (unsigned)tv.tv_usec);
-
- if (G.first_adj_done) {
- uint8_t synced = (tv.tv_sec == 0 && tv.tv_usec == 0);
- if (synced != G.synced) {
- G.synced = synced;
- bb_error_msg("clock is %ssynced", synced ? "" : "un");
- }
- }
- G.first_adj_done = 1;
-
- G.reftime = gettime1900d();
-
- clear_good:
- for (item = G.ntp_peers; item != NULL; item = item->link) {
- peer_t *p = (peer_t *) item->data;
- p->update.d_good = 0;
- }
-}
-
-static void
-update_peer_data(peer_t *p)
-{
- /* Clock filter.
- * Find the datapoint with the lowest delay.
- * Use that as the peer update.
- * Invalidate it and all older ones.
- */
- int i;
- int best = -1;
- int good = 0;
-
- for (i = 0; i < NUM_DATAPOINTS; i++) {
- if (p->p_datapoint[i].d_good) {
- good++;
- if (best < 0 || p->p_datapoint[i].d_delay < p->p_datapoint[best].d_delay)
- best = i;
- }
- }
-
- if (good < 8) //FIXME: was it meant to be NUM_DATAPOINTS, not 8?
- return;
-
- p->update = p->p_datapoint[best]; /* struct copy */
- slew_time();
-
- for (i = 0; i < NUM_DATAPOINTS; i++)
- if (p->p_datapoint[i].d_rcv_time <= p->p_datapoint[best].d_rcv_time)
- p->p_datapoint[i].d_good = 0;
-}
-
-static unsigned
-scale_interval(unsigned requested)
-{
- unsigned interval, r;
- interval = requested * G.scale;
- r = (unsigned)random() % (unsigned)(MAX(5, interval / 10));
- return (interval + r);
-}
-static void
-recv_and_process_peer_pkt(peer_t *p)
-{
- ssize_t size;
- msg_t msg;
- double T1, T2, T3, T4;
- unsigned interval;
- datapoint_t *datapoint;
-
- /* We can recvfrom here and check from.IP, but some multihomed
- * ntp servers reply from their *other IP*.
- * TODO: maybe we should check at least what we can: from.port == 123?
- */
- size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT);
- if (size == -1) {
- bb_perror_msg("recv(%s) error", p->p_dotted);
- if (errno == EHOSTUNREACH || errno == EHOSTDOWN
- || errno == ENETUNREACH || errno == ENETDOWN
- || errno == ECONNREFUSED || errno == EADDRNOTAVAIL
- || errno == EAGAIN
- ) {
-//TODO: always do this?
- set_next(p, error_interval());
- goto close_sock;
- }
- xfunc_die();
- }
-
- if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
- bb_error_msg("malformed packet received from %s", p->p_dotted);
- goto bail;
- }
-
- if (msg.m_orgtime.int_partl != p->p_xmt_msg.m_xmttime.int_partl
- || msg.m_orgtime.fractionl != p->p_xmt_msg.m_xmttime.fractionl
- ) {
- goto bail;
- }
-
- if ((msg.m_status & LI_ALARM) == LI_ALARM
- || msg.m_stratum == 0
- || msg.m_stratum > NTP_MAXSTRATUM
- ) {
-// TODO: stratum 0 responses may have commands in 32-bit m_refid field:
-// "DENY", "RSTR" - peer does not like us at all
-// "RATE" - peer is overloaded, reduce polling freq
- interval = error_interval();
- bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
- goto close_sock;
- }
-
- /*
- * From RFC 2030 (with a correction to the delay math):
- *
- * Timestamp Name ID When Generated
- * ------------------------------------------------------------
- * Originate Timestamp T1 time request sent by client
- * Receive Timestamp T2 time request received by server
- * Transmit Timestamp T3 time reply sent by server
- * Destination Timestamp T4 time reply received by client
- *
- * The roundtrip delay and local clock offset are defined as
- *
- * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2
- */
- T1 = p->p_xmttime;
- T2 = lfp_to_d(msg.m_rectime);
- T3 = lfp_to_d(msg.m_xmttime);
- T4 = gettime1900d();
-
- datapoint = &p->p_datapoint[p->p_datapoint_idx];
-
- datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2;
- datapoint->d_delay = (T4 - T1) - (T3 - T2);
- if (datapoint->d_delay < 0) {
- bb_error_msg("reply from %s: negative delay %f", p->p_dotted, datapoint->d_delay);
- interval = error_interval();
- set_next(p, interval);
- goto close_sock;
- }
- //UNUSED: datapoint->d_error = (T2 - T1) - (T3 - T4);
- datapoint->d_rcv_time = (time_t)(T4 - OFFSET_1900_1970); /* = time(NULL); */
- datapoint->d_good = 1;
-
- datapoint->d_leap = (msg.m_status & LI_MASK);
- //UNUSED: datapoint->o_precision = msg.m_precision_exp;
- //UNUSED: datapoint->o_rootdelay = sfp_to_d(msg.m_rootdelay);
- //UNUSED: datapoint->o_rootdispersion = sfp_to_d(msg.m_dispersion);
- //UNUSED: datapoint->d_refid = ntohl(msg.m_refid);
- datapoint->d_refid4 = msg.m_xmttime.fractionl;
- //UNUSED: datapoint->o_reftime = lfp_to_d(msg.m_reftime);
- //UNUSED: datapoint->o_poll = msg.m_ppoll;
- datapoint->d_stratum = msg.m_stratum;
-
- if (p->p_trustlevel < TRUSTLEVEL_PATHETIC)
- interval = scale_interval(INTERVAL_QUERY_PATHETIC);
- else if (p->p_trustlevel < TRUSTLEVEL_AGRESSIVE)
- interval = scale_interval(INTERVAL_QUERY_AGRESSIVE);
- else
- interval = scale_interval(INTERVAL_QUERY_NORMAL);
-
- set_next(p, interval);
-
- /* Every received reply which we do not discard increases trust */
- if (p->p_trustlevel < TRUSTLEVEL_MAX) {
- p->p_trustlevel++;
- if (p->p_trustlevel == TRUSTLEVEL_BADPEER)
- bb_error_msg("peer %s now valid", p->p_dotted);
- }
-
- if (G.verbose)
- bb_error_msg("reply from %s: offset %f delay %f, next query in %us", p->p_dotted,
- datapoint->d_offset, datapoint->d_delay, interval);
-
- update_peer_data(p);
-//TODO: do it after all peers had a chance to return at least one reply?
- step_time_once(datapoint->d_offset);
-
- p->p_datapoint_idx++;
- if (p->p_datapoint_idx >= NUM_DATAPOINTS)
- p->p_datapoint_idx = 0;
-
- close_sock:
- /* We do not expect any more packets from this peer for now.
- * Closing the socket informs kernel about it.
- * We open a new socket when we send a new query.
- */
- close(p->p_fd);
- p->p_fd = -1;
- bail:
- return;
-}
-
-#if ENABLE_FEATURE_NTPD_SERVER
-static void
-recv_and_process_client_pkt(void /*int fd*/)
-{
- ssize_t size;
- uint8_t version;
- double rectime;
- len_and_sockaddr *to;
- struct sockaddr *from;
- msg_t msg;
- uint8_t query_status;
- uint8_t query_ppoll;
- l_fixedpt_t query_xmttime;
-
- to = get_sock_lsa(G.listen_fd);
- from = xzalloc(to->len);
-
- size = recv_from_to(G.listen_fd, &msg, sizeof(msg), MSG_DONTWAIT, from, &to->u.sa, to->len);
- if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
- char *addr;
- if (size < 0) {
- if (errno == EAGAIN)
- goto bail;
- bb_perror_msg_and_die("recv");
- }
- addr = xmalloc_sockaddr2dotted_noport(from);
- bb_error_msg("malformed packet received from %s: size %u", addr, (int)size);
- free(addr);
- goto bail;
- }
-
- query_status = msg.m_status;
- query_ppoll = msg.m_ppoll;
- query_xmttime = msg.m_xmttime;
-
- /* Build a reply packet */
- memset(&msg, 0, sizeof(msg));
- msg.m_status = G.synced ? G.leap : LI_ALARM;
- msg.m_status |= (query_status & VERSION_MASK);
- msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
- MODE_SERVER : MODE_SYM_PAS;
- msg.m_stratum = G.stratum;
- msg.m_ppoll = query_ppoll;
- msg.m_precision_exp = G_precision_exp;
- rectime = gettime1900d();
- msg.m_xmttime = msg.m_rectime = d_to_lfp(rectime);
- msg.m_reftime = d_to_lfp(G.reftime);
- //msg.m_xmttime = d_to_lfp(gettime1900d()); // = msg.m_rectime
- msg.m_orgtime = query_xmttime;
- msg.m_rootdelay = d_to_sfp(G.rootdelay);
- version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
- msg.m_refid = (version > (3 << VERSION_SHIFT)) ? G.refid4 : G.refid;
-
- /* We reply from the local address packet was sent to,
- * this makes to/from look swapped here: */
- do_sendto(G.listen_fd,
- /*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
- &msg, size);
-
- bail:
- free(to);
- free(from);
-}
-#endif
-
-/* Upstream ntpd's options:
- *
- * -4 Force DNS resolution of host names to the IPv4 namespace.
- * -6 Force DNS resolution of host names to the IPv6 namespace.
- * -a Require cryptographic authentication for broadcast client,
- * multicast client and symmetric passive associations.
- * This is the default.
- * -A Do not require cryptographic authentication for broadcast client,
- * multicast client and symmetric passive associations.
- * This is almost never a good idea.
- * -b Enable the client to synchronize to broadcast servers.
- * -c conffile
- * Specify the name and path of the configuration file,
- * default /etc/ntp.conf
- * -d Specify debugging mode. This option may occur more than once,
- * with each occurrence indicating greater detail of display.
- * -D level
- * Specify debugging level directly.
- * -f driftfile
- * Specify the name and path of the frequency file.
- * This is the same operation as the "driftfile FILE"
- * configuration command.
- * -g Normally, ntpd exits with a message to the system log
- * if the offset exceeds the panic threshold, which is 1000 s
- * by default. This option allows the time to be set to any value
- * without restriction; however, this can happen only once.
- * If the threshold is exceeded after that, ntpd will exit
- * with a message to the system log. This option can be used
- * with the -q and -x options. See the tinker command for other options.
- * -i jaildir
- * Chroot the server to the directory jaildir. This option also implies
- * that the server attempts to drop root privileges at startup
- * (otherwise, chroot gives very little additional security).
- * You may need to also specify a -u option.
- * -k keyfile
- * Specify the name and path of the symmetric key file,
- * default /etc/ntp/keys. This is the same operation
- * as the "keys FILE" configuration command.
- * -l logfile
- * Specify the name and path of the log file. The default
- * is the system log file. This is the same operation as
- * the "logfile FILE" configuration command.
- * -L Do not listen to virtual IPs. The default is to listen.
- * -n Don't fork.
- * -N To the extent permitted by the operating system,
- * run the ntpd at the highest priority.
- * -p pidfile
- * Specify the name and path of the file used to record the ntpd
- * process ID. This is the same operation as the "pidfile FILE"
- * configuration command.
- * -P priority
- * To the extent permitted by the operating system,
- * run the ntpd at the specified priority.
- * -q Exit the ntpd just after the first time the clock is set.
- * This behavior mimics that of the ntpdate program, which is
- * to be retired. The -g and -x options can be used with this option.
- * Note: The kernel time discipline is disabled with this option.
- * -r broadcastdelay
- * Specify the default propagation delay from the broadcast/multicast
- * server to this client. This is necessary only if the delay
- * cannot be computed automatically by the protocol.
- * -s statsdir
- * Specify the directory path for files created by the statistics
- * facility. This is the same operation as the "statsdir DIR"
- * configuration command.
- * -t key
- * Add a key number to the trusted key list. This option can occur
- * more than once.
- * -u user[:group]
- * Specify a user, and optionally a group, to switch to.
- * -v variable
- * -V variable
- * Add a system variable listed by default.
- * -x Normally, the time is slewed if the offset is less than the step
- * threshold, which is 128 ms by default, and stepped if above
- * the threshold. This option sets the threshold to 600 s, which is
- * well within the accuracy window to set the clock manually.
- * Note: since the slew rate of typical Unix kernels is limited
- * to 0.5 ms/s, each second of adjustment requires an amortization
- * interval of 2000 s. Thus, an adjustment as much as 600 s
- * will take almost 14 days to complete. This option can be used
- * with the -g and -q options. See the tinker command for other options.
- * Note: The kernel time discipline is disabled with this option.
- */
-
-/* By doing init in a separate function we decrease stack usage
- * in main loop.
- */
-static NOINLINE void ntp_init(char **argv)
-{
- unsigned opts;
- llist_t *peers;
-
- srandom(getpid());
-
- if (getuid())
- bb_error_msg_and_die(bb_msg_you_must_be_root);
-
- peers = NULL;
- opt_complementary = "dd:p::"; /* d: counter, p: list */
- opts = getopt32(argv,
- "nqNx" /* compat */
- "p:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
- "d" /* compat */
- "46aAbgL", /* compat, ignored */
- &peers, &G.verbose);
- if (!(opts & (OPT_p|OPT_l)))
- bb_show_usage();
- if (opts & OPT_x) /* disable stepping, only slew is allowed */
- G.time_was_stepped = 1;
- while (peers)
- add_peers(llist_pop(&peers));
- if (!(opts & OPT_n)) {
- bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
- logmode = LOGMODE_NONE;
- }
-#if ENABLE_FEATURE_NTPD_SERVER
- G.listen_fd = -1;
- if (opts & OPT_l) {
- G.listen_fd = create_and_bind_dgram_or_die(NULL, 123);
- socket_want_pktinfo(G.listen_fd);
- setsockopt(G.listen_fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
- }
-#endif
- /* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
- if (opts & OPT_N)
- setpriority(PRIO_PROCESS, 0, -15);
-
- /* Set some globals */
-#if 0
- /* With constant b = 100, G.precision_exp is also constant -6.
- * Uncomment this and you'll see */
- {
- int prec = 0;
- int b;
-# if 0
- struct timespec tp;
- /* We can use sys_clock_getres but assuming 10ms tick should be fine */
- clock_getres(CLOCK_REALTIME, &tp);
- tp.tv_sec = 0;
- tp.tv_nsec = 10000000;
- b = 1000000000 / tp.tv_nsec; /* convert to Hz */
-# else
- b = 100; /* b = 1000000000/10000000 = 100 */
-# endif
- while (b > 1)
- prec--, b >>= 1;
- //G.precision_exp = prec;
- bb_error_msg("G.precision_exp:%d", prec); /* -6 */
- }
-#endif
- G.scale = 1;
-
- bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo);
- bb_signals((1 << SIGPIPE) | (1 << SIGHUP), SIG_IGN);
-}
-
-int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int ntpd_main(int argc UNUSED_PARAM, char **argv)
-{
- struct globals g;
- struct pollfd *pfd;
- peer_t **idx2peer;
-
- memset(&g, 0, sizeof(g));
- SET_PTR_TO_GLOBALS(&g);
-
- ntp_init(argv);
-
- {
- /* if ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */
- unsigned cnt = g.peer_cnt + ENABLE_FEATURE_NTPD_SERVER;
- idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
- pfd = xzalloc(sizeof(pfd[0]) * cnt);
- }
-
- while (!bb_got_signal) {
- llist_t *item;
- unsigned i, j;
- unsigned sent_cnt, trial_cnt;
- int nfds, timeout;
- time_t cur_time, nextaction;
-
- /* Nothing between here and poll() blocks for any significant time */
-
- cur_time = time(NULL);
- nextaction = cur_time + 3600;
-
- i = 0;
-#if ENABLE_FEATURE_NTPD_SERVER
- if (g.listen_fd != -1) {
- pfd[0].fd = g.listen_fd;
- pfd[0].events = POLLIN;
- i++;
- }
-#endif
- /* Pass over peer list, send requests, time out on receives */
- sent_cnt = trial_cnt = 0;
- for (item = g.ntp_peers; item != NULL; item = item->link) {
- peer_t *p = (peer_t *) item->data;
-
- /* Overflow-safe "if (p->next_action_time <= cur_time) ..." */
- if ((int)(cur_time - p->next_action_time) >= 0) {
- if (p->p_fd == -1) {
- /* Time to send new req */
- trial_cnt++;
- if (send_query_to_peer(p) == 0)
- sent_cnt++;
- } else {
- /* Timed out waiting for reply */
- close(p->p_fd);
- p->p_fd = -1;
- timeout = error_interval();
- bb_error_msg("timed out waiting for %s, "
- "next query in %us", p->p_dotted, timeout);
- if (p->p_trustlevel >= TRUSTLEVEL_BADPEER) {
- p->p_trustlevel /= 2;
- if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
- bb_error_msg("peer %s now invalid", p->p_dotted);
- }
- set_next(p, timeout);
- }
- }
-
- if (p->next_action_time < nextaction)
- nextaction = p->next_action_time;
-
- if (p->p_fd >= 0) {
- /* Wait for reply from this peer */
- pfd[i].fd = p->p_fd;
- pfd[i].events = POLLIN;
- idx2peer[i] = p;
- i++;
- }
- }
-
- if ((trial_cnt > 0 && sent_cnt == 0) || g.peer_cnt == 0)
- step_time_once(0); /* no good peers, don't wait */
-
- timeout = nextaction - cur_time;
- if (timeout < 1)
- timeout = 1;
-
- /* Here we may block */
- if (g.verbose >= 2)
- bb_error_msg("poll %us, sockets:%u", timeout, i);
- nfds = poll(pfd, i, timeout * 1000);
- if (nfds <= 0)
- continue;
-
- /* Process any received packets */
- j = 0;
-#if ENABLE_FEATURE_NTPD_SERVER
- if (g.listen_fd != -1) {
- if (pfd[0].revents /* & (POLLIN|POLLERR)*/) {
- nfds--;
- recv_and_process_client_pkt(/*g.listen_fd*/);
- }
- j = 1;
- }
-#endif
- for (; nfds != 0 && j < i; j++) {
- if (pfd[j].revents /* & (POLLIN|POLLERR)*/) {
- nfds--;
- recv_and_process_peer_pkt(idx2peer[j]);
- }
- }
- } /* while (!bb_got_signal) */
-
- kill_myself_with_sig(bb_got_signal);
-}
diff --git a/networking/ping.c b/networking/ping.c
index 46a3889..d541b79 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -28,6 +28,7 @@
#include <net/if.h>
#include <netinet/ip_icmp.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#ifdef __BIONIC__
/* should be in netinet/ip_icmp.h */
@@ -56,14 +57,14 @@
//config:config PING6
//config: bool "ping6"
//config: default y
-//config: depends on FEATURE_IPV6 && PING
+//config: depends on FEATURE_IPV6
//config: help
//config: This will give you a ping that can talk IPv6.
//config:
//config:config FEATURE_FANCY_PING
//config: bool "Enable fancy ping output"
//config: default y
-//config: depends on PING
+//config: depends on PING || PING6
//config: help
//config: Make the output from the ping applet include statistics, and at the
//config: same time provide full support for ICMP packets.
@@ -100,8 +101,9 @@
//usage: "\n (after all -c CNT packets are sent)"
//usage: "\n -w SEC Seconds until ping exits (default:infinite)"
//usage: "\n (can exit earlier with -c CNT)"
-//usage: "\n -q Quiet, only displays output at start"
+//usage: "\n -q Quiet, only display output at start"
//usage: "\n and when finished"
+//usage: "\n -p Pattern to use for payload"
//usage:
//usage:# define ping6_trivial_usage
//usage: "[OPTIONS] HOST"
@@ -110,8 +112,9 @@
//usage: "\n -c CNT Send only CNT pings"
//usage: "\n -s SIZE Send SIZE data bytes in packets (default:56)"
//usage: "\n -I IFACE/IP Use interface or IP address as source"
-//usage: "\n -q Quiet, only displays output at start"
+//usage: "\n -q Quiet, only display output at start"
//usage: "\n and when finished"
+//usage: "\n -p Pattern to use for payload"
//usage:
//usage:#endif
//usage:
@@ -168,22 +171,9 @@ create_icmp_socket(void)
#endif
sock = socket(AF_INET, SOCK_RAW, 1); /* 1 == ICMP */
if (sock < 0) {
- if (errno != EPERM)
- bb_perror_msg_and_die("%s", bb_msg_can_not_create_raw_socket);
-#if defined(__linux__) || defined(__APPLE__)
- /* We don't have root privileges. Try SOCK_DGRAM instead.
- * Linux needs net.ipv4.ping_group_range for this to work.
- * MacOSX allows ICMP_ECHO, ICMP_TSTAMP or ICMP_MASKREQ
- */
-#if ENABLE_PING6
- if (lsa->u.sa.sa_family == AF_INET6)
- sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
- else
-#endif
- sock = socket(AF_INET, SOCK_DGRAM, 1); /* 1 == ICMP */
- if (sock < 0)
-#endif
- bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
+ if (errno == EPERM)
+ bb_error_msg_and_die("%s", bb_msg_perm_denied_are_you_root);
+ bb_perror_msg_and_die("%s", bb_msg_can_not_create_raw_socket);
}
xmove_fd(sock, pingsock);
@@ -196,9 +186,10 @@ create_icmp_socket(void)
struct globals {
char *hostname;
char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+ uint16_t myid;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void noresp(int ign UNUSED_PARAM)
{
@@ -214,6 +205,7 @@ static void ping4(len_and_sockaddr *lsa)
pkt = (struct icmp *) G.packet;
/*memset(pkt, 0, sizeof(G.packet)); already is */
pkt->icmp_type = ICMP_ECHO;
+ pkt->icmp_id = G.myid;
pkt->icmp_cksum = inet_cksum((uint16_t *) pkt, sizeof(G.packet));
xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
@@ -238,6 +230,8 @@ static void ping4(len_and_sockaddr *lsa)
struct iphdr *iphdr = (struct iphdr *) G.packet;
pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2)); /* skip ip hdr */
+ if (pkt->icmp_id != G.myid)
+ continue; /* not our ping */
if (pkt->icmp_type == ICMP_ECHOREPLY)
break;
}
@@ -256,9 +250,10 @@ static void ping6(len_and_sockaddr *lsa)
pkt = (struct icmp6_hdr *) G.packet;
/*memset(pkt, 0, sizeof(G.packet)); already is */
pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+ pkt->icmp6_id = G.myid;
sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
- setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+ setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
xsendto(pingsock, G.packet, DEFDATALEN + sizeof(struct icmp6_hdr), &lsa->u.sa, lsa->len);
@@ -279,6 +274,8 @@ static void ping6(len_and_sockaddr *lsa)
continue;
}
if (c >= ICMP_MINLEN) { /* icmp6_hdr */
+ if (pkt->icmp6_id != G.myid)
+ continue; /* not our ping */
if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
break;
}
@@ -327,6 +324,7 @@ static int common_ping_main(sa_family_t af, char **argv)
alarm(5); /* give the host 5000ms to respond */
create_icmp_socket(lsa);
+ G.myid = (uint16_t) getpid();
#if ENABLE_PING6
if (lsa->u.sa.sa_family == AF_INET6)
ping6(lsa);
@@ -343,7 +341,7 @@ static int common_ping_main(sa_family_t af, char **argv)
/* Full(er) version */
-#define OPT_STRING ("qvc:s:t:w:W:I:n4" IF_PING6("6"))
+#define OPT_STRING ("qvc:+s:t:+w:+W:+I:np:4" IF_PING6("6"))
enum {
OPT_QUIET = 1 << 0,
OPT_VERBOSE = 1 << 1,
@@ -354,8 +352,9 @@ enum {
OPT_W = 1 << 6,
OPT_I = 1 << 7,
/*OPT_n = 1 << 8, - ignored */
- OPT_IPV4 = 1 << 9,
- OPT_IPV6 = (1 << 10) * ENABLE_PING6,
+ OPT_p = 1 << 9,
+ OPT_IPV4 = 1 << 10,
+ OPT_IPV6 = (1 << 11) * ENABLE_PING6,
};
@@ -368,6 +367,7 @@ struct globals {
unsigned opt_ttl;
unsigned long ntransmitted, nreceived, nrepeats;
uint16_t myid;
+ uint8_t pattern;
unsigned tmin, tmax; /* in us */
unsigned long long tsum; /* in us, sum of all times */
unsigned deadline;
@@ -387,7 +387,7 @@ struct globals {
} pingaddr;
unsigned char rcvd_tbl[MAX_DUP_CHK / 8];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define if_index (G.if_index )
#define source_lsa (G.source_lsa )
#define str_I (G.str_I )
@@ -405,10 +405,9 @@ struct globals {
#define dotted (G.dotted )
#define pingaddr (G.pingaddr )
#define rcvd_tbl (G.rcvd_tbl )
-void BUG_ping_globals_too_big(void);
#define INIT_G() do { \
- if (sizeof(G) > COMMON_BUFSIZE) \
- BUG_ping_globals_too_big(); \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
datalen = DEFDATALEN; \
timeout = MAXWAIT; \
tmin = UINT_MAX; \
@@ -465,7 +464,7 @@ static void sendping_tail(void (*sp)(int), int size_pkt)
* it doesn't matter */
sz = xsendto(pingsock, G.snd_packet, size_pkt, &pingaddr.sa, sizeof(pingaddr));
if (sz != size_pkt)
- bb_error_msg_and_die("%s", bb_msg_write_error);
+ bb_error_msg_and_die(bb_msg_write_error);
if (pingcount == 0 || deadline || G.ntransmitted < pingcount) {
/* Didn't send all pings yet - schedule next in 1s */
@@ -498,7 +497,7 @@ static void sendping4(int junk UNUSED_PARAM)
{
struct icmp *pkt = G.snd_packet;
- //memset(pkt, 0, datalen + ICMP_MINLEN + 4); - G.snd_packet was xzalloced
+ memset(pkt, G.pattern, datalen + ICMP_MINLEN + 4);
pkt->icmp_type = ICMP_ECHO;
/*pkt->icmp_code = 0;*/
pkt->icmp_cksum = 0; /* cksum is calculated with this field set to 0 */
@@ -521,7 +520,7 @@ static void sendping6(int junk UNUSED_PARAM)
{
struct icmp6_hdr *pkt = G.snd_packet;
- //memset(pkt, 0, datalen + sizeof(struct icmp6_hdr) + 4);
+ memset(pkt, G.pattern, datalen + sizeof(struct icmp6_hdr) + 4);
pkt->icmp6_type = ICMP6_ECHO_REQUEST;
/*pkt->icmp6_code = 0;*/
/*pkt->icmp6_cksum = 0;*/
@@ -591,7 +590,7 @@ static void unpack_tail(int sz, uint32_t *tp,
{
unsigned char *b, m;
const char *dupmsg = " (DUP!)";
- unsigned triptime = 0;
+ unsigned triptime = triptime; /* for gcc */
if (tp) {
/* (int32_t) cast is for hypothetical 64-bit unsigned */
@@ -633,7 +632,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
int hlen;
/* discard if too short */
- if (sz < (int) (datalen + ICMP_MINLEN))
+ if (sz < (datalen + ICMP_MINLEN))
return;
/* check IP header */
@@ -648,7 +647,7 @@ static void unpack4(char *buf, int sz, struct sockaddr_in *from)
uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
uint32_t *tp = NULL;
- if (sz >= (int) (ICMP_MINLEN + sizeof(uint32_t)))
+ if (sz >= ICMP_MINLEN + sizeof(uint32_t))
tp = (uint32_t *) icmppkt->icmp_data;
unpack_tail(sz, tp,
inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
@@ -666,7 +665,7 @@ static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimi
char buf[INET6_ADDRSTRLEN];
/* discard if too short */
- if (sz < (int) (datalen + sizeof(struct icmp6_hdr)))
+ if (sz < (datalen + sizeof(struct icmp6_hdr)))
return;
icmppkt = (struct icmp6_hdr *) packet;
@@ -677,7 +676,7 @@ static void unpack6(char *packet, int sz, struct sockaddr_in6 *from, int hoplimi
uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
uint32_t *tp = NULL;
- if (sz >= (int) (sizeof(struct icmp6_hdr) + sizeof(uint32_t)))
+ if (sz >= sizeof(struct icmp6_hdr) + sizeof(uint32_t))
tp = (uint32_t *) &icmppkt->icmp6_data8[4];
unpack_tail(sz, tp,
inet_ntop(AF_INET6, &from->sin6_addr,
@@ -709,12 +708,12 @@ static void ping4(len_and_sockaddr *lsa)
/* set recv buf (needed if we can get lots of responses: flood ping,
* broadcast ping etc) */
sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
- setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+ setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
if (opt_ttl != 0) {
- setsockopt(pingsock, IPPROTO_IP, IP_TTL, &opt_ttl, sizeof(opt_ttl));
+ setsockopt_int(pingsock, IPPROTO_IP, IP_TTL, opt_ttl);
/* above doesnt affect packets sent to bcast IP, so... */
- setsockopt(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, &opt_ttl, sizeof(opt_ttl));
+ setsockopt_int(pingsock, IPPROTO_IP, IP_MULTICAST_TTL, opt_ttl);
}
signal(SIGINT, print_stats_and_exit);
@@ -741,7 +740,6 @@ static void ping4(len_and_sockaddr *lsa)
}
}
#if ENABLE_PING6
-extern int BUG_bad_offsetof_icmp6_cksum(void);
static void ping6(len_and_sockaddr *lsa)
{
int sockopt;
@@ -765,7 +763,7 @@ static void ping6(len_and_sockaddr *lsa)
}
if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
sizeof(filt)) < 0)
- bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+ bb_error_msg_and_die("setsockopt(%s)", "ICMP6_FILTER");
}
#endif /*ICMP6_FILTER*/
@@ -775,15 +773,14 @@ static void ping6(len_and_sockaddr *lsa)
/* set recv buf (needed if we can get lots of responses: flood ping,
* broadcast ping etc) */
sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
- setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+ setsockopt_SOL_SOCKET_int(pingsock, SO_RCVBUF, sockopt);
sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
- if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
- BUG_bad_offsetof_icmp6_cksum();
- setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+ BUILD_BUG_ON(offsetof(struct icmp6_hdr, icmp6_cksum) != 2);
+ setsockopt_int(pingsock, SOL_RAW, IPV6_CHECKSUM, sockopt);
/* request ttl info to be returned in ancillary data */
- setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
+ setsockopt_1(pingsock, SOL_IPV6, IPV6_HOPLIMIT);
if (if_index)
pingaddr.sin6.sin6_scope_id = if_index;
@@ -863,13 +860,13 @@ static void ping(len_and_sockaddr *lsa)
static int common_ping_main(int opt, char **argv)
{
len_and_sockaddr *lsa;
- char *str_s;
+ char *str_s, *str_p;
INIT_G();
/* exactly one argument needed; -v and -q don't mix; -c NUM, -t NUM, -w NUM, -W NUM */
- opt_complementary = "=1:q--v:v--q:c+:t+:w+:W+";
- opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I);
+ opt_complementary = "=1:q--v:v--q";
+ opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p);
if (opt & OPT_s)
datalen = xatou16(str_s); // -s
if (opt & OPT_I) { // -I
@@ -880,6 +877,9 @@ static int common_ping_main(int opt, char **argv)
str_I = NULL; /* don't try to bind to device later */
}
}
+ if (opt & OPT_p)
+ G.pattern = xstrtou_range(str_p, 16, 0, 255);
+
myid = (uint16_t) getpid();
hostname = argv[optind];
#if ENABLE_PING6
@@ -907,15 +907,17 @@ static int common_ping_main(int opt, char **argv)
#endif /* FEATURE_FANCY_PING */
+#if ENABLE_PING
int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ping_main(int argc UNUSED_PARAM, char **argv)
{
-#if !ENABLE_FEATURE_FANCY_PING
+# if !ENABLE_FEATURE_FANCY_PING
return common_ping_main(AF_UNSPEC, argv);
-#else
+# else
return common_ping_main(0, argv);
-#endif
+# endif
}
+#endif
#if ENABLE_PING6
int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/networking/pscan.c b/networking/pscan.c
index 72ed8cd..0893c35 100644
--- a/networking/pscan.c
+++ b/networking/pscan.c
@@ -5,6 +5,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PSCAN
+//config: bool "pscan"
+//config: default y
+//config: help
+//config: Simple network port scanner.
+
+//applet:IF_PSCAN(APPLET(pscan, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PSCAN) += pscan.o
//usage:#define pscan_trivial_usage
//usage: "[-cb] [-p MIN_PORT] [-P MAX_PORT] [-t TIMEOUT] [-T MIN_RTT] HOST"
diff --git a/networking/route.c b/networking/route.c
index 75bb59a..0f90a19 100644
--- a/networking/route.c
+++ b/networking/route.c
@@ -24,6 +24,16 @@
* Rewritten to fix several bugs, add additional error checking, and
* remove ridiculous amounts of bloat.
*/
+//config:config ROUTE
+//config: bool "route"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Route displays or manipulates the kernel's IP routing tables.
+
+//applet:IF_ROUTE(APPLET(route, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ROUTE) += route.o
//usage:#define route_trivial_usage
//usage: "[{add|del|delete}]"
@@ -59,6 +69,7 @@
#define RTF_WINDOW 0x0080 /* per route window clamping */
#define RTF_IRTT 0x0100 /* Initial round trip time */
#define RTF_REJECT 0x0200 /* Reject route */
+#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
#endif
#ifndef RTF_CACHE
@@ -138,7 +149,7 @@ static const char tbl_ipvx[] ALIGN1 =
"\013\043reinstate" /* Since last, we can save a byte. */
;
-static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+static const uint16_t flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
#ifdef RTF_REJECT
RTF_REJECT,
#endif
@@ -293,7 +304,7 @@ static NOINLINE void INET_setroute(int action, char **args)
if (k == KW_IPVx_IRTT) {
rt->rt_flags |= RTF_IRTT;
rt->rt_irtt = xatoul(args_m1);
- rt->rt_irtt *= (sysconf(_SC_CLK_TCK) / 100); /* FIXME */
+ rt->rt_irtt *= (bb_clk_tck() / 100); /* FIXME */
#if 0 /* FIXME: do we need to check anything of this? */
if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) {
bb_error_msg_and_die("bad irtt");
@@ -304,7 +315,7 @@ static NOINLINE void INET_setroute(int action, char **args)
#endif
/* Device is special in that it can be the last arg specified
- * and doesn't requre the dev/device keyword in that case. */
+ * and doesn't require the dev/device keyword in that case. */
if (!rt->rt_dev && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
/* Don't use args_m1 here since args may have changed! */
rt->rt_dev = args[-1];
@@ -425,7 +436,7 @@ static NOINLINE void INET6_setroute(int action, char **args)
}
/* Device is special in that it can be the last arg specified
- * and doesn't requre the dev/device keyword in that case. */
+ * and doesn't require the dev/device keyword in that case. */
if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
/* Don't use args_m1 here since args may have changed! */
devname = args[-1];
@@ -459,7 +470,11 @@ static NOINLINE void INET6_setroute(int action, char **args)
}
#endif
-static const unsigned flagvals[] = { /* Must agree with flagchars[]. */
+static const
+IF_NOT_FEATURE_IPV6(uint16_t)
+IF_FEATURE_IPV6(unsigned)
+flagvals[] = { /* Must agree with flagchars[]. */
+ RTF_UP,
RTF_GATEWAY,
RTF_HOST,
RTF_REINSTATE,
@@ -468,27 +483,25 @@ static const unsigned flagvals[] = { /* Must agree with flagchars[]. */
#if ENABLE_FEATURE_IPV6
RTF_DEFAULT,
RTF_ADDRCONF,
- RTF_CACHE
+ RTF_CACHE,
+ RTF_REJECT,
+ RTF_NONEXTHOP, /* this one doesn't fit into 16 bits */
#endif
};
-
-#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
-#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
-
/* Must agree with flagvals[]. */
static const char flagchars[] ALIGN1 =
- "GHRDM"
+ "UGHRDM"
#if ENABLE_FEATURE_IPV6
- "DAC"
+ "DAC!n"
#endif
;
+#define IPV4_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE|RTF_REJECT|RTF_NONEXTHOP)
static void set_flags(char *flagstr, int flags)
{
int i;
- *flagstr++ = 'U';
-
for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
if (flags & flagvals[i]) {
++flagstr;
@@ -501,6 +514,7 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
{
char devname[64], flags[16], *sdest, *sgw;
unsigned long d, g, m;
+ int r;
int flgs, ref, use, metric, mtu, win, ir;
struct sockaddr_in s_addr;
struct in_addr mask;
@@ -511,20 +525,24 @@ void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
"Destination Gateway Genmask Flags %s Iface\n",
netstatfmt ? " MSS Window irtt" : "Metric Ref Use");
- if (fscanf(fp, "%*[^\n]\n") < 0) { /* Skip the first line. */
- goto ERROR; /* Empty or missing line, or read error. */
+ /* Skip the first line. */
+ r = fscanf(fp, "%*[^\n]\n");
+ if (r < 0) {
+ /* Empty line, read error, or EOF. Yes, if routing table
+ * is completely empty, /proc/net/route has no header.
+ */
+ goto ERROR;
}
while (1) {
- int r;
r = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n",
devname, &d, &g, &flgs, &ref, &use, &metric, &m,
&mtu, &win, &ir);
if (r != 11) {
+ ERROR:
if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
break;
}
- ERROR:
- bb_error_msg_and_die("fscanf");
+ bb_perror_msg_and_die(bb_msg_read_error);
}
if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
@@ -584,13 +602,13 @@ static void INET6_displayroutes(void)
int r;
r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
addr6x+14, &prefix_len, &slen, addr6x+40+7,
- &metric, &use, &refcnt, &iflags, iface);
+ &metric, &refcnt, &use, &iflags, iface);
if (r != 9) {
if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
break;
}
ERROR:
- bb_error_msg_and_die("fscanf");
+ bb_perror_msg_and_die(bb_msg_read_error);
}
/* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
@@ -616,10 +634,6 @@ static void INET6_displayroutes(void)
} while (i < 40+28+7);
}
- if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
- continue;
- }
-
set_flags(flags, (iflags & IPV6_MASK));
r = 0;
diff --git a/networking/slattach.c b/networking/slattach.c
index a500da6..9267eb1 100644
--- a/networking/slattach.c
+++ b/networking/slattach.c
@@ -12,6 +12,17 @@
*
* - The -F options allows disabling of RTS/CTS flow control.
*/
+//config:config SLATTACH
+//config: bool "slattach"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: slattach is a small utility to attach network interfaces to serial
+//config: lines.
+
+//applet:IF_SLATTACH(APPLET(slattach, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SLATTACH) += slattach.o
//usage:#define slattach_trivial_usage
//usage: "[-cehmLF] [-s SPEED] [-p PROTOCOL] DEVICE"
@@ -27,18 +38,19 @@
//usage: "\n -F Disable RTS/CTS flow control"
#include "libbb.h"
-#include "libiproute/utils.h" /* invarg() */
+#include "common_bufsiz.h"
+#include "libiproute/utils.h" /* invarg_1_to_2() */
struct globals {
int handle;
int saved_disc;
struct termios saved_state;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define handle (G.handle )
#define saved_disc (G.saved_disc )
#define saved_state (G.saved_state )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
/*
@@ -175,7 +187,7 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
encap = index_in_strings(proto_names, proto);
if (encap < 0)
- invarg(proto, "protocol");
+ invarg_1_to_2(proto, "protocol");
if (encap > 3)
encap = 8;
@@ -183,7 +195,7 @@ int slattach_main(int argc UNUSED_PARAM, char **argv)
if (opt & OPT_s_baud) {
baud_code = tty_value_to_baud(xatoi(baud_str));
if (baud_code < 0)
- invarg(baud_str, "baud rate");
+ invarg_1_to_2(baud_str, "baud rate");
}
/* Trap signals in order to restore tty states upon exit */
diff --git a/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.6.8 b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.6.8
new file mode 100755
index 0000000..50b6102
--- a/dev/null
+++ b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.6.8
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# How to configure & build a static wolfssl-3.6.8 library
+# suitable for static build of ssl_helper.
+
+export CC="i686-gcc"
+export CFLAGS="\
+-Os \
+-static \
+-fomit-frame-pointer \
+-falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 \
+-ffunction-sections -fdata-sections \
+"
+
+./configure \
+ --host=i686 \
+ --enable-static \
+ --enable-singlethreaded \
+ --disable-shared \
+|| exit $?
+
+make
diff --git a/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8 b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8
new file mode 100755
index 0000000..208a708
--- a/dev/null
+++ b/networking/ssl_helper-wolfssl/00cfg-wolfssl-3.9.8
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+# How to configure & build a static wolfssl library
+# suitable for static build of ssl_helper.
+
+export CC="i686-gcc"
+export CFLAGS="\
+-Os \
+-static \
+-fomit-frame-pointer \
+-falign-functions=1 -falign-labels=1 -falign-loops=1 -falign-jumps=1 \
+-ffunction-sections -fdata-sections \
+"
+
+{
+
+./configure \
+ --host="i686" \
+ --enable-static \
+ --enable-singlethreaded \
+ --disable-shared \
+\
+ C_EXTRA_FLAGS="-DWOLFSSL_STATIC_RSA" \
+|| exit $?
+
+# The second group of options was added when "vanilla" config did not work.
+# A good tool to debug problems is to try wolfssl's client tool, e.g.:
+# examples/client/client -h www.google.com -p 443 -d -x
+#
+# configure has many other options, see ./configure --help
+# --enable-ecc \
+# --enable-sni \
+#
+# Also consult "wolfSSL - Embedded SSL Library Product Support Forums"
+# for recent report of users having problems connecting.
+
+make
+
+} 2>&1 | tee "$0.log"
diff --git a/networking/ssl_helper-wolfssl/README b/networking/ssl_helper-wolfssl/README
new file mode 100644
index 0000000..ff46f4b
--- a/dev/null
+++ b/networking/ssl_helper-wolfssl/README
@@ -0,0 +1,27 @@
+A small SSL helper for busybox wget.
+
+Precompiled static binary may be found in
+http://busybox.net/downloads/binaries/
+
+Build instructions:
+
+* Unpack wolfssl-3.6.8.zip
+* Build it:
+ ./configure --enable-static --disable-shared && make
+* Drop this directory into wolfssl-3.6.8/ssl_helper
+* Run ssl_helper.sh to compile and link the helper
+
+* Unpack wolfssl-3.9.8.tar.gz from https://github.com/wolfSSL/wolfssl/releases
+* Create configure:
+ ./autogen.sh
+* Build it: see 00cfg-wolfssl-3.9.8 shell script
+* Drop this directory into wolfssl-x.y.z/ssl_helper
+* Run ssl_helper.sh to compile and link the helper
+
+Usage: "ssl_helper -d FILE_DESCRIPTOR" where FILE_DESCRIPTOR is open to the peer.
+
+In bash, you can do it this way:
+$ ssl_helper -d3 3<>/dev/tcp/HOST/PORT
+
+Stdin will be SSL-encrypted and sent to FILE_DESCRIPTOR.
+Data from FILE_DESCRIPTOR will be decrypted and sent to stdout.
diff --git a/networking/ssl_helper-wolfssl/ssl_helper.c b/networking/ssl_helper-wolfssl/ssl_helper.c
new file mode 100644
index 0000000..38b7b56
--- a/dev/null
+++ b/networking/ssl_helper-wolfssl/ssl_helper.c
@@ -0,0 +1,480 @@
+/*
+ * Adapted from:
+ *
+ * client.c
+ *
+ * Copyright (C) 2006-2015 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL. (formerly known as CyaSSL)
+ *
+ * wolfSSL is free software; you can 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.
+ *
+ * wolfSSL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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 <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/socket.h>
+
+#include <wolfssl/wolfcrypt/types.h>
+#include <wolfssl/ssl.h>
+
+#if 0
+# define dbg(...) say(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
+
+static ssize_t safe_write(int fd, const void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = write(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len) {
+ cc = safe_write(fd, buf, len);
+
+ if (cc < 0) {
+ if (total) {
+ /* we already wrote some! */
+ /* user can do another write to know the error code */
+ return total;
+ }
+ return cc; /* write() returns -1 on failure. */
+ }
+
+ total += cc;
+ buf = ((const char *)buf) + cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+static void say(const char *s, ...)
+{
+ char buf[256];
+ va_list p;
+ int sz;
+
+ va_start(p, s);
+ sz = vsnprintf(buf, sizeof(buf), s, p);
+ full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
+ va_end(p);
+}
+
+static void die(const char *s, ...)
+{
+ char buf[256];
+ va_list p;
+ int sz;
+
+ va_start(p, s);
+ sz = vsnprintf(buf, sizeof(buf), s, p);
+ full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
+ exit(1);
+ va_end(p);
+}
+
+static void err_sys(const char *msg)
+{
+ die("%s\n", msg);
+}
+
+/* ==== */
+
+#if 0
+static void showPeer(WOLFSSL* ssl)
+{
+ WOLFSSL_CIPHER* cipher;
+ WOLFSSL_X509* peer = wolfSSL_get_peer_certificate(ssl);
+ if (peer)
+ ShowX509(peer, "peer's cert info:");
+ else
+ say("peer has no cert!\n");
+ say("SSL version is %s\n", wolfSSL_get_version(ssl));
+
+ cipher = wolfSSL_get_current_cipher(ssl);
+ say("SSL cipher suite is %s\n", wolfSSL_CIPHER_get_name(cipher));
+
+ {
+ WOLFSSL_X509_CHAIN* chain = wolfSSL_get_peer_chain(ssl);
+ int count = wolfSSL_get_chain_count(chain);
+ int i;
+
+ for (i = 0; i < count; i++) {
+ int length;
+ unsigned char buffer[3072];
+ WOLFSSL_X509* chainX509;
+
+ wolfSSL_get_chain_cert_pem(chain, i, buffer, sizeof(buffer), &length);
+ buffer[length] = 0;
+ say("cert %d has length %d data = \n%s\n", i, length, buffer);
+
+ chainX509 = wolfSSL_get_chain_X509(chain, i);
+ if (chainX509)
+ ShowX509(chainX509, "session cert info:");
+ else
+ say("get_chain_X509 failed\n");
+ wolfSSL_FreeX509(chainX509);
+ }
+ }
+}
+#endif
+
+WOLFSSL *prepare(int sockfd)
+{
+ WOLFSSL_METHOD* method;
+ WOLFSSL_CTX* ctx;
+ WOLFSSL* ssl;
+
+ wolfSSL_Init();
+
+ method = wolfTLSv1_1_client_method();
+ if (method == NULL)
+ err_sys("out of memory");
+ ctx = wolfSSL_CTX_new(method);
+ if (ctx == NULL)
+ err_sys("out of memory");
+// if (cipherList)
+// if (wolfSSL_CTX_set_cipher_list(ctx, cipherList) != SSL_SUCCESS)
+// err_sys("client can't set cipher list 1");
+
+// if (fewerPackets)
+// wolfSSL_CTX_set_group_messages(ctx);
+
+//#ifndef NO_DH
+// wolfSSL_CTX_SetMinDhKey_Sz(ctx, (word16)minDhKeyBits);
+//#endif
+
+// if (usePsk) {
+// wolfSSL_CTX_set_psk_client_callback(ctx, my_psk_client_cb);
+// if (cipherList == NULL) {
+// const char *defaultCipherList;
+//#if defined(HAVE_AESGCM) && !defined(NO_DH)
+// defaultCipherList = "DHE-PSK-AES128-GCM-SHA256";
+//#elif defined(HAVE_NULL_CIPHER)
+// defaultCipherList = "PSK-NULL-SHA256";
+//#else
+// defaultCipherList = "PSK-AES128-CBC-SHA256";
+//#endif
+// if (wolfSSL_CTX_set_cipher_list(ctx,defaultCipherList) != SSL_SUCCESS)
+// err_sys("client can't set cipher list 2");
+// }
+// useClientCert = 0;
+// }
+
+// if (useAnon) {
+// if (cipherList == NULL) {
+// wolfSSL_CTX_allow_anon_cipher(ctx);
+// if (wolfSSL_CTX_set_cipher_list(ctx,"ADH-AES128-SHA") != SSL_SUCCESS)
+// err_sys("client can't set cipher list 4");
+// }
+// useClientCert = 0;
+// }
+
+//#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER)
+// wolfSSL_CTX_set_default_passwd_cb(ctx, PasswordCallBack);
+//#endif
+
+// if (useOcsp) {
+// if (ocspUrl != NULL) {
+// wolfSSL_CTX_SetOCSP_OverrideURL(ctx, ocspUrl);
+// wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_NO_NONCE
+// | WOLFSSL_OCSP_URL_OVERRIDE);
+// }
+// else
+// wolfSSL_CTX_EnableOCSP(ctx, WOLFSSL_OCSP_NO_NONCE);
+// }
+//
+//#ifdef USER_CA_CB
+// wolfSSL_CTX_SetCACb(ctx, CaCb);
+//#endif
+//
+//#ifdef VERIFY_CALLBACK
+// wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, myVerify);
+//#endif
+//#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
+// if (useClientCert) {
+// if (wolfSSL_CTX_use_certificate_chain_file(ctx, ourCert) != SSL_SUCCESS)
+// err_sys("can't load client cert file, check file and run from"
+// " wolfSSL home dir");
+// if (wolfSSL_CTX_use_PrivateKey_file(ctx, ourKey, SSL_FILETYPE_PEM) != SSL_SUCCESS)
+// err_sys("can't load client private key file, check file and run "
+// "from wolfSSL home dir");
+// }
+//
+// if (!usePsk && !useAnon) {
+// if (wolfSSL_CTX_load_verify_locations(ctx, verifyCert,0) != SSL_SUCCESS)
+// err_sys("can't load ca file, Please run from wolfSSL home dir");
+//#ifdef HAVE_ECC
+// /* load ecc verify too, echoserver uses it by default w/ ecc */
+// if (wolfSSL_CTX_load_verify_locations(ctx, eccCert, 0) != SSL_SUCCESS)
+// err_sys("can't load ecc ca file, Please run from wolfSSL home dir");
+//#endif
+// }
+//#endif /* !NO_FILESYSTEM && !NO_CERTS */
+
+//#if !defined(NO_CERTS)
+// if (!usePsk && !useAnon && doPeerCheck == 0)
+// wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
+// if (!usePsk && !useAnon && overrideDateErrors == 1)
+// wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, myDateCb);
+//#endif
+
+ wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
+
+//#ifdef HAVE_SNI
+// if (sniHostName)
+// if (wolfSSL_CTX_UseSNI(ctx, 0, sniHostName, XSTRLEN(sniHostName)) != SSL_SUCCESS)
+// err_sys("UseSNI failed");
+//#endif
+
+//#ifdef HAVE_MAX_FRAGMENT
+// if (maxFragment)
+// if (wolfSSL_CTX_UseMaxFragment(ctx, maxFragment) != SSL_SUCCESS)
+// err_sys("UseMaxFragment failed");
+//#endif
+//#ifdef HAVE_TRUNCATED_HMAC
+// if (truncatedHMAC)
+// if (wolfSSL_CTX_UseTruncatedHMAC(ctx) != SSL_SUCCESS)
+// err_sys("UseTruncatedHMAC failed");
+//#endif
+//#ifdef HAVE_SESSION_TICKET
+// if (wolfSSL_CTX_UseSessionTicket(ctx) != SSL_SUCCESS)
+// err_sys("UseSessionTicket failed");
+//#endif
+
+//#if defined(WOLFSSL_MDK_ARM)
+// wolfSSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
+//#endif
+
+ ssl = wolfSSL_new(ctx);
+ if (ssl == NULL)
+ err_sys("out of memory");
+
+//#ifdef HAVE_SESSION_TICKET
+// wolfSSL_set_SessionTicket_cb(ssl, sessionTicketCB, (void*)"initial session");
+//#endif
+
+// if (doDTLS) {
+// SOCKADDR_IN_T addr;
+// build_addr(&addr, host, port, 1);
+// wolfSSL_dtls_set_peer(ssl, &addr, sizeof(addr));
+// tcp_socket(&sockfd, 1);
+// } wlse {
+// tcp_connect(&sockfd, host, port, 0);
+// }
+
+//#ifdef HAVE_POLY1305
+// /* use old poly to connect with google server */
+// if (!XSTRNCMP(domain, "www.google.com", 14)) {
+// if (wolfSSL_use_old_poly(ssl, 1) != 0)
+// err_sys("unable to set to old poly");
+// }
+//#endif
+
+ wolfSSL_set_fd(ssl, sockfd);
+
+//#ifdef HAVE_CRL
+// if (disableCRL == 0) {
+// if (wolfSSL_EnableCRL(ssl, WOLFSSL_CRL_CHECKALL) != SSL_SUCCESS)
+// err_sys("can't enable crl check");
+// if (wolfSSL_LoadCRL(ssl, crlPemDir, SSL_FILETYPE_PEM, 0) != SSL_SUCCESS)
+// err_sys("can't load crl, check crlfile and date validity");
+// if (wolfSSL_SetCRL_Cb(ssl, CRL_CallBack) != SSL_SUCCESS)
+// err_sys("can't set crl callback");
+// }
+//#endif
+//#ifdef HAVE_SECURE_RENEGOTIATION
+// if (scr) {
+// if (wolfSSL_UseSecureRenegotiation(ssl) != SSL_SUCCESS)
+// err_sys("can't enable secure renegotiation");
+// }
+//#endif
+//#ifdef ATOMIC_USER
+// if (atomicUser)
+// SetupAtomicUser(ctx, ssl);
+//#endif
+//#ifdef HAVE_PK_CALLBACKS
+// if (pkCallbacks)
+// SetupPkCallbacks(ctx, ssl);
+//#endif
+// if (matchName && doPeerCheck)
+// wolfSSL_check_domain_name(ssl, domain);
+
+ if (wolfSSL_connect(ssl) != SSL_SUCCESS) {
+// /* see note at top of README */
+// int err = wolfSSL_get_error(ssl, 0);
+// char buffer[WOLFSSL_MAX_ERROR_SZ];
+// say("err = %d, %s\n", err,
+// wolfSSL_ERR_error_string(err, buffer));
+ err_sys("SSL_connect failed");
+ }
+// showPeer(ssl);
+
+//#ifdef HAVE_SECURE_RENEGOTIATION
+// if (scr && forceScr) {
+// if (wolfSSL_Rehandshake(ssl) != SSL_SUCCESS) {
+// int err = wolfSSL_get_error(ssl, 0);
+// char buffer[WOLFSSL_MAX_ERROR_SZ];
+// say("err = %d, %s\n", err,
+// wolfSSL_ERR_error_string(err, buffer));
+// err_sys("wolfSSL_Rehandshake failed");
+// }
+// }
+//#endif
+
+ return ssl;
+}
+
+static struct pollfd pfd[2] = {
+ { -1, POLLIN|POLLERR|POLLHUP, 0 },
+ { -1, POLLIN|POLLERR|POLLHUP, 0 },
+};
+#define STDIN pfd[0]
+#define NETWORK pfd[1]
+#define STDIN_READY() (pfd[0].revents & (POLLIN|POLLERR|POLLHUP))
+#define NETWORK_READY() (pfd[1].revents & (POLLIN|POLLERR|POLLHUP))
+
+static void wait_for_input(void)
+{
+ if (STDIN.fd == NETWORK.fd) /* means both are -1 */
+ exit(0);
+ dbg("polling\n");
+ STDIN.revents = NETWORK.revents = 0;
+ while (poll(pfd, 2, -1) < 0 && errno == EINTR)
+ continue;
+}
+
+static void do_io_until_eof_and_exit(WOLFSSL *ssl, int fd)
+{
+ int len;
+ char ibuf[4 * 1024];
+
+ NETWORK.fd = fd;
+ STDIN.fd = 0;
+
+ len = 0; /* only to suppress compiler warning */
+ for (;;) {
+ wait_for_input();
+
+ if (STDIN_READY()) {
+ dbg("reading stdin\n");
+ len = read(STDIN_FILENO, ibuf, sizeof(ibuf));
+ if (len < 0)
+ die("read error on stdin\n");
+ if (len == 0) {
+ dbg("read len = 0, stdin not polled anymore\n");
+ STDIN.fd = -1;
+ } else {
+ int n = wolfSSL_write(ssl, ibuf, len);
+ if (n != len)
+ die("SSL_write(%d) failed (returned %d)\n", len, n);
+ }
+ }
+
+ if (NETWORK_READY()) {
+ dbg("%s%s%s\n",
+ (pfd[1].revents & POLLIN) ? "POLLIN" : "",
+ (pfd[1].revents & POLLERR) ? "|POLLERR" : "",
+ (pfd[1].revents & POLLHUP) ? "|POLLHUP" : ""
+ );
+/* We are using blocking socket here.
+ * (Nonblocking socket would complicate writing to it).
+ * Therefore, SSL_read _can block_ here.
+ * This is not what wget expects (it wants to see short reads).
+ * Therefore, we use smallish buffer here, to approximate that.
+ */
+ len = wolfSSL_read(ssl, ibuf,
+ sizeof(ibuf) < 1024 ? sizeof(ibuf) : 1024
+ );
+ if (len < 0)
+ die("SSL_read error on network (%d)\n", len);
+ if (len > 0) {
+ int n;
+ n = full_write(STDOUT_FILENO, ibuf, len);
+ if (n != len)
+ die("write(%d) to stdout returned %d\n", len, n);
+ continue;
+ }
+/* Blocking reads are easier wtr EOF detection (no EAGAIN error to check for) */
+ dbg("read len = 0, network not polled anymore\n");
+ NETWORK.fd = -1;
+ /* saw EOF on network, and we processed
+ * and wrote out all ssl data. Signal it:
+ */
+ close(STDOUT_FILENO);
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ WOLFSSL *ssl;
+ int fd;
+ char *fd_str;
+
+ if (!argv[1])
+ die("Syntax error\n");
+ if (argv[1][0] != '-')
+ die("Syntax error\n");
+ if (argv[1][1] != 'd')
+ die("Syntax error\n");
+ fd_str = argv[1] + 2;
+ if (!fd_str[0])
+ fd_str = argv[2];
+ if (!fd_str || fd_str[0] < '0' || fd_str[0] > '9')
+ die("Syntax error\n");
+
+ fd = atoi(fd_str);
+ if (fd < 3)
+ die("Syntax error\n");
+
+ ssl = prepare(fd);
+ do_io_until_eof_and_exit(ssl, fd);
+ /* does not return */
+
+// if (doDTLS == 0) { /* don't send alert after "break" command */
+// ret = wolfSSL_shutdown(ssl);
+// if (wc_shutdown && ret == SSL_SHUTDOWN_NOT_DONE)
+// wolfSSL_shutdown(ssl); /* bidirectional shutdown */
+// }
+//#ifdef ATOMIC_USER
+// if (atomicUser)
+// FreeAtomicUser(ssl);
+//#endif
+// wolfSSL_free(ssl);
+// CloseSocket(sockfd);
+// wolfSSL_CTX_free(ctx);
+
+ return 0;
+}
diff --git a/networking/ssl_helper-wolfssl/ssl_helper.sh b/networking/ssl_helper-wolfssl/ssl_helper.sh
new file mode 100755
index 0000000..c6cbf35
--- a/dev/null
+++ b/networking/ssl_helper-wolfssl/ssl_helper.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# I use this to build static uclibc based binary using Aboriginal Linux toolchain:
+PREFIX="i686-"
+STATIC="-static"
+# Standard build:
+#PREFIX=""
+#STATIC=""
+
+${PREFIX}gcc -Os -Wall -I.. -c ssl_helper.c -o ssl_helper.o
+${PREFIX}gcc $STATIC --start-group ssl_helper.o -lm ../src/.libs/libwolfssl.a --end-group -o ssl_helper
+${PREFIX}strip ssl_helper
diff --git a/networking/ssl_helper/README b/networking/ssl_helper/README
new file mode 100644
index 0000000..4d0508f
--- a/dev/null
+++ b/networking/ssl_helper/README
@@ -0,0 +1,16 @@
+Build instructions:
+
+* Unpack matrixssl-3-4-2-open.tgz.
+* Build it: "make"
+* Drop this directory into matrixssl-3-4-2-open/ssl_helper
+* Run ssl_helper.sh to compile and link the helper
+
+Usage: "ssl_helper -d <FILE_DESCRIPTOR>" where FILE_DESCRIPTOR is open to the peer.
+
+In bash, you can do it this way:
+$ ssl_helper -d3 3<>/dev/tcp/HOST/PORT
+
+Stdin will be SSL-encrypted and sent to FILE_DESCRIPTOR.
+Data from FILE_DESCRIPTOR will be decrypted and sent to stdout.
+
+The plan is to adapt it for wget https helper, and for ssl support in nc.
diff --git a/networking/ssl_helper/ssl_helper.c b/networking/ssl_helper/ssl_helper.c
new file mode 100644
index 0000000..d840b1b
--- a/dev/null
+++ b/networking/ssl_helper/ssl_helper.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2013 INSIDE Secure Corporation
+ * Copyright (c) PeerSec Networks, 2002-2011
+ * All Rights Reserved
+ *
+ * The latest version of this code is available at http://www.matrixssl.org
+ *
+ * This software is open source; you can 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 WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See 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
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <time.h>
+#include <poll.h>
+#include <sys/socket.h>
+
+#include "matrixssl/matrixsslApi.h"
+
+//#warning "DO NOT USE THESE DEFAULT KEYS IN PRODUCTION ENVIRONMENTS."
+
+/*
+ * If supporting client authentication, pick ONE identity to auto select a
+ * certificate and private key that support desired algorithms.
+ */
+#define ID_RSA /* RSA Certificate and Key */
+
+#define USE_HEADER_KEYS
+
+/* If the algorithm type is supported, load a CA for it */
+#ifdef USE_HEADER_KEYS
+/* CAs */
+# include "sampleCerts/RSA/ALL_RSA_CAS.h"
+/* Identity Certs and Keys for use with Client Authentication */
+# ifdef ID_RSA
+# define EXAMPLE_RSA_KEYS
+# include "sampleCerts/RSA/2048_RSA.h"
+# include "sampleCerts/RSA/2048_RSA_KEY.h"
+# endif
+#endif
+
+static ssize_t safe_write(int fd, const void *buf, size_t count)
+{
+ ssize_t n;
+
+ do {
+ n = write(fd, buf, count);
+ } while (n < 0 && errno == EINTR);
+
+ return n;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+ ssize_t cc;
+ ssize_t total;
+
+ total = 0;
+
+ while (len) {
+ cc = safe_write(fd, buf, len);
+
+ if (cc < 0) {
+ if (total) {
+ /* we already wrote some! */
+ /* user can do another write to know the error code */
+ return total;
+ }
+ return cc; /* write() returns -1 on failure. */
+ }
+
+ total += cc;
+ buf = ((const char *)buf) + cc;
+ len -= cc;
+ }
+
+ return total;
+}
+
+static void say(const char *s, ...)
+{
+ char buf[256];
+ va_list p;
+ int sz;
+
+ va_start(p, s);
+ sz = vsnprintf(buf, sizeof(buf), s, p);
+ full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
+ va_end(p);
+}
+
+static void die(const char *s, ...)
+{
+ char buf[256];
+ va_list p;
+ int sz;
+
+ va_start(p, s);
+ sz = vsnprintf(buf, sizeof(buf), s, p);
+ full_write(STDERR_FILENO, buf, sz >= 0 && sz < sizeof(buf) ? sz : strlen(buf));
+ exit(1);
+ va_end(p);
+}
+
+#if 0
+# define dbg(...) say(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
+
+static struct pollfd pfd[2] = {
+ { -1, POLLIN|POLLERR|POLLHUP, 0 },
+ { -1, POLLIN|POLLERR|POLLHUP, 0 },
+};
+#define STDIN pfd[0]
+#define NETWORK pfd[1]
+#define STDIN_READY() (pfd[0].revents & (POLLIN|POLLERR|POLLHUP))
+#define NETWORK_READY() (pfd[1].revents & (POLLIN|POLLERR|POLLHUP))
+
+static int wait_for_input(void)
+{
+ if (STDIN.fd == NETWORK.fd) /* means both are -1 */
+ exit(0);
+ dbg("polling\n");
+ STDIN.revents = NETWORK.revents = 0;
+ return poll(pfd, 2, -1);
+}
+
+static int32 certCb(ssl_t *ssl, psX509Cert_t *cert, int32 alert)
+{
+ /* Example to allow anonymous connections based on a define */
+ if (alert > 0) {
+ return SSL_ALLOW_ANON_CONNECTION; // = 254
+ }
+#if 0
+ /* Validate the 'not before' and 'not after' dates, etc */
+ return PS_FAILURE; /* if we don't like this cert */
+#endif
+ return PS_SUCCESS;
+}
+
+static void close_conn_and_exit(ssl_t *ssl, int fd)
+{
+ unsigned char *buf;
+ int len;
+
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ /* Quick attempt to send a closure alert, don't worry about failure */
+ if (matrixSslEncodeClosureAlert(ssl) >= 0) {
+ len = matrixSslGetOutdata(ssl, &buf);
+ if (len > 0) {
+ len = safe_write(fd, buf, len);
+ //if (len > 0) {
+ // matrixSslSentData(ssl, len);
+ //}
+ }
+ }
+ //matrixSslDeleteSession(ssl);
+ shutdown(fd, SHUT_WR);
+ exit(0);
+}
+
+static int encode_data(ssl_t *ssl, const void *data, int len)
+{
+ unsigned char *buf;
+ int available;
+
+ available = matrixSslGetWritebuf(ssl, &buf, len);
+ if (available < 0)
+ die("matrixSslGetWritebuf\n");
+ if (len > available)
+ die("len > available\n");
+ memcpy(buf, data, len);
+ if (matrixSslEncodeWritebuf(ssl, len) < 0)
+ die("matrixSslEncodeWritebuf\n");
+ return len;
+}
+
+static void flush_to_net(ssl_t *ssl, int fd)
+{
+ int rc;
+ int len;
+ unsigned char *buf;
+
+ while ((len = matrixSslGetOutdata(ssl, &buf)) > 0) {
+ dbg("writing net %d bytes\n", len);
+ if (full_write(fd, buf, len) != len)
+ die("write to network\n");
+ rc = matrixSslSentData(ssl, len);
+ if (rc < 0)
+ die("matrixSslSentData\n");
+ }
+}
+
+static void do_io_until_eof_and_exit(int fd, sslKeys_t *keys)
+{
+ int rc;
+ int len;
+ uint32_t len32u;
+ sslSessionId_t *sid;
+ ssl_t *ssl;
+ unsigned char *buf;
+
+ NETWORK.fd = fd;
+ /* Note! STDIN.fd is disabled (-1) until SSL handshake is over:
+ * we do not attempt to feed any user data to MatrixSSL
+ * before it is ready.
+ */
+
+ matrixSslNewSessionId(&sid);
+ rc = matrixSslNewClientSession(&ssl, keys, sid, 0, certCb, NULL, NULL, 0);
+dbg("matrixSslNewClientSession:rc=%d\n", rc);
+ if (rc != MATRIXSSL_REQUEST_SEND)
+ die("matrixSslNewClientSession\n");
+
+ len = 0; /* only to suppress compiler warning */
+ again:
+ switch (rc) {
+ case MATRIXSSL_REQUEST_SEND:
+ dbg("MATRIXSSL_REQUEST_SEND\n");
+ flush_to_net(ssl, fd);
+ goto poll_input;
+
+ case 0:
+ dbg("rc==0\n");
+ flush_to_net(ssl, fd);
+ goto poll_input;
+
+ case MATRIXSSL_REQUEST_CLOSE:
+ /* what does this mean if we are here? */
+ dbg("MATRIXSSL_REQUEST_CLOSE\n");
+ close_conn_and_exit(ssl, fd);
+
+ case MATRIXSSL_HANDSHAKE_COMPLETE:
+ dbg("MATRIXSSL_HANDSHAKE_COMPLETE\n");
+ /* Init complete, can start reading local user's data: */
+ STDIN.fd = STDIN_FILENO;
+ poll_input:
+ wait_for_input();
+ if (STDIN_READY()) {
+ char ibuf[4 * 1024];
+ dbg("reading stdin\n");
+ len = read(STDIN_FILENO, ibuf, sizeof(ibuf));
+ if (len < 0)
+ die("read error on stdin\n");
+ if (len == 0)
+ STDIN.fd = -1;
+ else {
+ len = encode_data(ssl, ibuf, len);
+ if (len) {
+ rc = MATRIXSSL_REQUEST_SEND;
+dbg("rc=%d\n", rc);
+ goto again;
+ }
+ }
+ }
+ read_network:
+ if (NETWORK_READY()) {
+ dbg("%s%s%s\n",
+ (pfd[1].revents & POLLIN) ? "POLLIN" : "",
+ (pfd[1].revents & POLLERR) ? "|POLLERR" : "",
+ (pfd[1].revents & POLLHUP) ? "|POLLHUP" : ""
+ );
+ len = matrixSslGetReadbuf(ssl, &buf);
+ if (len <= 0)
+ die("matrixSslGetReadbuf\n");
+ dbg("reading net up to %d\n", len);
+ len = read(fd, buf, len);
+ dbg("reading net:%d\n", len);
+ if (len < 0)
+ die("read error on network\n");
+ if (len == 0) /*eof*/
+ NETWORK.fd = -1;
+ len32u = len;
+ rc = matrixSslReceivedData(ssl, len, &buf, &len32u);
+dbg("matrixSslReceivedData:rc=%d\n", rc);
+ len = len32u;
+ if (rc < 0)
+ die("matrixSslReceivedData\n");
+ }
+ goto again;
+
+ case MATRIXSSL_APP_DATA:
+ dbg("MATRIXSSL_APP_DATA: writing stdout\n");
+ do {
+ if (full_write(STDOUT_FILENO, buf, len) != len)
+ die("write to stdout\n");
+ len32u = len;
+ rc = matrixSslProcessedData(ssl, &buf, &len32u);
+//this was seen returning rc=0:
+dbg("matrixSslProcessedData:rc=%d\n", rc);
+ len = len32u;
+ } while (rc == MATRIXSSL_APP_DATA);
+ if (pfd[1].fd == -1) {
+ /* Already saw EOF on network, and we processed
+ * and wrote out all ssl data. Signal it:
+ */
+ close(STDOUT_FILENO);
+ }
+ goto again;
+
+ case MATRIXSSL_REQUEST_RECV:
+ dbg("MATRIXSSL_REQUEST_RECV\n");
+ wait_for_input();
+ goto read_network;
+
+ case MATRIXSSL_RECEIVED_ALERT:
+ dbg("MATRIXSSL_RECEIVED_ALERT\n");
+ /* The first byte of the buffer is the level */
+ /* The second byte is the description */
+ if (buf[0] == SSL_ALERT_LEVEL_FATAL)
+ die("Fatal alert\n");
+ /* Closure alert is normal (and best) way to close */
+ if (buf[1] == SSL_ALERT_CLOSE_NOTIFY)
+ close_conn_and_exit(ssl, fd);
+ die("Warning alert\n");
+ len32u = len;
+ rc = matrixSslProcessedData(ssl, &buf, &len32u);
+dbg("matrixSslProcessedData:rc=%d\n", rc);
+ len = len32u;
+ goto again;
+
+ default:
+ /* If rc < 0 it is an error */
+ die("bad rc:%d\n", rc);
+ }
+}
+
+static sslKeys_t* make_keys(void)
+{
+ int rc, CAstreamLen;
+ char *CAstream;
+ sslKeys_t *keys;
+
+ if (matrixSslNewKeys(&keys) < 0)
+ die("matrixSslNewKeys\n");
+
+#ifdef USE_HEADER_KEYS
+ /*
+ * In-memory based keys
+ * Build the CA list first for potential client auth usage
+ */
+ CAstream = NULL;
+ CAstreamLen = sizeof(RSACAS);
+ if (CAstreamLen > 0) {
+ CAstream = psMalloc(NULL, CAstreamLen);
+ memcpy(CAstream, RSACAS, sizeof(RSACAS));
+ }
+
+ #ifdef ID_RSA
+ rc = matrixSslLoadRsaKeysMem(keys, RSA2048, sizeof(RSA2048),
+ RSA2048KEY, sizeof(RSA2048KEY), (unsigned char*)CAstream,
+ CAstreamLen);
+ if (rc < 0)
+ die("matrixSslLoadRsaKeysMem\n");
+ #endif
+
+ if (CAstream)
+ psFree(CAstream);
+#endif /* USE_HEADER_KEYS */
+ return keys;
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+ char *fd_str;
+
+ if (!argv[1])
+ die("Syntax error\n");
+ if (argv[1][0] != '-')
+ die("Syntax error\n");
+ if (argv[1][1] != 'd')
+ die("Syntax error\n");
+ fd_str = argv[1] + 2;
+ if (!fd_str[0])
+ fd_str = argv[2];
+ if (!fd_str || fd_str[0] < '0' || fd_str[0] > '9')
+ die("Syntax error\n");
+
+ fd = atoi(fd_str);
+ if (fd < 3)
+ die("Syntax error\n");
+
+ if (matrixSslOpen() < 0)
+ die("matrixSslOpen\n");
+
+ do_io_until_eof_and_exit(fd, make_keys());
+ /* does not return */
+
+ return 0;
+}
diff --git a/networking/ssl_helper/ssl_helper.sh b/networking/ssl_helper/ssl_helper.sh
new file mode 100755
index 0000000..dc52de7
--- a/dev/null
+++ b/networking/ssl_helper/ssl_helper.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# I use this to build static uclibc based binary using Aboriginal Linux toolchain:
+PREFIX=x86_64-
+STATIC=-static
+# Standard build:
+PREFIX=""
+STATIC=""
+
+${PREFIX}gcc -Os -DPOSIX -I.. -I../sampleCerts -Wall -c ssl_helper.c -o ssl_helper.o
+${PREFIX}gcc $STATIC ssl_helper.o ../libmatrixssl.a -lc ../libmatrixssl.a -o ssl_helper
diff --git a/networking/tc.c b/networking/tc.c
index 533f7c0..25875aa 100644
--- a/networking/tc.c
+++ b/networking/tc.c
@@ -7,6 +7,22 @@
* Bernhard Reutner-Fischer adjusted for busybox
*/
+/* Was disabled in 2008 by Bernhard, not known why.
+--//config:#config TC
+--//config:# bool "tc"
+--//config:# default y
+--//config:# help
+--//config:# Show / manipulate traffic control settings
+--//config:#
+--//config:#config FEATURE_TC_INGRESS
+--//config:# default y
+--//config:# depends on TC
+--
+--//applet:IF_TC(APPLET(tc, BB_DIR_SBIN, BB_SUID_DROP))
+--
+--//kbuild:lib-$(CONFIG_TC) += tc.o
+*/
+
//usage:#define tc_trivial_usage
/* //usage: "[OPTIONS] OBJECT CMD [dev STRING]" */
//usage: "OBJECT CMD [dev STRING]"
@@ -29,6 +45,7 @@
//usage: "filter show [ dev STRING ] [ root | parent CLASSID ]"
#include "libbb.h"
+#include "common_bufsiz.h"
#include "libiproute/utils.h"
#include "libiproute/ip_common.h"
@@ -63,16 +80,16 @@ struct globals {
uint32_t filter_prio;
uint32_t filter_proto;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
+#define G (*(struct globals*)bb_common_bufsiz1)
#define filter_ifindex (G.filter_ifindex)
#define filter_qdisc (G.filter_qdisc)
#define filter_parent (G.filter_parent)
#define filter_prio (G.filter_prio)
#define filter_proto (G.filter_proto)
-#define INIT_G() do { } while (0)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+} while (0)
/* Allocates a buffer containing the name of a class id.
* The caller must free the returned memory. */
@@ -99,7 +116,7 @@ static int get_qdisc_handle(uint32_t *h, const char *str) {
char *p;
maj = TC_H_UNSPEC;
- if (!strcmp(str, "none"))
+ if (strcmp(str, "none") == 0)
goto ok;
maj = strtoul(str, &p, 16);
if (p == str)
@@ -118,10 +135,10 @@ static int get_tc_classid(uint32_t *h, const char *str) {
char *p;
maj = TC_H_ROOT;
- if (!strcmp(str, "root"))
+ if (strcmp(str, "root") == 0)
goto ok;
maj = TC_H_UNSPEC;
- if (!strcmp(str, "none"))
+ if (strcmp(str, "none") == 0)
goto ok;
maj = strtoul(str, &p, 16);
if (p == str) {
@@ -151,17 +168,17 @@ static void print_rate(char *buf, int len, uint32_t rate)
double tmp = (double)rate*8;
if (use_iec) {
- if (tmp >= 1000.0*1024.0*1024.0)
- snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0);
- else if (tmp >= 1000.0*1024)
+ if (tmp >= 1000*1024*1024)
+ snprintf(buf, len, "%.0fMibit", tmp/(1024*1024));
+ else if (tmp >= 1000*1024)
snprintf(buf, len, "%.0fKibit", tmp/1024);
else
snprintf(buf, len, "%.0fbit", tmp);
} else {
- if (tmp >= 1000.0*1000000.0)
- snprintf(buf, len, "%.0fMbit", tmp/1000000.0);
- else if (tmp >= 1000.0 * 1000.0)
- snprintf(buf, len, "%.0fKbit", tmp/1000.0);
+ if (tmp >= 1000*1000000)
+ snprintf(buf, len, "%.0fMbit", tmp/1000000);
+ else if (tmp >= 1000*1000)
+ snprintf(buf, len, "%.0fKbit", tmp/1000);
else
snprintf(buf, len, "%.0fbit", tmp);
}
@@ -460,14 +477,14 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
obj = index_in_substrings(objects, *argv++);
- if (obj < OBJ_qdisc)
+ if (obj < 0)
bb_show_usage();
if (!*argv)
cmd = CMD_show; /* list is the default */
else {
cmd = index_in_substrings(commands, *argv);
if (cmd < 0)
- bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+ invarg_1_to_2(*argv, argv[-1]);
argv++;
}
memset(&msg, 0, sizeof(msg));
@@ -490,7 +507,7 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
NEXT_ARG();
/* We don't care about duparg2("qdisc handle",*argv) for now */
if (get_qdisc_handle(&filter_qdisc, *argv))
- invarg(*argv, "qdisc");
+ invarg_1_to_2(*argv, "qdisc");
} else
if (obj != OBJ_qdisc
&& (arg == ARG_root
@@ -500,7 +517,7 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
) {
/* nothing */
} else {
- invarg(*argv, "command");
+ invarg_1_to_2(*argv, "command");
}
NEXT_ARG();
if (arg == ARG_root) {
@@ -514,7 +531,7 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
if (msg.tcm_parent)
duparg(*argv, "parent");
if (get_tc_classid(&handle, *argv))
- invarg(*argv, "parent");
+ invarg_1_to_2(*argv, "parent");
msg.tcm_parent = handle;
if (obj == OBJ_filter)
filter_parent = handle;
@@ -539,7 +556,7 @@ int tc_main(int argc UNUSED_PARAM, char **argv)
if (filter_proto)
duparg(*argv, "protocol");
if (ll_proto_a2n(&tmp, *argv))
- invarg(*argv, "protocol");
+ invarg_1_to_2(*argv, "protocol");
filter_proto = tmp;
}
}
diff --git a/networking/tcpudp.c b/networking/tcpudp.c
index 3df6a98..3a6c686 100644
--- a/networking/tcpudp.c
+++ b/networking/tcpudp.c
@@ -28,50 +28,89 @@
* with wrong source IP...
* - don't know how to retrieve ORIGDST for udp.
*/
+//config:config TCPSVD
+//config: bool "tcpsvd"
+//config: default y
+//config: help
+//config: tcpsvd listens on a TCP port and runs a program for each new
+//config: connection.
+//config:
+//config:config UDPSVD
+//config: bool "udpsvd"
+//config: default y
+//config: help
+//config: udpsvd listens on an UDP port and runs a program for each new
+//config: connection.
+
+//applet:IF_TCPSVD(APPLET_ODDNAME(tcpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, tcpsvd))
+//applet:IF_UDPSVD(APPLET_ODDNAME(udpsvd, tcpudpsvd, BB_DIR_USR_BIN, BB_SUID_DROP, udpsvd))
+
+//kbuild:lib-$(CONFIG_TCPSVD) += tcpudp.o tcpudp_perhost.o
+//kbuild:lib-$(CONFIG_UDPSVD) += tcpudp.o tcpudp_perhost.o
//usage:#define tcpsvd_trivial_usage
//usage: "[-hEv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] IP PORT PROG"
/* with not-implemented options: */
/* //usage: "[-hpEvv] [-c N] [-C N[:MSG]] [-b N] [-u USER] [-l NAME] [-i DIR|-x CDB] [-t SEC] IP PORT PROG" */
//usage:#define tcpsvd_full_usage "\n\n"
-//usage: "Create TCP socket, bind to IP:PORT and listen\n"
-//usage: "for incoming connection. Run PROG for each connection.\n"
-//usage: "\n IP IP to listen on, 0 = all"
-//usage: "\n PORT Port to listen on"
+//usage: "Create TCP socket, bind to IP:PORT and listen for incoming connections.\n"
+//usage: "Run PROG for each connection.\n"
+//usage: "\n IP PORT IP:PORT to listen on"
//usage: "\n PROG ARGS Program to run"
-//usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)"
//usage: "\n -u USER[:GRP] Change to user/group after bind"
-//usage: "\n -c N Handle up to N connections simultaneously"
-//usage: "\n -b N Allow a backlog of approximately N TCP SYNs"
-//usage: "\n -C N[:MSG] Allow only up to N connections from the same IP"
-//usage: "\n New connections from this IP address are closed"
-//usage: "\n immediately. MSG is written to the peer before close"
+//usage: "\n -c N Up to N connections simultaneously (default 30)"
+//usage: "\n -b N Allow backlog of approximately N TCP SYNs (default 20)"
+//usage: "\n -C N[:MSG] Allow only up to N connections from the same IP:"
+//usage: "\n new connections from this IP address are closed"
+//usage: "\n immediately, MSG is written to the peer before close"
+//usage: "\n -E Don't set up environment"
//usage: "\n -h Look up peer's hostname"
-//usage: "\n -E Don't set up environment variables"
+//usage: "\n -l NAME Local hostname (else look up local hostname in DNS)"
//usage: "\n -v Verbose"
+//usage: "\n"
+//usage: "\nEnvironment if no -E:"
+//usage: "\nPROTO='TCP'"
+//usage: "\nTCPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
+//usage: "\nTCPLOCALADDR='ip:port'"
+//usage: "\nTCPORIGDSTADDR='ip:port' of destination before firewall"
+//usage: "\n Useful for REDIRECTed-to-local connections:"
+//usage: "\n iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to 8080"
+//usage: "\nTCPCONCURRENCY=num_of_connects_from_this_ip"
+//usage: "\nIf -h:"
+//usage: "\nTCPLOCALHOST='hostname' (-l NAME is used if specified)"
+//usage: "\nTCPREMOTEHOST='hostname'"
+
//usage:
//usage:#define udpsvd_trivial_usage
//usage: "[-hEv] [-c N] [-u USER] [-l NAME] IP PORT PROG"
//usage:#define udpsvd_full_usage "\n\n"
-//usage: "Create UDP socket, bind to IP:PORT and wait\n"
-//usage: "for incoming packets. Run PROG for each packet,\n"
-//usage: "redirecting all further packets with same peer ip:port to it.\n"
-//usage: "\n IP IP to listen on, 0 = all"
-//usage: "\n PORT Port to listen on"
+//usage: "Create UDP socket, bind to IP:PORT and wait for incoming packets.\n"
+//usage: "Run PROG for each packet, redirecting all further packets with same\n"
+//usage: "peer ip:port to it.\n"
+//usage: "\n IP PORT IP:PORT to listen on"
//usage: "\n PROG ARGS Program to run"
-//usage: "\n -l NAME Local hostname (else looks up local hostname in DNS)"
//usage: "\n -u USER[:GRP] Change to user/group after bind"
-//usage: "\n -c N Handle up to N connections simultaneously"
+//usage: "\n -c N Up to N connections simultaneously (default 30)"
+//usage: "\n -E Don't set up environment"
//usage: "\n -h Look up peer's hostname"
-//usage: "\n -E Don't set up environment variables"
+//usage: "\n -l NAME Local hostname (else look up local hostname in DNS)"
//usage: "\n -v Verbose"
+//usage: "\n"
+//usage: "\nEnvironment if no -E:"
+//usage: "\nPROTO='UDP'"
+//usage: "\nUDPREMOTEADDR='ip:port'" IF_FEATURE_IPV6(" ('[ip]:port' for IPv6)")
+//usage: "\nUDPLOCALADDR='ip:port'"
+//usage: "\nIf -h:"
+//usage: "\nUDPLOCALHOST='hostname' (-l NAME is used if specified)"
+//usage: "\nUDPREMOTEHOST='hostname'"
#include "libbb.h"
+#include "common_bufsiz.h"
-/* Wants <limits.h> etc, thus included after libbb.h: */
#ifdef __linux__
-#include <linux/types.h> /* for __be32 etc */
-#include <linux/netfilter_ipv4.h>
+/* from linux/netfilter_ipv4.h: */
+# undef SO_ORIGINAL_DST
+# define SO_ORIGINAL_DST 80
#endif
// TODO: move into this file:
@@ -91,7 +130,7 @@ struct globals {
char **env_cur;
char *env_var[1]; /* actually bigger */
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define verbose (G.verbose )
#define max_per_host (G.max_per_host)
#define cur_per_host (G.cur_per_host)
@@ -100,6 +139,7 @@ struct globals {
#define env_cur (G.env_cur )
#define env_var (G.env_var )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
cmax = 30; \
env_cur = &env_var[0]; \
} while (0)
@@ -230,15 +270,15 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
tcp = (applet_name[0] == 't');
/* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
- opt_complementary = "-3:i--i:ph:vv:b+:c+";
+ opt_complementary = "-3:i--i:ph:vv";
#ifdef SSLSVD
- opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
+ opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:",
&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
&backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
);
#else
/* "+": stop on first non-option */
- opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
+ opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:hpt:v",
&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
&backlog, &str_t, &verbose
);
@@ -347,16 +387,20 @@ int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
again:
hccp = NULL;
+ again1:
+ close(0);
+ /* It's important to close(0) _before_ wait loop:
+ * fd#0 can be a shared connection fd.
+ * If kept open by us, peer can't detect PROG closing it.
+ */
while (cnum >= cmax)
wait_for_any_sig(); /* expecting SIGCHLD */
- /* Accept a connection to fd #0 */
- again1:
- close(0);
again2:
sig_unblock(SIGCHLD);
local.len = remote.len = sa_len;
if (tcp) {
+ /* Accept a connection to fd #0 */
conn = accept(sock, &remote.u.sa, &remote.len);
} else {
/* In case recv_from_to won't be able to recover local addr.
diff --git a/networking/telnet.c b/networking/telnet.c
index a255797..f520fe1 100644
--- a/networking/telnet.c
+++ b/networking/telnet.c
@@ -20,6 +20,35 @@
* by Fernando Silveira <swrh@gmx.net>
*
*/
+//config:config TELNET
+//config: bool "telnet"
+//config: default y
+//config: help
+//config: Telnet is an interface to the TELNET protocol, but is also commonly
+//config: used to test other simple protocols.
+//config:
+//config:config FEATURE_TELNET_TTYPE
+//config: bool "Pass TERM type to remote host"
+//config: default y
+//config: depends on TELNET
+//config: help
+//config: Setting this option will forward the TERM environment variable to the
+//config: remote host you are connecting to. This is useful to make sure that
+//config: things like ANSI colors and other control sequences behave.
+//config:
+//config:config FEATURE_TELNET_AUTOLOGIN
+//config: bool "Pass USER type to remote host"
+//config: default y
+//config: depends on TELNET
+//config: help
+//config: Setting this option will forward the USER environment variable to the
+//config: remote host you are connecting to. This is useful when you need to
+//config: log into a machine without telling the username (autologin). This
+//config: option enables `-a' and `-l USER' arguments.
+
+//applet:IF_TELNET(APPLET(telnet, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TELNET) += telnet.o
//usage:#if ENABLE_FEATURE_TELNET_AUTOLOGIN
//usage:#define telnet_trivial_usage
@@ -39,6 +68,7 @@
#include <arpa/telnet.h>
#include <netinet/in.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#ifdef __BIONIC__
/* should be in arpa/telnet.h */
@@ -108,11 +138,10 @@ struct globals {
struct termios termios_def;
struct termios termios_raw;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
- struct G_sizecheck { \
- char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
- }; \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
} while (0)
@@ -311,15 +340,16 @@ static void put_iac(int c)
G.iacbuf[G.iaclen++] = c;
}
-static void put_iac2(byte wwdd, byte c)
+static void put_iac2_merged(unsigned wwdd_and_c)
{
if (G.iaclen + 3 > IACBUFSIZE)
iac_flush();
put_iac(IAC);
- put_iac(wwdd);
- put_iac(c);
+ put_iac(wwdd_and_c >> 8);
+ put_iac(wwdd_and_c & 0xff);
}
+#define put_iac2(wwdd,c) put_iac2_merged(((wwdd)<<8) + (c))
#if ENABLE_FEATURE_TELNET_TTYPE
static void put_iac_subopt(byte c, char *str)
@@ -623,7 +653,7 @@ int telnet_main(int argc UNUSED_PARAM, char **argv)
xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
- setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_keepalive(netfd);
signal(SIGINT, record_signo);
diff --git a/networking/telnetd.IAC_test.sh b/networking/telnetd.IAC_test.sh
new file mode 100644
index 0000000..a36ee3a
--- a/dev/null
+++ b/networking/telnetd.IAC_test.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+# Testcase for IAC input processing.
+# The bug also required a small and odd BUFSIZE ("enum { BUFSIZE = 37 };")
+# in telnetd.c to trigger easily.
+
+echo "\
+Run telnetd like this:
+ busybox telnetd -l./save.sh -F
+where save.sh is
+ #!/bin/sh
+ stty -echo
+ exec cat >save.dat
+Now I'll try to connect to it and feed it 2048 0xff bytes.
+Check that save.dat does contain 2048 0xff bytes.
+"
+
+ff()
+{
+echo -en \
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff'\
+'\r\n'; }
+
+ff | wc -c
+{ ff; sleep 2; } | busybox telnet 127.0.0.1
diff --git a/networking/telnetd.c b/networking/telnetd.c
index d00d87d..f06e958 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -20,6 +20,78 @@
* Vladimir Oleynik <dzo@simtreas.ru> 2001
* Set process group corrections, initial busybox port
*/
+//config:config TELNETD
+//config: bool "telnetd"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: A daemon for the TELNET protocol, allowing you to log onto the host
+//config: running the daemon. Please keep in mind that the TELNET protocol
+//config: sends passwords in plain text. If you can't afford the space for an
+//config: SSH daemon and you trust your network, you may say 'y' here. As a
+//config: more secure alternative, you should seriously consider installing the
+//config: very small Dropbear SSH daemon instead:
+//config: http://matt.ucc.asn.au/dropbear/dropbear.html
+//config:
+//config: Note that for busybox telnetd to work you need several things:
+//config: First of all, your kernel needs:
+//config: CONFIG_UNIX98_PTYS=y
+//config:
+//config: Next, you need a /dev/pts directory on your root filesystem:
+//config:
+//config: $ ls -ld /dev/pts
+//config: drwxr-xr-x 2 root root 0 Sep 23 13:21 /dev/pts/
+//config:
+//config: Next you need the pseudo terminal master multiplexer /dev/ptmx:
+//config:
+//config: $ ls -la /dev/ptmx
+//config: crw-rw-rw- 1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+//config:
+//config: Any /dev/ttyp[0-9]* files you may have can be removed.
+//config: Next, you need to mount the devpts filesystem on /dev/pts using:
+//config:
+//config: mount -t devpts devpts /dev/pts
+//config:
+//config: You need to be sure that busybox has LOGIN and
+//config: FEATURE_SUID enabled. And finally, you should make
+//config: certain that Busybox has been installed setuid root:
+//config:
+//config: chown root.root /bin/busybox
+//config: chmod 4755 /bin/busybox
+//config:
+//config: with all that done, telnetd _should_ work....
+//config:
+//config:config FEATURE_TELNETD_STANDALONE
+//config: bool "Support standalone telnetd (not inetd only)"
+//config: default y
+//config: depends on TELNETD
+//config: help
+//config: Selecting this will make telnetd able to run standalone.
+//config:
+//config:config FEATURE_TELNETD_INETD_WAIT
+//config: bool "Support -w SEC option (inetd wait mode)"
+//config: default y
+//config: depends on FEATURE_TELNETD_STANDALONE
+//config: help
+//config: This option allows you to run telnetd in "inet wait" mode.
+//config: Example inetd.conf line (note "wait", not usual "nowait"):
+//config:
+//config: telnet stream tcp wait root /bin/telnetd telnetd -w10
+//config:
+//config: In this example, inetd passes _listening_ socket_ as fd 0
+//config: to telnetd when connection appears.
+//config: telnetd will wait for connections until all existing
+//config: connections are closed, and no new connections
+//config: appear during 10 seconds. Then it exits, and inetd continues
+//config: to listen for new connections.
+//config:
+//config: This option is rarely used. "tcp nowait" is much more usual
+//config: way of running tcp services, including telnetd.
+//config: You most probably want to say N here.
+
+//applet:IF_TELNETD(APPLET(telnetd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TELNETD) += telnetd.o
//usage:#define telnetd_trivial_usage
//usage: "[OPTIONS]"
@@ -44,6 +116,7 @@
#define DEBUG 0
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
#if DEBUG
@@ -59,6 +132,7 @@ struct tsession {
int sockfd_read;
int sockfd_write;
int ptyfd;
+ smallint buffered_IAC_for_pty;
/* two circular buffers */
/*char *buf1, *buf2;*/
@@ -82,114 +156,205 @@ struct globals {
const char *issuefile;
int maxfd;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
- G.loginpath = "/system/xbin/login"; \
+ setup_common_bufsiz(); \
+ G.loginpath = "/bin/login"; \
G.issuefile = "/etc/issue.net"; \
} while (0)
-/*
- Remove all IAC's from buf1 (received IACs are ignored and must be removed
- so as to not be interpreted by the terminal). Make an uninterrupted
- string of characters fit for the terminal. Do this by packing
- all characters meant for the terminal sequentially towards the end of buf.
-
- Return a pointer to the beginning of the characters meant for the terminal
- and make *num_totty the number of characters that should be sent to
- the terminal.
-
- Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
- past (bf + len) then that IAC will be left unprocessed and *processed
- will be less than len.
-
- CR-LF ->'s CR mapping is also done here, for convenience.
-
- NB: may fail to remove iacs which wrap around buffer!
+/* Write some buf1 data to pty, processing IACs.
+ * Update wridx1 and size1. Return < 0 on error.
+ * Buggy if IAC is present but incomplete: skips them.
*/
-static unsigned char *
-remove_iacs(struct tsession *ts, int *pnum_totty)
+static ssize_t
+safe_write_to_pty_decode_iac(struct tsession *ts)
{
- unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
- unsigned char *ptr = ptr0;
- unsigned char *totty = ptr;
- unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
- int num_totty;
-
- while (ptr < end) {
- if (*ptr != IAC) {
- char c = *ptr;
-
- *totty++ = c;
- ptr++;
- /* We map \r\n ==> \r for pragmatic reasons.
- * Many client implementations send \r\n when
- * the user hits the CarriageReturn key.
- * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
- */
- if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
- ptr++;
- continue;
- }
+ unsigned wr;
+ ssize_t rc;
+ unsigned char *buf;
+ unsigned char *found;
+
+ buf = TS_BUF1(ts) + ts->wridx1;
+ wr = MIN(BUFSIZE - ts->wridx1, ts->size1);
+ /* wr is at least 1 here */
+
+ if (ts->buffered_IAC_for_pty) {
+ /* Last time we stopped on a "dangling" IAC byte.
+ * We removed it from the buffer back then.
+ * Now pretend it's still there, and jump to IAC processing.
+ */
+ ts->buffered_IAC_for_pty = 0;
+ wr++;
+ ts->size1++;
+ buf--; /* Yes, this can point before the buffer. It's ok */
+ ts->wridx1--;
+ goto handle_iac;
+ }
- if ((ptr+1) >= end)
- break;
- if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
- ptr += 2;
- continue;
- }
- if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
- *totty++ = ptr[1];
- ptr += 2;
- continue;
- }
+ found = memchr(buf, IAC, wr);
+ if (found != buf) {
+ /* There is a "prefix" of non-IAC chars.
+ * Write only them, and return.
+ */
+ if (found)
+ wr = found - buf;
- /*
- * TELOPT_NAWS support!
+ /* We map \r\n ==> \r for pragmatic reasons:
+ * many client implementations send \r\n when
+ * the user hits the CarriageReturn key.
+ * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
*/
- if ((ptr+2) >= end) {
- /* Only the beginning of the IAC is in the
- buffer we were asked to process, we can't
- process this char */
- break;
+ rc = wr;
+ found = memchr(buf, '\r', wr);
+ if (found)
+ rc = found - buf + 1;
+ rc = safe_write(ts->ptyfd, buf, rc);
+ if (rc <= 0)
+ return rc;
+ if (rc < wr /* don't look past available data */
+ && buf[rc-1] == '\r' /* need this: imagine that write was _short_ */
+ && (buf[rc] == '\n' || buf[rc] == '\0')
+ ) {
+ rc++;
}
+ goto update_and_return;
+ }
+
+ /* buf starts with IAC char. Process that sequence.
+ * Example: we get this from our own (bbox) telnet client:
+ * read(5, "\377\374\1""\377\373\37""\377\372\37\0\262\0@\377\360""\377\375\1""\377\375\3"):
+ * IAC WONT ECHO, IAC WILL NAWS, IAC SB NAWS <cols> <rows> IAC SE, IAC DO SGA
+ * Another example (telnet-0.17 from old-netkit):
+ * read(4, "\377\375\3""\377\373\30""\377\373\37""\377\373 ""\377\373!""\377\373\"""\377\373'"
+ * "\377\375\5""\377\373#""\377\374\1""\377\372\37\0\257\0I\377\360""\377\375\1"):
+ * IAC DO SGA, IAC WILL TTYPE, IAC WILL NAWS, IAC WILL TSPEED, IAC WILL LFLOW, IAC WILL LINEMODE, IAC WILL NEW_ENVIRON,
+ * IAC DO STATUS, IAC WILL XDISPLOC, IAC WONT ECHO, IAC SB NAWS <cols> <rows> IAC SE, IAC DO ECHO
+ */
+ if (wr <= 1) {
+ /* Only the single IAC byte is in the buffer, eat it
+ * and set a flag "process the rest of the sequence
+ * next time we are here".
+ */
+ //bb_error_msg("dangling IAC!");
+ ts->buffered_IAC_for_pty = 1;
+ rc = 1;
+ goto update_and_return;
+ }
+
+ handle_iac:
+ /* 2-byte commands (240..250 and 255):
+ * IAC IAC (255) Literal 255. Supported.
+ * IAC SE (240) End of subnegotiation. Treated as NOP.
+ * IAC NOP (241) NOP. Supported.
+ * IAC BRK (243) Break. Like serial line break. TODO via tcsendbreak()?
+ * IAC AYT (246) Are you there. Send back evidence that AYT was seen. TODO (send NOP back)?
+ * These don't look useful:
+ * IAC DM (242) Data mark. What is this?
+ * IAC IP (244) Suspend, interrupt or abort the process. (Ancient cousin of ^C).
+ * IAC AO (245) Abort output. "You can continue running, but do not send me the output".
+ * IAC EC (247) Erase character. The receiver should delete the last received char.
+ * IAC EL (248) Erase line. The receiver should delete everything up tp last newline.
+ * IAC GA (249) Go ahead. For half-duplex lines: "now you talk".
+ * Implemented only as part of NAWS:
+ * IAC SB (250) Subnegotiation of an option follows.
+ */
+ if (buf[1] == IAC) {
+ /* Literal 255 (emacs M-DEL) */
+ //bb_error_msg("255!");
+ rc = safe_write(ts->ptyfd, &buf[1], 1);
/*
- * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+ * If we went through buffered_IAC_for_pty==1 path,
+ * bailing out on error like below messes up the buffer.
+ * EAGAIN is highly unlikely here, other errors will be
+ * repeated on next write, let's just skip error check.
*/
- if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+#if 0
+ if (rc <= 0)
+ return rc;
+#endif
+ rc = 2;
+ goto update_and_return;
+ }
+ if (buf[1] >= 240 && buf[1] <= 249) {
+ /* NOP (241). Ignore (putty keepalive, etc) */
+ /* All other 2-byte commands also treated as NOPs here */
+ rc = 2;
+ goto update_and_return;
+ }
+
+ if (wr <= 2) {
+/* BUG: only 2 bytes of the IAC is in the buffer, we just eat them.
+ * This is not a practical problem since >2 byte IACs are seen only
+ * in initial negotiation, when buffer is empty
+ */
+ rc = 2;
+ goto update_and_return;
+ }
+
+ if (buf[1] == SB) {
+ if (buf[2] == TELOPT_NAWS) {
+ /* IAC SB, TELOPT_NAWS, 4-byte, IAC SE */
struct winsize ws;
- if ((ptr+8) >= end)
- break; /* incomplete, can't process */
- ws.ws_col = (ptr[3] << 8) | ptr[4];
- ws.ws_row = (ptr[5] << 8) | ptr[6];
+ if (wr <= 6) {
+/* BUG: incomplete, can't process */
+ rc = wr;
+ goto update_and_return;
+ }
+ memset(&ws, 0, sizeof(ws)); /* pixel sizes are set to 0 */
+ ws.ws_col = (buf[3] << 8) | buf[4];
+ ws.ws_row = (buf[5] << 8) | buf[6];
ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
- ptr += 9;
- continue;
+ rc = 7;
+ /* trailing IAC SE will be eaten separately, as 2-byte NOP */
+ goto update_and_return;
}
- /* skip 3-byte IAC non-SB cmd */
+ /* else: other subnegs not supported yet */
+ }
+
+ /* Assume it is a 3-byte WILL/WONT/DO/DONT 251..254 command and skip it */
#if DEBUG
- fprintf(stderr, "Ignoring IAC %s,%s\n",
- TELCMD(ptr[1]), TELOPT(ptr[2]));
+ fprintf(stderr, "Ignoring IAC %s,%s\n",
+ TELCMD(buf[1]), TELOPT(buf[2]));
#endif
- ptr += 3;
+ rc = 3;
+
+ update_and_return:
+ ts->wridx1 += rc;
+ if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
+ ts->wridx1 = 0;
+ ts->size1 -= rc;
+ /*
+ * Hack. We cannot process IACs which wrap around buffer's end.
+ * Since properly fixing it requires writing bigger code,
+ * we rely instead on this code making it virtually impossible
+ * to have wrapped IAC (people don't type at 2k/second).
+ * It also allows for bigger reads in common case.
+ */
+ if (ts->size1 == 0) { /* very typical */
+ //bb_error_msg("zero size1");
+ ts->rdidx1 = 0;
+ ts->wridx1 = 0;
+ return rc;
}
-
- num_totty = totty - ptr0;
- *pnum_totty = num_totty;
- /* The difference between ptr and totty is number of iacs
- we removed from the stream. Adjust buf1 accordingly */
- if ((ptr - totty) == 0) /* 99.999% of cases */
- return ptr0;
- ts->wridx1 += ptr - totty;
- ts->size1 -= ptr - totty;
- /* Move chars meant for the terminal towards the end of the buffer */
- return memmove(ptr - num_totty, ptr0, num_totty);
+ wr = ts->wridx1;
+ if (wr != 0 && wr < ts->rdidx1) {
+ /* Buffer is not wrapped yet.
+ * We can easily move it to the beginning.
+ */
+ //bb_error_msg("moved %d", wr);
+ memmove(TS_BUF1(ts), TS_BUF1(ts) + wr, ts->size1);
+ ts->rdidx1 -= wr;
+ ts->wridx1 = 0;
+ }
+ return rc;
}
/*
* Converting single IAC into double on output
*/
-static size_t iac_safe_write(int fd, const char *buf, size_t count)
+static size_t safe_write_double_iac(int fd, const char *buf, size_t count)
{
const char *IACptr;
size_t wr, rc, total;
@@ -201,6 +366,7 @@ static size_t iac_safe_write(int fd, const char *buf, size_t count)
if (*buf == (char)IAC) {
static const char IACIAC[] ALIGN1 = { IAC, IAC };
rc = safe_write(fd, IACIAC, 2);
+/* BUG: if partial write was only 1 byte long, we end up emitting just one IAC */
if (rc != 2)
break;
buf++;
@@ -265,7 +431,7 @@ make_new_session(
close_on_exec_on(fd);
/* SO_KEEPALIVE by popular demand */
- setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+ setsockopt_keepalive(sock);
#if ENABLE_FEATURE_TELNETD_STANDALONE
ts->sockfd_read = sock;
ndelay_on(sock);
@@ -296,7 +462,7 @@ make_new_session(
IAC, WILL, TELOPT_ECHO,
IAC, WILL, TELOPT_SGA
};
- /* This confuses iac_safe_write(), it will try to duplicate
+ /* This confuses safe_write_double_iac(), it will try to duplicate
* each IAC... */
//memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
//ts->rdidx2 = sizeof(iacs_to_send);
@@ -462,15 +628,7 @@ static void handle_sigchld(int sig UNUSED_PARAM)
while (ts) {
if (ts->shell_pid == pid) {
ts->shell_pid = -1;
-// man utmp:
-// When init(8) finds that a process has exited, it locates its utmp entry
-// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
-// and ut_time with null bytes.
-// [same applies to other processes which maintain utmp entries, like telnetd]
-//
-// We do not bother actually clearing fields:
-// it might be interesting to know who was logged in and from where
- update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
+ update_utmp_DEAD_PROCESS(pid);
break;
}
ts = ts->next;
@@ -489,8 +647,8 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
struct tsession *ts;
#if ENABLE_FEATURE_TELNETD_STANDALONE
#define IS_INETD (opt & OPT_INETD)
- int master_fd = 0;
- int sec_linger = 0;
+ int master_fd = master_fd; /* for compiler */
+ int sec_linger = sec_linger;
char *opt_bindaddr = NULL;
char *opt_portnbr;
#else
@@ -502,12 +660,12 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
/* -w NUM, and implies -F. -w and -i don't mix */
- IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
+ IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:i--w:w--i";)
/* Even if !STANDALONE, we accept (and ignore) -i, thus people
* don't need to guess whether it's ok to pass -i to us */
opt = getopt32(argv, "f:l:Ki"
IF_FEATURE_TELNETD_STANDALONE("p:b:F")
- IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
+ IF_FEATURE_TELNETD_INETD_WAIT("Sw:+"),
&G.issuefile, &G.loginpath
IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
@@ -655,51 +813,34 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
struct tsession *next = ts->next; /* in case we free ts */
if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
- int num_totty;
- unsigned char *ptr;
/* Write to pty from buffer 1 */
- ptr = remove_iacs(ts, &num_totty);
- count = safe_write(ts->ptyfd, ptr, num_totty);
+ count = safe_write_to_pty_decode_iac(ts);
if (count < 0) {
if (errno == EAGAIN)
goto skip1;
goto kill_session;
}
- ts->size1 -= count;
- ts->wridx1 += count;
- if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
- ts->wridx1 = 0;
}
skip1:
if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
/* Write to socket from buffer 2 */
count = MIN(BUFSIZE - ts->wridx2, ts->size2);
- count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
+ count = safe_write_double_iac(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
if (count < 0) {
if (errno == EAGAIN)
goto skip2;
goto kill_session;
}
- ts->size2 -= count;
ts->wridx2 += count;
if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
ts->wridx2 = 0;
+ ts->size2 -= count;
+ if (ts->size2 == 0) {
+ ts->rdidx2 = 0;
+ ts->wridx2 = 0;
+ }
}
skip2:
- /* Should not be needed, but... remove_iacs is actually buggy
- * (it cannot process iacs which wrap around buffer's end)!
- * Since properly fixing it requires writing bigger code,
- * we rely instead on this code making it virtually impossible
- * to have wrapped iac (people don't type at 2k/second).
- * It also allows for bigger reads in common case. */
- if (ts->size1 == 0) {
- ts->rdidx1 = 0;
- ts->wridx1 = 0;
- }
- if (ts->size2 == 0) {
- ts->rdidx2 = 0;
- ts->wridx2 = 0;
- }
if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
/* Read from socket to buffer 1 */
@@ -739,7 +880,7 @@ int telnetd_main(int argc UNUSED_PARAM, char **argv)
continue;
kill_session:
if (ts->shell_pid > 0)
- update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
+ update_utmp_DEAD_PROCESS(ts->shell_pid);
free_session(ts);
ts = next;
}
diff --git a/networking/telnetd.ctrlSQ.patch b/networking/telnetd.ctrlSQ.patch
index 7060e1c..bc26d22 100644
--- a/networking/telnetd.ctrlSQ.patch
+++ b/networking/telnetd.ctrlSQ.patch
@@ -94,9 +94,9 @@ exceptional conditions.
#endif
+#ifdef TIOCPKT
+ int control;
-+ static const char lflow_on[] =
++ static const char lflow_on[] ALIGN1 =
+ {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE};
-+ static const char lflow_off[] =
++ static const char lflow_off[] ALIGN1 =
+ {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE};
+# define RESERVED sizeof(lflow_on)
+#else
diff --git a/networking/tftp.c b/networking/tftp.c
index 48bc6fa..ed86720 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -18,6 +18,78 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config TFTP
+//config: bool "tftp"
+//config: default y
+//config: help
+//config: This enables the Trivial File Transfer Protocol client program. TFTP
+//config: is usually used for simple, small transfers such as a root image
+//config: for a network-enabled bootloader.
+//config:
+//config:config TFTPD
+//config: bool "tftpd"
+//config: default y
+//config: help
+//config: This enables the Trivial File Transfer Protocol server program.
+//config: It expects that stdin is a datagram socket and a packet
+//config: is already pending on it. It will exit after one transfer.
+//config: In other words: it should be run from inetd in nowait mode,
+//config: or from udpsvd. Example: "udpsvd -E 0 69 tftpd DIR"
+//config:
+//config:comment "Common options for tftp/tftpd"
+//config: depends on TFTP || TFTPD
+//config:
+//config:config FEATURE_TFTP_GET
+//config: bool "Enable 'tftp get' and/or tftpd upload code"
+//config: default y
+//config: depends on TFTP || TFTPD
+//config: help
+//config: Add support for the GET command within the TFTP client. This allows
+//config: a client to retrieve a file from a TFTP server.
+//config: Also enable upload support in tftpd, if tftpd is selected.
+//config:
+//config: Note: this option does _not_ make tftpd capable of download
+//config: (the usual operation people need from it)!
+//config:
+//config:config FEATURE_TFTP_PUT
+//config: bool "Enable 'tftp put' and/or tftpd download code"
+//config: default y
+//config: depends on TFTP || TFTPD
+//config: help
+//config: Add support for the PUT command within the TFTP client. This allows
+//config: a client to transfer a file to a TFTP server.
+//config: Also enable download support in tftpd, if tftpd is selected.
+//config:
+//config:config FEATURE_TFTP_BLOCKSIZE
+//config: bool "Enable 'blksize' and 'tsize' protocol options"
+//config: default y
+//config: depends on TFTP || TFTPD
+//config: help
+//config: Allow tftp to specify block size, and tftpd to understand
+//config: "blksize" and "tsize" options.
+//config:
+//config:config FEATURE_TFTP_PROGRESS_BAR
+//config: bool "Enable tftp progress meter"
+//config: default y
+//config: depends on TFTP && FEATURE_TFTP_BLOCKSIZE
+//config: help
+//config: Show progress bar.
+//config:
+//config:config TFTP_DEBUG
+//config: bool "Enable debug"
+//config: default n
+//config: depends on TFTP || TFTPD
+//config: help
+//config: Make tftp[d] print debugging messages on stderr.
+//config: This is useful if you are diagnosing a bug in tftp[d].
+
+//applet:#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
+//applet:IF_TFTP(APPLET(tftp, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_TFTPD(APPLET(tftpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:#endif
+
+//kbuild:lib-$(CONFIG_TFTP) += tftp.o
+//kbuild:lib-$(CONFIG_TFTPD) += tftp.o
//usage:#define tftp_trivial_usage
//usage: "[OPTIONS] HOST [PORT]"
@@ -51,6 +123,7 @@
//usage: "\n -l Log to syslog (inetd mode requires this)"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
@@ -117,8 +190,10 @@ struct globals {
/* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
uint8_t error_pkt[4 + 32];
struct passwd *pw;
- /* used in tftpd_main(), a bit big for stack: */
- char block_buf[TFTP_BLKSIZE_DEFAULT];
+ /* Used in tftpd_main() for initial packet */
+ /* Some HP PA-RISC firmware always sends fixed 516-byte requests */
+ char block_buf[516];
+ char block_buf_tail[1];
#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
off_t pos;
off_t size;
@@ -126,11 +201,11 @@ struct globals {
bb_progress_t pmt;
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_G_too_big {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
-};
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+} while (0)
#define G_error_pkt_reason (G.error_pkt[3])
#define G_error_pkt_str ((char*)(G.error_pkt + 4))
@@ -346,7 +421,6 @@ static int tftp_protocol(
* as if it is "block 0" */
block_nr = 0;
}
-
} else { /* tftp */
/* Open file (must be after changing user) */
local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
@@ -376,7 +450,7 @@ static int tftp_protocol(
/* add filename and mode */
/* fill in packet if the filename fits into xbuf */
len = strlen(remote_file) + 1;
- if (2 + len + sizeof("octet") >= (uint32_t) io_bufsize) {
+ if (2 + len + sizeof("octet") >= io_bufsize) {
bb_error_msg("remote filename is too long");
goto ret;
}
@@ -757,7 +831,8 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
{
len_and_sockaddr *our_lsa;
len_and_sockaddr *peer_lsa;
- char *local_file, *mode, *user_opt;
+ char *mode, *user_opt;
+ char *local_file = local_file;
const char *error_msg;
int opt, result, opcode;
IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
@@ -793,14 +868,16 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
xchroot(argv[0]);
}
- result = recv_from_to(STDIN_FILENO, G.block_buf, sizeof(G.block_buf),
+ result = recv_from_to(STDIN_FILENO,
+ G.block_buf, sizeof(G.block_buf) + 1,
+ /* ^^^ sizeof+1 to reliably detect oversized input */
0 /* flags */,
&peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len);
error_msg = "malformed packet";
opcode = ntohs(*(uint16_t*)G.block_buf);
- if (result < 4 || result >= (int) sizeof(G.block_buf)
- || G.block_buf[result-1] != '\0'
+ if (result < 4 || result > sizeof(G.block_buf)
+ /*|| G.block_buf[result-1] != '\0' - bug compatibility, see below */
|| (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
IF_GETPUT(&&)
IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
@@ -808,6 +885,13 @@ int tftpd_main(int argc UNUSED_PARAM, char **argv)
) {
goto err;
}
+ /* Some HP PA-RISC firmware always sends fixed 516-byte requests,
+ * with trailing garbage.
+ * Support that by not requiring NUL to be the last byte (see above).
+ * To make strXYZ() ops safe, force NUL termination:
+ */
+ G.block_buf_tail[0] = '\0';
+
local_file = G.block_buf + 2;
if (local_file[0] == '.' || strstr(local_file, "/.")) {
error_msg = "dot in file name";
diff --git a/networking/traceroute.c b/networking/traceroute.c
index c3b9b71..fccfcb3 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -209,62 +209,98 @@
* -- Van Jacobson (van@ee.lbl.gov)
* Tue Dec 20 03:50:13 PST 1988
*/
+//config:config TRACEROUTE
+//config: bool "traceroute"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Utility to trace the route of IP packets.
+//config:
+//config:config TRACEROUTE6
+//config: bool "traceroute6"
+//config: default y
+//config: depends on FEATURE_IPV6
+//config: help
+//config: Utility to trace the route of IPv6 packets.
+//config:
+//config:config FEATURE_TRACEROUTE_VERBOSE
+//config: bool "Enable verbose output"
+//config: default y
+//config: depends on TRACEROUTE || TRACEROUTE6
+//config: help
+//config: Add some verbosity to traceroute. This includes among other things
+//config: hostnames and ICMP response types.
+//config:
+//config:config FEATURE_TRACEROUTE_USE_ICMP
+//config: bool "Enable -I option (use ICMP instead of UDP)"
+//config: default y
+//config: depends on TRACEROUTE || TRACEROUTE6
+//config: help
+//config: Add option -I to use ICMP ECHO instead of UDP datagrams.
+
+/* Needs socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), therefore BB_SUID_MAYBE: */
+//applet:IF_TRACEROUTE(APPLET(traceroute, BB_DIR_USR_BIN, BB_SUID_MAYBE))
+//applet:IF_TRACEROUTE6(APPLET(traceroute6, BB_DIR_USR_BIN, BB_SUID_MAYBE))
+
+//kbuild:lib-$(CONFIG_TRACEROUTE) += traceroute.o
+//kbuild:lib-$(CONFIG_TRACEROUTE6) += traceroute.o
//usage:#define traceroute_trivial_usage
-//usage: "[-"IF_TRACEROUTE6("46")"FIldnrv] [-f 1ST_TTL] [-m MAXTTL] [-p PORT] [-q PROBES]\n"
-//usage: " [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-g GATEWAY] [-i IFACE]\n"
+//usage: "[-"IF_TRACEROUTE6("46")"FIlnrv] [-f 1ST_TTL] [-m MAXTTL] [-q PROBES] [-p PORT]\n"
+//usage: " [-t TOS] [-w WAIT_SEC] [-s SRC_IP] [-i IFACE]\n"
//usage: " [-z PAUSE_MSEC] HOST [BYTES]"
//usage:#define traceroute_full_usage "\n\n"
//usage: "Trace the route to HOST\n"
//usage: IF_TRACEROUTE6(
//usage: "\n -4,-6 Force IP or IPv6 name resolution"
//usage: )
-//usage: "\n -F Set the don't fragment bit"
+//usage: "\n -F Set don't fragment bit"
+//usage: IF_FEATURE_TRACEROUTE_USE_ICMP(
//usage: "\n -I Use ICMP ECHO instead of UDP datagrams"
-//usage: "\n -l Display the TTL value of the returned packet"
-//usage: "\n -d Set SO_DEBUG options to socket"
+//usage: )
+//usage: "\n -l Display TTL value of the returned packet"
+//Currently disabled (TRACEROUTE_SO_DEBUG==0)
+////usage: "\n -d Set SO_DEBUG options to socket"
//usage: "\n -n Print numeric addresses"
//usage: "\n -r Bypass routing tables, send directly to HOST"
+//usage: IF_FEATURE_TRACEROUTE_VERBOSE(
//usage: "\n -v Verbose"
-//usage: "\n -m Max time-to-live (max number of hops)"
-//usage: "\n -p Base UDP port number used in probes"
+//usage: )
+//usage: "\n -f N First number of hops (default 1)"
+//usage: "\n -m N Max number of hops"
+//usage: "\n -q N Number of probes per hop (default 3)"
+//usage: "\n -p N Base UDP port number used in probes"
//usage: "\n (default 33434)"
-//usage: "\n -q Number of probes per TTL (default 3)"
-//usage: "\n -s IP address to use as the source address"
-//usage: "\n -t Type-of-service in probe packets (default 0)"
-//usage: "\n -w Time in seconds to wait for a response (default 3)"
-//usage: "\n -g Loose source route gateway (8 max)"
+//usage: "\n -s IP Source address"
+//usage: "\n -i IFACE Source interface"
+//usage: "\n -t N Type-of-service in probe packets (default 0)"
+//usage: "\n -w SEC Time to wait for a response (default 3)"
+//usage: "\n -g IP Loose source route gateway (8 max)"
//usage:
//usage:#define traceroute6_trivial_usage
-//usage: "[-dnrv] [-m MAXTTL] [-p PORT] [-q PROBES]\n"
-//usage: " [-s SRC_IP] [-t TOS] [-w WAIT_SEC] [-i IFACE]\n"
+//usage: "[-nrv] [-m MAXTTL] [-q PROBES] [-p PORT]\n"
+//usage: " [-t TOS] [-w WAIT_SEC] [-s SRC_IP] [-i IFACE]\n"
//usage: " HOST [BYTES]"
//usage:#define traceroute6_full_usage "\n\n"
//usage: "Trace the route to HOST\n"
-//usage: "\n -d Set SO_DEBUG options to socket"
+//Currently disabled (TRACEROUTE_SO_DEBUG==0)
+////usage: "\n -d Set SO_DEBUG options to socket"
//usage: "\n -n Print numeric addresses"
//usage: "\n -r Bypass routing tables, send directly to HOST"
+//usage: IF_FEATURE_TRACEROUTE_VERBOSE(
//usage: "\n -v Verbose"
-//usage: "\n -m Max time-to-live (max number of hops)"
-//usage: "\n -p Base UDP port number used in probes"
-//usage: "\n (default is 33434)"
-//usage: "\n -q Number of probes per TTL (default 3)"
-//usage: "\n -s IP address to use as the source address"
-//usage: "\n -t Type-of-service in probe packets (default 0)"
-//usage: "\n -w Time in seconds to wait for a response (default 3)"
+//usage: )
+//usage: "\n -m N Max number of hops"
+//usage: "\n -q N Number of probes per hop (default 3)"
+//usage: "\n -p N Base UDP port number used in probes"
+//usage: "\n (default 33434)"
+//usage: "\n -s IP Source address"
+//usage: "\n -i IFACE Source interface"
+//usage: "\n -t N Type-of-service in probe packets (default 0)"
+//usage: "\n -w SEC Time wait for a response (default 3)"
#define TRACEROUTE_SO_DEBUG 0
-/* TODO: undefs were uncommented - ??! we have config system for that! */
-/* probably ok to remove altogether */
-//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
-//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
-//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
-//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
-//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
-//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
-
-
#include <net/if.h>
#include <arpa/inet.h>
#include <netinet/in.h>
@@ -297,7 +333,6 @@
#define OPT_STRING \
"FIlnrdvxt:i:m:p:q:s:w:z:f:" \
- IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
"4" IF_TRACEROUTE6("6")
enum {
OPT_DONT_FRAGMNT = (1 << 0), /* F */
@@ -317,9 +352,8 @@ enum {
OPT_WAITTIME = (1 << 14), /* w */
OPT_PAUSE_MS = (1 << 15), /* z */
OPT_FIRST_TTL = (1 << 16), /* f */
- OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */
- OPT_IPV4 = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)), /* 4 */
- OPT_IPV6 = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */
+ OPT_IPV4 = (1 << 17), /* 4 */
+ OPT_IPV6 = (1 << 18) * ENABLE_TRACEROUTE6, /* 6 */
};
#define verbose (option_mask32 & OPT_VERBOSE)
@@ -346,26 +380,18 @@ struct outdata6_t {
#endif
struct globals {
+ /* Pointer to entire malloced IP packet, "packlen" bytes long: */
struct ip *outip;
+ /* Pointer to ICMP or UDP payload (not header): */
struct outdata_t *outdata;
+
len_and_sockaddr *dest_lsa;
int packlen; /* total length of packet */
int pmtu; /* Path MTU Discovery (RFC1191) */
uint32_t ident;
- uint16_t port; // 32768 + 666; /* start udp dest port # for probe packets */
+ uint16_t port; // 33434; /* start udp dest port # for probe packets */
int waittime; // 5; /* time to wait for response (in seconds) */
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
- int optlen; /* length of ip options */
-#else
-#define optlen 0
-#endif
unsigned char recv_pkt[512]; /* last inbound (icmp) packet */
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
- /* Maximum number of gateways (include room for one noop) */
-#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
- /* loose source route gateway list (including room for final destination) */
- uint32_t gwlist[NGATEWAYS + 1];
-#endif
};
#define G (*ptr_to_globals)
@@ -377,14 +403,11 @@ struct globals {
#define ident (G.ident )
#define port (G.port )
#define waittime (G.waittime )
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
-# define optlen (G.optlen )
-#endif
#define recv_pkt (G.recv_pkt )
#define gwlist (G.gwlist )
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
- port = 32768 + 666; \
+ port = 33434; \
waittime = 5; \
} while (0)
@@ -392,15 +415,6 @@ struct globals {
#define outudp ((struct udphdr *)(outip + 1))
-/* libbb candidate? tftp uses this idiom too */
-static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
-{
- len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len);
- memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len);
- return new_lsa;
-}
-
-
static int
wait_for_reply(len_and_sockaddr *from_lsa, struct sockaddr *to, unsigned *timestamp_us, int *left_ms)
{
@@ -433,7 +447,7 @@ send_probe(int seq, int ttl)
/* Payload */
#if ENABLE_TRACEROUTE6
if (dest_lsa->u.sa.sa_family == AF_INET6) {
- struct outdata6_t *pkt = (struct outdata6_t *) outip;
+ struct outdata6_t *pkt = (struct outdata6_t *) outdata;
pkt->ident6 = htonl(ident);
pkt->seq6 = htonl(seq);
/*gettimeofday(&pkt->tv, &tz);*/
@@ -450,8 +464,10 @@ send_probe(int seq, int ttl)
/* Always calculate checksum for icmp packets */
outicmp->icmp_cksum = 0;
- outicmp->icmp_cksum = inet_cksum((uint16_t *)outicmp,
- packlen - (sizeof(*outip) + optlen));
+ outicmp->icmp_cksum = inet_cksum(
+ (uint16_t *)outicmp,
+ ((char*)outip + packlen) - (char*)outicmp
+ );
if (outicmp->icmp_cksum == 0)
outicmp->icmp_cksum = 0xffff;
}
@@ -483,33 +499,31 @@ send_probe(int seq, int ttl)
}
#endif
+ out = outdata;
#if ENABLE_TRACEROUTE6
if (dest_lsa->u.sa.sa_family == AF_INET6) {
- res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
- if (res < 0)
- bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl);
- out = outip;
- len = packlen;
+ res = setsockopt_int(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, ttl);
+ if (res != 0)
+ bb_perror_msg_and_die("setsockopt(%s) %d", "UNICAST_HOPS", ttl);
} else
#endif
{
#if defined IP_TTL
- res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
- if (res < 0)
- bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+ res = setsockopt_int(sndsock, IPPROTO_IP, IP_TTL, ttl);
+ if (res != 0)
+ bb_perror_msg_and_die("setsockopt(%s) %d", "TTL", ttl);
#endif
- out = outicmp;
- len = packlen - sizeof(*outip);
- if (!(option_mask32 & OPT_USE_ICMP)) {
- out = outdata;
- len -= sizeof(*outudp);
- set_nport(&dest_lsa->u.sa, htons(port + seq));
- }
+ if (option_mask32 & OPT_USE_ICMP)
+ out = outicmp;
}
+ if (!(option_mask32 & OPT_USE_ICMP)) {
+ set_nport(&dest_lsa->u.sa, htons(port + seq));
+ }
+ len = ((char*)outip + packlen) - (char*)out;
res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len);
if (res != len)
- bb_info_msg("sent %d octets, ret=%d", len, res);
+ bb_error_msg("sent %d octets, ret=%d", len, res);
}
#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
@@ -813,10 +827,6 @@ common_traceroute_main(int op, char **argv)
char *pausemsecs_str;
char *first_ttl_str;
char *dest_str;
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
- llist_t *source_route_list = NULL;
- int lsrr = 0;
-#endif
#if ENABLE_TRACEROUTE6
sa_family_t af;
#else
@@ -831,13 +841,10 @@ common_traceroute_main(int op, char **argv)
INIT_G();
/* minimum 1 arg */
- opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::");
+ opt_complementary = "-1:x-x";
op |= getopt32(argv, OPT_STRING
, &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
, &source, &waittime_str, &pausemsecs_str, &first_ttl_str
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
- , &source_route_list
-#endif
);
argv += optind;
@@ -870,26 +877,14 @@ common_traceroute_main(int op, char **argv)
if (op & OPT_FIRST_TTL)
first_ttl = xatou_range(first_ttl_str, 1, max_ttl);
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
- if (source_route_list) {
- while (source_route_list) {
- len_and_sockaddr *lsa;
-
- if (lsrr >= NGATEWAYS)
- bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
- lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
- gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
- free(lsa);
- ++lsrr;
- }
- optlen = (lsrr + 1) * sizeof(gwlist[0]);
- }
-#endif
-
/* Process destination and optional packet size */
- minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen;
+ minpacket = sizeof(struct ip)
+ + SIZEOF_ICMP_HDR
+ + sizeof(struct outdata_t);
if (!(op & OPT_USE_ICMP))
- minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR;
+ minpacket = sizeof(struct ip)
+ + sizeof(struct udphdr)
+ + sizeof(struct outdata_t);
#if ENABLE_TRACEROUTE6
af = AF_UNSPEC;
if (op & OPT_IPV4)
@@ -899,7 +894,9 @@ common_traceroute_main(int op, char **argv)
dest_lsa = xhost_and_af2sockaddr(argv[0], port, af);
af = dest_lsa->u.sa.sa_family;
if (af == AF_INET6)
- minpacket = sizeof(struct outdata6_t);
+ minpacket = sizeof(struct ip6_hdr)
+ + sizeof(struct udphdr)
+ + sizeof(struct outdata6_t);
#else
dest_lsa = xhost2sockaddr(argv[0], port);
#endif
@@ -914,13 +911,10 @@ common_traceroute_main(int op, char **argv)
if (af == AF_INET6) {
xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock);
# ifdef IPV6_RECVPKTINFO
- setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO,
- &const_int_1, sizeof(const_int_1));
- setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_1(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO);
+ setsockopt_1(rcvsock, SOL_IPV6, IPV6_2292PKTINFO);
# else
- setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_1(rcvsock, SOL_IPV6, IPV6_PKTINFO);
# endif
} else
#endif
@@ -930,18 +924,15 @@ common_traceroute_main(int op, char **argv)
#if TRACEROUTE_SO_DEBUG
if (op & OPT_DEBUG)
- setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_SOL_SOCKET_1(rcvsock, SO_DEBUG);
#endif
if (op & OPT_BYPASS_ROUTE)
- setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_SOL_SOCKET_1(rcvsock, SO_DONTROUTE);
#if ENABLE_TRACEROUTE6
if (af == AF_INET6) {
- static const int two = 2;
- if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0)
- bb_perror_msg_and_die("setsockopt RAW_CHECKSUM");
+ if (setsockopt_int(rcvsock, SOL_RAW, IPV6_CHECKSUM, 2) != 0)
+ bb_perror_msg_and_die("setsockopt(%s)", "IPV6_CHECKSUM");
xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock);
} else
#endif
@@ -950,62 +941,34 @@ common_traceroute_main(int op, char **argv)
xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
else
xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
-#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE && defined IP_OPTIONS
- if (lsrr > 0) {
- unsigned char optlist[MAX_IPOPTLEN];
- unsigned size;
-
- /* final hop */
- gwlist[lsrr] = dest_lsa->u.sin.sin_addr.s_addr;
- ++lsrr;
-
- /* force 4 byte alignment */
- optlist[0] = IPOPT_NOP;
- /* loose source route option */
- optlist[1] = IPOPT_LSRR;
- size = lsrr * sizeof(gwlist[0]);
- optlist[2] = size + 3;
- /* pointer to LSRR addresses */
- optlist[3] = IPOPT_MINOFF;
- memcpy(optlist + 4, gwlist, size);
-
- if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
- (char *)optlist, size + sizeof(gwlist[0])) < 0) {
- bb_perror_msg_and_die("IP_OPTIONS");
- }
- }
-#endif
}
#ifdef SO_SNDBUF
- if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
- bb_perror_msg_and_die("SO_SNDBUF");
+ if (setsockopt_SOL_SOCKET_int(sndsock, SO_SNDBUF, packlen) != 0) {
+ bb_perror_msg_and_die("setsockopt(%s)", "SO_SNDBUF");
}
#endif
#ifdef IP_TOS
- if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
- bb_perror_msg_and_die("setsockopt tos %d", tos);
+ if ((op & OPT_TOS) && setsockopt_int(sndsock, IPPROTO_IP, IP_TOS, tos) != 0) {
+ bb_perror_msg_and_die("setsockopt(%s) %d", "TOS", tos);
}
#endif
#ifdef IP_DONTFRAG
if (op & OPT_DONT_FRAGMNT)
- setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_1(sndsock, IPPROTO_IP, IP_DONTFRAG);
#endif
#if TRACEROUTE_SO_DEBUG
if (op & OPT_DEBUG)
- setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_SOL_SOCKET_1(sndsock, SO_DEBUG);
#endif
if (op & OPT_BYPASS_ROUTE)
- setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
- &const_int_1, sizeof(const_int_1));
+ setsockopt_SOL_SOCKET_1(sndsock, SO_DONTROUTE);
outip = xzalloc(packlen);
ident = getpid();
- if (af == AF_INET) {
+ if (!ENABLE_TRACEROUTE6 || af == AF_INET) {
if (op & OPT_USE_ICMP) {
ident |= 0x8000;
outicmp->icmp_type = ICMP_ECHO;
@@ -1015,6 +978,14 @@ common_traceroute_main(int op, char **argv)
outdata = (struct outdata_t *)(outudp + 1);
}
}
+#if ENABLE_TRACEROUTE6
+ if (af == AF_INET6) {
+ outdata = (void*)((char*)outip
+ + sizeof(struct ip6_hdr)
+ + sizeof(struct udphdr)
+ );
+ }
+#endif
if (op & OPT_DEVICE) /* hmm, do we need error check? */
setsockopt_bindtodevice(sndsock, device);
@@ -1079,7 +1050,7 @@ common_traceroute_main(int op, char **argv)
printf(" from %s", source);
printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
- from_lsa = dup_sockaddr(dest_lsa);
+ from_lsa = xmemdup(dest_lsa, LSA_LEN_SIZE + dest_lsa->len);
lastaddr = xzalloc(dest_lsa->len);
to = xzalloc(dest_lsa->len);
seq = 0;
@@ -1239,11 +1210,13 @@ common_traceroute_main(int op, char **argv)
return 0;
}
+#if ENABLE_TRACEROUTE
int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int traceroute_main(int argc UNUSED_PARAM, char **argv)
{
return common_traceroute_main(0, argv);
}
+#endif
#if ENABLE_TRACEROUTE6
int traceroute6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
diff --git a/networking/tunctl.c b/networking/tunctl.c
index 3a0870e..fa904c2 100644
--- a/networking/tunctl.c
+++ b/networking/tunctl.c
@@ -9,6 +9,24 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config TUNCTL
+//config: bool "tunctl"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: tunctl creates or deletes tun devices.
+//config:
+//config:config FEATURE_TUNCTL_UG
+//config: bool "Support owner:group assignment"
+//config: default y
+//config: depends on TUNCTL
+//config: help
+//config: Allow to specify owner and group of newly created interface.
+//config: 340 bytes of pure bloat. Say no here.
+
+//applet:IF_TUNCTL(APPLET(tunctl, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TUNCTL) += tunctl.o
//usage:#define tunctl_trivial_usage
//usage: "[-f device] ([-t name] | -d name)" IF_FEATURE_TUNCTL_UG(" [-u owner] [-g group] [-b]")
@@ -82,7 +100,7 @@ int tunctl_main(int argc UNUSED_PARAM, char **argv)
// delete?
if (opts & OPT_d) {
IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)0);
- bb_info_msg("Set '%s' %spersistent", ifr.ifr_name, "non");
+ printf("Set '%s' nonpersistent\n", ifr.ifr_name);
return EXIT_SUCCESS;
}
diff --git a/networking/udhcp/Config.src b/networking/udhcp/Config.src
index 6bfa398..90fb313 100644
--- a/networking/udhcp/Config.src
+++ b/networking/udhcp/Config.src
@@ -16,7 +16,6 @@ config UDHCPD
config DHCPRELAY
bool "dhcprelay"
default y
- depends on UDHCPD
help
dhcprelay listens for dhcp requests on one or more interfaces
and forwards these requests to a different interface or dhcp
@@ -25,7 +24,6 @@ config DHCPRELAY
config DUMPLEASES
bool "Lease display utility (dumpleases)"
default y
- depends on UDHCPD
help
dumpleases displays the leases written out by the udhcpd server.
Lease times are stored in the file by time remaining in lease, or
@@ -84,6 +82,17 @@ config FEATURE_UDHCPC_ARPING
will DHCPDECLINE the offer if the address is in use,
and restart the discover process.
+config FEATURE_UDHCPC_SANITIZEOPT
+ bool "Do not pass malformed host and domain names"
+ default y
+ depends on UDHCPC
+ help
+ If selected, udhcpc will check some options (such as option 12 -
+ hostname) and if they don't look like valid hostnames
+ (for example, if they start with dash or contain spaces),
+ they will be replaced with string "bad" when exporting
+ to the environment.
+
config FEATURE_UDHCP_PORT
bool "Enable '-P port' option for udhcpd and udhcpc"
default n
diff --git a/networking/udhcp/Kbuild.src b/networking/udhcp/Kbuild.src
index b8767ba..fcb725f 100644
--- a/networking/udhcp/Kbuild.src
+++ b/networking/udhcp/Kbuild.src
@@ -13,9 +13,9 @@ lib-$(CONFIG_UDHCPC) += common.o packet.o signalpipe.o socket.o
lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o
lib-$(CONFIG_UDHCPC) += dhcpc.o
-lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o files.o leases.o static_leases.o
+lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o
lib-$(CONFIG_DUMPLEASES) += dumpleases.o
-lib-$(CONFIG_DHCPRELAY) += dhcprelay.o
+lib-$(CONFIG_DHCPRELAY) += dhcprelay.o common.o socket.o packet.o
lib-$(CONFIG_FEATURE_UDHCPC_ARPING) += arpping.o
lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
diff --git a/networking/udhcp/arpping.c b/networking/udhcp/arpping.c
index 155ec01..778b1d7 100644
--- a/networking/udhcp/arpping.c
+++ b/networking/udhcp/arpping.c
@@ -9,7 +9,6 @@
#include <net/if_arp.h>
#include "common.h"
-#include "dhcpd.h"
struct arpMsg {
/* Ethernet header */
@@ -39,7 +38,8 @@ int FAST_FUNC arpping(uint32_t test_nip,
const uint8_t *safe_mac,
uint32_t from_ip,
uint8_t *from_mac,
- const char *interface)
+ const char *interface,
+ unsigned timeo)
{
int timeout_ms;
struct pollfd pfd[1];
@@ -48,6 +48,9 @@ int FAST_FUNC arpping(uint32_t test_nip,
struct sockaddr addr; /* for interface name */
struct arpMsg arp;
+ if (!timeo)
+ return 1;
+
s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
if (s == -1) {
bb_perror_msg("%s", bb_msg_can_not_create_raw_socket);
@@ -83,7 +86,7 @@ int FAST_FUNC arpping(uint32_t test_nip,
}
/* wait for arp reply, and check it */
- timeout_ms = 2000;
+ timeout_ms = (int)timeo;
do {
typedef uint32_t aliased_uint32_t FIX_ALIASING;
int r;
@@ -124,10 +127,10 @@ int FAST_FUNC arpping(uint32_t test_nip,
* this is more under/overflow-resistant
* (people did see overflows here when system time jumps):
*/
- } while ((unsigned)timeout_ms <= 2000);
+ } while ((unsigned)timeout_ms <= timeo);
ret:
close(s);
- log1("%srp reply received for this address", rv ? "No a" : "A");
+ log1("%srp reply received for this address", rv ? "no a" : "A");
return rv;
}
diff --git a/networking/udhcp/common.c b/networking/udhcp/common.c
index fe322db..1aaf525 100644
--- a/networking/udhcp/common.c
+++ b/networking/udhcp/common.c
@@ -63,6 +63,7 @@ const struct dhcp_optflag dhcp_optflags[] = {
{ OPTION_U8 , 0x85 }, /* DHCP_VLAN_PRIORITY */
#endif
{ OPTION_STRING , 0xd1 }, /* DHCP_PXE_CONF_FILE */
+ { OPTION_STRING , 0xd2 }, /* DHCP_PXE_PATH_PREFIX */
{ OPTION_6RD , 0xd4 }, /* DHCP_6RD */
{ OPTION_STATIC_ROUTES | OPTION_LIST , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
{ OPTION_STRING , 0xfc }, /* DHCP_WPAD */
@@ -130,6 +131,7 @@ const char dhcp_option_strings[] ALIGN1 =
"vlanpriority" "\0"/* DHCP_VLAN_PRIORITY */
#endif
"pxeconffile" "\0" /* DHCP_PXE_CONF_FILE */
+ "pxepathprefix" "\0" /* DHCP_PXE_PATH_PREFIX */
"ip6rd" "\0" /* DHCP_6RD */
"msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
"wpad" "\0" /* DHCP_WPAD */
@@ -140,7 +142,7 @@ const char dhcp_option_strings[] ALIGN1 =
* udhcp_str2optset: to determine how many bytes to allocate.
* xmalloc_optname_optval: to estimate string length
* from binary option length: (option[LEN] / dhcp_option_lengths[opt_type])
- * is the number of elements, multiply in by one element's string width
+ * is the number of elements, multiply it by one element's string width
* (len_of_option_as_string[opt_type]) and you know how wide string you need.
*/
const uint8_t dhcp_option_lengths[] ALIGN1 = {
@@ -160,7 +162,18 @@ const uint8_t dhcp_option_lengths[] ALIGN1 = {
[OPTION_S32] = 4,
/* Just like OPTION_STRING, we use minimum length here */
[OPTION_STATIC_ROUTES] = 5,
- [OPTION_6RD] = 22, /* ignored by udhcp_str2optset */
+ [OPTION_6RD] = 12, /* ignored by udhcp_str2optset */
+ /* The above value was chosen as follows:
+ * len_of_option_as_string[] for this option is >60: it's a string of the form
+ * "32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 ".
+ * Each additional ipv4 address takes 4 bytes in binary option and appends
+ * another "255.255.255.255 " 16-byte string. We can set [OPTION_6RD] = 4
+ * but this severely overestimates string length: instead of 16 bytes,
+ * it adds >60 for every 4 bytes in binary option.
+ * We cheat and declare here that option is in units of 12 bytes.
+ * This adds more than 60 bytes for every three ipv4 addresses - more than enough.
+ * (Even 16 instead of 12 should work, but let's be paranoid).
+ */
};
@@ -170,7 +183,7 @@ static void log_option(const char *pfx, const uint8_t *opt)
if (dhcp_verbose >= 2) {
char buf[256 * 2 + 2];
*bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
- bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
+ bb_error_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
}
}
#else
@@ -213,9 +226,12 @@ uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
rem = sizeof(packet->options);
while (1) {
if (rem <= 0) {
+ complain:
bb_error_msg("bad packet, malformed option field");
return NULL;
}
+
+ /* DHCP_PADDING and DHCP_END have no [len] byte */
if (optionptr[OPT_CODE] == DHCP_PADDING) {
rem--;
optionptr++;
@@ -238,25 +254,29 @@ uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
}
break;
}
+
+ if (rem <= OPT_LEN)
+ goto complain; /* complain and return NULL */
len = 2 + optionptr[OPT_LEN];
rem -= len;
if (rem < 0)
- continue; /* complain and return NULL */
+ goto complain; /* complain and return NULL */
if (optionptr[OPT_CODE] == code) {
- log_option("Option found", optionptr);
+ log_option("option found", optionptr);
return optionptr + OPT_DATA;
}
if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
- overload |= optionptr[OPT_DATA];
+ if (len >= 3)
+ overload |= optionptr[OPT_DATA];
/* fall through */
}
optionptr += len;
}
/* log3 because udhcpc uses it a lot - very noisy */
- log3("Option 0x%02x not found", code);
+ log3("option 0x%02x not found", code);
return NULL;
}
@@ -290,7 +310,7 @@ void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addo
addopt[OPT_CODE]);
return;
}
- log_option("Adding option", addopt);
+ log_option("adding option", addopt);
memcpy(optionptr + end, addopt, len);
optionptr[end + len] = DHCP_END;
}
@@ -389,7 +409,7 @@ static NOINLINE void attach_option(
struct option_set *new, **curr;
/* make a new option */
- log2("Attaching option %02x to list", optflag->code);
+ log2("attaching option %02x to list", optflag->code);
new = xmalloc(sizeof(*new));
new->data = xmalloc(length + OPT_DATA);
new->data[OPT_CODE] = optflag->code;
@@ -409,7 +429,7 @@ static NOINLINE void attach_option(
unsigned old_len;
/* add it to an existing option */
- log2("Attaching option %02x to existing member of list", optflag->code);
+ log2("attaching option %02x to existing member of list", optflag->code);
old_len = existing->data[OPT_LEN];
if (old_len + length < 255) {
/* actually 255 is ok too, but adding a space can overlow it */
diff --git a/networking/udhcp/common.h b/networking/udhcp/common.h
index 5e70d60..a526494 100644
--- a/networking/udhcp/common.h
+++ b/networking/udhcp/common.h
@@ -9,6 +9,7 @@
#define UDHCP_COMMON_H 1
#include "libbb.h"
+#include "common_bufsiz.h"
#include <netinet/udp.h>
#include <netinet/ip.h>
@@ -152,6 +153,7 @@ enum {
//#define DHCP_VLAN_ID 0x84 /* 802.1P VLAN ID */
//#define DHCP_VLAN_PRIORITY 0x85 /* 802.1Q VLAN priority */
//#define DHCP_PXE_CONF_FILE 0xd1 /* RFC 5071 Configuration File */
+//#define DHCP_PXE_PATH_PREFIX 0xd2 /* RFC 5071 Configuration File */
//#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */
//#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */
#define DHCP_END 0xff
@@ -255,16 +257,16 @@ struct option_set *udhcp_find_option(struct option_set *opt_list, uint8_t code)
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
# define IF_UDHCP_VERBOSE(...) __VA_ARGS__
extern unsigned dhcp_verbose;
-# define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0)
+# define log1(...) do { if (dhcp_verbose >= 1) bb_error_msg(__VA_ARGS__); } while (0)
# if CONFIG_UDHCP_DEBUG >= 2
void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
-# define log2(...) do { if (dhcp_verbose >= 2) bb_info_msg(__VA_ARGS__); } while (0)
+# define log2(...) do { if (dhcp_verbose >= 2) bb_error_msg(__VA_ARGS__); } while (0)
# else
# define udhcp_dump_packet(...) ((void)0)
# define log2(...) ((void)0)
# endif
# if CONFIG_UDHCP_DEBUG >= 3
-# define log3(...) do { if (dhcp_verbose >= 3) bb_info_msg(__VA_ARGS__); } while (0)
+# define log3(...) do { if (dhcp_verbose >= 3) bb_error_msg(__VA_ARGS__); } while (0)
# else
# define log3(...) ((void)0)
# endif
@@ -310,7 +312,8 @@ int arpping(uint32_t test_nip,
const uint8_t *safe_mac,
uint32_t from_ip,
uint8_t *from_mac,
- const char *interface) FAST_FUNC;
+ const char *interface,
+ unsigned timeo) FAST_FUNC;
/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC;
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index b0f0798..ddf3412 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -118,7 +118,7 @@ static void *d6_copy_option(uint8_t *option, uint8_t *option_end, unsigned code)
uint8_t *opt = d6_find_option(option, option_end, code);
if (!opt)
return opt;
- return memcpy(xmalloc(opt[3] + 4), opt, opt[3] + 4);
+ return xmemdup(opt, opt[3] + 4);
}
static void *d6_store_blob(void *dst, const void *src, unsigned len)
@@ -251,7 +251,7 @@ static void d6_run_script(struct d6_packet *packet, const char *name)
envp = fill_envp(packet);
/* call script */
- log1("Executing %s %s", client_config.script, name);
+ log1("executing %s %s", client_config.script, name);
argv[0] = (char*) client_config.script;
argv[1] = (char*) name;
argv[2] = NULL;
@@ -428,7 +428,7 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip
*/
opt_ptr = add_d6_client_options(opt_ptr);
- bb_info_msg("Sending discover...");
+ bb_error_msg("sending %s", "discover");
return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
}
@@ -481,7 +481,7 @@ static NOINLINE int send_d6_select(uint32_t xid)
*/
opt_ptr = add_d6_client_options(opt_ptr);
- bb_info_msg("Sending select...");
+ bb_error_msg("sending %s", "select");
return d6_mcast_from_client_config_ifindex(&packet, opt_ptr);
}
@@ -550,7 +550,7 @@ static NOINLINE int send_d6_renew(uint32_t xid, struct in6_addr *server_ipv6, st
*/
opt_ptr = add_d6_client_options(opt_ptr);
- bb_info_msg("Sending renew...");
+ bb_error_msg("sending %s", "renew");
if (server_ipv6)
return d6_send_kernel_packet(
&packet, (opt_ptr - (uint8_t*) &packet),
@@ -573,7 +573,7 @@ static int send_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cu
/* IA NA (contains our current IP) */
opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
- bb_info_msg("Sending release...");
+ bb_error_msg("sending %s", "release");
return d6_send_kernel_packet(
&packet, (opt_ptr - (uint8_t*) &packet),
our_cur_ipv6, CLIENT_PORT6,
@@ -592,19 +592,19 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
bytes = safe_read(fd, &packet, sizeof(packet));
if (bytes < 0) {
- log1("Packet read error, ignoring");
+ log1("packet read error, ignoring");
/* NB: possible down interface, etc. Caller should pause. */
return bytes; /* returns -1 */
}
if (bytes < (int) (sizeof(packet.ip6) + sizeof(packet.udp))) {
- log1("Packet is too short, ignoring");
+ log1("packet is too short, ignoring");
return -2;
}
if (bytes < sizeof(packet.ip6) + ntohs(packet.ip6.ip6_plen)) {
/* packet is bigger than sizeof(packet), we did partial read */
- log1("Oversized packet, ignoring");
+ log1("oversized packet, ignoring");
return -2;
}
@@ -618,7 +618,7 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
/* || bytes > (int) sizeof(packet) - can't happen */
|| packet.udp.len != packet.ip6.ip6_plen
) {
- log1("Unrelated/bogus packet, ignoring");
+ log1("unrelated/bogus packet, ignoring");
return -2;
}
@@ -630,11 +630,11 @@ static NOINLINE int d6_recv_raw_packet(struct in6_addr *peer_ipv6
// check = packet.udp.check;
// packet.udp.check = 0;
// if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
-// log1("Packet with bad UDP checksum received, ignoring");
+// log1("packet with bad UDP checksum received, ignoring");
// return -2;
// }
- log1("Received a packet");
+ log1("received %s", "a packet");
d6_dump_packet(&packet.data);
bytes -= sizeof(packet.ip6) + sizeof(packet.udp);
@@ -711,7 +711,7 @@ static int d6_raw_socket(int ifindex)
/* jump to L3 if udp dport is CLIENT_PORT6, else to L4 */
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
/* L3: accept packet */
- BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
+ BPF_STMT(BPF_RET|BPF_K, 0x7fffffff),
/* L4: discard packet */
BPF_STMT(BPF_RET|BPF_K, 0),
};
@@ -722,10 +722,10 @@ static int d6_raw_socket(int ifindex)
};
#endif
- log1("Opening raw socket on ifindex %d", ifindex); //log2?
+ log1("opening raw socket on ifindex %d", ifindex); //log2?
fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IPV6));
- log1("Got raw socket fd %d", fd); //log2?
+ log1("got raw socket fd %d", fd); //log2?
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IPV6);
@@ -738,18 +738,18 @@ static int d6_raw_socket(int ifindex)
/* Ignoring error (kernel may lack support for this) */
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
sizeof(filter_prog)) >= 0)
- log1("Attached filter to raw socket fd %d", fd); // log?
+ log1("attached filter to raw socket fd %d", fd); // log?
}
#endif
- log1("Created raw socket");
+ log1("created raw socket");
return fd;
}
static void change_listen_mode(int new_mode)
{
- log1("Entering listen mode: %s",
+ log1("entering listen mode: %s",
new_mode != LISTEN_NONE
? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
: "none"
@@ -770,7 +770,7 @@ static void change_listen_mode(int new_mode)
/* Called only on SIGUSR1 */
static void perform_renew(void)
{
- bb_info_msg("Performing a DHCP renew");
+ bb_error_msg("performing DHCP renew");
switch (state) {
case BOUND:
change_listen_mode(LISTEN_KERNEL);
@@ -793,13 +793,22 @@ static void perform_renew(void)
static void perform_d6_release(struct in6_addr *server_ipv6, struct in6_addr *our_cur_ipv6)
{
/* send release packet */
- if (state == BOUND || state == RENEWING || state == REBINDING) {
- bb_info_msg("Unicasting a release");
+ if (state == BOUND
+ || state == RENEWING
+ || state == REBINDING
+ || state == RENEW_REQUESTED
+ ) {
+ bb_error_msg("unicasting a release");
send_d6_release(server_ipv6, our_cur_ipv6); /* unicast */
- d6_run_script(NULL, "deconfig");
}
- bb_info_msg("Entering released state");
-
+ bb_error_msg("entering released state");
+/*
+ * We can be here on: SIGUSR2,
+ * or on exit (SIGTERM) and -R "release on quit" is specified.
+ * Users requested to be notified in all cases, even if not in one
+ * of the states above.
+ */
+ d6_run_script(NULL, "deconfig");
change_listen_mode(LISTEN_NONE);
state = RELEASED;
}
@@ -930,6 +939,8 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
int retval;
fd_set rfds;
+ setup_common_bufsiz();
+
/* Default options */
IF_FEATURE_UDHCP_PORT(SERVER_PORT6 = 547;)
IF_FEATURE_UDHCP_PORT(CLIENT_PORT6 = 546;)
@@ -938,9 +949,9 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
/* Parse command line */
/* O,x: list; -T,-t,-A take numeric param */
- opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
+ IF_UDHCP_VERBOSE(opt_complementary = "vv";)
IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;)
- opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f"
+ opt = getopt32(argv, "i:np:qRr:s:T:+t:+SA:+O:*ox:*f"
USE_FOR_MMU("b")
///IF_FEATURE_UDHCPC_ARPING("a")
IF_FEATURE_UDHCP_PORT("P:")
@@ -1034,7 +1045,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
/* Create pidfile */
write_pidfile(client_config.pidfile);
/* Goes to stdout (unless NOMMU) and possibly syslog */
- bb_info_msg("%s (v"BB_VER") started", applet_name);
+ bb_error_msg("started, v"BB_VER);
/* Set up the signal pipe */
udhcp_sp_setup();
/* We want random_xid to be random... */
@@ -1074,7 +1085,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
retval = 0;
/* If we already timed out, fall through with retval = 0, else... */
if ((int)tv.tv_sec > 0) {
- log1("Waiting on select %u seconds", (int)tv.tv_sec);
+ log1("waiting on select %u seconds", (int)tv.tv_sec);
timestamp_before_wait = (unsigned)monotonic_sec();
retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
if (retval < 0) {
@@ -1124,14 +1135,14 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
d6_run_script(NULL, "leasefail");
#if BB_MMU /* -b is not supported on NOMMU */
if (opt & OPT_b) { /* background if no lease */
- bb_info_msg("No lease, forking to background");
+ bb_error_msg("no lease, forking to background");
client_background();
/* do not background again! */
opt = ((opt & ~OPT_b) | OPT_f);
} else
#endif
if (opt & OPT_n) { /* abort if no lease */
- bb_info_msg("No lease, failing");
+ bb_error_msg("no lease, failing");
retval = 1;
goto ret;
}
@@ -1159,7 +1170,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
state = RENEWING;
client_config.first_secs = 0; /* make secs field count from 0 */
change_listen_mode(LISTEN_KERNEL);
- log1("Entering renew state");
+ log1("entering renew state");
/* fall right through */
case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
case_RENEW_REQUESTED:
@@ -1179,7 +1190,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
continue;
}
/* Timed out, enter rebinding state */
- log1("Entering rebinding state");
+ log1("entering rebinding state");
state = REBINDING;
/* fall right through */
case REBINDING:
@@ -1194,7 +1205,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
continue;
}
/* Timed out, enter init state */
- bb_info_msg("Lease lost, entering init state");
+ bb_error_msg("lease lost, entering init state");
d6_run_script(NULL, "deconfig");
state = INIT_SELECTING;
client_config.first_secs = 0; /* make secs field count from 0 */
@@ -1242,7 +1253,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
timeout = INT_MAX;
continue;
case SIGTERM:
- bb_info_msg("Received SIGTERM");
+ bb_error_msg("received %s", "SIGTERM");
goto ret0;
}
@@ -1260,7 +1271,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
len = d6_recv_raw_packet(&srv6_buf, &packet, sockfd);
if (len == -1) {
/* Error is severe, reopen socket */
- bb_info_msg("Read error: %s, reopening socket", strerror(errno));
+ bb_error_msg("read error: %s, reopening socket", strerror(errno));
sleep(discover_timeout); /* 3 seconds by default */
change_listen_mode(listen_mode); /* just close and reopen */
}
@@ -1298,7 +1309,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
if (option && option->data[4] != 0) {
/* return to init state */
- bb_info_msg("Received DHCP NAK (%u)", option->data[4]);
+ bb_error_msg("received DHCP NAK (%u)", option->data[4]);
d6_run_script(&packet, "nak");
if (state != REQUESTING)
d6_run_script(NULL, "deconfig");
@@ -1453,7 +1464,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
lease_seconds = 0x0fffffff;
/* enter bound state */
timeout = lease_seconds / 2;
- bb_info_msg("Lease obtained, lease time %u",
+ bb_error_msg("lease obtained, lease time %u",
/*inet_ntoa(temp_addr),*/ (unsigned)lease_seconds);
d6_run_script(&packet, state == REQUESTING ? "bound" : "renew");
diff --git a/networking/udhcp/d6_packet.c b/networking/udhcp/d6_packet.c
index 79b2946..e166f52 100644
--- a/networking/udhcp/d6_packet.c
+++ b/networking/udhcp/d6_packet.c
@@ -17,12 +17,12 @@ void FAST_FUNC d6_dump_packet(struct d6_packet *packet)
if (dhcp_verbose < 2)
return;
- bb_info_msg(
- " xid %x"
+ bb_error_msg(
+ "xid %x"
, packet->d6_xid32
);
//*bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
- //bb_info_msg(" chaddr %s", buf);
+ //bb_error_msg(" chaddr %s", buf);
}
#endif
@@ -35,15 +35,15 @@ int FAST_FUNC d6_recv_kernel_packet(struct in6_addr *peer_ipv6
memset(packet, 0, sizeof(*packet));
bytes = safe_read(fd, packet, sizeof(*packet));
if (bytes < 0) {
- log1("Packet read error, ignoring");
+ log1("packet read error, ignoring");
return bytes; /* returns -1 */
}
if (bytes < offsetof(struct d6_packet, d6_options)) {
- bb_info_msg("Packet with bad magic, ignoring");
+ bb_error_msg("packet with bad magic, ignoring");
return -2;
}
- log1("Received a packet");
+ log1("received %s", "a packet");
d6_dump_packet(packet);
return bytes;
diff --git a/networking/udhcp/d6_socket.c b/networking/udhcp/d6_socket.c
index 56f69f6..910f296 100644
--- a/networking/udhcp/d6_socket.c
+++ b/networking/udhcp/d6_socket.c
@@ -13,7 +13,7 @@ int FAST_FUNC d6_listen_socket(int port, const char *inf)
int fd;
struct sockaddr_in6 addr;
- log1("Opening listen socket on *:%d %s", port, inf);
+ log1("opening listen socket on *:%d %s", port, inf);
fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
setsockopt_reuseaddr(fd);
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 8dee916..0e23626 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -18,6 +18,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+//applet:IF_UDHCPC(APPLET(udhcpc, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UDHCPC) += common.o packet.o signalpipe.o socket.o
+//kbuild:lib-$(CONFIG_UDHCPC) += dhcpc.o
+//kbuild:lib-$(CONFIG_FEATURE_UDHCPC_ARPING) += arpping.o
+//kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
+
#include <syslog.h>
/* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */
#define WANT_PIDFILE 1
@@ -29,6 +36,20 @@
#include <linux/filter.h>
#include <linux/if_packet.h>
+#ifndef PACKET_AUXDATA
+# define PACKET_AUXDATA 8
+struct tpacket_auxdata {
+ uint32_t tp_status;
+ uint32_t tp_len;
+ uint32_t tp_snaplen;
+ uint16_t tp_mac;
+ uint16_t tp_net;
+ uint16_t tp_vlan_tci;
+ uint16_t tp_padding;
+};
+#endif
+
+
/* "struct client_config_t client_config" is in bb_common_bufsiz1 */
@@ -54,7 +75,7 @@ static const char udhcpc_longopts[] ALIGN1 =
"foreground\0" No_argument "f"
"background\0" No_argument "b"
"broadcast\0" No_argument "B"
- IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")
+ IF_FEATURE_UDHCPC_ARPING("arping\0" Optional_argument "a")
IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
;
#endif
@@ -95,11 +116,11 @@ enum {
/*** Script execution code ***/
/* get a rough idea of how long an option will be (rounding up...) */
-static const uint8_t len_of_option_as_string[] = {
+static const uint8_t len_of_option_as_string[] ALIGN1 = {
[OPTION_IP ] = sizeof("255.255.255.255 "),
[OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2,
[OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "),
- [OPTION_6RD ] = sizeof("32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "),
+ [OPTION_6RD ] = sizeof("132 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "),
[OPTION_STRING ] = 1,
[OPTION_STRING_HOST ] = 1,
#if ENABLE_FEATURE_UDHCP_RFC3397
@@ -136,6 +157,7 @@ static int mton(uint32_t mask)
return i;
}
+#if ENABLE_FEATURE_UDHCPC_SANITIZEOPT
/* Check if a given label represents a valid DNS label
* Return pointer to the first character after the label upon success,
* NULL otherwise.
@@ -155,10 +177,6 @@ static const char *valid_domain_label(const char *label)
for (;;) {
ch = *label;
if ((ch|0x20) < 'a' || (ch|0x20) > 'z') {
- if (pos == 0) {
- /* label must begin with letter */
- return NULL;
- }
if (ch < '0' || ch > '9') {
if (ch == '\0' || ch == '.')
return label;
@@ -190,8 +208,13 @@ static int good_hostname(const char *name)
//Do we want this?
//return ((name - start) < 1025); /* NS_MAXDNAME */
name++;
+ if (*name == '\0')
+ return 1; // We allow trailing dot too
}
}
+#else
+# define good_hostname(name) 1
+#endif
/* Create "opt_name=opt_value" string */
static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name)
@@ -206,7 +229,7 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
type = optflag->flags & OPTION_TYPE_MASK;
optlen = dhcp_option_lengths[type];
upper_length = len_of_option_as_string[type]
- * ((unsigned)(len + optlen - 1) / (unsigned)optlen);
+ * ((unsigned)(len + optlen) / (unsigned)optlen);
dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
dest += sprintf(ret, "%s=", opt_name);
@@ -434,7 +457,7 @@ static char **fill_envp(struct dhcp_packet *packet)
temp = udhcp_get_option(packet, i);
if (temp) {
if (i == DHCP_OPTION_OVERLOAD)
- overload = *temp;
+ overload |= *temp;
else if (i == DHCP_SUBNET)
envc++; /* for $mask */
envc++;
@@ -460,7 +483,7 @@ static char **fill_envp(struct dhcp_packet *packet)
* uint16_t secs; // elapsed since client began acquisition/renewal
* uint16_t flags; // only one flag so far: bcast. Never set by server
* uint32_t ciaddr; // client IP (usually == yiaddr. can it be different
- * // if during renew server wants to give us differn IP?)
+ * // if during renew server wants to give us different IP?)
* uint32_t gateway_nip; // relay agent IP address
* uint8_t chaddr[16]; // link-layer client hardware address (MAC)
* TODO: export gateway_nip as $giaddr?
@@ -545,7 +568,7 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
envp = fill_envp(packet);
/* call script */
- log1("Executing %s %s", client_config.script, name);
+ log1("executing %s %s", client_config.script, name);
argv[0] = (char*) client_config.script;
argv[1] = (char*) name;
argv[2] = NULL;
@@ -659,10 +682,10 @@ static void add_client_options(struct dhcp_packet *packet)
* client reverts to using the IP broadcast address.
*/
-static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet)
+static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet, uint32_t src_nip)
{
return udhcp_send_raw_packet(packet,
- /*src*/ INADDR_ANY, CLIENT_PORT,
+ /*src*/ src_nip, CLIENT_PORT,
/*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR,
client_config.ifindex);
}
@@ -673,7 +696,7 @@ static int bcast_or_ucast(struct dhcp_packet *packet, uint32_t ciaddr, uint32_t
return udhcp_send_kernel_packet(packet,
ciaddr, CLIENT_PORT,
server, SERVER_PORT);
- return raw_bcast_from_client_config_ifindex(packet);
+ return raw_bcast_from_client_config_ifindex(packet, ciaddr);
}
/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
@@ -698,8 +721,8 @@ static NOINLINE int send_discover(uint32_t xid, uint32_t requested)
*/
add_client_options(&packet);
- bb_info_msg("Sending discover...");
- return raw_bcast_from_client_config_ifindex(&packet);
+ bb_error_msg("sending %s", "discover");
+ return raw_bcast_from_client_config_ifindex(&packet, INADDR_ANY);
}
/* Broadcast a DHCP request message */
@@ -742,8 +765,8 @@ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requeste
add_client_options(&packet);
addr.s_addr = requested;
- bb_info_msg("Sending select for %s...", inet_ntoa(addr));
- return raw_bcast_from_client_config_ifindex(&packet);
+ bb_error_msg("sending select for %s", inet_ntoa(addr));
+ return raw_bcast_from_client_config_ifindex(&packet, INADDR_ANY);
}
/* Unicast or broadcast a DHCP renew message */
@@ -781,7 +804,7 @@ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr)
*/
add_client_options(&packet);
- bb_info_msg("Sending renew...");
+ bb_error_msg("sending %s", "renew");
return bcast_or_ucast(&packet, ciaddr, server);
}
@@ -810,8 +833,8 @@ static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t req
udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
- bb_info_msg("Sending decline...");
- return raw_bcast_from_client_config_ifindex(&packet);
+ bb_error_msg("sending %s", "decline");
+ return raw_bcast_from_client_config_ifindex(&packet, INADDR_ANY);
}
#endif
@@ -830,7 +853,7 @@ static int send_release(uint32_t server, uint32_t ciaddr)
udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server);
- bb_info_msg("Sending release...");
+ bb_error_msg("sending %s", "release");
/* Note: normally we unicast here since "server" is not zero.
* However, there _are_ people who run "address-less" DHCP servers,
* and reportedly ISC dhcp client and Windows allow that.
@@ -865,7 +888,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
if (bytes < 0) {
if (errno == EINTR)
continue;
- log1("Packet read error, ignoring");
+ log1("packet read error, ignoring");
/* NB: possible down interface, etc. Caller should pause. */
return bytes; /* returns -1 */
}
@@ -873,13 +896,13 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
}
if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
- log1("Packet is too short, ignoring");
+ log1("packet is too short, ignoring");
return -2;
}
if (bytes < ntohs(packet.ip.tot_len)) {
/* packet is bigger than sizeof(packet), we did partial read */
- log1("Oversized packet, ignoring");
+ log1("oversized packet, ignoring");
return -2;
}
@@ -894,7 +917,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
/* || bytes > (int) sizeof(packet) - can't happen */
|| ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
) {
- log1("Unrelated/bogus packet, ignoring");
+ log1("unrelated/bogus packet, ignoring");
return -2;
}
@@ -902,7 +925,7 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
check = packet.ip.check;
packet.ip.check = 0;
if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) {
- log1("Bad IP header checksum, ignoring");
+ log1("bad IP header checksum, ignoring");
return -2;
}
@@ -927,17 +950,17 @@ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
check = packet.udp.check;
packet.udp.check = 0;
if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
- log1("Packet with bad UDP checksum received, ignoring");
+ log1("packet with bad UDP checksum received, ignoring");
return -2;
}
skip_udp_sum_check:
if (packet.data.cookie != htonl(DHCP_MAGIC)) {
- bb_info_msg("Packet with bad magic, ignoring");
+ bb_error_msg("packet with bad magic, ignoring");
return -2;
}
- log1("Received a packet");
+ log1("received %s", "a packet");
udhcp_dump_packet(&packet.data);
bytes -= sizeof(packet.ip) + sizeof(packet.udp);
@@ -976,86 +999,86 @@ static int udhcp_raw_socket(int ifindex)
int fd;
struct sockaddr_ll sock;
- /*
- * Comment:
- *
- * I've selected not to see LL header, so BPF doesn't see it, too.
- * The filter may also pass non-IP and non-ARP packets, but we do
- * a more complete check when receiving the message in userspace.
- *
- * and filter shamelessly stolen from:
- *
- * http://www.flamewarmaster.de/software/dhcpclient/
- *
- * There are a few other interesting ideas on that page (look under
- * "Motivation"). Use of netlink events is most interesting. Think
- * of various network servers listening for events and reconfiguring.
- * That would obsolete sending HUP signals and/or make use of restarts.
- *
- * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
- * License: GPL v2.
- *
- * TODO: make conditional?
- */
- static const struct sock_filter filter_instr[] = {
- /* load 9th byte (protocol) */
- BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
- /* jump to L1 if it is IPPROTO_UDP, else to L4 */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
- /* L1: load halfword from offset 6 (flags and frag offset) */
- BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
- /* jump to L4 if any bits in frag offset field are set, else to L2 */
- BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
- /* L2: skip IP header (load index reg with header len) */
- BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
- /* load udp destination port from halfword[header_len + 2] */
- BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
- /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
- /* L3: accept packet */
- BPF_STMT(BPF_RET|BPF_K, 0xffffffff),
- /* L4: discard packet */
- BPF_STMT(BPF_RET|BPF_K, 0),
- };
- static const struct sock_fprog filter_prog = {
- .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
- /* casting const away: */
- .filter = (struct sock_filter *) filter_instr,
- };
-
- log1("Opening raw socket on ifindex %d", ifindex); //log2?
+ log1("opening raw socket on ifindex %d", ifindex); //log2?
fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
- log1("Got raw socket fd"); //log2?
+ /* ^^^^^
+ * SOCK_DGRAM: remove link-layer headers on input (SOCK_RAW keeps them)
+ * ETH_P_IP: want to receive only packets with IPv4 eth type
+ */
+ log1("got raw socket fd"); //log2?
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IP);
sock.sll_ifindex = ifindex;
xbind(fd, (struct sockaddr *) &sock, sizeof(sock));
+#if 0 /* Several users reported breakage when BPF filter is used */
if (CLIENT_PORT == 68) {
/* Use only if standard port is in use */
+ /*
+ * I've selected not to see LL header, so BPF doesn't see it, too.
+ * The filter may also pass non-IP and non-ARP packets, but we do
+ * a more complete check when receiving the message in userspace.
+ *
+ * and filter shamelessly stolen from:
+ *
+ * http://www.flamewarmaster.de/software/dhcpclient/
+ *
+ * There are a few other interesting ideas on that page (look under
+ * "Motivation"). Use of netlink events is most interesting. Think
+ * of various network servers listening for events and reconfiguring.
+ * That would obsolete sending HUP signals and/or make use of restarts.
+ *
+ * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>.
+ * License: GPL v2.
+ */
+ static const struct sock_filter filter_instr[] = {
+ /* load 9th byte (protocol) */
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9),
+ /* jump to L1 if it is IPPROTO_UDP, else to L4 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6),
+ /* L1: load halfword from offset 6 (flags and frag offset) */
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6),
+ /* jump to L4 if any bits in frag offset field are set, else to L2 */
+ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0),
+ /* L2: skip IP header (load index reg with header len) */
+ BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0),
+ /* load udp destination port from halfword[header_len + 2] */
+ BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2),
+ /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1),
+ /* L3: accept packet ("accept 0x7fffffff bytes") */
+ /* Accepting 0xffffffff works too but kernel 2.6.19 is buggy */
+ BPF_STMT(BPF_RET|BPF_K, 0x7fffffff),
+ /* L4: discard packet ("accept zero bytes") */
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ };
+ static const struct sock_fprog filter_prog = {
+ .len = sizeof(filter_instr) / sizeof(filter_instr[0]),
+ /* casting const away: */
+ .filter = (struct sock_filter *) filter_instr,
+ };
/* Ignoring error (kernel may lack support for this) */
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog,
sizeof(filter_prog)) >= 0)
- log1("Attached filter to raw socket fd"); // log?
+ log1("attached filter to raw socket fd"); // log?
}
+#endif
- if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA,
- &const_int_1, sizeof(int)) < 0
- ) {
+ if (setsockopt_1(fd, SOL_PACKET, PACKET_AUXDATA) != 0) {
if (errno != ENOPROTOOPT)
- log1("Can't set PACKET_AUXDATA on raw socket");
+ log1("can't set PACKET_AUXDATA on raw socket");
}
- log1("Created raw socket");
+ log1("created raw socket");
return fd;
}
static void change_listen_mode(int new_mode)
{
- log1("Entering listen mode: %s",
+ log1("entering listen mode: %s",
new_mode != LISTEN_NONE
? (new_mode == LISTEN_KERNEL ? "kernel" : "raw")
: "none"
@@ -1076,7 +1099,7 @@ static void change_listen_mode(int new_mode)
/* Called only on SIGUSR1 */
static void perform_renew(void)
{
- bb_info_msg("Performing a DHCP renew");
+ bb_error_msg("performing DHCP renew");
switch (state) {
case BOUND:
change_listen_mode(LISTEN_KERNEL);
@@ -1102,16 +1125,26 @@ static void perform_release(uint32_t server_addr, uint32_t requested_ip)
struct in_addr temp_addr;
/* send release packet */
- if (state == BOUND || state == RENEWING || state == REBINDING) {
+ if (state == BOUND
+ || state == RENEWING
+ || state == REBINDING
+ || state == RENEW_REQUESTED
+ ) {
temp_addr.s_addr = server_addr;
strcpy(buffer, inet_ntoa(temp_addr));
temp_addr.s_addr = requested_ip;
- bb_info_msg("Unicasting a release of %s to %s",
+ bb_error_msg("unicasting a release of %s to %s",
inet_ntoa(temp_addr), buffer);
send_release(server_addr, requested_ip); /* unicast */
- udhcp_run_script(NULL, "deconfig");
}
- bb_info_msg("Entering released state");
+ bb_error_msg("entering released state");
+/*
+ * We can be here on: SIGUSR2,
+ * or on exit (SIGTERM) and -R "release on quit" is specified.
+ * Users requested to be notified in all cases, even if not in one
+ * of the states above.
+ */
+ udhcp_run_script(NULL, "deconfig");
change_listen_mode(LISTEN_NONE);
state = RELEASED;
@@ -1144,7 +1177,7 @@ static void client_background(void)
//usage:# define IF_UDHCP_VERBOSE(...)
//usage:#endif
//usage:#define udhcpc_trivial_usage
-//usage: "[-fbq"IF_UDHCP_VERBOSE("v")IF_FEATURE_UDHCPC_ARPING("a")"RB] [-t N] [-T SEC] [-A SEC/-n]\n"
+//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC/-n]\n"
//usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n"
//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..."
//usage:#define udhcpc_full_usage "\n"
@@ -1168,7 +1201,7 @@ static void client_background(void)
//usage: )
//usage: "\n -S,--syslog Log to syslog too"
//usage: IF_FEATURE_UDHCPC_ARPING(
-//usage: "\n -a,--arping Use arping to validate offered address"
+//usage: "\n -a[MSEC],--arping[=MSEC] Validate offered address with ARP ping"
//usage: )
//usage: "\n -r,--request IP Request this IP address"
//usage: "\n -o,--no-default-options Don't request any options (unless -O is given)"
@@ -1205,7 +1238,7 @@ static void client_background(void)
//usage: )
//usage: "\n -S Log to syslog too"
//usage: IF_FEATURE_UDHCPC_ARPING(
-//usage: "\n -a Use arping to validate offered address"
+//usage: "\n -a[MSEC] Validate offered address with ARP ping"
//usage: )
//usage: "\n -r IP Request this IP address"
//usage: "\n -o Don't request any options (unless -O is given)"
@@ -1230,8 +1263,9 @@ static void client_background(void)
int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int udhcpc_main(int argc UNUSED_PARAM, char **argv)
{
- uint8_t *temp, *message;
+ uint8_t *message;
const char *str_V, *str_h, *str_F, *str_r;
+ IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";)
IF_FEATURE_UDHCP_PORT(char *str_P;)
void *clientid_mac_ptr;
llist_t *list_O = NULL;
@@ -1246,10 +1280,13 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
int timeout; /* must be signed */
unsigned already_waited_sec;
unsigned opt;
+ IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;)
int max_fd;
int retval;
fd_set rfds;
+ setup_common_bufsiz();
+
/* Default options */
IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
@@ -1259,11 +1296,11 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
/* Parse command line */
/* O,x: list; -T,-t,-A take numeric param */
- opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
+ IF_UDHCP_VERBOSE(opt_complementary = "vv";)
IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
- opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
+ opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB"
USE_FOR_MMU("b")
- IF_FEATURE_UDHCPC_ARPING("a")
+ IF_FEATURE_UDHCPC_ARPING("a::")
IF_FEATURE_UDHCP_PORT("P:")
"v"
, &str_V, &str_h, &str_h, &str_F
@@ -1272,6 +1309,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
, &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
, &list_O
, &list_x
+ IF_FEATURE_UDHCPC_ARPING(, &str_a)
IF_FEATURE_UDHCP_PORT(, &str_P)
IF_UDHCP_VERBOSE(, &dhcp_verbose)
);
@@ -1303,6 +1341,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
SERVER_PORT = CLIENT_PORT - 1;
}
#endif
+ IF_FEATURE_UDHCPC_ARPING(arpping_ms = xatou(str_a);)
while (list_O) {
char *optstr = llist_pop(&list_O);
unsigned n = bb_strtou(optstr, NULL, 0);
@@ -1375,7 +1414,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
/* Create pidfile */
write_pidfile(client_config.pidfile);
/* Goes to stdout (unless NOMMU) and possibly syslog */
- bb_info_msg("%s (v"BB_VER") started", applet_name);
+ bb_error_msg("started, v"BB_VER);
/* Set up the signal pipe */
udhcp_sp_setup();
/* We want random_xid to be random... */
@@ -1414,7 +1453,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
retval = 0;
/* If we already timed out, fall through with retval = 0, else... */
if ((int)tv.tv_sec > 0) {
- log1("Waiting on select %u seconds", (int)tv.tv_sec);
+ log1("waiting on select %u seconds", (int)tv.tv_sec);
timestamp_before_wait = (unsigned)monotonic_sec();
retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
if (retval < 0) {
@@ -1465,14 +1504,14 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
udhcp_run_script(NULL, "leasefail");
#if BB_MMU /* -b is not supported on NOMMU */
if (opt & OPT_b) { /* background if no lease */
- bb_info_msg("No lease, forking to background");
+ bb_error_msg("no lease, forking to background");
client_background();
/* do not background again! */
opt = ((opt & ~OPT_b) | OPT_f);
} else
#endif
if (opt & OPT_n) { /* abort if no lease */
- bb_info_msg("No lease, failing");
+ bb_error_msg("no lease, failing");
retval = 1;
goto ret;
}
@@ -1481,7 +1520,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
packet_num = 0;
continue;
case REQUESTING:
- if (!discover_retries || packet_num < discover_retries) {
+ if (packet_num < 3) {
/* send broadcast select packet */
send_select(xid, server_addr, requested_ip);
timeout = discover_timeout;
@@ -1500,7 +1539,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
state = RENEWING;
client_config.first_secs = 0; /* make secs field count from 0 */
change_listen_mode(LISTEN_KERNEL);
- log1("Entering renew state");
+ log1("entering renew state");
/* fall right through */
case RENEW_REQUESTED: /* manual (SIGUSR1) renew */
case_RENEW_REQUESTED:
@@ -1520,7 +1559,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
continue;
}
/* Timed out, enter rebinding state */
- log1("Entering rebinding state");
+ log1("entering rebinding state");
state = REBINDING;
/* fall right through */
case REBINDING:
@@ -1535,7 +1574,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
continue;
}
/* Timed out, enter init state */
- bb_info_msg("Lease lost, entering init state");
+ bb_error_msg("lease lost, entering init state");
udhcp_run_script(NULL, "deconfig");
state = INIT_SELECTING;
client_config.first_secs = 0; /* make secs field count from 0 */
@@ -1583,7 +1622,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
timeout = INT_MAX;
continue;
case SIGTERM:
- bb_info_msg("Received SIGTERM");
+ bb_error_msg("received %s", "SIGTERM");
goto ret0;
}
@@ -1601,7 +1640,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
len = udhcp_recv_raw_packet(&packet, sockfd);
if (len == -1) {
/* Error is severe, reopen socket */
- bb_info_msg("Read error: %s, reopening socket", strerror(errno));
+ bb_error_msg("read error: %s, reopening socket", strerror(errno));
sleep(discover_timeout); /* 3 seconds by default */
change_listen_mode(listen_mode); /* just close and reopen */
}
@@ -1638,6 +1677,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
case INIT_SELECTING:
/* Must be a DHCPOFFER */
if (*message == DHCPOFFER) {
+ uint8_t *temp;
+
/* What exactly is server's IP? There are several values.
* Example DHCP offer captured with tchdump:
*
@@ -1685,8 +1726,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
case RENEW_REQUESTED:
case REBINDING:
if (*message == DHCPACK) {
+ unsigned start;
uint32_t lease_seconds;
struct in_addr temp_addr;
+ uint8_t *temp;
temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
if (!temp) {
@@ -1717,9 +1760,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
NULL,
(uint32_t) 0,
client_config.client_mac,
- client_config.interface)
+ client_config.interface,
+ arpping_ms)
) {
- bb_info_msg("Offered address is in use "
+ bb_error_msg("offered address is in use "
"(got ARP reply), declining");
send_decline(/*xid,*/ server_addr, packet.yiaddr);
@@ -1737,12 +1781,19 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
}
#endif
/* enter bound state */
- timeout = lease_seconds / 2;
temp_addr.s_addr = packet.yiaddr;
- bb_info_msg("Lease of %s obtained, lease time %u",
+ bb_error_msg("lease of %s obtained, lease time %u",
inet_ntoa(temp_addr), (unsigned)lease_seconds);
requested_ip = packet.yiaddr;
+
+ start = monotonic_sec();
udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew");
+ already_waited_sec = (unsigned)monotonic_sec() - start;
+ timeout = lease_seconds / 2;
+ if ((unsigned)timeout < already_waited_sec) {
+ /* Something went wrong. Back to discover state */
+ timeout = already_waited_sec = 0;
+ }
state = BOUND;
change_listen_mode(LISTEN_NONE);
@@ -1760,12 +1811,31 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
#endif
/* make future renew packets use different xid */
/* xid = random_xid(); ...but why bother? */
- already_waited_sec = 0;
+
continue; /* back to main loop */
}
if (*message == DHCPNAK) {
+ /* If network has more than one DHCP server,
+ * "wrong" server can reply first, with a NAK.
+ * Do not interpret it as a NAK from "our" server.
+ */
+ if (server_addr != 0) {
+ uint32_t svid;
+ uint8_t *temp;
+
+ temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
+ if (!temp) {
+ non_matching_svid:
+ log1("received DHCP NAK with wrong"
+ " server ID, ignoring packet");
+ continue;
+ }
+ move_from_unaligned32(svid, temp);
+ if (svid != server_addr)
+ goto non_matching_svid;
+ }
/* return to init state */
- bb_info_msg("Received DHCP NAK");
+ bb_error_msg("received %s", "DHCP NAK");
udhcp_run_script(&packet, "nak");
if (state != REQUESTING)
udhcp_run_script(NULL, "deconfig");
diff --git a/networking/udhcp/dhcpd.c b/networking/udhcp/dhcpd.c
index a1a7f6b..e116ba3 100644
--- a/networking/udhcp/dhcpd.c
+++ b/networking/udhcp/dhcpd.c
@@ -20,6 +20,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+//applet:IF_UDHCPD(APPLET(udhcpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UDHCPD) += common.o packet.o signalpipe.o socket.o
+//kbuild:lib-$(CONFIG_UDHCPD) += dhcpd.o arpping.o
+//kbuild:lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
//usage:#define udhcpd_trivial_usage
//usage: "[-fS] [-I ADDR]" IF_FEATURE_UDHCP_PORT(" [-P N]") " [CONFFILE]"
@@ -28,15 +33,512 @@
//usage: "\n -f Run in foreground"
//usage: "\n -S Log to syslog too"
//usage: "\n -I ADDR Local address"
+//usage: "\n -a MSEC Timeout for ARP ping (default 2000)"
//usage: IF_FEATURE_UDHCP_PORT(
//usage: "\n -P N Use port N (default 67)"
//usage: )
+#include <netinet/ether.h>
#include <syslog.h>
#include "common.h"
#include "dhcpc.h"
#include "dhcpd.h"
+/* globals */
+struct dyn_lease *g_leases;
+/* struct server_config_t server_config is in bb_common_bufsiz1 */
+
+/* Takes the address of the pointer to the static_leases linked list,
+ * address to a 6 byte mac address,
+ * 4 byte IP address */
+static void add_static_lease(struct static_lease **st_lease_pp,
+ uint8_t *mac,
+ uint32_t nip)
+{
+ struct static_lease *st_lease;
+
+ /* Find the tail of the list */
+ while ((st_lease = *st_lease_pp) != NULL) {
+ st_lease_pp = &st_lease->next;
+ }
+
+ /* Add new node */
+ *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
+ memcpy(st_lease->mac, mac, 6);
+ st_lease->nip = nip;
+ /*st_lease->next = NULL;*/
+}
+
+/* Find static lease IP by mac */
+static uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
+{
+ while (st_lease) {
+ if (memcmp(st_lease->mac, mac, 6) == 0)
+ return st_lease->nip;
+ st_lease = st_lease->next;
+ }
+
+ return 0;
+}
+
+static int is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
+{
+ while (st_lease) {
+ if (st_lease->nip == nip)
+ return 1;
+ st_lease = st_lease->next;
+ }
+
+ return 0;
+}
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+static void log_static_leases(struct static_lease **st_lease_pp)
+{
+ struct static_lease *cur;
+
+ if (dhcp_verbose < 2)
+ return;
+
+ cur = *st_lease_pp;
+ while (cur) {
+ bb_error_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
+ cur->mac[0], cur->mac[1], cur->mac[2],
+ cur->mac[3], cur->mac[4], cur->mac[5],
+ cur->nip
+ );
+ cur = cur->next;
+ }
+}
+#else
+# define log_static_leases(st_lease_pp) ((void)0)
+#endif
+
+/* Find the oldest expired lease, NULL if there are no expired leases */
+static struct dyn_lease *oldest_expired_lease(void)
+{
+ struct dyn_lease *oldest_lease = NULL;
+ leasetime_t oldest_time = time(NULL);
+ unsigned i;
+
+ /* Unexpired leases have g_leases[i].expires >= current time
+ * and therefore can't ever match */
+ for (i = 0; i < server_config.max_leases; i++) {
+ if (g_leases[i].expires == 0 /* empty entry */
+ || g_leases[i].expires < oldest_time
+ ) {
+ oldest_time = g_leases[i].expires;
+ oldest_lease = &g_leases[i];
+ }
+ }
+ return oldest_lease;
+}
+
+/* Clear out all leases with matching nonzero chaddr OR yiaddr.
+ * If chaddr == NULL, this is a conflict lease.
+ */
+static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
+{
+ unsigned i;
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
+ || (yiaddr && g_leases[i].lease_nip == yiaddr)
+ ) {
+ memset(&g_leases[i], 0, sizeof(g_leases[i]));
+ }
+ }
+}
+
+/* Add a lease into the table, clearing out any old ones.
+ * If chaddr == NULL, this is a conflict lease.
+ */
+static struct dyn_lease *add_lease(
+ const uint8_t *chaddr, uint32_t yiaddr,
+ leasetime_t leasetime,
+ const char *hostname, int hostname_len)
+{
+ struct dyn_lease *oldest;
+
+ /* clean out any old ones */
+ clear_leases(chaddr, yiaddr);
+
+ oldest = oldest_expired_lease();
+
+ if (oldest) {
+ memset(oldest, 0, sizeof(*oldest));
+ if (hostname) {
+ char *p;
+
+ hostname_len++; /* include NUL */
+ if (hostname_len > sizeof(oldest->hostname))
+ hostname_len = sizeof(oldest->hostname);
+ p = safe_strncpy(oldest->hostname, hostname, hostname_len);
+ /*
+ * Sanitization (s/bad_char/./g).
+ * The intent is not to allow only "DNS-valid" hostnames,
+ * but merely make dumpleases output safe for shells to use.
+ * We accept "0-9A-Za-z._-", all other chars turn to dots.
+ */
+ while (*p) {
+ if (!isalnum(*p) && *p != '-' && *p != '_')
+ *p = '.';
+ p++;
+ }
+ }
+ if (chaddr)
+ memcpy(oldest->lease_mac, chaddr, 6);
+ oldest->lease_nip = yiaddr;
+ oldest->expires = time(NULL) + leasetime;
+ }
+
+ return oldest;
+}
+
+/* True if a lease has expired */
+static int is_expired_lease(struct dyn_lease *lease)
+{
+ return (lease->expires < (leasetime_t) time(NULL));
+}
+
+/* Find the first lease that matches MAC, NULL if no match */
+static struct dyn_lease *find_lease_by_mac(const uint8_t *mac)
+{
+ unsigned i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
+ return &g_leases[i];
+
+ return NULL;
+}
+
+/* Find the first lease that matches IP, NULL is no match */
+static struct dyn_lease *find_lease_by_nip(uint32_t nip)
+{
+ unsigned i;
+
+ for (i = 0; i < server_config.max_leases; i++)
+ if (g_leases[i].lease_nip == nip)
+ return &g_leases[i];
+
+ return NULL;
+}
+
+/* Check if the IP is taken; if it is, add it to the lease table */
+static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
+{
+ struct in_addr temp;
+ int r;
+
+ r = arpping(nip, safe_mac,
+ server_config.server_nip,
+ server_config.server_mac,
+ server_config.interface,
+ arpping_ms);
+ if (r)
+ return r;
+
+ temp.s_addr = nip;
+ bb_error_msg("%s belongs to someone, reserving it for %u seconds",
+ inet_ntoa(temp), (unsigned)server_config.conflict_time);
+ add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
+ return 0;
+}
+
+/* Find a new usable (we think) address */
+static uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
+{
+ uint32_t addr;
+ struct dyn_lease *oldest_lease = NULL;
+
+#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
+ uint32_t stop;
+ unsigned i, hash;
+
+ /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
+ * dispersal even with similarly-valued "strings".
+ */
+ hash = 0;
+ for (i = 0; i < 6; i++)
+ hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
+
+ /* pick a seed based on hwaddr then iterate until we find a free address. */
+ addr = server_config.start_ip
+ + (hash % (1 + server_config.end_ip - server_config.start_ip));
+ stop = addr;
+#else
+ addr = server_config.start_ip;
+#define stop (server_config.end_ip + 1)
+#endif
+ do {
+ uint32_t nip;
+ struct dyn_lease *lease;
+
+ /* ie, 192.168.55.0 */
+ if ((addr & 0xff) == 0)
+ goto next_addr;
+ /* ie, 192.168.55.255 */
+ if ((addr & 0xff) == 0xff)
+ goto next_addr;
+ nip = htonl(addr);
+ /* skip our own address */
+ if (nip == server_config.server_nip)
+ goto next_addr;
+ /* is this a static lease addr? */
+ if (is_nip_reserved(server_config.static_leases, nip))
+ goto next_addr;
+
+ lease = find_lease_by_nip(nip);
+ if (!lease) {
+//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
+ if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
+ return nip;
+ } else {
+ if (!oldest_lease || lease->expires < oldest_lease->expires)
+ oldest_lease = lease;
+ }
+
+ next_addr:
+ addr++;
+#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
+ if (addr > server_config.end_ip)
+ addr = server_config.start_ip;
+#endif
+ } while (addr != stop);
+
+ if (oldest_lease
+ && is_expired_lease(oldest_lease)
+ && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
+ ) {
+ return oldest_lease->lease_nip;
+ }
+
+ return 0;
+}
+
+/* On these functions, make sure your datatype matches */
+static int FAST_FUNC read_str(const char *line, void *arg)
+{
+ char **dest = arg;
+
+ free(*dest);
+ *dest = xstrdup(line);
+ return 1;
+}
+
+static int FAST_FUNC read_u32(const char *line, void *arg)
+{
+ *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
+ return errno == 0;
+}
+
+static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
+{
+ char *line;
+ char *mac_string;
+ char *ip_string;
+ struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
+ uint32_t nip;
+
+ /* Read mac */
+ line = (char *) const_line;
+ mac_string = strtok_r(line, " \t", &line);
+ if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
+ return 0;
+
+ /* Read ip */
+ ip_string = strtok_r(NULL, " \t", &line);
+ if (!ip_string || !udhcp_str2nip(ip_string, &nip))
+ return 0;
+
+ add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
+
+ log_static_leases(arg);
+
+ return 1;
+}
+
+struct config_keyword {
+ const char *keyword;
+ int (*handler)(const char *line, void *var) FAST_FUNC;
+ unsigned ofs;
+ const char *def;
+};
+
+#define OFS(field) offsetof(struct server_config_t, field)
+
+static const struct config_keyword keywords[] = {
+ /* keyword handler variable address default */
+ {"start" , udhcp_str2nip , OFS(start_ip ), "192.168.0.20"},
+ {"end" , udhcp_str2nip , OFS(end_ip ), "192.168.0.254"},
+ {"interface" , read_str , OFS(interface ), "eth0"},
+ /* Avoid "max_leases value not sane" warning by setting default
+ * to default_end_ip - default_start_ip + 1: */
+ {"max_leases" , read_u32 , OFS(max_leases ), "235"},
+ {"auto_time" , read_u32 , OFS(auto_time ), "7200"},
+ {"decline_time" , read_u32 , OFS(decline_time ), "3600"},
+ {"conflict_time", read_u32 , OFS(conflict_time), "3600"},
+ {"offer_time" , read_u32 , OFS(offer_time ), "60"},
+ {"min_lease" , read_u32 , OFS(min_lease_sec), "60"},
+ {"lease_file" , read_str , OFS(lease_file ), LEASES_FILE},
+ {"pidfile" , read_str , OFS(pidfile ), "/var/run/udhcpd.pid"},
+ {"siaddr" , udhcp_str2nip , OFS(siaddr_nip ), "0.0.0.0"},
+ /* keywords with no defaults must be last! */
+ {"option" , udhcp_str2optset, OFS(options ), ""},
+ {"opt" , udhcp_str2optset, OFS(options ), ""},
+ {"notify_file" , read_str , OFS(notify_file ), NULL},
+ {"sname" , read_str , OFS(sname ), NULL},
+ {"boot_file" , read_str , OFS(boot_file ), NULL},
+ {"static_lease" , read_staticlease, OFS(static_leases), ""},
+};
+enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
+
+static NOINLINE void read_config(const char *file)
+{
+ parser_t *parser;
+ const struct config_keyword *k;
+ unsigned i;
+ char *token[2];
+
+ for (i = 0; i < KWS_WITH_DEFAULTS; i++)
+ keywords[i].handler(keywords[i].def, (char*)&server_config + keywords[i].ofs);
+
+ parser = config_open(file);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
+ if (strcasecmp(token[0], k->keyword) == 0) {
+ if (!k->handler(token[1], (char*)&server_config + k->ofs)) {
+ bb_error_msg("can't parse line %u in %s",
+ parser->lineno, file);
+ /* reset back to the default value */
+ k->handler(k->def, (char*)&server_config + k->ofs);
+ }
+ break;
+ }
+ }
+ }
+ config_close(parser);
+
+ server_config.start_ip = ntohl(server_config.start_ip);
+ server_config.end_ip = ntohl(server_config.end_ip);
+}
+
+static void write_leases(void)
+{
+ int fd;
+ unsigned i;
+ leasetime_t curr;
+ int64_t written_at;
+
+ fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
+ if (fd < 0)
+ return;
+
+ curr = written_at = time(NULL);
+
+ written_at = SWAP_BE64(written_at);
+ full_write(fd, &written_at, sizeof(written_at));
+
+ for (i = 0; i < server_config.max_leases; i++) {
+ leasetime_t tmp_time;
+
+ if (g_leases[i].lease_nip == 0)
+ continue;
+
+ /* Screw with the time in the struct, for easier writing */
+ tmp_time = g_leases[i].expires;
+
+ g_leases[i].expires -= curr;
+ if ((signed_leasetime_t) g_leases[i].expires < 0)
+ g_leases[i].expires = 0;
+ g_leases[i].expires = htonl(g_leases[i].expires);
+
+ /* No error check. If the file gets truncated,
+ * we lose some leases on restart. Oh well. */
+ full_write(fd, &g_leases[i], sizeof(g_leases[i]));
+
+ /* Then restore it when done */
+ g_leases[i].expires = tmp_time;
+ }
+ close(fd);
+
+ if (server_config.notify_file) {
+ char *argv[3];
+ argv[0] = server_config.notify_file;
+ argv[1] = server_config.lease_file;
+ argv[2] = NULL;
+ spawn_and_wait(argv);
+ }
+}
+
+static NOINLINE void read_leases(const char *file)
+{
+ struct dyn_lease lease;
+ int64_t written_at, time_passed;
+ int fd;
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+ unsigned i = 0;
+#endif
+
+ fd = open_or_warn(file, O_RDONLY);
+ if (fd < 0)
+ return;
+
+ if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
+ goto ret;
+ written_at = SWAP_BE64(written_at);
+
+ time_passed = time(NULL) - written_at;
+ /* Strange written_at, or lease file from old version of udhcpd
+ * which had no "written_at" field? */
+ if ((uint64_t)time_passed > 12 * 60 * 60)
+ goto ret;
+
+ while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+ uint32_t y = ntohl(lease.lease_nip);
+ if (y >= server_config.start_ip && y <= server_config.end_ip) {
+ signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
+ uint32_t static_nip;
+
+ if (expires <= 0)
+ /* We keep expired leases: add_lease() will add
+ * a lease with 0 seconds remaining.
+ * Fewer IP address changes this way for mass reboot scenario.
+ */
+ expires = 0;
+
+ /* Check if there is a different static lease for this IP or MAC */
+ static_nip = get_static_nip_by_mac(server_config.static_leases, lease.lease_mac);
+ if (static_nip) {
+ /* NB: we do not add lease even if static_nip == lease.lease_nip.
+ */
+ continue;
+ }
+ if (is_nip_reserved(server_config.static_leases, lease.lease_nip))
+ continue;
+
+ /* NB: add_lease takes "relative time", IOW,
+ * lease duration, not lease deadline. */
+ if (add_lease(lease.lease_mac, lease.lease_nip,
+ expires,
+ lease.hostname, sizeof(lease.hostname)
+ ) == 0
+ ) {
+ bb_error_msg("too many leases while loading %s", file);
+ break;
+ }
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+ i++;
+#endif
+ }
+ }
+ log1("read %d leases", i);
+ ret:
+ close(fd);
+}
/* Send a packet to a specific mac address and ip address by creating our own ip packet */
static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
@@ -60,11 +562,11 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc
|| (dhcp_pkt->flags & htons(BROADCAST_FLAG))
|| dhcp_pkt->ciaddr == 0
) {
- log1("Broadcasting packet to client");
+ log1("broadcasting packet to client");
ciaddr = INADDR_BROADCAST;
chaddr = MAC_BCAST_ADDR;
} else {
- log1("Unicasting packet to client ciaddr");
+ log1("unicasting packet to client ciaddr");
ciaddr = dhcp_pkt->ciaddr;
chaddr = dhcp_pkt->chaddr;
}
@@ -78,7 +580,7 @@ static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadc
/* Send a packet to gateway_nip using the kernel ip stack */
static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
{
- log1("Forwarding packet to relay");
+ log1("forwarding packet to relay");
udhcp_send_kernel_packet(dhcp_pkt,
server_config.server_nip, SERVER_PORT,
@@ -148,7 +650,8 @@ static uint32_t select_lease_time(struct dhcp_packet *packet)
static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
uint32_t static_lease_nip,
struct dyn_lease *lease,
- uint8_t *requested_ip_opt)
+ uint8_t *requested_ip_opt,
+ unsigned arpping_ms)
{
struct dhcp_packet packet;
uint32_t lease_time_sec;
@@ -187,7 +690,7 @@ static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
}
else {
/* Otherwise, find a free IP */
- packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
+ packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
}
if (!packet.yiaddr) {
@@ -212,7 +715,7 @@ static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
add_server_options(&packet);
addr.s_addr = packet.yiaddr;
- bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
+ bb_error_msg("sending OFFER of %s", inet_ntoa(addr));
/* send_packet emits error message itself if it detects failure */
send_packet(&packet, /*force_bcast:*/ 0);
}
@@ -224,7 +727,7 @@ static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
init_packet(&packet, oldpacket, DHCPNAK);
- log1("Sending NAK");
+ log1("sending %s", "NAK");
send_packet(&packet, /*force_bcast:*/ 1);
}
@@ -245,7 +748,7 @@ static NOINLINE void send_ACK(struct dhcp_packet *oldpacket, uint32_t yiaddr)
add_server_options(&packet);
addr.s_addr = yiaddr;
- bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
+ bb_error_msg("sending ACK to %s", inet_ntoa(addr));
send_packet(&packet, /*force_bcast:*/ 0);
p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
@@ -288,12 +791,6 @@ static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
send_packet(&packet, /*force_bcast:*/ 0);
}
-
-/* globals */
-struct dyn_lease *g_leases;
-/* struct server_config_t server_config is in bb_common_bufsiz1 */
-
-
int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int udhcpd_main(int argc UNUSED_PARAM, char **argv)
{
@@ -304,19 +801,22 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
unsigned opt;
struct option_set *option;
char *str_I = str_I;
+ const char *str_a = "2000";
+ unsigned arpping_ms;
IF_FEATURE_UDHCP_PORT(char *str_P;)
-#if ENABLE_FEATURE_UDHCP_PORT
- SERVER_PORT = 67;
- CLIENT_PORT = 68;
-#endif
+ setup_common_bufsiz();
+
+ IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
+ IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
opt_complementary = "vv";
#endif
- opt = getopt32(argv, "fSI:v"
+ opt = getopt32(argv, "fSI:va:"
IF_FEATURE_UDHCP_PORT("P:")
, &str_I
+ , &str_a
IF_FEATURE_UDHCP_PORT(, &str_P)
IF_UDHCP_VERBOSE(, &dhcp_verbose)
);
@@ -336,11 +836,13 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
free(lsa);
}
#if ENABLE_FEATURE_UDHCP_PORT
- if (opt & 16) { /* -P */
+ if (opt & 32) { /* -P */
SERVER_PORT = xatou16(str_P);
CLIENT_PORT = SERVER_PORT + 1;
}
#endif
+ arpping_ms = xatou(str_a);
+
/* Would rather not do read_config before daemonization -
* otherwise NOMMU machines will parse config twice */
read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
@@ -354,7 +856,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
write_pidfile(server_config.pidfile);
/* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
- bb_info_msg("%s (v"BB_VER") started", applet_name);
+ bb_error_msg("started, v"BB_VER);
option = udhcp_find_option(server_config.options, DHCP_LEASE_TIME);
server_config.max_lease_sec = DEFAULT_LEASE_TIME;
@@ -406,7 +908,8 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
max_sock = udhcp_sp_fd_set(&rfds, server_socket);
if (server_config.auto_time) {
- tv.tv_sec = timeout_end - monotonic_sec();
+ /* cast to signed is essential if tv_sec is wider than int */
+ tv.tv_sec = (int)(timeout_end - monotonic_sec());
tv.tv_usec = 0;
}
retval = 0;
@@ -419,18 +922,18 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
goto continue_with_autotime;
}
if (retval < 0 && errno != EINTR) {
- log1("Error on select");
+ log1("error on select");
continue;
}
switch (udhcp_sp_read(&rfds)) {
case SIGUSR1:
- bb_info_msg("Received SIGUSR1");
+ bb_error_msg("received %s", "SIGUSR1");
write_leases();
/* why not just reset the timeout, eh */
goto continue_with_autotime;
case SIGTERM:
- bb_info_msg("Received SIGTERM");
+ bb_error_msg("received %s", "SIGTERM");
write_leases();
goto ret0;
case 0: /* no signal: read a packet */
@@ -443,7 +946,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
if (bytes < 0) {
/* bytes can also be -2 ("bad packet data") */
if (bytes == -1 && errno != EINTR) {
- log1("Read error: %s, reopening socket", strerror(errno));
+ log1("read error: %s, reopening socket", strerror(errno));
close(server_socket);
server_socket = -1;
}
@@ -478,7 +981,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
/* Look for a static/dynamic lease */
static_lease_nip = get_static_nip_by_mac(server_config.static_leases, &packet.chaddr);
if (static_lease_nip) {
- bb_info_msg("Found static lease: %x", static_lease_nip);
+ bb_error_msg("found static lease: %x", static_lease_nip);
memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
fake_lease.lease_nip = static_lease_nip;
fake_lease.expires = 0;
@@ -496,13 +999,13 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
switch (state[0]) {
case DHCPDISCOVER:
- log1("Received DISCOVER");
+ log1("received %s", "DISCOVER");
- send_offer(&packet, static_lease_nip, lease, requested_ip_opt);
+ send_offer(&packet, static_lease_nip, lease, requested_ip_opt, arpping_ms);
break;
case DHCPREQUEST:
- log1("Received REQUEST");
+ log1("received %s", "REQUEST");
/* RFC 2131:
o DHCPREQUEST generated during SELECTING state:
@@ -627,7 +1130,7 @@ o DHCPREQUEST generated during REBINDING state:
* chaddr must be filled in,
* ciaddr must be 0 (we do not check this)
*/
- log1("Received DECLINE");
+ log1("received %s", "DECLINE");
if (server_id_opt
&& requested_ip_opt
&& lease /* chaddr matches this lease */
@@ -647,7 +1150,7 @@ o DHCPREQUEST generated during REBINDING state:
* chaddr must be filled in,
* ciaddr must be filled in
*/
- log1("Received RELEASE");
+ log1("received %s", "RELEASE");
if (server_id_opt
&& lease /* chaddr matches this lease */
&& packet.ciaddr == lease->lease_nip
@@ -657,7 +1160,7 @@ o DHCPREQUEST generated during REBINDING state:
break;
case DHCPINFORM:
- log1("Received INFORM");
+ log1("received %s", "INFORM");
send_inform(&packet);
break;
}
diff --git a/networking/udhcp/dhcpd.h b/networking/udhcp/dhcpd.h
index a77724f..b8f96b0 100644
--- a/networking/udhcp/dhcpd.h
+++ b/networking/udhcp/dhcpd.h
@@ -57,7 +57,7 @@ struct server_config_t {
struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
} FIX_ALIASING;
-#define server_config (*(struct server_config_t*)&bb_common_bufsiz1)
+#define server_config (*(struct server_config_t*)bb_common_bufsiz1)
/* client_config sits in 2nd half of bb_common_bufsiz1 */
#if ENABLE_FEATURE_UDHCP_PORT
@@ -90,39 +90,6 @@ struct dyn_lease {
/* total size is a multiply of 4 */
} PACKED;
-extern struct dyn_lease *g_leases;
-
-struct dyn_lease *add_lease(
- const uint8_t *chaddr, uint32_t yiaddr,
- leasetime_t leasetime,
- const char *hostname, int hostname_len
- ) FAST_FUNC;
-int is_expired_lease(struct dyn_lease *lease) FAST_FUNC;
-struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC;
-struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC;
-uint32_t find_free_or_expired_nip(const uint8_t *safe_mac) FAST_FUNC;
-
-
-/* Config file parser will pass static lease info to this function
- * which will add it to a data structure that can be searched later */
-void add_static_lease(struct static_lease **st_lease_pp, uint8_t *mac, uint32_t nip) FAST_FUNC;
-/* Find static lease IP by mac */
-uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *arg) FAST_FUNC;
-/* Check to see if an IP is reserved as a static IP */
-int is_nip_reserved(struct static_lease *st_lease, uint32_t nip) FAST_FUNC;
-/* Print out static leases just to check what's going on (debug code) */
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
-void log_static_leases(struct static_lease **st_lease_pp) FAST_FUNC;
-#else
-# define log_static_leases(st_lease_pp) ((void)0)
-#endif
-
-
-void read_config(const char *file) FAST_FUNC;
-void write_leases(void) FAST_FUNC;
-void read_leases(const char *file) FAST_FUNC;
-
-
POP_SAVED_FUNCTION_VISIBILITY
#endif
diff --git a/networking/udhcp/dhcprelay.c b/networking/udhcp/dhcprelay.c
index f82ac05..7cb19b1 100644
--- a/networking/udhcp/dhcprelay.c
+++ b/networking/udhcp/dhcprelay.c
@@ -9,6 +9,9 @@
* Netbeat AG
* Upstream has GPL v2 or later
*/
+//applet:IF_DHCPRELAY(APPLET(dhcprelay, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DHCPRELAY) += dhcprelay.o
//usage:#define dhcprelay_trivial_usage
//usage: "CLIENT_IFACE[,CLIENT_IFACE2]... SERVER_IFACE [SERVER_IP]"
@@ -33,7 +36,8 @@ struct xid_item {
struct xid_item *next;
} FIX_ALIASING;
-#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
+#define dhcprelay_xid_list (*(struct xid_item*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
{
@@ -257,6 +261,8 @@ int dhcprelay_main(int argc, char **argv)
int num_sockets, max_socket;
uint32_t our_nip;
+ INIT_G();
+
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
server_addr.sin_port = htons(SERVER_PORT);
diff --git a/networking/udhcp/domain_codec.c b/networking/udhcp/domain_codec.c
index c1325d8..5a923cc 100644
--- a/networking/udhcp/domain_codec.c
+++ b/networking/udhcp/domain_codec.c
@@ -7,6 +7,7 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
#ifdef DNS_COMPR_TESTING
+# define _GNU_SOURCE
# define FAST_FUNC /* nothing */
# define xmalloc malloc
# include <stdlib.h>
@@ -42,7 +43,7 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
*/
while (1) {
/* note: "return NULL" below are leak-safe since
- * dst isn't yet allocated */
+ * dst isn't allocated yet */
const uint8_t *c;
unsigned crtpos, retpos, depth, len;
@@ -63,11 +64,10 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
if (crtpos + *c + 1 > clen) /* label too long? abort */
return NULL;
if (dst)
- memcpy(dst + len, c + 1, *c);
+ /* \3com ---> "com." */
+ ((char*)mempcpy(dst + len, c + 1, *c))[0] = '.';
len += *c + 1;
crtpos += *c + 1;
- if (dst)
- dst[len - 1] = '.';
} else {
/* NUL: end of current domain name */
if (retpos == 0) {
@@ -78,7 +78,10 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
crtpos = retpos;
retpos = depth = 0;
}
- if (dst)
+ if (dst && len != 0)
+ /* \4host\3com\0\4host and we are at \0:
+ * \3com was converted to "com.", change dot to space.
+ */
dst[len - 1] = ' ';
}
@@ -95,9 +98,8 @@ char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
if (!dst) { /* first pass? */
/* allocate dst buffer and copy pre */
unsigned plen = strlen(pre);
- ret = dst = xmalloc(plen + len);
- memcpy(dst, pre, plen);
- dst += plen;
+ ret = xmalloc(plen + len);
+ dst = stpcpy(ret, pre);
} else {
dst[len - 1] = '\0';
break;
@@ -228,6 +230,9 @@ int main(int argc, char **argv)
int len;
uint8_t *encoded;
+ uint8_t str[6] = { 0x00, 0x00, 0x02, 0x65, 0x65, 0x00 };
+ printf("NUL:'%s'\n", dname_dec(str, 6, ""));
+
#define DNAME_DEC(encoded,pre) dname_dec((uint8_t*)(encoded), sizeof(encoded), (pre))
printf("'%s'\n", DNAME_DEC("\4host\3com\0", "test1:"));
printf("test2:'%s'\n", DNAME_DEC("\4host\3com\0\4host\3com\0", ""));
diff --git a/networking/udhcp/dumpleases.c b/networking/udhcp/dumpleases.c
index 64cd73e..d83344a 100644
--- a/networking/udhcp/dumpleases.c
+++ b/networking/udhcp/dumpleases.c
@@ -2,20 +2,25 @@
/*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//applet:IF_DUMPLEASES(APPLET(dumpleases, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DUMPLEASES) += dumpleases.o
//usage:#define dumpleases_trivial_usage
-//usage: "[-r|-a] [-f LEASEFILE]"
+//usage: "[-r|-a] [-d] [-f LEASEFILE]"
//usage:#define dumpleases_full_usage "\n\n"
//usage: "Display DHCP leases granted by udhcpd\n"
//usage: IF_LONG_OPTS(
//usage: "\n -f,--file=FILE Lease file"
//usage: "\n -r,--remaining Show remaining time"
//usage: "\n -a,--absolute Show expiration time"
+//usage: "\n -d,--decimal Show time in seconds"
//usage: )
//usage: IF_NOT_LONG_OPTS(
//usage: "\n -f FILE Lease file"
//usage: "\n -r Show remaining time"
//usage: "\n -a Show expiration time"
+//usage: "\n -d Show time in seconds"
//usage: )
#include "common.h"
@@ -28,21 +33,22 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
int fd;
int i;
unsigned opt;
- int64_t written_at, curr, expires_abs;
+ int64_t written_at, curr;
const char *file = LEASES_FILE;
struct dyn_lease lease;
- struct in_addr addr;
enum {
OPT_a = 0x1, // -a
OPT_r = 0x2, // -r
OPT_f = 0x4, // -f
+ OPT_d = 0x8, // -d
};
#if ENABLE_LONG_OPTS
static const char dumpleases_longopts[] ALIGN1 =
"absolute\0" No_argument "a"
"remaining\0" No_argument "r"
"file\0" Required_argument "f"
+ "decimal\0" No_argument "d"
;
applet_long_options = dumpleases_longopts;
@@ -50,13 +56,16 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
init_unicode();
opt_complementary = "=0:a--r:r--a";
- opt = getopt32(argv, "arf:", &file);
+ opt = getopt32(argv, "arf:d", &file);
fd = xopen(file, O_RDONLY);
- printf("Mac Address IP Address Host Name Expires %s\n", (opt & OPT_a) ? "at" : "in");
- /* "00:00:00:00:00:00 255.255.255.255 ABCDEFGHIJKLMNOPQRS Wed Jun 30 21:49:08 1993" */
/* "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
+ /* "00:00:00:00:00:00 255.255.255.255 ABCDEFGHIJKLMNOPQRS Wed Jun 30 21:49:08 1993" */
+ printf("Mac %-14s" "IP %-13s" "Host %-15s" "Expires %s\n",
+ "Address", "Address", "Name",
+ (opt & OPT_a) ? "at" : "in"
+ );
xread(fd, &written_at, sizeof(written_at));
written_at = SWAP_BE64(written_at);
@@ -65,6 +74,9 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
written_at = curr; /* lease file from future! :) */
while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+ struct in_addr addr;
+ int64_t expires_abs;
+
const char *fmt = ":%02x" + 1;
for (i = 0; i < 6; i++) {
printf(fmt, lease.lease_mac[i]);
@@ -87,6 +99,13 @@ int dumpleases_main(int argc UNUSED_PARAM, char **argv)
puts("expired");
continue;
}
+ if (opt & OPT_d) {
+ /* -d: decimal time */
+ if (!(opt & OPT_a))
+ expires_abs -= curr;
+ printf("%llu\n", (unsigned long long) expires_abs);
+ continue;
+ }
if (!(opt & OPT_a)) { /* no -a */
unsigned d, h, m;
unsigned expires = expires_abs - curr;
diff --git a/networking/udhcp/files.c b/networking/udhcp/files.c
deleted file mode 100644
index 6840f3c..0000000
--- a/networking/udhcp/files.c
+++ b/dev/null
@@ -1,216 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * DHCP server config and lease file manipulation
- *
- * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-#include <netinet/ether.h>
-
-#include "common.h"
-#include "dhcpd.h"
-
-/* on these functions, make sure your datatype matches */
-static int FAST_FUNC read_str(const char *line, void *arg)
-{
- char **dest = arg;
-
- free(*dest);
- *dest = xstrdup(line);
- return 1;
-}
-
-static int FAST_FUNC read_u32(const char *line, void *arg)
-{
- *(uint32_t*)arg = bb_strtou32(line, NULL, 10);
- return errno == 0;
-}
-
-static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
-{
- char *line;
- char *mac_string;
- char *ip_string;
- struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
- uint32_t nip;
-
- /* Read mac */
- line = (char *) const_line;
- mac_string = strtok_r(line, " \t", &line);
- if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
- return 0;
-
- /* Read ip */
- ip_string = strtok_r(NULL, " \t", &line);
- if (!ip_string || !udhcp_str2nip(ip_string, &nip))
- return 0;
-
- add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
-
- log_static_leases(arg);
-
- return 1;
-}
-
-
-struct config_keyword {
- const char *keyword;
- int (*handler)(const char *line, void *var) FAST_FUNC;
- void *var;
- const char *def;
-};
-
-static const struct config_keyword keywords[] = {
- /* keyword handler variable address default */
- {"start" , udhcp_str2nip , &server_config.start_ip , "192.168.0.20"},
- {"end" , udhcp_str2nip , &server_config.end_ip , "192.168.0.254"},
- {"interface" , read_str , &server_config.interface , "eth0"},
- /* Avoid "max_leases value not sane" warning by setting default
- * to default_end_ip - default_start_ip + 1: */
- {"max_leases" , read_u32 , &server_config.max_leases , "235"},
- {"auto_time" , read_u32 , &server_config.auto_time , "7200"},
- {"decline_time" , read_u32 , &server_config.decline_time , "3600"},
- {"conflict_time", read_u32 , &server_config.conflict_time, "3600"},
- {"offer_time" , read_u32 , &server_config.offer_time , "60"},
- {"min_lease" , read_u32 , &server_config.min_lease_sec, "60"},
- {"lease_file" , read_str , &server_config.lease_file , LEASES_FILE},
- {"pidfile" , read_str , &server_config.pidfile , "/var/run/udhcpd.pid"},
- {"siaddr" , udhcp_str2nip , &server_config.siaddr_nip , "0.0.0.0"},
- /* keywords with no defaults must be last! */
- {"option" , udhcp_str2optset, &server_config.options , ""},
- {"opt" , udhcp_str2optset, &server_config.options , ""},
- {"notify_file" , read_str , &server_config.notify_file , NULL},
- {"sname" , read_str , &server_config.sname , NULL},
- {"boot_file" , read_str , &server_config.boot_file , NULL},
- {"static_lease" , read_staticlease, &server_config.static_leases, ""},
-};
-enum { KWS_WITH_DEFAULTS = ARRAY_SIZE(keywords) - 6 };
-
-void FAST_FUNC read_config(const char *file)
-{
- parser_t *parser;
- const struct config_keyword *k;
- unsigned i;
- char *token[2];
-
- for (i = 0; i < KWS_WITH_DEFAULTS; i++)
- keywords[i].handler(keywords[i].def, keywords[i].var);
-
- parser = config_open(file);
- while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
- for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
- if (strcasecmp(token[0], k->keyword) == 0) {
- if (!k->handler(token[1], k->var)) {
- bb_error_msg("can't parse line %u in %s",
- parser->lineno, file);
- /* reset back to the default value */
- k->handler(k->def, k->var);
- }
- break;
- }
- }
- }
- config_close(parser);
-
- server_config.start_ip = ntohl(server_config.start_ip);
- server_config.end_ip = ntohl(server_config.end_ip);
-}
-
-void FAST_FUNC write_leases(void)
-{
- int fd;
- unsigned i;
- leasetime_t curr;
- int64_t written_at;
-
- fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
- if (fd < 0)
- return;
-
- curr = written_at = time(NULL);
-
- written_at = SWAP_BE64(written_at);
- full_write(fd, &written_at, sizeof(written_at));
-
- for (i = 0; i < server_config.max_leases; i++) {
- leasetime_t tmp_time;
-
- if (g_leases[i].lease_nip == 0)
- continue;
-
- /* Screw with the time in the struct, for easier writing */
- tmp_time = g_leases[i].expires;
-
- g_leases[i].expires -= curr;
- if ((signed_leasetime_t) g_leases[i].expires < 0)
- g_leases[i].expires = 0;
- g_leases[i].expires = htonl(g_leases[i].expires);
-
- /* No error check. If the file gets truncated,
- * we lose some leases on restart. Oh well. */
- full_write(fd, &g_leases[i], sizeof(g_leases[i]));
-
- /* Then restore it when done */
- g_leases[i].expires = tmp_time;
- }
- close(fd);
-
- if (server_config.notify_file) {
- char *argv[3];
- argv[0] = server_config.notify_file;
- argv[1] = server_config.lease_file;
- argv[2] = NULL;
- spawn_and_wait(argv);
- }
-}
-
-void FAST_FUNC read_leases(const char *file)
-{
- struct dyn_lease lease;
- int64_t written_at, time_passed;
- int fd;
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
- unsigned i = 0;
-#endif
-
- fd = open_or_warn(file, O_RDONLY);
- if (fd < 0)
- return;
-
- if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
- goto ret;
- written_at = SWAP_BE64(written_at);
-
- time_passed = time(NULL) - written_at;
- /* Strange written_at, or lease file from old version of udhcpd
- * which had no "written_at" field? */
- if ((uint64_t)time_passed > 12 * 60 * 60)
- goto ret;
-
- while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
-//FIXME: what if it matches some static lease?
- uint32_t y = ntohl(lease.lease_nip);
- if (y >= server_config.start_ip && y <= server_config.end_ip) {
- signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
- if (expires <= 0)
- continue;
- /* NB: add_lease takes "relative time", IOW,
- * lease duration, not lease deadline. */
- if (add_lease(lease.lease_mac, lease.lease_nip,
- expires,
- lease.hostname, sizeof(lease.hostname)
- ) == 0
- ) {
- bb_error_msg("too many leases while loading %s", file);
- break;
- }
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
- i++;
-#endif
- }
- }
- log1("Read %d leases", i);
- ret:
- close(fd);
-}
diff --git a/networking/udhcp/leases.c b/networking/udhcp/leases.c
deleted file mode 100644
index c5b60b1..0000000
--- a/networking/udhcp/leases.c
+++ b/dev/null
@@ -1,203 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Russ Dill <Russ.Dill@asu.edu> July 2001
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-#include "common.h"
-#include "dhcpd.h"
-
-/* Find the oldest expired lease, NULL if there are no expired leases */
-static struct dyn_lease *oldest_expired_lease(void)
-{
- struct dyn_lease *oldest_lease = NULL;
- leasetime_t oldest_time = time(NULL);
- unsigned i;
-
- /* Unexpired leases have g_leases[i].expires >= current time
- * and therefore can't ever match */
- for (i = 0; i < server_config.max_leases; i++) {
- if (g_leases[i].expires < oldest_time) {
- oldest_time = g_leases[i].expires;
- oldest_lease = &g_leases[i];
- }
- }
- return oldest_lease;
-}
-
-/* Clear out all leases with matching nonzero chaddr OR yiaddr.
- * If chaddr == NULL, this is a conflict lease.
- */
-static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
-{
- unsigned i;
-
- for (i = 0; i < server_config.max_leases; i++) {
- if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
- || (yiaddr && g_leases[i].lease_nip == yiaddr)
- ) {
- memset(&g_leases[i], 0, sizeof(g_leases[i]));
- }
- }
-}
-
-/* Add a lease into the table, clearing out any old ones.
- * If chaddr == NULL, this is a conflict lease.
- */
-struct dyn_lease* FAST_FUNC add_lease(
- const uint8_t *chaddr, uint32_t yiaddr,
- leasetime_t leasetime,
- const char *hostname, int hostname_len)
-{
- struct dyn_lease *oldest;
-
- /* clean out any old ones */
- clear_leases(chaddr, yiaddr);
-
- oldest = oldest_expired_lease();
-
- if (oldest) {
- memset(oldest, 0, sizeof(*oldest));
- if (hostname) {
- char *p;
-
- hostname_len++; /* include NUL */
- if (hostname_len > sizeof(oldest->hostname))
- hostname_len = sizeof(oldest->hostname);
- p = safe_strncpy(oldest->hostname, hostname, hostname_len);
- /* sanitization (s/non-ASCII/^/g) */
- while (*p) {
- if (*p < ' ' || *p > 126)
- *p = '^';
- p++;
- }
- }
- if (chaddr)
- memcpy(oldest->lease_mac, chaddr, 6);
- oldest->lease_nip = yiaddr;
- oldest->expires = time(NULL) + leasetime;
- }
-
- return oldest;
-}
-
-/* True if a lease has expired */
-int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
-{
- return (lease->expires < (leasetime_t) time(NULL));
-}
-
-/* Find the first lease that matches MAC, NULL if no match */
-struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
-{
- unsigned i;
-
- for (i = 0; i < server_config.max_leases; i++)
- if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
- return &g_leases[i];
-
- return NULL;
-}
-
-/* Find the first lease that matches IP, NULL is no match */
-struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
-{
- unsigned i;
-
- for (i = 0; i < server_config.max_leases; i++)
- if (g_leases[i].lease_nip == nip)
- return &g_leases[i];
-
- return NULL;
-}
-
-/* Check if the IP is taken; if it is, add it to the lease table */
-static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac)
-{
- struct in_addr temp;
- int r;
-
- r = arpping(nip, safe_mac,
- server_config.server_nip,
- server_config.server_mac,
- server_config.interface);
- if (r)
- return r;
-
- temp.s_addr = nip;
- bb_info_msg("%s belongs to someone, reserving it for %u seconds",
- inet_ntoa(temp), (unsigned)server_config.conflict_time);
- add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
- return 0;
-}
-
-/* Find a new usable (we think) address */
-uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
-{
- uint32_t addr;
- struct dyn_lease *oldest_lease = NULL;
-
-#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
- uint32_t stop;
- unsigned i, hash;
-
- /* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
- * dispersal even with similarly-valued "strings".
- */
- hash = 0;
- for (i = 0; i < 6; i++)
- hash += safe_mac[i] + (hash << 6) + (hash << 16) - hash;
-
- /* pick a seed based on hwaddr then iterate until we find a free address. */
- addr = server_config.start_ip
- + (hash % (1 + server_config.end_ip - server_config.start_ip));
- stop = addr;
-#else
- addr = server_config.start_ip;
-#define stop (server_config.end_ip + 1)
-#endif
- do {
- uint32_t nip;
- struct dyn_lease *lease;
-
- /* ie, 192.168.55.0 */
- if ((addr & 0xff) == 0)
- goto next_addr;
- /* ie, 192.168.55.255 */
- if ((addr & 0xff) == 0xff)
- goto next_addr;
- nip = htonl(addr);
- /* skip our own address */
- if (nip == server_config.server_nip)
- goto next_addr;
- /* is this a static lease addr? */
- if (is_nip_reserved(server_config.static_leases, nip))
- goto next_addr;
-
- lease = find_lease_by_nip(nip);
- if (!lease) {
-//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
- if (nobody_responds_to_arp(nip, safe_mac))
- return nip;
- } else {
- if (!oldest_lease || lease->expires < oldest_lease->expires)
- oldest_lease = lease;
- }
-
- next_addr:
- addr++;
-#if ENABLE_FEATURE_UDHCPD_BASE_IP_ON_MAC
- if (addr > server_config.end_ip)
- addr = server_config.start_ip;
-#endif
- } while (addr != stop);
-
- if (oldest_lease
- && is_expired_lease(oldest_lease)
- && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac)
- ) {
- return oldest_lease->lease_nip;
- }
-
- return 0;
-}
diff --git a/networking/udhcp/packet.c b/networking/udhcp/packet.c
index 148f525..0a31f26 100644
--- a/networking/udhcp/packet.c
+++ b/networking/udhcp/packet.c
@@ -38,8 +38,8 @@ void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
if (dhcp_verbose < 2)
return;
- bb_info_msg(
- //" op %x"
+ bb_error_msg(
+ //"op %x"
//" htype %x"
" hlen %x"
//" hops %x"
@@ -73,7 +73,7 @@ void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
//, packet->options[]
);
*bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
- bb_info_msg(" chaddr %s", buf);
+ bb_error_msg("chaddr %s", buf);
}
#endif
@@ -85,17 +85,17 @@ int FAST_FUNC udhcp_recv_kernel_packet(struct dhcp_packet *packet, int fd)
memset(packet, 0, sizeof(*packet));
bytes = safe_read(fd, packet, sizeof(*packet));
if (bytes < 0) {
- log1("Packet read error, ignoring");
+ log1("packet read error, ignoring");
return bytes; /* returns -1 */
}
if (bytes < offsetof(struct dhcp_packet, options)
|| packet->cookie != htonl(DHCP_MAGIC)
) {
- bb_info_msg("Packet with bad magic, ignoring");
+ bb_error_msg("packet with bad magic, ignoring");
return -2;
}
- log1("Received a packet");
+ log1("received %s", "a packet");
udhcp_dump_packet(packet);
return bytes;
diff --git a/networking/udhcp/socket.c b/networking/udhcp/socket.c
index a421069..4fd79f4 100644
--- a/networking/udhcp/socket.c
+++ b/networking/udhcp/socket.c
@@ -56,7 +56,7 @@ int FAST_FUNC udhcp_read_interface(const char *interface, int *ifindex, uint32_t
close(fd);
return -1;
}
- log1("Adapter index %d", ifr->ifr_ifindex);
+ log1("adapter index %d", ifr->ifr_ifindex);
*ifindex = ifr->ifr_ifindex;
}
@@ -82,7 +82,7 @@ int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
struct sockaddr_in addr;
char *colon;
- log1("Opening listen socket on *:%d %s", port, inf);
+ log1("opening listen socket on *:%d %s", port, inf);
fd = xsocket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt_reuseaddr(fd);
diff --git a/networking/udhcp/static_leases.c b/networking/udhcp/static_leases.c
deleted file mode 100644
index f4a24ab..0000000
--- a/networking/udhcp/static_leases.c
+++ b/dev/null
@@ -1,77 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * Storing and retrieving data for static leases
- *
- * Wade Berrier <wberrier@myrealbox.com> September 2004
- *
- * Licensed under GPLv2, see file LICENSE in this source tree.
- */
-#include "common.h"
-#include "dhcpd.h"
-
-/* Takes the address of the pointer to the static_leases linked list,
- * address to a 6 byte mac address,
- * 4 byte IP address */
-void FAST_FUNC add_static_lease(struct static_lease **st_lease_pp,
- uint8_t *mac,
- uint32_t nip)
-{
- struct static_lease *st_lease;
-
- /* Find the tail of the list */
- while ((st_lease = *st_lease_pp) != NULL) {
- st_lease_pp = &st_lease->next;
- }
-
- /* Add new node */
- *st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
- memcpy(st_lease->mac, mac, 6);
- st_lease->nip = nip;
- /*st_lease->next = NULL;*/
-}
-
-/* Find static lease IP by mac */
-uint32_t FAST_FUNC get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
-{
- while (st_lease) {
- if (memcmp(st_lease->mac, mac, 6) == 0)
- return st_lease->nip;
- st_lease = st_lease->next;
- }
-
- return 0;
-}
-
-/* Check to see if an IP is reserved as a static IP */
-int FAST_FUNC is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
-{
- while (st_lease) {
- if (st_lease->nip == nip)
- return 1;
- st_lease = st_lease->next;
- }
-
- return 0;
-}
-
-#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
-/* Print out static leases just to check what's going on */
-/* Takes the address of the pointer to the static_leases linked list */
-void FAST_FUNC log_static_leases(struct static_lease **st_lease_pp)
-{
- struct static_lease *cur;
-
- if (dhcp_verbose < 2)
- return;
-
- cur = *st_lease_pp;
- while (cur) {
- bb_info_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
- cur->mac[0], cur->mac[1], cur->mac[2],
- cur->mac[3], cur->mac[4], cur->mac[5],
- cur->nip
- );
- cur = cur->next;
- }
-}
-#endif
diff --git a/networking/vconfig.c b/networking/vconfig.c
index 924b2f0..f302040 100644
--- a/networking/vconfig.c
+++ b/networking/vconfig.c
@@ -9,6 +9,17 @@
/* BB_AUDIT SUSv3 N/A */
+//config:config VCONFIG
+//config: bool "vconfig"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Creates, removes, and configures VLAN interfaces
+
+//applet:IF_VCONFIG(APPLET(vconfig, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_VCONFIG) += vconfig.o
+
//usage:#define vconfig_trivial_usage
//usage: "COMMAND [OPTIONS]"
//usage:#define vconfig_full_usage "\n\n"
diff --git a/networking/wget.c b/networking/wget.c
index e15f68d..cf0769e 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -9,49 +9,182 @@
* Kuhn's copyrights are licensed GPLv2-or-later. File as a whole remains GPLv2.
*/
+//config:config WGET
+//config: bool "wget"
+//config: default y
+//config: help
+//config: wget is a utility for non-interactive download of files from HTTP
+//config: and FTP servers.
+//config:
+//config:config FEATURE_WGET_STATUSBAR
+//config: bool "Enable a nifty process meter (+2k)"
+//config: default y
+//config: depends on WGET
+//config: help
+//config: Enable the transfer progress bar for wget transfers.
+//config:
+//config:config FEATURE_WGET_AUTHENTICATION
+//config: bool "Enable HTTP authentication"
+//config: default y
+//config: depends on WGET
+//config: help
+//config: Support authenticated HTTP transfers.
+//config:
+//config:config FEATURE_WGET_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on WGET && LONG_OPTS
+//config: help
+//config: Support long options for the wget applet.
+//config:
+//config:config FEATURE_WGET_TIMEOUT
+//config: bool "Enable timeout option -T SEC"
+//config: default y
+//config: depends on WGET
+//config: help
+//config: Supports network read and connect timeouts for wget,
+//config: so that wget will give up and timeout, through the -T
+//config: command line option.
+//config:
+//config: Currently only connect and network data read timeout are
+//config: supported (i.e., timeout is not applied to the DNS query). When
+//config: FEATURE_WGET_LONG_OPTIONS is also enabled, the --timeout option
+//config: will work in addition to -T.
+//config:
+//config:config FEATURE_WGET_OPENSSL
+//config: bool "Try to connect to HTTPS using openssl"
+//config: default y
+//config: depends on WGET
+//config: help
+//config: Choose how wget establishes SSL connection for https:// URLs.
+//config:
+//config: Busybox itself contains no SSL code. wget will spawn
+//config: a helper program to talk over HTTPS.
+//config:
+//config: OpenSSL has a simple SSL client for debug purposes.
+//config: If you select "openssl" helper, wget will effectively run:
+//config: "openssl s_client -quiet -connect hostname:443
+//config: -servername hostname 2>/dev/null" and pipe its data
+//config: through it. -servername is not used if hostname is numeric.
+//config: Note inconvenient API: host resolution is done twice,
+//config: and there is no guarantee openssl's idea of IPv6 address
+//config: format is the same as ours.
+//config: Another problem is that s_client prints debug information
+//config: to stderr, and it needs to be suppressed. This means
+//config: all error messages get suppressed too.
+//config: openssl is also a big binary, often dynamically linked
+//config: against ~15 libraries.
+//config:
+//config:config FEATURE_WGET_SSL_HELPER
+//config: bool "Try to connect to HTTPS using ssl_helper"
+//config: default y
+//config: depends on WGET
+//config: help
+//config: Choose how wget establishes SSL connection for https:// URLs.
+//config:
+//config: Busybox itself contains no SSL code. wget will spawn
+//config: a helper program to talk over HTTPS.
+//config:
+//config: ssl_helper is a tool which can be built statically
+//config: from busybox sources against a small embedded SSL library.
+//config: Please see networking/ssl_helper/README.
+//config: It does not require double host resolution and emits
+//config: error messages to stderr.
+//config:
+//config: Precompiled static binary may be available at
+//config: http://busybox.net/downloads/binaries/
+
+//applet:IF_WGET(APPLET(wget, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WGET) += wget.o
+
//usage:#define wget_trivial_usage
//usage: IF_FEATURE_WGET_LONG_OPTIONS(
-//usage: "[-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]\n"
+//usage: "[-c|--continue] [--spider] [-q|--quiet] [-O|--output-document FILE]\n"
//usage: " [--header 'header: value'] [-Y|--proxy on/off] [-P DIR]\n"
/* Since we ignore these opts, we don't show them in --help */
-/* //usage: " [--no-check-certificate] [--no-cache]" */
+/* //usage: " [--no-check-certificate] [--no-cache] [--passive-ftp] [-t TRIES]" */
+/* //usage: " [-nv] [-nc] [-nH] [-np]" */
//usage: " [-U|--user-agent AGENT]" IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
//usage: )
//usage: IF_NOT_FEATURE_WGET_LONG_OPTIONS(
-//usage: "[-csq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
+//usage: "[-cq] [-O FILE] [-Y on/off] [-P DIR] [-U AGENT]"
//usage: IF_FEATURE_WGET_TIMEOUT(" [-T SEC]") " URL..."
//usage: )
//usage:#define wget_full_usage "\n\n"
//usage: "Retrieve files via HTTP or FTP\n"
-//usage: "\n -s Spider mode - only check file existence"
-//usage: "\n -c Continue retrieval of aborted transfer"
-//usage: "\n -q Quiet"
-//usage: "\n -P DIR Save to DIR (default .)"
+//usage: IF_FEATURE_WGET_LONG_OPTIONS(
+//usage: "\n --spider Spider mode - only check file existence"
+//usage: )
+//usage: "\n -c Continue retrieval of aborted transfer"
+//usage: "\n -q Quiet"
+//usage: "\n -P DIR Save to DIR (default .)"
//usage: IF_FEATURE_WGET_TIMEOUT(
-//usage: "\n -T SEC Network read timeout is SEC seconds"
+//usage: "\n -T SEC Network read timeout is SEC seconds"
//usage: )
-//usage: "\n -O FILE Save to FILE ('-' for stdout)"
-//usage: "\n -U STR Use STR for User-Agent header"
-//usage: "\n -Y Use proxy ('on' or 'off')"
+//usage: "\n -O FILE Save to FILE ('-' for stdout)"
+//usage: "\n -U STR Use STR for User-Agent header"
+//usage: "\n -Y on/off Use proxy"
#include "libbb.h"
#if 0
# define log_io(...) bb_error_msg(__VA_ARGS__)
+# define SENDFMT(fp, fmt, ...) \
+ do { \
+ log_io("> " fmt, ##__VA_ARGS__); \
+ fprintf(fp, fmt, ##__VA_ARGS__); \
+ } while (0);
#else
# define log_io(...) ((void)0)
+# define SENDFMT(fp, fmt, ...) fprintf(fp, fmt, ##__VA_ARGS__)
#endif
struct host_info {
char *allocated;
const char *path;
- const char *user;
+ char *user;
+ const char *protocol;
char *host;
int port;
- smallint is_ftp;
};
+static const char P_FTP[] ALIGN1 = "ftp";
+static const char P_HTTP[] ALIGN1 = "http";
+#if ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_SSL_HELPER
+static const char P_HTTPS[] ALIGN1 = "https";
+#endif
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+/* User-specified headers prevent using our corresponding built-in headers. */
+enum {
+ HDR_HOST = (1<<0),
+ HDR_USER_AGENT = (1<<1),
+ HDR_RANGE = (1<<2),
+ HDR_AUTH = (1<<3) * ENABLE_FEATURE_WGET_AUTHENTICATION,
+ HDR_PROXY_AUTH = (1<<4) * ENABLE_FEATURE_WGET_AUTHENTICATION,
+};
+static const char wget_user_headers[] ALIGN1 =
+ "Host:\0"
+ "User-Agent:\0"
+ "Range:\0"
+# if ENABLE_FEATURE_WGET_AUTHENTICATION
+ "Authorization:\0"
+ "Proxy-Authorization:\0"
+# endif
+ ;
+# define USR_HEADER_HOST (G.user_headers & HDR_HOST)
+# define USR_HEADER_USER_AGENT (G.user_headers & HDR_USER_AGENT)
+# define USR_HEADER_RANGE (G.user_headers & HDR_RANGE)
+# define USR_HEADER_AUTH (G.user_headers & HDR_AUTH)
+# define USR_HEADER_PROXY_AUTH (G.user_headers & HDR_PROXY_AUTH)
+#else /* No long options, no user-headers :( */
+# define USR_HEADER_HOST 0
+# define USR_HEADER_USER_AGENT 0
+# define USR_HEADER_RANGE 0
+# define USR_HEADER_AUTH 0
+# define USR_HEADER_PROXY_AUTH 0
+#endif
/* Globals */
struct globals {
@@ -66,13 +199,14 @@ struct globals {
#if ENABLE_FEATURE_WGET_LONG_OPTIONS
char *post_data;
char *extra_headers;
+ unsigned char user_headers; /* Headers mentioned by the user */
#endif
char *fname_out; /* where to direct output (-O) */
const char *proxy_flag; /* Use proxies if env vars are set */
const char *user_agent; /* "User-Agent" header field */
#if ENABLE_FEATURE_WGET_TIMEOUT
unsigned timeout_seconds;
- bool connecting;
+ bool die_if_timed_out;
#endif
int output_fd;
int o_flags;
@@ -97,17 +231,17 @@ struct globals {
/* Must match option string! */
enum {
WGET_OPT_CONTINUE = (1 << 0),
- WGET_OPT_SPIDER = (1 << 1),
- WGET_OPT_QUIET = (1 << 2),
- WGET_OPT_OUTNAME = (1 << 3),
- WGET_OPT_PREFIX = (1 << 4),
- WGET_OPT_PROXY = (1 << 5),
- WGET_OPT_USER_AGENT = (1 << 6),
- WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 7),
- WGET_OPT_RETRIES = (1 << 8),
- WGET_OPT_PASSIVE = (1 << 9),
- WGET_OPT_HEADER = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
- WGET_OPT_POST_DATA = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+ WGET_OPT_QUIET = (1 << 1),
+ WGET_OPT_OUTNAME = (1 << 2),
+ WGET_OPT_PREFIX = (1 << 3),
+ WGET_OPT_PROXY = (1 << 4),
+ WGET_OPT_USER_AGENT = (1 << 5),
+ WGET_OPT_NETWORK_READ_TIMEOUT = (1 << 6),
+ WGET_OPT_RETRIES = (1 << 7),
+ WGET_OPT_nsomething = (1 << 8),
+ WGET_OPT_HEADER = (1 << 9) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+ WGET_OPT_POST_DATA = (1 << 10) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
+ WGET_OPT_SPIDER = (1 << 11) * ENABLE_FEATURE_WGET_LONG_OPTIONS,
};
enum {
@@ -202,9 +336,44 @@ static char* sanitize_string(char *s)
static void alarm_handler(int sig UNUSED_PARAM)
{
/* This is theoretically unsafe (uses stdio and malloc in signal handler) */
- if (G.connecting)
+ if (G.die_if_timed_out)
bb_error_msg_and_die("download timed out");
}
+static void set_alarm(void)
+{
+ if (G.timeout_seconds) {
+ alarm(G.timeout_seconds);
+ G.die_if_timed_out = 1;
+ }
+}
+# define clear_alarm() ((void)(G.die_if_timed_out = 0))
+#else
+# define set_alarm() ((void)0)
+# define clear_alarm() ((void)0)
+#endif
+
+#if ENABLE_FEATURE_WGET_OPENSSL
+/*
+ * is_ip_address() attempts to verify whether or not a string
+ * contains an IPv4 or IPv6 address (vs. an FQDN). The result
+ * of inet_pton() can be used to determine this.
+ *
+ * TODO add proper error checking when inet_pton() returns -1
+ * (some form of system error has occurred, and errno is set)
+ */
+static int is_ip_address(const char *string)
+{
+ struct sockaddr_in sa;
+
+ int result = inet_pton(AF_INET, string, &(sa.sin_addr));
+# if ENABLE_FEATURE_IPV6
+ if (result == 0) {
+ struct sockaddr_in6 sa6;
+ result = inet_pton(AF_INET6, string, &(sa6.sin6_addr));
+ }
+# endif
+ return (result == 1);
+}
#endif
static FILE *open_socket(len_and_sockaddr *lsa)
@@ -212,28 +381,29 @@ static FILE *open_socket(len_and_sockaddr *lsa)
int fd;
FILE *fp;
- IF_FEATURE_WGET_TIMEOUT(alarm(G.timeout_seconds); G.connecting = 1;)
+ set_alarm();
fd = xconnect_stream(lsa);
- IF_FEATURE_WGET_TIMEOUT(G.connecting = 0;)
+ clear_alarm();
/* glibc 2.4 seems to try seeking on it - ??! */
/* hopefully it understands what ESPIPE means... */
fp = fdopen(fd, "r+");
- if (fp == NULL)
+ if (!fp)
bb_perror_msg_and_die("%s", bb_msg_memory_exhausted);
return fp;
}
/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\n' */
-/* FIXME: does not respect FEATURE_WGET_TIMEOUT and -T N: */
static char fgets_and_trim(FILE *fp)
{
char c;
char *buf_ptr;
+ set_alarm();
if (fgets(G.wget_buf, sizeof(G.wget_buf) - 1, fp) == NULL)
bb_perror_msg_and_die("error getting response");
+ clear_alarm();
buf_ptr = strchrnul(G.wget_buf, '\n');
c = *buf_ptr;
@@ -274,23 +444,33 @@ static void parse_url(const char *src_url, struct host_info *h)
free(h->allocated);
h->allocated = url = xstrdup(src_url);
- if (strncmp(url, "ftp://", 6) == 0) {
- h->port = bb_lookup_port("ftp", "tcp", 21);
- h->host = url + 6;
- h->is_ftp = 1;
- } else
- if (strncmp(url, "http://", 7) == 0) {
- h->host = url + 7;
+ h->protocol = P_FTP;
+ p = strstr(url, "://");
+ if (p) {
+ *p = '\0';
+ h->host = p + 3;
+ if (strcmp(url, P_FTP) == 0) {
+ h->port = bb_lookup_port(P_FTP, "tcp", 21);
+ } else
+#if ENABLE_FEATURE_WGET_OPENSSL || ENABLE_FEATURE_WGET_SSL_HELPER
+ if (strcmp(url, P_HTTPS) == 0) {
+ h->port = bb_lookup_port(P_HTTPS, "tcp", 443);
+ h->protocol = P_HTTPS;
+ } else
+#endif
+ if (strcmp(url, P_HTTP) == 0) {
http:
- h->port = bb_lookup_port("http", "tcp", 80);
- h->is_ftp = 0;
- } else
- if (!strstr(url, "//")) {
+ h->port = bb_lookup_port(P_HTTP, "tcp", 80);
+ h->protocol = P_HTTP;
+ } else {
+ *p = ':';
+ bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
+ }
+ } else {
// GNU wget is user-friendly and falls back to http://
h->host = url;
goto http;
- } else
- bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
+ }
// FYI:
// "Real" wget 'http://busybox.net?var=a/b' sends this request:
@@ -322,9 +502,6 @@ static void parse_url(const char *src_url, struct host_info *h)
h->path = sp;
}
- // We used to set h->user to NULL here, but this interferes
- // with handling of code 302 ("object was moved")
-
sp = strrchr(h->host, '@');
if (sp != NULL) {
// URL-decode "user:password" string before base64-encoding:
@@ -333,11 +510,13 @@ static void parse_url(const char *src_url, struct host_info *h)
// which decodes to "test:my pass".
// Standard wget and curl do this too.
*sp = '\0';
- h->user = percent_decode_in_place(h->host, /*strict:*/ 0);
+ free(h->user);
+ h->user = xstrdup(percent_decode_in_place(h->host, /*strict:*/ 0));
h->host = sp + 1;
}
-
- sp = h->host;
+ /* else: h->user remains NULL, or as set by original request
+ * before redirect (if we are here after a redirect).
+ */
}
static char *gethdr(FILE *fp)
@@ -473,6 +652,118 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
return sfp;
}
+#if ENABLE_FEATURE_WGET_OPENSSL
+static int spawn_https_helper_openssl(const char *host, unsigned port)
+{
+ char *allocated = NULL;
+ char *servername;
+ int sp[2];
+ int pid;
+ IF_FEATURE_WGET_SSL_HELPER(volatile int child_failed = 0;)
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
+ /* Kernel can have AF_UNIX support disabled */
+ bb_perror_msg_and_die("socketpair");
+
+ if (!strchr(host, ':'))
+ host = allocated = xasprintf("%s:%u", host, port);
+ servername = xstrdup(host);
+ strrchr(servername, ':')[0] = '\0';
+
+ fflush_all();
+ pid = xvfork();
+ if (pid == 0) {
+ /* Child */
+ char *argv[8];
+
+ close(sp[0]);
+ xmove_fd(sp[1], 0);
+ xdup2(0, 1);
+ /*
+ * openssl s_client -quiet -connect www.kernel.org:443 2>/dev/null
+ * It prints some debug stuff on stderr, don't know how to suppress it.
+ * Work around by dev-nulling stderr. We lose all error messages :(
+ */
+ xmove_fd(2, 3);
+ xopen("/dev/null", O_RDWR);
+ memset(&argv, 0, sizeof(argv));
+ argv[0] = (char*)"openssl";
+ argv[1] = (char*)"s_client";
+ argv[2] = (char*)"-quiet";
+ argv[3] = (char*)"-connect";
+ argv[4] = (char*)host;
+ /*
+ * Per RFC 6066 Section 3, the only permitted values in the
+ * TLS server_name (SNI) field are FQDNs (DNS hostnames).
+ * IPv4 and IPv6 addresses, port numbers are not allowed.
+ */
+ if (!is_ip_address(servername)) {
+ argv[5] = (char*)"-servername";
+ argv[6] = (char*)servername;
+ }
+
+ BB_EXECVP(argv[0], argv);
+ xmove_fd(3, 2);
+# if ENABLE_FEATURE_WGET_SSL_HELPER
+ child_failed = 1;
+ xfunc_die();
+# else
+ bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+# endif
+ /* notreached */
+ }
+
+ /* Parent */
+ free(servername);
+ free(allocated);
+ close(sp[1]);
+# if ENABLE_FEATURE_WGET_SSL_HELPER
+ if (child_failed) {
+ close(sp[0]);
+ return -1;
+ }
+# endif
+ return sp[0];
+}
+#endif
+
+/* See networking/ssl_helper/README how to build one */
+#if ENABLE_FEATURE_WGET_SSL_HELPER
+static void spawn_https_helper_small(int network_fd)
+{
+ int sp[2];
+ int pid;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
+ /* Kernel can have AF_UNIX support disabled */
+ bb_perror_msg_and_die("socketpair");
+
+ pid = BB_MMU ? xfork() : xvfork();
+ if (pid == 0) {
+ /* Child */
+ char *argv[3];
+
+ close(sp[0]);
+ xmove_fd(sp[1], 0);
+ xdup2(0, 1);
+ xmove_fd(network_fd, 3);
+ /*
+ * A simple ssl/tls helper
+ */
+ argv[0] = (char*)"ssl_helper";
+ argv[1] = (char*)"-d3";
+ argv[2] = NULL;
+ BB_EXECVP(argv[0], argv);
+ bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+ /* notreached */
+ }
+
+ /* Parent */
+ close(sp[1]);
+ xmove_fd(sp[0], network_fd);
+}
+#endif
+
static void NOINLINE retrieve_file_data(FILE *dfp)
{
#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
@@ -541,7 +832,7 @@ static void NOINLINE retrieve_file_data(FILE *dfp)
#if ENABLE_FEATURE_WGET_TIMEOUT
second_cnt = G.timeout_seconds;
#endif
- continue;
+ goto bump;
}
/* n <= 0.
@@ -574,11 +865,12 @@ static void NOINLINE retrieve_file_data(FILE *dfp)
* to try reading anyway.
*/
}
+#endif
+ bump:
/* Need to do it _every_ second for "stalled" indicator
* to be shown properly.
*/
progress_meter(PROGRESS_BUMP);
-#endif
} /* while (reading data) */
#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
@@ -645,7 +937,8 @@ static void download_one_url(const char *url)
/* Use the proxy if necessary */
use_proxy = (strcmp(G.proxy_flag, "off") != 0);
if (use_proxy) {
- proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+ proxy = getenv(target.protocol == P_FTP ? "ftp_proxy" : "http_proxy");
+//FIXME: what if protocol is https? Ok to use http_proxy?
use_proxy = (proxy && proxy[0]);
if (use_proxy)
parse_url(proxy, &server);
@@ -705,57 +998,88 @@ static void download_one_url(const char *url)
/*G.content_len = 0; - redundant, got_clen = 0 is enough */
G.got_clen = 0;
G.chunked = 0;
- if (use_proxy || !target.is_ftp) {
+ if (use_proxy || target.protocol != P_FTP) {
/*
* HTTP session
*/
char *str;
int status;
-
- /* Open socket to http server */
+ /* Open socket to http(s) server */
+#if ENABLE_FEATURE_WGET_OPENSSL
+ /* openssl (and maybe ssl_helper) support is configured */
+ if (target.protocol == P_HTTPS) {
+ /* openssl-based helper
+ * Inconvenient API since we can't give it an open fd
+ */
+ int fd = spawn_https_helper_openssl(server.host, server.port);
+# if ENABLE_FEATURE_WGET_SSL_HELPER
+ if (fd < 0) { /* no openssl? try ssl_helper */
+ sfp = open_socket(lsa);
+ spawn_https_helper_small(fileno(sfp));
+ goto socket_opened;
+ }
+# else
+ /* We don't check for exec("openssl") failure in this case */
+# endif
+ sfp = fdopen(fd, "r+");
+ if (!sfp)
+ bb_perror_msg_and_die("%s", bb_msg_memory_exhausted);
+ goto socket_opened;
+ }
sfp = open_socket(lsa);
-
+ socket_opened:
+#elif ENABLE_FEATURE_WGET_SSL_HELPER
+ /* Only ssl_helper support is configured */
+ sfp = open_socket(lsa);
+ if (target.protocol == P_HTTPS)
+ spawn_https_helper_small(fileno(sfp));
+#else
+ /* ssl (https) support is not configured */
+ sfp = open_socket(lsa);
+#endif
/* Send HTTP request */
if (use_proxy) {
- fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
- target.is_ftp ? "f" : "ht", target.host,
+ SENDFMT(sfp, "GET %s://%s/%s HTTP/1.1\r\n",
+ target.protocol, target.host,
target.path);
} else {
- if (option_mask32 & WGET_OPT_POST_DATA)
- fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
- else
- fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
+ SENDFMT(sfp, "%s /%s HTTP/1.1\r\n",
+ (option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET",
+ target.path);
}
-
- fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",
- target.host, G.user_agent);
+ if (!USR_HEADER_HOST)
+ SENDFMT(sfp, "Host: %s\r\n", target.host);
+ if (!USR_HEADER_USER_AGENT)
+ SENDFMT(sfp, "User-Agent: %s\r\n", G.user_agent);
/* Ask server to close the connection as soon as we are done
* (IOW: we do not intend to send more requests)
*/
- fprintf(sfp, "Connection: close\r\n");
+ SENDFMT(sfp, "Connection: close\r\n");
#if ENABLE_FEATURE_WGET_AUTHENTICATION
- if (target.user) {
- fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+ if (target.user && !USR_HEADER_AUTH) {
+ SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
base64enc(target.user));
}
- if (use_proxy && server.user) {
- fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+ if (use_proxy && server.user && !USR_HEADER_PROXY_AUTH) {
+ SENDFMT(sfp, "Proxy-Authorization: Basic %s\r\n",
base64enc(server.user));
}
#endif
- if (G.beg_range != 0)
- fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
+ if (G.beg_range != 0 && !USR_HEADER_RANGE)
+ SENDFMT(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
#if ENABLE_FEATURE_WGET_LONG_OPTIONS
- if (G.extra_headers)
+ if (G.extra_headers) {
+ log_io(G.extra_headers);
fputs(G.extra_headers, sfp);
+ }
if (option_mask32 & WGET_OPT_POST_DATA) {
- fprintf(sfp,
+ SENDFMT(sfp,
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %u\r\n"
"\r\n"
@@ -765,10 +1089,16 @@ static void download_one_url(const char *url)
} else
#endif
{
- fprintf(sfp, "\r\n");
+ SENDFMT(sfp, "\r\n");
}
fflush(sfp);
+ /* If we use SSL helper, keeping our end of the socket open for writing
+ * makes our end (i.e. the same fd!) readable (EAGAIN instead of EOF)
+ * even after child closes its copy of the fd.
+ * This helps:
+ */
+ shutdown(fileno(sfp), SHUT_WR);
/*
* Retrieve HTTP response line and check for "200" status code.
@@ -788,7 +1118,21 @@ static void download_one_url(const char *url)
while (gethdr(sfp) != NULL)
/* eat all remaining headers */;
goto read_response;
+
+ /* Success responses */
case 200:
+ /* fall through */
+ case 201: /* 201 Created */
+/* "The request has been fulfilled and resulted in a new resource being created" */
+ /* Standard wget is reported to treat this as success */
+ /* fall through */
+ case 202: /* 202 Accepted */
+/* "The request has been accepted for processing, but the processing has not been completed" */
+ /* Treat as success: fall through */
+ case 203: /* 203 Non-Authoritative Information */
+/* "Use of this response code is not required and is only appropriate when the response would otherwise be 200 (OK)" */
+ /* fall through */
+ case 204: /* 204 No Content */
/*
Response 204 doesn't say "null file", it says "metadata
has changed but data didn't":
@@ -813,7 +1157,6 @@ is always terminated by the first empty line after the header fields."
However, in real world it was observed that some web servers
(e.g. Boa/0.94.14rc21) simply use code 204 when file size is zero.
*/
- case 204:
if (G.beg_range != 0) {
/* "Range:..." was not honored by the server.
* Restart download from the beginning.
@@ -821,11 +1164,14 @@ However, in real world it was observed that some web servers
reset_beg_range_to_zero();
}
break;
+ /* 205 Reset Content ?? what to do on this ?? */
+
case 300: /* redirection */
case 301:
case 302:
case 303:
break;
+
case 206: /* Partial Content */
if (G.beg_range != 0)
/* "Range:..." worked. Good. */
@@ -880,6 +1226,7 @@ However, in real world it was observed that some web servers
} else {
parse_url(str, &target);
if (!use_proxy) {
+ /* server.user remains untouched */
free(server.allocated);
server.allocated = NULL;
server.host = target.host;
@@ -898,7 +1245,6 @@ However, in real world it was observed that some web servers
/* For HTTP, data is pumped over the same connection */
dfp = sfp;
-
} else {
/*
* FTP session
@@ -929,6 +1275,8 @@ However, in real world it was observed that some web servers
free(server.allocated);
free(target.allocated);
+ free(server.user);
+ free(target.user);
free(fname_out_alloc);
free(redirected_path);
}
@@ -940,26 +1288,28 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
static const char wget_longopts[] ALIGN1 =
/* name, has_arg, val */
"continue\0" No_argument "c"
-//FIXME: -s isn't --spider, it's --save-headers!
- "spider\0" No_argument "s"
"quiet\0" No_argument "q"
"output-document\0" Required_argument "O"
"directory-prefix\0" Required_argument "P"
"proxy\0" Required_argument "Y"
"user-agent\0" Required_argument "U"
-#if ENABLE_FEATURE_WGET_TIMEOUT
- "timeout\0" Required_argument "T"
-#endif
+IF_FEATURE_WGET_TIMEOUT(
+ "timeout\0" Required_argument "T")
/* Ignored: */
- // "tries\0" Required_argument "t"
+IF_DESKTOP( "tries\0" Required_argument "t")
+ "header\0" Required_argument "\xff"
+ "post-data\0" Required_argument "\xfe"
+ "spider\0" No_argument "\xfd"
/* Ignored (we always use PASV): */
- "passive-ftp\0" No_argument "\xff"
- "header\0" Required_argument "\xfe"
- "post-data\0" Required_argument "\xfd"
+IF_DESKTOP( "passive-ftp\0" No_argument "\xf0")
/* Ignored (we don't do ssl) */
- "no-check-certificate\0" No_argument "\xfc"
+IF_DESKTOP( "no-check-certificate\0" No_argument "\xf0")
/* Ignored (we don't support caching) */
- "no-cache\0" No_argument "\xfb"
+IF_DESKTOP( "no-cache\0" No_argument "\xf0")
+IF_DESKTOP( "no-verbose\0" No_argument "\xf0")
+IF_DESKTOP( "no-clobber\0" No_argument "\xf0")
+IF_DESKTOP( "no-host-directories\0" No_argument "\xf0")
+IF_DESKTOP( "no-parent\0" No_argument "\xf0")
;
#endif
@@ -979,29 +1329,65 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_WGET_LONG_OPTIONS
applet_long_options = wget_longopts;
#endif
- opt_complementary = "-1" IF_FEATURE_WGET_TIMEOUT(":T+") IF_FEATURE_WGET_LONG_OPTIONS(":\xfe::");
- getopt32(argv, "csqO:P:Y:U:T:" /*ignored:*/ "t:",
- &G.fname_out, &G.dir_prefix,
+ opt_complementary = "-1" /* at least one URL */
+ IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */
+ getopt32(argv, "cqO:P:Y:U:T:+"
+ /*ignored:*/ "t:"
+ /*ignored:*/ "n::"
+ /* wget has exactly four -n<letter> opts, all of which we can ignore:
+ * -nv --no-verbose: be moderately quiet (-q is full quiet)
+ * -nc --no-clobber: abort if exists, neither download to FILE.n nor overwrite FILE
+ * -nH --no-host-directories: wget -r http://host/ won't create host/
+ * -np --no-parent
+ * "n::" above says that we accept -n[ARG].
+ * Specifying "n:" would be a bug: "-n ARG" would eat ARG!
+ */
+ , &G.fname_out, &G.dir_prefix,
&G.proxy_flag, &G.user_agent,
IF_FEATURE_WGET_TIMEOUT(&G.timeout_seconds) IF_NOT_FEATURE_WGET_TIMEOUT(NULL),
- NULL /* -t RETRIES */
+ NULL, /* -t RETRIES */
+ NULL /* -n[ARG] */
IF_FEATURE_WGET_LONG_OPTIONS(, &headers_llist)
IF_FEATURE_WGET_LONG_OPTIONS(, &G.post_data)
);
+#if 0 /* option bits debug */
+ if (option_mask32 & WGET_OPT_RETRIES) bb_error_msg("-t NUM");
+ if (option_mask32 & WGET_OPT_nsomething) bb_error_msg("-nsomething");
+ if (option_mask32 & WGET_OPT_HEADER) bb_error_msg("--header");
+ if (option_mask32 & WGET_OPT_POST_DATA) bb_error_msg("--post-data");
+ if (option_mask32 & WGET_OPT_SPIDER) bb_error_msg("--spider");
+ exit(0);
+#endif
argv += optind;
#if ENABLE_FEATURE_WGET_LONG_OPTIONS
if (headers_llist) {
- int size = 1;
- char *cp;
+ int size = 0;
+ char *hdr;
llist_t *ll = headers_llist;
while (ll) {
size += strlen(ll->data) + 2;
ll = ll->link;
}
- G.extra_headers = cp = xmalloc(size);
+ G.extra_headers = hdr = xmalloc(size + 1);
while (headers_llist) {
- cp += sprintf(cp, "%s\r\n", (char*)llist_pop(&headers_llist));
+ int bit;
+ const char *words;
+
+ size = sprintf(hdr, "%s\r\n",
+ (char*)llist_pop(&headers_llist));
+ /* a bit like index_in_substrings but don't match full key */
+ bit = 1;
+ words = wget_user_headers;
+ while (*words) {
+ if (strstr(hdr, words) == hdr) {
+ G.user_headers |= bit;
+ break;
+ }
+ bit <<= 1;
+ words += strlen(words) + 1;
+ }
+ hdr += size;
}
}
#endif
diff --git a/networking/whois.c b/networking/whois.c
index bf33033..c9dfcf5 100644
--- a/networking/whois.c
+++ b/networking/whois.c
@@ -21,43 +21,158 @@
//kbuild:lib-$(CONFIG_WHOIS) += whois.o
//usage:#define whois_trivial_usage
-//usage: "[-h SERVER] [-p PORT] NAME..."
+//usage: "[-i] [-h SERVER] [-p PORT] NAME..."
//usage:#define whois_full_usage "\n\n"
//usage: "Query WHOIS info about NAME\n"
+//usage: "\n -i Show redirect results too"
//usage: "\n -h,-p Server to query"
#include "libbb.h"
-static void pipe_out(int fd)
+enum {
+ OPT_i = (1 << 0),
+};
+
+static char *query(const char *host, int port, const char *domain)
{
+ int fd;
FILE *fp;
- char buf[1024];
+ bool success;
+ char *redir = NULL;
+ const char *pfx = "";
+ char linebuf[1024];
+ char *buf = NULL;
+ unsigned bufpos = 0;
+ again:
+ printf("[Querying %s:%d '%s%s']\n", host, port, pfx, domain);
+ fd = create_and_connect_stream_or_die(host, port);
+ success = 0;
+ fdprintf(fd, "%s%s\r\n", pfx, domain);
fp = xfdopen_for_read(fd);
- while (fgets(buf, sizeof(buf), fp)) {
- char *p = strpbrk(buf, "\r\n");
- if (p)
- *p = '\0';
- puts(buf);
- }
+ while (fgets(linebuf, sizeof(linebuf), fp)) {
+ unsigned len = strcspn(linebuf, "\r\n");
+ linebuf[len++] = '\n';
+
+ buf = xrealloc(buf, bufpos + len + 1);
+ memcpy(buf + bufpos, linebuf, len);
+ bufpos += len;
+ buf[bufpos] = '\0';
+
+ if (!redir || !success) {
+ trim(linebuf);
+ str_tolower(linebuf);
+ if (!success) {
+ success = is_prefixed_with(linebuf, "domain:")
+ || is_prefixed_with(linebuf, "domain name:");
+ }
+ else if (!redir) {
+ char *p = is_prefixed_with(linebuf, "whois server:");
+ if (!p)
+ p = is_prefixed_with(linebuf, "whois:");
+ if (p)
+ redir = xstrdup(skip_whitespace(p));
+ }
+ }
+ }
fclose(fp); /* closes fd too */
+ if (!success && !pfx[0]) {
+ /*
+ * Looking at /etc/jwhois.conf, some whois servers use
+ * "domain = DOMAIN", "DOMAIN ID <DOMAIN>"
+ * and "domain=DOMAIN_WITHOUT_LAST_COMPONENT"
+ * formats, but those are rare.
+ * (There are a few even more contrived ones.)
+ * We are trying only "domain DOMAIN", the typical one.
+ */
+ pfx = "domain ";
+ bufpos = 0;
+ goto again;
+ }
+
+ /* Success */
+ if (redir && strcmp(redir, host) == 0) {
+ /* Redirect to self does not count */
+ free(redir);
+ redir = NULL;
+ }
+ if (!redir || (option_mask32 & OPT_i)) {
+ /* Output saved text */
+ printf("[%s]\n%s", host, buf ? buf : "");
+ }
+ free(buf);
+ return redir;
+}
+
+static void recursive_query(const char *host, int port, const char *domain)
+{
+ char *free_me = NULL;
+ char *redir;
+ again:
+ redir = query(host, port, domain);
+ free(free_me);
+ if (redir) {
+ printf("[Redirected to %s]\n", redir);
+ host = free_me = redir;
+ port = 43;
+ goto again;
+ }
}
+/* One of "big" whois implementations has these options:
+ *
+ * $ whois --help
+ * jwhois version 4.0, Copyright (C) 1999-2007 Free Software Foundation, Inc.
+ * -v, --verbose verbose debug output
+ * -c FILE, --config=FILE use FILE as configuration file
+ * -h HOST, --host=HOST explicitly query HOST
+ * -n, --no-redirect disable content redirection
+ * -s, --no-whoisservers disable whois-servers.net service support
+ * -a, --raw disable reformatting of the query
+ * -i, --display-redirections display all redirects instead of hiding them
+ * -p PORT, --port=PORT use port number PORT (in conjunction with HOST)
+ * -r, --rwhois force an rwhois query to be made
+ * --rwhois-display=DISPLAY sets the display option in rwhois queries
+ * --rwhois-limit=LIMIT sets the maximum number of matches to return
+ *
+ * Example of its output:
+ * $ whois cnn.com
+ * [Querying whois.verisign-grs.com]
+ * [Redirected to whois.corporatedomains.com]
+ * [Querying whois.corporatedomains.com]
+ * [whois.corporatedomains.com]
+ * ...text of the reply...
+ *
+ * With -i, reply from each server is printed, after all redirects are done:
+ * [Querying whois.verisign-grs.com]
+ * [Redirected to whois.corporatedomains.com]
+ * [Querying whois.corporatedomains.com]
+ * [whois.verisign-grs.com]
+ * ...text of the reply...
+ * [whois.corporatedomains.com]
+ * ...text of the reply...
+ *
+ * With -a, no "DOMAIN" -> "domain DOMAIN" transformation is attempted.
+
+ * With -n, the first reply is shown, redirects are not followed:
+ * [Querying whois.verisign-grs.com]
+ * [whois.verisign-grs.com]
+ * ...text of the reply...
+ */
+
int whois_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int whois_main(int argc UNUSED_PARAM, char **argv)
{
int port = 43;
- const char *host = "whois-servers.net";
-
- opt_complementary = "-1:p+";
- getopt32(argv, "h:p:", &host, &port);
+ const char *host = "whois.iana.org";
+ opt_complementary = "-1";
+ getopt32(argv, "ih:p:+", &host, &port);
argv += optind;
+
do {
- int fd = create_and_connect_stream_or_die(host, port);
- fdprintf(fd, "%s\r\n", *argv);
- pipe_out(fd);
+ recursive_query(host, port, *argv);
}
while (*++argv);
diff --git a/networking/zcip.c b/networking/zcip.c
index 7314ff8..9122bd6 100644
--- a/networking/zcip.c
+++ b/networking/zcip.c
@@ -14,6 +14,22 @@
* routed at the IP level, though various proxies or bridges can
* certainly be used. Its naming is built over multicast DNS.
*/
+//config:config ZCIP
+//config: bool "zcip"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: select FEATURE_SYSLOG
+//config: help
+//config: ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
+//config: It's a daemon that allocates and defends a dynamically assigned
+//config: address on the 169.254/16 network, requiring no system administrator.
+//config:
+//config: See http://www.zeroconf.org for further details, and "zcip.script"
+//config: in the busybox examples.
+
+//applet:IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ZCIP) += zcip.o
//#define DEBUG
@@ -30,12 +46,17 @@
//usage: "\n -f Run in foreground"
//usage: "\n -q Quit after obtaining address"
//usage: "\n -r 169.254.x.x Request this address first"
+//usage: "\n -l x.x.0.0 Use this range instead of 169.254"
//usage: "\n -v Verbose"
//usage: "\n"
+//usage: "\n$LOGGING=none Suppress logging"
+//usage: "\n$LOGGING=syslog Log to syslog"
+//usage: "\n"
//usage: "\nWith no -q, runs continuously monitoring for ARP conflicts,"
//usage: "\nexits only on I/O errors (link down etc)"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <netinet/ether.h>
#include <net/if.h>
#include <net/if_arp.h>
@@ -52,26 +73,23 @@ struct arp_packet {
} PACKED;
enum {
-/* 169.254.0.0 */
- LINKLOCAL_ADDR = 0xa9fe0000,
-
-/* protocol timeout parameters, specified in seconds */
+ /* 0-1 seconds before sending 1st probe */
PROBE_WAIT = 1,
+ /* 1-2 seconds between probes */
PROBE_MIN = 1,
PROBE_MAX = 2,
- PROBE_NUM = 3,
- MAX_CONFLICTS = 10,
- RATE_LIMIT_INTERVAL = 60,
- ANNOUNCE_WAIT = 2,
- ANNOUNCE_NUM = 2,
- ANNOUNCE_INTERVAL = 2,
- DEFEND_INTERVAL = 10
+ PROBE_NUM = 3, /* total probes to send */
+ ANNOUNCE_INTERVAL = 2, /* 2 seconds between announces */
+ ANNOUNCE_NUM = 3, /* announces to send */
+ /* if probe/announce sees a conflict, multiply RANDOM(NUM_CONFLICT) by... */
+ CONFLICT_MULTIPLIER = 2,
+ /* if we monitor and see a conflict, how long is defend state? */
+ DEFEND_INTERVAL = 10,
};
/* States during the configuration process. */
enum {
PROBE = 0,
- RATE_LIMIT_PROBE,
ANNOUNCE,
MONITOR,
DEFEND
@@ -85,40 +103,46 @@ enum {
};
struct globals {
- struct sockaddr saddr;
- struct ether_addr eth_addr;
+ struct sockaddr iface_sockaddr;
+ struct ether_addr our_ethaddr;
+ uint32_t localnet_ip;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define saddr (G.saddr )
-#define eth_addr (G.eth_addr)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
/**
* Pick a random link local IP address on 169.254/16, except that
* the first and last 256 addresses are reserved.
*/
-static uint32_t pick(void)
+static uint32_t pick_nip(void)
{
unsigned tmp;
do {
tmp = rand() & IN_CLASSB_HOST;
} while (tmp > (IN_CLASSB_HOST - 0x0200));
- return htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+ return htonl((G.localnet_ip + 0x0100) + tmp);
+}
+
+static const char *nip_to_a(uint32_t nip)
+{
+ struct in_addr in;
+ in.s_addr = nip;
+ return inet_ntoa(in);
}
/**
* Broadcast an ARP packet.
*/
-static void arp(
+static void send_arp_request(
/* int op, - always ARPOP_REQUEST */
- /* const struct ether_addr *source_eth, - always &eth_addr */
- struct in_addr source_ip,
- const struct ether_addr *target_eth, struct in_addr target_ip)
+ /* const struct ether_addr *source_eth, - always &G.our_ethaddr */
+ uint32_t source_nip,
+ const struct ether_addr *target_eth, uint32_t target_nip)
{
enum { op = ARPOP_REQUEST };
-#define source_eth (&eth_addr)
+#define source_eth (&G.our_ethaddr)
struct arp_packet p;
memset(&p, 0, sizeof(p));
@@ -135,18 +159,18 @@ static void arp(
p.arp.arp_pln = 4;
p.arp.arp_op = htons(op);
memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN);
- memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
+ memcpy(&p.arp.arp_spa, &source_nip, 4);
memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN);
- memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
+ memcpy(&p.arp.arp_tpa, &target_nip, 4);
// send it
- // Even though sock_fd is already bound to saddr, just send()
+ // Even though sock_fd is already bound to G.iface_sockaddr, just send()
// won't work, because "socket is not connected"
// (and connect() won't fix that, "operation not supported").
- // Thus we sendto() to saddr. I wonder which sockaddr
+ // Thus we sendto() to G.iface_sockaddr. I wonder which sockaddr
// (from bind() or from sendto()?) kernel actually uses
// to determine iface to emit the packet from...
- xsendto(sock_fd, &p, sizeof(p), &saddr, sizeof(saddr));
+ xsendto(sock_fd, &p, sizeof(p), &G.iface_sockaddr, sizeof(G.iface_sockaddr));
#undef source_eth
}
@@ -154,22 +178,22 @@ static void arp(
* Run a script.
* argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
*/
-static int run(char *argv[3], const char *param, struct in_addr *ip)
+static int run(char *argv[3], const char *param, uint32_t nip)
{
int status;
- char *addr = addr; /* for gcc */
+ const char *addr = addr; /* for gcc */
const char *fmt = "%s %s %s" + 3;
argv[2] = (char*)param;
VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);
- if (ip) {
- addr = inet_ntoa(*ip);
+ if (nip != 0) {
+ addr = nip_to_a(nip);
xsetenv("ip", addr);
fmt -= 3;
}
- bb_info_msg(fmt, argv[2], argv[0], addr);
+ bb_error_msg(fmt, argv[2], argv[0], addr);
status = spawn_and_wait(argv + 1);
if (status < 0) {
@@ -186,7 +210,7 @@ static int run(char *argv[3], const char *param, struct in_addr *ip)
*/
static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
{
- return rand() % (secs * 1000);
+ return (unsigned)rand() % (secs * 1000);
}
/**
@@ -195,49 +219,43 @@ static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int zcip_main(int argc UNUSED_PARAM, char **argv)
{
- int state;
char *r_opt;
+ const char *l_opt = "169.254.0.0";
+ int state;
+ int nsent;
unsigned opts;
- // ugly trick, but I want these zeroed in one go
+ // Ugly trick, but I want these zeroed in one go
struct {
- const struct in_addr null_ip;
- const struct ether_addr null_addr;
- struct in_addr ip;
+ const struct ether_addr null_ethaddr;
struct ifreq ifr;
- int timeout_ms; /* must be signed */
- unsigned conflicts;
- unsigned nprobes;
- unsigned nclaims;
- int ready;
+ uint32_t chosen_nip;
+ int conflicts;
+ int timeout_ms; // must be signed
int verbose;
} L;
-#define null_ip (L.null_ip )
-#define null_addr (L.null_addr )
-#define ip (L.ip )
-#define ifr (L.ifr )
-#define timeout_ms (L.timeout_ms)
-#define conflicts (L.conflicts )
-#define nprobes (L.nprobes )
-#define nclaims (L.nclaims )
-#define ready (L.ready )
-#define verbose (L.verbose )
+#define null_ethaddr (L.null_ethaddr)
+#define ifr (L.ifr )
+#define chosen_nip (L.chosen_nip )
+#define conflicts (L.conflicts )
+#define timeout_ms (L.timeout_ms )
+#define verbose (L.verbose )
memset(&L, 0, sizeof(L));
INIT_G();
#define FOREGROUND (opts & 1)
#define QUIT (opts & 2)
- // parse commandline: prog [options] ifname script
+ // Parse commandline: prog [options] ifname script
// exactly 2 args; -v accumulates and implies -f
opt_complementary = "=2:vv:vf";
- opts = getopt32(argv, "fqr:v", &r_opt, &verbose);
+ opts = getopt32(argv, "fqr:l:v", &r_opt, &l_opt, &verbose);
#if !BB_MMU
// on NOMMU reexec early (or else we will rerun things twice)
if (!FOREGROUND)
bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
#endif
- // open an ARP socket
+ // Open an ARP socket
// (need to do it before openlog to prevent openlog from taking
// fd 3 (sock_fd==3))
xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
@@ -246,12 +264,25 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
openlog(applet_name, 0, LOG_DAEMON);
logmode |= LOGMODE_SYSLOG;
}
+ bb_logenv_override();
+
+ { // -l n.n.n.n
+ struct in_addr net;
+ if (inet_aton(l_opt, &net) == 0
+ || (net.s_addr & htonl(IN_CLASSB_NET)) != net.s_addr
+ ) {
+ bb_error_msg_and_die("invalid network address");
+ }
+ G.localnet_ip = ntohl(net.s_addr);
+ }
if (opts & 4) { // -r n.n.n.n
+ struct in_addr ip;
if (inet_aton(r_opt, &ip) == 0
- || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
+ || (ntohl(ip.s_addr) & IN_CLASSB_NET) != G.localnet_ip
) {
bb_error_msg_and_die("invalid link address");
}
+ chosen_nip = ip.s_addr;
}
argv += optind - 1;
@@ -264,26 +295,26 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
xsetenv("interface", argv_intf);
- // initialize the interface (modprobe, ifup, etc)
- if (run(argv, "init", NULL))
+ // Initialize the interface (modprobe, ifup, etc)
+ if (run(argv, "init", 0))
return EXIT_FAILURE;
- // initialize saddr
- // saddr is: { u16 sa_family; u8 sa_data[14]; }
- //memset(&saddr, 0, sizeof(saddr));
+ // Initialize G.iface_sockaddr
+ // G.iface_sockaddr is: { u16 sa_family; u8 sa_data[14]; }
+ //memset(&G.iface_sockaddr, 0, sizeof(G.iface_sockaddr));
//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
- safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data));
+ safe_strncpy(G.iface_sockaddr.sa_data, argv_intf, sizeof(G.iface_sockaddr.sa_data));
- // bind to the interface's ARP socket
- xbind(sock_fd, &saddr, sizeof(saddr));
+ // Bind to the interface's ARP socket
+ xbind(sock_fd, &G.iface_sockaddr, sizeof(G.iface_sockaddr));
- // get the interface's ethernet address
+ // Get the interface's ethernet address
//memset(&ifr, 0, sizeof(ifr));
strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
- memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+ memcpy(&G.our_ethaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
- // start with some stable ip address, either a function of
+ // Start with some stable ip address, either a function of
// the hardware address or else the last address we used.
// we are taking low-order four bytes, as top-order ones
// aren't random enough.
@@ -291,30 +322,29 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
// depending on when we detect conflicts.
{
uint32_t t;
- move_from_unaligned32(t, ((char *)&eth_addr + 2));
+ move_from_unaligned32(t, ((char *)&G.our_ethaddr + 2));
srand(t);
}
- if (ip.s_addr == 0)
- ip.s_addr = pick();
-
// FIXME cases to handle:
// - zcip already running!
// - link already has local address... just defend/update
- // daemonize now; don't delay system startup
+ // Daemonize now; don't delay system startup
if (!FOREGROUND) {
#if BB_MMU
bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
#endif
- bb_info_msg("start, interface %s", argv_intf);
+ bb_error_msg("start, interface %s", argv_intf);
}
- // run the dynamic address negotiation protocol,
+ // Run the dynamic address negotiation protocol,
// restarting after address conflicts:
// - start with some address we want to try
// - short random delay
// - arp probes to see if another host uses it
+ // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 tell 0.0.0.0
// - arp announcements that we're claiming it
+ // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 (00:04:e2:64:23:c2) tell 169.254.194.171
// - use it
// - defend it, within limits
// exit if:
@@ -322,257 +352,193 @@ int zcip_main(int argc UNUSED_PARAM, char **argv)
// run "<script> config", then exit with exitcode 0
// - poll error (when does this happen?)
// - read error (when does this happen?)
- // - sendto error (in arp()) (when does this happen?)
+ // - sendto error (in send_arp_request()) (when does this happen?)
// - revents & POLLERR (link down). run "<script> deconfig" first
+ if (chosen_nip == 0) {
+ new_nip_and_PROBE:
+ chosen_nip = pick_nip();
+ }
+ nsent = 0;
state = PROBE;
while (1) {
struct pollfd fds[1];
- unsigned deadline_us;
+ unsigned deadline_us = deadline_us;
struct arp_packet p;
- int source_ip_conflict;
- int target_ip_conflict;
+ int ip_conflict;
+ int n;
fds[0].fd = sock_fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
- // poll, being ready to adjust current timeout
+ // Poll, being ready to adjust current timeout
if (!timeout_ms) {
timeout_ms = random_delay_ms(PROBE_WAIT);
// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
// make the kernel filter out all packets except
// ones we'd care about.
}
- // set deadline_us to the point in time when we timeout
- deadline_us = MONOTONIC_US() + timeout_ms * 1000;
-
- VDBG("...wait %d %s nprobes=%u, nclaims=%u\n",
- timeout_ms, argv_intf, nprobes, nclaims);
+ if (timeout_ms >= 0) {
+ // Set deadline_us to the point in time when we timeout
+ deadline_us = MONOTONIC_US() + timeout_ms * 1000;
+ }
- switch (safe_poll(fds, 1, timeout_ms)) {
+ VDBG("...wait %d %s nsent=%u\n",
+ timeout_ms, argv_intf, nsent);
- default:
+ n = safe_poll(fds, 1, timeout_ms);
+ if (n < 0) {
//bb_perror_msg("poll"); - done in safe_poll
return EXIT_FAILURE;
-
- // timeout
- case 0:
- VDBG("state = %d\n", state);
+ }
+ if (n == 0) { // timed out?
+ VDBG("state:%d\n", state);
switch (state) {
case PROBE:
- // timeouts in the PROBE state mean no conflicting ARP packets
- // have been received, so we can progress through the states
- if (nprobes < PROBE_NUM) {
- nprobes++;
+ // No conflicting ARP packets were seen:
+ // we can progress through the states
+ if (nsent < PROBE_NUM) {
+ nsent++;
VDBG("probe/%u %s@%s\n",
- nprobes, argv_intf, inet_ntoa(ip));
- arp(/* ARPOP_REQUEST, */
- /* &eth_addr, */ null_ip,
- &null_addr, ip);
+ nsent, argv_intf, nip_to_a(chosen_nip));
timeout_ms = PROBE_MIN * 1000;
timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
+ send_arp_request(0, &null_ethaddr, chosen_nip);
+ continue;
}
- else {
- // Switch to announce state.
- state = ANNOUNCE;
- nclaims = 0;
- VDBG("announce/%u %s@%s\n",
- nclaims, argv_intf, inet_ntoa(ip));
- arp(/* ARPOP_REQUEST, */
- /* &eth_addr, */ ip,
- &eth_addr, ip);
- timeout_ms = ANNOUNCE_INTERVAL * 1000;
- }
- break;
- case RATE_LIMIT_PROBE:
- // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
- // have been received, so we can move immediately to the announce state
+ // Switch to announce state
+ nsent = 0;
state = ANNOUNCE;
- nclaims = 0;
- VDBG("announce/%u %s@%s\n",
- nclaims, argv_intf, inet_ntoa(ip));
- arp(/* ARPOP_REQUEST, */
- /* &eth_addr, */ ip,
- &eth_addr, ip);
- timeout_ms = ANNOUNCE_INTERVAL * 1000;
- break;
+ goto send_announce;
case ANNOUNCE:
- // timeouts in the ANNOUNCE state mean no conflicting ARP packets
- // have been received, so we can progress through the states
- if (nclaims < ANNOUNCE_NUM) {
- nclaims++;
+ // No conflicting ARP packets were seen:
+ // we can progress through the states
+ if (nsent < ANNOUNCE_NUM) {
+ send_announce:
+ nsent++;
VDBG("announce/%u %s@%s\n",
- nclaims, argv_intf, inet_ntoa(ip));
- arp(/* ARPOP_REQUEST, */
- /* &eth_addr, */ ip,
- &eth_addr, ip);
+ nsent, argv_intf, nip_to_a(chosen_nip));
timeout_ms = ANNOUNCE_INTERVAL * 1000;
+ send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
+ continue;
}
- else {
- // Switch to monitor state.
- state = MONITOR;
- // link is ok to use earlier
- // FIXME update filters
- run(argv, "config", &ip);
- ready = 1;
- conflicts = 0;
- timeout_ms = -1; // Never timeout in the monitor state.
-
- // NOTE: all other exit paths
- // should deconfig ...
- if (QUIT)
- return EXIT_SUCCESS;
- }
- break;
- case DEFEND:
- // We won! No ARP replies, so just go back to monitor.
- state = MONITOR;
- timeout_ms = -1;
- conflicts = 0;
- break;
+ // Switch to monitor state
+ // FIXME update filters
+ run(argv, "config", chosen_nip);
+ // NOTE: all other exit paths should deconfig...
+ if (QUIT)
+ return EXIT_SUCCESS;
+ // fall through: switch to MONITOR
default:
- // Invalid, should never happen. Restart the whole protocol.
- state = PROBE;
- ip.s_addr = pick();
- timeout_ms = 0;
- nprobes = 0;
- nclaims = 0;
- break;
- } // switch (state)
- break; // case 0 (timeout)
-
- // packets arriving, or link went down
- case 1:
- // We need to adjust the timeout in case we didn't receive
- // a conflicting packet.
- if (timeout_ms > 0) {
- unsigned diff = deadline_us - MONOTONIC_US();
- if ((int)(diff) < 0) {
- // Current time is greater than the expected timeout time.
- // Should never happen.
- VDBG("missed an expected timeout\n");
- timeout_ms = 0;
- } else {
- VDBG("adjusting timeout\n");
- timeout_ms = (diff / 1000) | 1; /* never 0 */
- }
- }
-
- if ((fds[0].revents & POLLIN) == 0) {
- if (fds[0].revents & POLLERR) {
- // FIXME: links routinely go down;
- // this shouldn't necessarily exit.
- bb_error_msg("iface %s is down", argv_intf);
- if (ready) {
- run(argv, "deconfig", &ip);
- }
- return EXIT_FAILURE;
- }
+ // case DEFEND:
+ // case MONITOR: (shouldn't happen, MONITOR timeout is infinite)
+ // Defend period ended with no ARP replies - we won
+ timeout_ms = -1; // never timeout in monitor state
+ state = MONITOR;
continue;
}
+ }
- // read ARP packet
- if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
- bb_perror_msg_and_die(bb_msg_read_error);
+ // Packet arrived, or link went down.
+ // We need to adjust the timeout in case we didn't receive
+ // a conflicting packet.
+ if (timeout_ms > 0) {
+ unsigned diff = deadline_us - MONOTONIC_US();
+ if ((int)(diff) < 0) {
+ // Current time is greater than the expected timeout time.
+ diff = 0;
}
- if (p.eth.ether_type != htons(ETHERTYPE_ARP))
- continue;
-#ifdef DEBUG
- {
- struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
- struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
- struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
- struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
- VDBG("%s recv arp type=%d, op=%d,\n",
- argv_intf, ntohs(p.eth.ether_type),
- ntohs(p.arp.arp_op));
- VDBG("\tsource=%s %s\n",
- ether_ntoa(sha),
- inet_ntoa(*spa));
- VDBG("\ttarget=%s %s\n",
- ether_ntoa(tha),
- inet_ntoa(*tpa));
+ VDBG("adjusting timeout\n");
+ timeout_ms = (diff / 1000) | 1; // never 0
+ }
+
+ if ((fds[0].revents & POLLIN) == 0) {
+ if (fds[0].revents & POLLERR) {
+ // FIXME: links routinely go down;
+ // this shouldn't necessarily exit.
+ bb_error_msg("iface %s is down", argv_intf);
+ if (state >= MONITOR) {
+ // Only if we are in MONITOR or DEFEND
+ run(argv, "deconfig", chosen_nip);
+ }
+ return EXIT_FAILURE;
}
-#endif
- if (p.arp.arp_op != htons(ARPOP_REQUEST)
- && p.arp.arp_op != htons(ARPOP_REPLY))
- continue;
+ continue;
+ }
- source_ip_conflict = 0;
- target_ip_conflict = 0;
+ // Read ARP packet
+ if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
+ bb_perror_msg_and_die(bb_msg_read_error);
+ }
- if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0
- && memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0
- ) {
- source_ip_conflict = 1;
+ if (p.eth.ether_type != htons(ETHERTYPE_ARP))
+ continue;
+ if (p.arp.arp_op != htons(ARPOP_REQUEST)
+ && p.arp.arp_op != htons(ARPOP_REPLY)
+ ) {
+ continue;
+ }
+#ifdef DEBUG
+ {
+ struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
+ struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
+ struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
+ struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
+ VDBG("source=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa));
+ VDBG("target=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa));
+ }
+#endif
+ ip_conflict = 0;
+ if (memcmp(&p.arp.arp_sha, &G.our_ethaddr, ETH_ALEN) != 0) {
+ if (memcmp(p.arp.arp_spa, &chosen_nip, 4) == 0) {
+ // A probe or reply with source_ip == chosen ip
+ ip_conflict = 1;
}
if (p.arp.arp_op == htons(ARPOP_REQUEST)
- && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
- && memcmp(&p.arp.arp_tha, &eth_addr, ETH_ALEN) != 0
+ && memcmp(p.arp.arp_spa, &const_int_0, 4) == 0
+ && memcmp(p.arp.arp_tpa, &chosen_nip, 4) == 0
) {
- target_ip_conflict = 1;
+ // A probe with source_ip == 0.0.0.0, target_ip == chosen ip:
+ // another host trying to claim this ip!
+ ip_conflict |= 2;
}
+ }
+ VDBG("state:%d ip_conflict:%d\n", state, ip_conflict);
+ if (!ip_conflict)
+ continue;
+
+ // Either src or target IP conflict exists
+ if (state <= ANNOUNCE) {
+ // PROBE or ANNOUNCE
+ conflicts++;
+ timeout_ms = PROBE_MIN * 1000
+ + CONFLICT_MULTIPLIER * random_delay_ms(conflicts);
+ goto new_nip_and_PROBE;
+ }
- VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
- state, source_ip_conflict, target_ip_conflict);
- switch (state) {
- case PROBE:
- case ANNOUNCE:
- // When probing or announcing, check for source IP conflicts
- // and other hosts doing ARP probes (target IP conflicts).
- if (source_ip_conflict || target_ip_conflict) {
- conflicts++;
- if (conflicts >= MAX_CONFLICTS) {
- VDBG("%s ratelimit\n", argv_intf);
- timeout_ms = RATE_LIMIT_INTERVAL * 1000;
- state = RATE_LIMIT_PROBE;
- }
-
- // restart the whole protocol
- ip.s_addr = pick();
- timeout_ms = 0;
- nprobes = 0;
- nclaims = 0;
- }
- break;
- case MONITOR:
- // If a conflict, we try to defend with a single ARP probe.
- if (source_ip_conflict) {
- VDBG("monitor conflict -- defending\n");
- state = DEFEND;
- timeout_ms = DEFEND_INTERVAL * 1000;
- arp(/* ARPOP_REQUEST, */
- /* &eth_addr, */ ip,
- &eth_addr, ip);
- }
- break;
- case DEFEND:
- // Well, we tried. Start over (on conflict).
- if (source_ip_conflict) {
- state = PROBE;
- VDBG("defend conflict -- starting over\n");
- ready = 0;
- run(argv, "deconfig", &ip);
-
- // restart the whole protocol
- ip.s_addr = pick();
- timeout_ms = 0;
- nprobes = 0;
- nclaims = 0;
- }
- break;
- default:
- // Invalid, should never happen. Restart the whole protocol.
- VDBG("invalid state -- starting over\n");
- state = PROBE;
- ip.s_addr = pick();
- timeout_ms = 0;
- nprobes = 0;
- nclaims = 0;
- break;
- } // switch state
- break; // case 1 (packets arriving)
- } // switch poll
+ // MONITOR or DEFEND: only src IP conflict is a problem
+ if (ip_conflict & 1) {
+ if (state == MONITOR) {
+ // Src IP conflict, defend with a single ARP probe
+ VDBG("monitor conflict - defending\n");
+ timeout_ms = DEFEND_INTERVAL * 1000;
+ state = DEFEND;
+ send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
+ continue;
+ }
+ // state == DEFEND
+ // Another src IP conflict, start over
+ VDBG("defend conflict - starting over\n");
+ run(argv, "deconfig", chosen_nip);
+ conflicts = 0;
+ timeout_ms = 0;
+ goto new_nip_and_PROBE;
+ }
+ // Note: if we only have a target IP conflict here (ip_conflict & 2),
+ // IOW: if we just saw this sort of ARP packet:
+ // aa:bb:cc:dd:ee:ff > xx:xx:xx:xx:xx:xx arp who-has <chosen_nip> tell 0.0.0.0
+ // we expect _kernel_ to respond to that, because <chosen_nip>
+ // is (expected to be) configured on this iface.
} // while (1)
#undef argv_intf
}
diff --git a/printutils/Config.src b/printutils/Config.src
index cc4ab8d..e53b9d0 100644
--- a/printutils/Config.src
+++ b/printutils/Config.src
@@ -7,22 +7,4 @@ menu "Print Utilities"
INSERT
-config LPD
- bool "lpd"
- default y
- help
- lpd is a print spooling daemon.
-
-config LPR
- bool "lpr"
- default y
- help
- lpr sends files (or standard input) to a print spooling daemon.
-
-config LPQ
- bool "lpq"
- default y
- help
- lpq is a print spool queue examination and manipulation program.
-
endmenu
diff --git a/printutils/Kbuild.src b/printutils/Kbuild.src
index 194fe01..10c8230 100644
--- a/printutils/Kbuild.src
+++ b/printutils/Kbuild.src
@@ -4,6 +4,4 @@
lib-y :=
-lib-$(CONFIG_LPD) += lpd.o
-lib-$(CONFIG_LPR) += lpr.o
-lib-$(CONFIG_LPQ) += lpr.o
+INSERT
diff --git a/printutils/lpd.c b/printutils/lpd.c
index 642e8a8..8823934 100644
--- a/printutils/lpd.c
+++ b/printutils/lpd.c
@@ -69,6 +69,15 @@
* cat ./"$DATAFILE" >/dev/lp0
* mv -f ./"$DATAFILE" save/
*/
+//config:config LPD
+//config: bool "lpd"
+//config: default y
+//config: help
+//config: lpd is a print spooling daemon.
+
+//applet:IF_LPD(APPLET(lpd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LPD) += lpd.o
//usage:#define lpd_trivial_usage
//usage: "SPOOLDIR [HELPER [ARGS]]"
@@ -200,15 +209,15 @@ int lpd_main(int argc UNUSED_PARAM, char *argv[])
if (2 != s[0] && 3 != s[0])
goto unsupported_cmd;
if (spooling & (1 << (s[0]-1))) {
- printf("Duplicated subcommand\n");
+ puts("Duplicated subcommand");
goto err_exit;
}
// get filename
- *strchrnul(s, '\n') = '\0';
+ chomp(s);
fname = strchr(s, ' ');
if (!fname) {
// bad_fname:
- printf("No or bad filename\n");
+ puts("No or bad filename");
goto err_exit;
}
*fname++ = '\0';
@@ -219,13 +228,13 @@ int lpd_main(int argc UNUSED_PARAM, char *argv[])
// get length
expected_len = bb_strtou(s + 1, NULL, 10);
if (errno || expected_len < 0) {
- printf("Bad length\n");
+ puts("Bad length");
goto err_exit;
}
if (2 == s[0] && expected_len > 16 * 1024) {
// SECURITY:
// ctrlfile can't be big (we want to read it back later!)
- printf("File is too big\n");
+ puts("File is too big");
goto err_exit;
}
diff --git a/printutils/lpr.c b/printutils/lpr.c
index b3fc029..29e2fd6 100644
--- a/printutils/lpr.c
+++ b/printutils/lpr.c
@@ -11,6 +11,23 @@
*
* See RFC 1179 for protocol description.
*/
+//config:config LPR
+//config: bool "lpr"
+//config: default y
+//config: help
+//config: lpr sends files (or standard input) to a print spooling daemon.
+//config:
+//config:config LPQ
+//config: bool "lpq"
+//config: default y
+//config: help
+//config: lpq is a print spool queue examination and manipulation program.
+
+//applet:IF_LPQ(APPLET_ODDNAME(lpq, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpq))
+//applet:IF_LPR(APPLET_ODDNAME(lpr, lpqr, BB_DIR_USR_BIN, BB_SUID_DROP, lpr))
+
+//kbuild:lib-$(CONFIG_LPR) += lpr.o
+//kbuild:lib-$(CONFIG_LPQ) += lpr.o
//usage:#define lpr_trivial_usage
//usage: "-P queue[@host[:port]] -U USERNAME -J TITLE -Vmh [FILE]..."
diff --git a/procps/Config.src b/procps/Config.src
index 527d9ee..eb47607 100644
--- a/procps/Config.src
+++ b/procps/Config.src
@@ -7,139 +7,6 @@ menu "Process Utilities"
INSERT
-config FREE
- bool "free"
- default y
- select PLATFORM_LINUX #sysinfo()
- help
- free displays the total amount of free and used physical and swap
- memory in the system, as well as the buffers used by the kernel.
- The shared memory column should be ignored; it is obsolete.
-
-config FUSER
- bool "fuser"
- default y
- help
- fuser lists all PIDs (Process IDs) that currently have a given
- file open. fuser can also list all PIDs that have a given network
- (TCP or UDP) port open.
-
-config KILL
- bool "kill"
- default y
- help
- The command kill sends the specified signal to the specified
- process or process group. If no signal is specified, the TERM
- signal is sent.
-
-config KILLALL
- bool "killall"
- default y
- depends on KILL
- help
- killall sends a signal to all processes running any of the
- specified commands. If no signal name is specified, SIGTERM is
- sent.
-
-config KILLALL5
- bool "killall5"
- default y
- depends on KILL
-
-config PGREP
- bool "pgrep"
- default y
- help
- Look for processes by name.
-
-config PIDOF
- bool "pidof"
- default y
- help
- Pidof finds the process id's (pids) of the named programs. It prints
- those id's on the standard output.
-
-config FEATURE_PIDOF_SINGLE
- bool "Enable argument for single shot (-s)"
- default y
- depends on PIDOF
- help
- Support argument '-s' for returning only the first pid found.
-
-config FEATURE_PIDOF_OMIT
- bool "Enable argument for omitting pids (-o)"
- default y
- depends on PIDOF
- help
- Support argument '-o' for omitting the given pids in output.
- The special pid %PPID can be used to name the parent process
- of the pidof, in other words the calling shell or shell script.
-
-config PKILL
- bool "pkill"
- default y
- help
- Send signals to processes by name.
-
-config PS
- bool "ps"
- default y
- help
- ps gives a snapshot of the current processes.
-
-config FEATURE_PS_WIDE
- bool "Enable wide output option (-w)"
- default y
- depends on PS && !DESKTOP
- help
- Support argument 'w' for wide output.
- If given once, 132 chars are printed, and if given more
- than once, the length is unlimited.
-
-config FEATURE_PS_LONG
- bool "Enable long output option (-l)"
- default y
- depends on PS && !DESKTOP
- help
- Support argument 'l' for long output.
- Adds fields PPID, RSS, START, TIME & TTY
-
-config FEATURE_PS_TIME
- bool "Enable time and elapsed time output"
- default y
- depends on PS && DESKTOP
- select PLATFORM_LINUX
- help
- Support -o time and -o etime output specifiers.
-
-config FEATURE_PS_ADDITIONAL_COLUMNS
- bool "Enable additional ps columns"
- default y
- depends on PS && DESKTOP
- help
- Support -o rgroup, -o ruser, -o nice output specifiers.
-
-config FEATURE_PS_UNUSUAL_SYSTEMS
- bool "Support Linux prior to 2.4.0 and non-ELF systems"
- default n
- depends on FEATURE_PS_TIME
- help
- Include support for measuring HZ on old kernels and non-ELF systems
- (if you are on Linux 2.4.0+ and use ELF, you don't need this)
-
-config RENICE
- bool "renice"
- default y
- help
- Renice alters the scheduling priority of one or more running
- processes.
-
-config BB_SYSCTL
- bool "sysctl"
- default y
- help
- Configure kernel parameters at runtime.
-
config FEATURE_SHOW_THREADS
bool "Support for showing threads in ps/pstree/top"
default y
@@ -148,11 +15,4 @@ config FEATURE_SHOW_THREADS
Enables the ps -T option, showing of threads in pstree,
and 'h' command in top.
-config WATCH
- bool "watch"
- default y
- help
- watch is used to execute a program periodically, showing
- output to the screen.
-
endmenu
diff --git a/procps/Kbuild.src b/procps/Kbuild.src
index 89b1cc0..e7adc73 100644
--- a/procps/Kbuild.src
+++ b/procps/Kbuild.src
@@ -7,16 +7,7 @@
lib-y:=
INSERT
-lib-$(CONFIG_FREE) += free.o
-lib-$(CONFIG_FUSER) += fuser.o
-lib-$(CONFIG_KILL) += kill.o
-lib-$(CONFIG_ASH) += kill.o # used for built-in kill by ash
-lib-$(CONFIG_PGREP) += pgrep.o
-lib-$(CONFIG_PKILL) += pgrep.o
-lib-$(CONFIG_PIDOF) += pidof.o
-lib-$(CONFIG_PS) += ps.o
-lib-$(CONFIG_RENICE) += renice.o
-lib-$(CONFIG_BB_SYSCTL) += sysctl.o
-lib-$(CONFIG_TOP) += top.o
-lib-$(CONFIG_UPTIME) += uptime.o
-lib-$(CONFIG_WATCH) += watch.o
+
+lib-$(CONFIG_ASH) += kill.o # used for built-in kill by ash
+lib-$(CONFIG_SH_IS_ASH) += kill.o # used for built-in kill by ash
+lib-$(CONFIG_BASH_IS_ASH) += kill.o # used for built-in kill by ash
diff --git a/procps/free.c b/procps/free.c
index 47f2fc3..f3a2c3c 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -6,8 +6,18 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FREE
+//config: bool "free"
+//config: default y
+//config: select PLATFORM_LINUX #sysinfo()
+//config: help
+//config: free displays the total amount of free and used physical and swap
+//config: memory in the system, as well as the buffers used by the kernel.
+//config: The shared memory column should be ignored; it is obsolete.
-/* getopt not needed */
+//applet:IF_FREE(APPLET(free, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FREE) += free.o
//usage:#define free_trivial_usage
//usage: "" IF_DESKTOP("[-b/k/m/g]")
@@ -22,6 +32,7 @@
//usage: "Total: 386144 257128 129016\n"
#include "libbb.h"
+#include "common_bufsiz.h"
#ifdef __linux__
# include <sys/sysinfo.h>
#endif
@@ -35,8 +46,8 @@ struct globals {
# define G_unit_steps 10
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define INIT_G() do { } while (0)
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static unsigned long long scale(unsigned long d)
@@ -44,11 +55,28 @@ static unsigned long long scale(unsigned long d)
return ((unsigned long long)d * G.mem_unit) >> G_unit_steps;
}
+static unsigned long parse_cached_kb(void)
+{
+ char buf[60]; /* actual lines we expect are ~30 chars or less */
+ FILE *fp;
+ unsigned long cached = 0;
+
+ fp = xfopen_for_read("/proc/meminfo");
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (sscanf(buf, "Cached: %lu %*s\n", &cached) == 1)
+ break;
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(fp);
+
+ return cached;
+}
int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
{
struct sysinfo info;
+ unsigned long long cached;
INIT_G();
@@ -73,49 +101,47 @@ int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
}
}
#endif
+ printf(" %11s%11s%11s%11s%11s%11s\n"
+ "Mem: ",
+ "total",
+ "used",
+ "free",
+ "shared", "buffers", "cached" /* swap and total don't have these columns */
+ );
sysinfo(&info);
-
/* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
+ /* Extract cached from /proc/meminfo and convert to mem_units */
+ cached = ((unsigned long long) parse_cached_kb() * 1024) / G.mem_unit;
- printf(" %13s%13s%13s%13s%13s\n",
- "total",
- "used",
- "free",
- "shared", "buffers" /* swap and total don't have these columns */
- /* procps version 3.2.8 also shows "cached" column, but
- * sysinfo() does not provide this value, need to parse
- * /proc/meminfo instead and get "Cached: NNN kB" from there.
- */
- );
+#define FIELDS_6 "%11llu%11llu%11llu%11llu%11llu%11llu\n"
+#define FIELDS_3 (FIELDS_6 + 3*6)
+#define FIELDS_2 (FIELDS_6 + 4*6)
-#define FIELDS_5 "%13llu%13llu%13llu%13llu%13llu\n"
-#define FIELDS_3 (FIELDS_5 + 2*6)
-#define FIELDS_2 (FIELDS_5 + 3*6)
-
- printf("Mem: ");
- printf(FIELDS_5,
- scale(info.totalram),
- scale(info.totalram - info.freeram),
- scale(info.freeram),
- scale(info.sharedram),
- scale(info.bufferram)
+ printf(FIELDS_6,
+ scale(info.totalram), //total
+ scale(info.totalram - info.freeram), //used
+ scale(info.freeram), //free
+ scale(info.sharedram), //shared
+ scale(info.bufferram), //buffers
+ scale(cached) //cached
);
/* Show alternate, more meaningful busy/free numbers by counting
- * buffer cache as free memory (make it "-/+ buffers/cache"
- * if/when we add support for "cached" column): */
- printf("-/+ buffers: ");
+ * buffer cache as free memory. */
+ printf("-/+ buffers/cache:");
+ cached += info.freeram;
+ cached += info.bufferram;
printf(FIELDS_2,
- scale(info.totalram - info.freeram - info.bufferram),
- scale(info.freeram + info.bufferram)
+ scale(info.totalram - cached), //used
+ scale(cached) //free
);
#if BB_MMU
- printf("Swap:");
+ printf("Swap: ");
printf(FIELDS_3,
- scale(info.totalswap),
- scale(info.totalswap - info.freeswap),
- scale(info.freeswap)
+ scale(info.totalswap), //total
+ scale(info.totalswap - info.freeswap), //used
+ scale(info.freeswap) //free
);
#endif
return EXIT_SUCCESS;
diff --git a/procps/fuser.c b/procps/fuser.c
index 05b52ab..db28cca 100644
--- a/procps/fuser.c
+++ b/procps/fuser.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FUSER
+//config: bool "fuser"
+//config: default y
+//config: help
+//config: fuser lists all PIDs (Process IDs) that currently have a given
+//config: file open. fuser can also list all PIDs that have a given network
+//config: (TCP or UDP) port open.
+
+//applet:IF_FUSER(APPLET(fuser, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FUSER) += fuser.o
//usage:#define fuser_trivial_usage
//usage: "[OPTIONS] FILE or PORT/PROTO"
@@ -18,6 +29,7 @@
//usage: "\n -SIGNAL Signal to send (default: KILL)"
#include "libbb.h"
+#include "common_bufsiz.h"
#define MAX_LINE 255
@@ -43,8 +55,9 @@ struct globals {
smallint kill_failed;
int killsig;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
G.mypid = getpid(); \
G.killsig = SIGKILL; \
} while (0)
diff --git a/procps/iostat.c b/procps/iostat.c
index 978d234..c290c59 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -109,11 +109,6 @@ enum {
OPT_m = 1 << 5,
};
-static ALWAYS_INLINE unsigned get_user_hz(void)
-{
- return sysconf(_SC_CLK_TCK);
-}
-
static ALWAYS_INLINE int this_is_smp(void)
{
return (G.total_cpus > 1);
@@ -147,7 +142,7 @@ static void print_timestamp(void)
/* %x: date representation for the current locale */
/* %X: time representation for the current locale */
strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
- printf("%s\n", buf);
+ puts(buf);
}
static cputime_t get_smp_uptime(void)
@@ -414,7 +409,7 @@ int iostat_main(int argc UNUSED_PARAM, char **argv)
memset(&stats_data, 0, sizeof(stats_data));
/* Get number of clock ticks per sec */
- G.clk_tck = get_user_hz();
+ G.clk_tck = bb_clk_tck();
/* Determine number of CPUs */
G.total_cpus = get_cpu_count();
diff --git a/procps/kill.c b/procps/kill.c
index f4637c4..846e9e2 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -7,6 +7,38 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config KILL
+//config: bool "kill"
+//config: default y
+//config: help
+//config: The command kill sends the specified signal to the specified
+//config: process or process group. If no signal is specified, the TERM
+//config: signal is sent.
+//config:
+//config:config KILLALL
+//config: bool "killall"
+//config: default y
+//config: help
+//config: killall sends a signal to all processes running any of the
+//config: specified commands. If no signal name is specified, SIGTERM is
+//config: sent.
+//config:
+//config:config KILLALL5
+//config: bool "killall5"
+//config: default y
+//config: help
+//config: The SystemV killall command. killall5 sends a signal
+//config: to all processes except kernel threads and the processes
+//config: in its own session, so it won't kill the shell that is running
+//config: the script it was called from.
+
+//applet:IF_KILL(APPLET(kill, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_KILLALL(APPLET_ODDNAME(killall, kill, BB_DIR_USR_BIN, BB_SUID_DROP, killall))
+//applet:IF_KILLALL5(APPLET_ODDNAME(killall5, kill, BB_DIR_USR_SBIN, BB_SUID_DROP, killall5))
+
+//kbuild:lib-$(CONFIG_KILL) += kill.o
+//kbuild:lib-$(CONFIG_KILLALL) += kill.o
+//kbuild:lib-$(CONFIG_KILLALL5) += kill.o
//usage:#define kill_trivial_usage
//usage: "[-l] [-SIG] PID..."
@@ -65,17 +97,23 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
char *arg;
pid_t pid;
int signo = SIGTERM, errors = 0, quiet = 0;
-#if !ENABLE_KILLALL && !ENABLE_KILLALL5
-#define killall 0
-#define killall5 0
+#if ENABLE_KILL && !ENABLE_KILLALL && !ENABLE_KILLALL5
+# define killall 0
+# define killall5 0
+#elif !ENABLE_KILL && ENABLE_KILLALL && !ENABLE_KILLALL5
+# define killall 1
+# define killall5 0
+#elif !ENABLE_KILL && !ENABLE_KILLALL && ENABLE_KILLALL5
+# define killall 0
+# define killall5 1
#else
/* How to determine who we are? find 3rd char from the end:
* kill, killall, killall5
* ^i ^a ^l - it's unique
* (checking from the start is complicated by /bin/kill... case) */
const char char3 = argv[0][strlen(argv[0]) - 3];
-#define killall (ENABLE_KILLALL && char3 == 'a')
-#define killall5 (ENABLE_KILLALL5 && char3 == 'l')
+# define killall (ENABLE_KILLALL && char3 == 'a')
+# define killall5 (ENABLE_KILLALL5 && char3 == 'l')
#endif
/* Parse any options */
diff --git a/procps/mpstat.c b/procps/mpstat.c
index 1dbe3fd..d23c9c7 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -522,13 +522,11 @@ static void get_irqs_from_stat(struct stats_irq *irq)
FILE *fp;
char buf[1024];
- fp = fopen_for_read(PROCFS_STAT);
- if (!fp)
- return;
+ fp = xfopen_for_read(PROCFS_STAT);
while (fgets(buf, sizeof(buf), fp)) {
//bb_error_msg("/proc/stat:'%s'", buf);
- if (strncmp(buf, "intr ", 5) == 0) {
+ if (is_prefixed_with(buf, "intr ")) {
/* Read total number of IRQs since system boot */
sscanf(buf + 5, "%"FMT_DATA"u", &irq->irq_nr);
}
@@ -644,9 +642,7 @@ static void get_uptime(data_t *uptime)
char buf[sizeof(long)*3 * 2 + 4]; /* enough for long.long */
unsigned long uptime_sec, decimal;
- fp = fopen_for_read(PROCFS_UPTIME);
- if (!fp)
- return;
+ fp = xfopen_for_read(PROCFS_UPTIME);
if (fgets(buf, sizeof(buf), fp)) {
if (sscanf(buf, "%lu.%lu", &uptime_sec, &decimal) == 2) {
*uptime = (data_t)uptime_sec * G.hz + decimal * G.hz / 100;
@@ -775,12 +771,6 @@ static void main_loop(void)
/* Initialization */
-/* Get number of clock ticks per sec */
-static ALWAYS_INLINE unsigned get_hz(void)
-{
- return sysconf(_SC_CLK_TCK);
-}
-
static void alloc_struct(int cpus)
{
int i;
@@ -873,7 +863,7 @@ int mpstat_main(int UNUSED_PARAM argc, char **argv)
G.cpu_nr = get_cpu_count();
/* Get number of clock ticks per sec */
- G.hz = get_hz();
+ G.hz = bb_clk_tck();
/* Calculate number of interrupts per processor */
G.irqcpu_nr = get_irqcpu_nr(PROCFS_INTERRUPTS, NR_IRQS) + NR_IRQCPU_PREALLOC;
diff --git a/procps/nmeter.c b/procps/nmeter.c
index 5d5b83b..3eac2d3 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -21,7 +21,7 @@
//usage:#define nmeter_full_usage "\n\n"
//usage: "Monitor system in real time"
//usage: "\n"
-//usage: "\n -d MSEC Milliseconds between updates (default:1000)"
+//usage: "\n -d MSEC Milliseconds between updates, default:1000, none:-1"
//usage: "\n"
//usage: "\nFormat specifiers:"
//usage: "\n %Nc or %[cN] CPU. N - bar size (default:10)"
@@ -53,6 +53,7 @@
// totalswap=134209536, freeswap=134209536, procs=157})
#include "libbb.h"
+#include "common_bufsiz.h"
typedef unsigned long long ullong;
@@ -83,10 +84,10 @@ struct globals {
smallint is26;
// 1 if sample delay is not an integer fraction of a second
smallint need_seconds;
+ char final_char;
char *cur_outbuf;
- const char *final_str;
int delta;
- int deltanz;
+ unsigned deltanz;
struct timeval tv;
#define first_proc_file proc_stat
proc_file proc_stat; // Must match the order of proc_name's!
@@ -101,9 +102,6 @@ struct globals {
#define is26 (G.is26 )
#define need_seconds (G.need_seconds )
#define cur_outbuf (G.cur_outbuf )
-#define final_str (G.final_str )
-#define delta (G.delta )
-#define deltanz (G.deltanz )
#define tv (G.tv )
#define proc_stat (G.proc_stat )
#define proc_loadavg (G.proc_loadavg )
@@ -111,16 +109,15 @@ struct globals {
#define proc_meminfo (G.proc_meminfo )
#define proc_diskstats (G.proc_diskstats )
#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
+#define outbuf bb_common_bufsiz1
#define INIT_G() do { \
+ setup_common_bufsiz(); \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
cur_outbuf = outbuf; \
- final_str = "\n"; \
- deltanz = delta = 1000000; \
+ G.final_char = '\n'; \
+ G.deltanz = G.delta = 1000000; \
} while (0)
-// We depend on this being a char[], not char* - we take sizeof() of it
-#define outbuf bb_common_bufsiz1
-
static inline void reset_outbuf(void)
{
cur_outbuf = outbuf;
@@ -142,16 +139,16 @@ static void print_outbuf(void)
static void put(const char *s)
{
- int sz = strlen(s);
- if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
- sz = outbuf + sizeof(outbuf) - cur_outbuf;
- memcpy(cur_outbuf, s, sz);
- cur_outbuf += sz;
+ char *p = cur_outbuf;
+ int sz = outbuf + COMMON_BUFSIZE - p;
+ while (*s && --sz >= 0)
+ *p++ = *s++;
+ cur_outbuf = p;
}
static void put_c(char c)
{
- if (cur_outbuf < outbuf + sizeof(outbuf))
+ if (cur_outbuf < outbuf + COMMON_BUFSIZE)
*cur_outbuf++ = c;
}
@@ -208,65 +205,50 @@ static ullong read_after_slash(const char *p)
return strtoull(p+1, NULL, 10);
}
-enum conv_type { conv_decimal, conv_slash };
+enum conv_type {
+ conv_decimal = 0,
+ conv_slash = 1
+};
// Reads decimal values from line. Values start after key, for example:
// "cpu 649369 0 341297 4336769..." - key is "cpu" here.
-// Values are stored in vec[]. arg_ptr has list of positions
-// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
-static int vrdval(const char* p, const char* key,
- enum conv_type conv, ullong *vec, va_list arg_ptr)
+// Values are stored in vec[].
+// posbits is a bit lit of positions we are interested in.
+// for example: 00100110 - we want 1st, 2nd and 5th value.
+// posbits.bit0 encodes conversion type.
+static int rdval(const char* p, const char* key, ullong *vec, long posbits)
{
- int indexline;
- int indexnext;
+ unsigned curpos;
p = strstr(p, key);
if (!p) return 1;
p += strlen(key);
- indexline = 1;
- indexnext = va_arg(arg_ptr, int);
+ curpos = 1 << 1;
while (1) {
while (*p == ' ' || *p == '\t') p++;
if (*p == '\n' || *p == '\0') break;
- if (indexline == indexnext) { // read this value
- *vec++ = conv==conv_decimal ?
+ if (curpos & posbits) { // read this value
+ *vec++ = (posbits & 1) == conv_decimal ?
strtoull(p, NULL, 10) :
read_after_slash(p);
- indexnext = va_arg(arg_ptr, int);
+ posbits -= curpos;
+ if (posbits <= 1)
+ return 0;
}
- while (*p > ' ') p++; // skip over value
- indexline++;
+ while (*p > ' ') // skip over the value
+ p++;
+ curpos <<= 1;
}
return 0;
}
-// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
-// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
-// value# start with 1
-static int rdval(const char* p, const char* key, ullong *vec, ...)
-{
- va_list arg_ptr;
- int result;
-
- va_start(arg_ptr, vec);
- result = vrdval(p, key, conv_decimal, vec, arg_ptr);
- va_end(arg_ptr);
-
- return result;
-}
-
// Parses files with lines like "... ... ... 3/148 ...."
-static int rdval_loadavg(const char* p, ullong *vec, ...)
+static int rdval_loadavg(const char* p, ullong *vec, long posbits)
{
- va_list arg_ptr;
int result;
-
- va_start(arg_ptr, vec);
- result = vrdval(p, "", conv_slash, vec, arg_ptr);
- va_end(arg_ptr);
-
+ result = rdval(p, "", vec, posbits | conv_slash);
return result;
}
@@ -337,7 +319,6 @@ static void scale(ullong ul)
put(buf);
}
-
#define S_STAT(a) \
typedef struct a { \
struct s_stat *next; \
@@ -359,21 +340,12 @@ static s_stat* init_literal(void)
return (s_stat*)s;
}
-static s_stat* init_delay(const char *param)
-{
- delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
- deltanz = delta > 0 ? delta : 1;
- need_seconds = (1000000%deltanz) != 0;
- return NULL;
-}
-
static s_stat* init_cr(const char *param UNUSED_PARAM)
{
- final_str = "\r";
- return (s_stat*)0;
+ G.final_char = '\r';
+ return NULL;
}
-
// user nice system idle iowait irq softirq (last 3 only in 2.6)
//cpu 649369 0 341297 4336769 11640 7122 1183
//cpuN 649369 0 341297 4336769 11640 7122 1183
@@ -381,10 +353,9 @@ enum { CPU_FIELDCNT = 7 };
S_STAT(cpu_stat)
ullong old[CPU_FIELDCNT];
int bar_sz;
- char *bar;
+ char bar[1];
S_STAT_END(cpu_stat)
-
static void FAST_FUNC collect_cpu(cpu_stat *s)
{
ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
@@ -395,7 +366,15 @@ static void FAST_FUNC collect_cpu(cpu_stat *s)
char *bar = s->bar;
int i;
- if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
+ if (rdval(get_file(&proc_stat), "cpu ", data, 0
+ | (1 << 1)
+ | (1 << 2)
+ | (1 << 3)
+ | (1 << 4)
+ | (1 << 5)
+ | (1 << 6)
+ | (1 << 7))
+ ) {
put_question_marks(bar_sz);
return;
}
@@ -438,22 +417,20 @@ static void FAST_FUNC collect_cpu(cpu_stat *s)
put(s->bar);
}
-
static s_stat* init_cpu(const char *param)
{
int sz;
- cpu_stat *s = xzalloc(sizeof(*s));
- s->collect = collect_cpu;
+ cpu_stat *s;
sz = strtoul(param, NULL, 0); /* param can be "" */
if (sz < 10) sz = 10;
if (sz > 1000) sz = 1000;
- s->bar = xzalloc(sz+1);
+ s = xzalloc(sizeof(*s) + sz);
/*s->bar[sz] = '\0'; - xzalloc did it */
s->bar_sz = sz;
+ s->collect = collect_cpu;
return (s_stat*)s;
}
-
S_STAT(int_stat)
ullong old;
int no;
@@ -464,7 +441,7 @@ static void FAST_FUNC collect_int(int_stat *s)
ullong data[1];
ullong old;
- if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
+ if (rdval(get_file(&proc_stat), "intr", data, 1 << s->no)) {
put_question_marks(4);
return;
}
@@ -488,7 +465,6 @@ static s_stat* init_int(const char *param)
return (s_stat*)s;
}
-
S_STAT(ctx_stat)
ullong old;
S_STAT_END(ctx_stat)
@@ -498,7 +474,7 @@ static void FAST_FUNC collect_ctx(ctx_stat *s)
ullong data[1];
ullong old;
- if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
+ if (rdval(get_file(&proc_stat), "ctxt", data, 1 << 1)) {
put_question_marks(4);
return;
}
@@ -516,7 +492,6 @@ static s_stat* init_ctx(const char *param UNUSED_PARAM)
return (s_stat*)s;
}
-
S_STAT(blk_stat)
const char* lookfor;
ullong old[2];
@@ -530,7 +505,10 @@ static void FAST_FUNC collect_blk(blk_stat *s)
if (is26) {
i = rdval_diskstats(get_file(&proc_diskstats), data);
} else {
- i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
+ i = rdval(get_file(&proc_stat), s->lookfor, data, 0
+ | (1 << 1)
+ | (1 << 2)
+ );
// Linux 2.4 reports bio in Kbytes, convert to sectors:
data[0] *= 2;
data[1] *= 2;
@@ -559,7 +537,6 @@ static s_stat* init_blk(const char *param UNUSED_PARAM)
return (s_stat*)s;
}
-
S_STAT(fork_stat)
ullong old;
S_STAT_END(fork_stat)
@@ -568,7 +545,7 @@ static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
{
ullong data[1];
- if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
+ if (rdval_loadavg(get_file(&proc_loadavg), data, 1 << 4)) {
put_question_marks(4);
return;
}
@@ -580,7 +557,7 @@ static void FAST_FUNC collect_fork(fork_stat *s)
ullong data[1];
ullong old;
- if (rdval(get_file(&proc_stat), "processes", data, 1)) {
+ if (rdval(get_file(&proc_stat), "processes", data, 1 << 1)) {
put_question_marks(4);
return;
}
@@ -602,7 +579,6 @@ static s_stat* init_fork(const char *param)
return (s_stat*)s;
}
-
S_STAT(if_stat)
ullong old[4];
const char *device;
@@ -614,7 +590,12 @@ static void FAST_FUNC collect_if(if_stat *s)
ullong data[4];
int i;
- if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
+ if (rdval(get_file(&proc_net_dev), s->device_colon, data, 0
+ | (1 << 1)
+ | (1 << 3)
+ | (1 << 9)
+ | (1 << 11))
+ ) {
put_question_marks(10);
return;
}
@@ -644,7 +625,6 @@ static s_stat* init_if(const char *device)
return (s_stat*)s;
}
-
S_STAT(mem_stat)
char opt;
S_STAT_END(mem_stat)
@@ -692,7 +672,7 @@ static void FAST_FUNC collect_mem(mem_stat *s)
ullong m_cached = 0;
ullong m_slab = 0;
- if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
+ if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1 << 1)) {
put_question_marks(4);
return;
}
@@ -701,10 +681,10 @@ static void FAST_FUNC collect_mem(mem_stat *s)
return;
}
- if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1)
- || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1)
- || rdval(proc_meminfo.file, "Cached:", &m_cached, 1)
- || rdval(proc_meminfo.file, "Slab:", &m_slab , 1)
+ if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1 << 1)
+ || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1 << 1)
+ || rdval(proc_meminfo.file, "Cached:", &m_cached, 1 << 1)
+ || rdval(proc_meminfo.file, "Slab:", &m_slab , 1 << 1)
) {
put_question_marks(4);
return;
@@ -727,7 +707,6 @@ static s_stat* init_mem(const char *param)
return (s_stat*)s;
}
-
S_STAT(swp_stat)
S_STAT_END(swp_stat)
@@ -735,8 +714,8 @@ static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
{
ullong s_total[1];
ullong s_free[1];
- if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
- || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1)
+ if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1 << 1)
+ || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1 << 1)
) {
put_question_marks(4);
return;
@@ -751,7 +730,6 @@ static s_stat* init_swp(const char *param UNUSED_PARAM)
return (s_stat*)s;
}
-
S_STAT(fd_stat)
S_STAT_END(fd_stat)
@@ -759,7 +737,10 @@ static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
{
ullong data[2];
- if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
+ if (rdval(get_file(&proc_sys_fs_filenr), "", data, 0
+ | (1 << 1)
+ | (1 << 2))
+ ) {
put_question_marks(4);
return;
}
@@ -774,17 +755,16 @@ static s_stat* init_fd(const char *param UNUSED_PARAM)
return (s_stat*)s;
}
-
S_STAT(time_stat)
- int prec;
- int scale;
+ unsigned prec;
+ unsigned scale;
S_STAT_END(time_stat)
static void FAST_FUNC collect_time(time_stat *s)
{
char buf[sizeof("12:34:56.123456")];
struct tm* tm;
- int us = tv.tv_usec + s->scale/2;
+ unsigned us = tv.tv_usec + s->scale/2;
time_t t = tv.tv_sec;
if (us >= 1000000) {
@@ -825,11 +805,9 @@ static void FAST_FUNC collect_info(s_stat *s)
}
}
-
typedef s_stat* init_func(const char *param);
-// Deprecated %NNNd is to be removed, -d MSEC supersedes it
-static const char options[] ALIGN1 = "ncmsfixptbdr";
+static const char options[] ALIGN1 = "ncmsfixptbr";
static init_func *const init_functions[] = {
init_if,
init_cpu,
@@ -841,7 +819,6 @@ static init_func *const init_functions[] = {
init_fork,
init_time,
init_blk,
- init_delay,
init_cr
};
@@ -864,8 +841,11 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
is26 = (strstr(buf, " 2.4.") == NULL);
}
- if (getopt32(argv, "d:", &opt_d))
- init_delay(opt_d);
+ if (getopt32(argv, "d:", &opt_d)) {
+ G.delta = xatoi(opt_d) * 1000;
+ G.deltanz = G.delta > 0 ? G.delta : 1;
+ need_seconds = (1000000 % G.deltanz) != 0;
+ }
argv += optind;
if (!argv[0])
@@ -920,8 +900,8 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
last->next = s;
last = s;
} else {
- // %NNNNd or %r option. remove it from string
- strcpy(prev + strlen(prev), cur);
+ // %r option. remove it from string
+ overlapping_strcpy(prev + strlen(prev), cur);
cur = prev;
}
}
@@ -939,15 +919,15 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
// Generate first samples but do not print them, they're bogus
collect_info(first);
reset_outbuf();
- if (delta >= 0) {
+ if (G.delta >= 0) {
gettimeofday(&tv, NULL);
- usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
+ usleep(G.delta > 1000000 ? 1000000 : G.delta - tv.tv_usec % G.deltanz);
}
while (1) {
gettimeofday(&tv, NULL);
collect_info(first);
- put(final_str);
+ put_c(G.final_char);
print_outbuf();
// Negative delta -> no usleep at all
@@ -955,18 +935,18 @@ int nmeter_main(int argc UNUSED_PARAM, char **argv)
// time resolution ;)
// TODO: detect and avoid useless updates
// (like: nothing happens except time)
- if (delta >= 0) {
+ if (G.delta >= 0) {
int rem;
// can be commented out, will sacrifice sleep time precision a bit
gettimeofday(&tv, NULL);
if (need_seconds)
- rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
+ rem = G.delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % G.deltanz;
else
- rem = delta - tv.tv_usec%deltanz;
+ rem = G.delta - (unsigned)tv.tv_usec % G.deltanz;
// Sometimes kernel wakes us up just a tiny bit earlier than asked
// Do not go to very short sleep in this case
- if (rem < delta/128) {
- rem += delta;
+ if (rem < (unsigned)G.delta / 128) {
+ rem += G.delta;
}
usleep(rem);
}
diff --git a/procps/pgrep.c b/procps/pgrep.c
index 4ab458b..ac82b51 100644
--- a/procps/pgrep.c
+++ b/procps/pgrep.c
@@ -6,6 +6,23 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config PGREP
+//config: bool "pgrep"
+//config: default y
+//config: help
+//config: Look for processes by name.
+//config:
+//config:config PKILL
+//config: bool "pkill"
+//config: default y
+//config: help
+//config: Send signals to processes by name.
+
+//applet:IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
+
+//kbuild:lib-$(CONFIG_PGREP) += pgrep.o
+//kbuild:lib-$(CONFIG_PKILL) += pgrep.o
//usage:#define pgrep_trivial_usage
//usage: "[-flnovx] [-s SID|-P PPID|PATTERN]"
@@ -106,8 +123,7 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
/* Parse remaining options */
ppid2match = -1;
sid2match = -1;
- opt_complementary = "s+:P+"; /* numeric opts */
- opt = getopt32(argv, "vlfxons:P:", &sid2match, &ppid2match);
+ opt = getopt32(argv, "vlfxons:+P:+", &sid2match, &ppid2match);
argv += optind;
if (pkill && OPT_LIST) { /* -l: print the whole signal list */
@@ -150,9 +166,9 @@ int pgrep_main(int argc UNUSED_PARAM, char **argv)
}
}
- if (ppid2match >= 0 && (pid_t) ppid2match != (pid_t) proc->ppid)
+ if (ppid2match >= 0 && ppid2match != proc->ppid)
continue;
- if (sid2match >= 0 && sid2match != (int) proc->sid)
+ if (sid2match >= 0 && sid2match != proc->sid)
continue;
/* NB: OPT_INVERT is always 0 or 1 */
diff --git a/procps/pidof.c b/procps/pidof.c
index 6d7b591..069adb7 100644
--- a/procps/pidof.c
+++ b/procps/pidof.c
@@ -6,6 +6,32 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config PIDOF
+//config: bool "pidof"
+//config: default y
+//config: help
+//config: Pidof finds the process id's (pids) of the named programs. It prints
+//config: those id's on the standard output.
+//config:
+//config:config FEATURE_PIDOF_SINGLE
+//config: bool "Enable argument for single shot (-s)"
+//config: default y
+//config: depends on PIDOF
+//config: help
+//config: Support argument '-s' for returning only the first pid found.
+//config:
+//config:config FEATURE_PIDOF_OMIT
+//config: bool "Enable argument for omitting pids (-o)"
+//config: default y
+//config: depends on PIDOF
+//config: help
+//config: Support argument '-o' for omitting the given pids in output.
+//config: The special pid %PPID can be used to name the parent process
+//config: of the pidof, in other words the calling shell or shell script.
+
+//applet:IF_PIDOF(APPLET(pidof, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PIDOF) += pidof.o
//usage:#if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
//usage:#define pidof_trivial_usage
@@ -51,13 +77,12 @@ int pidof_main(int argc UNUSED_PARAM, char **argv)
unsigned opt;
#if ENABLE_FEATURE_PIDOF_OMIT
llist_t *omits = NULL; /* list of pids to omit */
- opt_complementary = "o::";
#endif
/* do unconditional option parsing */
opt = getopt32(argv, ""
IF_FEATURE_PIDOF_SINGLE ("s")
- IF_FEATURE_PIDOF_OMIT("o:", &omits));
+ IF_FEATURE_PIDOF_OMIT("o:*", &omits));
#if ENABLE_FEATURE_PIDOF_OMIT
/* fill omit list. */
diff --git a/procps/pmap.c b/procps/pmap.c
index fd995a5..aa221cf 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -20,7 +20,7 @@
//usage:#define pmap_trivial_usage
//usage: "[-xq] PID"
//usage:#define pmap_full_usage "\n\n"
-//usage: "Display detailed process memory usage"
+//usage: "Display process memory usage"
//usage: "\n"
//usage: "\n -x Show details"
//usage: "\n -q Quiet"
@@ -66,7 +66,7 @@ static int procps_get_maps(pid_t pid, unsigned opt)
int ret;
char buf[256];
- read_cmdline(buf, sizeof(buf), pid, "no such process");
+ read_cmdline(buf, sizeof(buf), pid, NULL);
printf("%u: %s\n", (int)pid, buf);
if (!(opt & OPT_q) && (opt & OPT_x))
diff --git a/procps/powertop.c b/procps/powertop.c
index e3c29d1..ce85f41 100644
--- a/procps/powertop.c
+++ b/procps/powertop.c
@@ -360,7 +360,7 @@ static void process_irq_counts(void)
}
name = p;
- strchrnul(name, '\n')[0] = '\0';
+ chomp(p);
/* Save description of the interrupt */
if (nr >= 20000)
sprintf(irq_desc, " <kernel IPI> : %s", name);
@@ -458,9 +458,9 @@ static NOINLINE int process_timer_stats(void)
// func = "Load balancing tick";
//}
- if (strncmp(func, "tick_nohz_", 10) == 0)
+ if (is_prefixed_with(func, "tick_nohz_"))
continue;
- if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
+ if (is_prefixed_with(func, "tick_setup_sched_timer"))
continue;
//if (strcmp(process, "powertop") == 0)
// continue;
@@ -470,7 +470,7 @@ static NOINLINE int process_timer_stats(void)
process = idx < 2 ? "[kernel module]" : "<kernel core>";
}
- strchrnul(p, '\n')[0] = '\0';
+ chomp(p);
// 46D\01136\0kondemand/1\0do_dbs_timer (delayed_work_timer_fn)
// ^ ^ ^
@@ -591,7 +591,7 @@ static NOINLINE void print_intel_cstates(void)
if (!edx || !(ecx & 1))
return;
- printf("Your CPU supports the following C-states: ");
+ printf("Your %s the following C-states: ", "CPU supports");
i = 0;
while (edx) {
if (edx & 7)
@@ -602,7 +602,7 @@ static NOINLINE void print_intel_cstates(void)
bb_putchar('\n');
/* Print BIOS C-States */
- printf("Your BIOS reports the following C-states: ");
+ printf("Your %s the following C-states: ", "BIOS reports");
for (i = 0; i < ARRAY_SIZE(bios_table); i++)
if (bios_table[i])
printf("C%u ", i);
@@ -674,7 +674,7 @@ static void show_timerstats(void)
//usage:#define powertop_trivial_usage
//usage: ""
//usage:#define powertop_full_usage "\n\n"
-//usage: "Analyze power consumption on Intel-based laptops\n"
+//usage: "Analyze power consumption on Intel-based laptops"
int powertop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
@@ -704,7 +704,7 @@ int powertop_main(int UNUSED_PARAM argc, char UNUSED_PARAM **argv)
/* Get number of CPUs */
G.total_cpus = get_cpu_count();
- printf("Collecting data for "DEFAULT_SLEEP_STR" seconds\n");
+ puts("Collecting data for "DEFAULT_SLEEP_STR" seconds");
#if ENABLE_FEATURE_USE_TERMIOS
tcgetattr(0, (void *)&G.init_settings);
diff --git a/procps/ps.c b/procps/ps.c
index ba8bc9a..e291ecd 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -8,6 +8,55 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config PS
+//config: bool "ps"
+//config: default y
+//config: help
+//config: ps gives a snapshot of the current processes.
+//config:
+//config:config FEATURE_PS_WIDE
+//config: bool "Enable wide output option (-w)"
+//config: default y
+//config: depends on PS && !DESKTOP
+//config: help
+//config: Support argument 'w' for wide output.
+//config: If given once, 132 chars are printed, and if given more
+//config: than once, the length is unlimited.
+//config:
+//config:config FEATURE_PS_LONG
+//config: bool "Enable long output option (-l)"
+//config: default y
+//config: depends on PS && !DESKTOP
+//config: help
+//config: Support argument 'l' for long output.
+//config: Adds fields PPID, RSS, START, TIME & TTY
+//config:
+//config:config FEATURE_PS_TIME
+//config: bool "Enable time and elapsed time output"
+//config: default y
+//config: depends on PS && DESKTOP
+//config: select PLATFORM_LINUX
+//config: help
+//config: Support -o time and -o etime output specifiers.
+//config:
+//config:config FEATURE_PS_ADDITIONAL_COLUMNS
+//config: bool "Enable additional ps columns"
+//config: default y
+//config: depends on PS && DESKTOP
+//config: help
+//config: Support -o rgroup, -o ruser, -o nice output specifiers.
+//config:
+//config:config FEATURE_PS_UNUSUAL_SYSTEMS
+//config: bool "Support Linux prior to 2.4.0 and non-ELF systems"
+//config: default n
+//config: depends on FEATURE_PS_TIME
+//config: help
+//config: Include support for measuring HZ on old kernels and non-ELF systems
+//config: (if you are on Linux 2.4.0+ and use ELF, you don't need this)
+
+//applet:IF_PS(APPLET(ps, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PS) += ps.o
//usage:#if ENABLE_DESKTOP
//usage:
@@ -62,6 +111,7 @@
//usage: " 2990 andersen andersen R ps\n"
#include "libbb.h"
+#include "common_bufsiz.h"
#ifdef __linux__
# include <sys/sysinfo.h>
#endif
@@ -144,7 +194,7 @@ struct globals {
unsigned long seconds_since_boot;
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define out (G.out )
#define out_cnt (G.out_cnt )
#define print_header (G.print_header )
@@ -152,7 +202,7 @@ struct globals {
#define buffer (G.buffer )
#define terminal_width (G.terminal_width )
#define kernel_HZ (G.kernel_HZ )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#if ENABLE_FEATURE_PS_TIME
/* for ELF executables, notes are pushed before environment and args */
@@ -592,8 +642,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
* procps v3.2.7 supports -T and shows tids as SPID column,
* it also supports -L where it shows tids as LWP column.
*/
- opt_complementary = "o::";
- opt = getopt32(argv, "Zo:aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
+ opt = getopt32(argv, "Zo:*aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
if (opt_o) {
do {
parse_o(llist_pop(&opt_o));
@@ -622,7 +671,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
* and such large widths */
terminal_width = MAX_WIDTH;
if (isatty(1)) {
- get_terminal_width_height(0, &terminal_width, NULL);
+ terminal_width = get_terminal_width(0);
if (--terminal_width > MAX_WIDTH)
terminal_width = MAX_WIDTH;
}
@@ -654,13 +703,8 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
OPT_l = (1 << ENABLE_SELINUX) * (1 << ENABLE_FEATURE_SHOW_THREADS) * ENABLE_FEATURE_PS_LONG,
};
#if ENABLE_FEATURE_PS_LONG
- #ifdef __BIONIC__
- time_t now = 0;
- long uptime = 0;
- #else
- time_t now = now;
- unsigned long uptime;
- #endif
+ time_t now = now; /* for compiler */
+ unsigned long uptime = uptime;
#endif
/* If we support any options, parse argv */
#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE || ENABLE_FEATURE_PS_LONG
@@ -677,7 +721,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
if (w_count) {
terminal_width = (w_count == 1) ? 132 : MAX_WIDTH;
} else {
- get_terminal_width_height(0, &terminal_width, NULL);
+ terminal_width = get_terminal_width(0);
/* Go one less... */
if (--terminal_width > MAX_WIDTH)
terminal_width = MAX_WIDTH;
@@ -791,9 +835,11 @@ int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
{
int sz = terminal_width - len;
- char buf[sz + 1];
- read_cmdline(buf, sz, p->pid, p->comm);
- puts(buf);
+ if (sz >= 0) {
+ char buf[sz + 1];
+ read_cmdline(buf, sz, p->pid, p->comm);
+ puts(buf);
+ }
}
}
if (ENABLE_FEATURE_CLEAN_UP)
diff --git a/procps/pstree.c b/procps/pstree.c
index 2ff5ccb..000959c 100644
--- a/procps/pstree.c
+++ b/procps/pstree.c
@@ -381,7 +381,7 @@ int pstree_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
- get_terminal_width_height(0, &G.output_width, NULL);
+ G.output_width = get_terminal_width(0);
opt_complementary = "?1";
getopt32(argv, "p");
diff --git a/procps/pwdx.c b/procps/pwdx.c
index 7818104..4e34149 100644
--- a/procps/pwdx.c
+++ b/procps/pwdx.c
@@ -21,7 +21,7 @@
//usage:#define pwdx_trivial_usage
//usage: "PID..."
//usage:#define pwdx_full_usage "\n\n"
-//usage: "Show current directory for PIDs\n"
+//usage: "Show current directory for PIDs"
#include "libbb.h"
@@ -41,7 +41,7 @@ int pwdx_main(int argc UNUSED_PARAM, char **argv)
// Allowed on the command line:
// /proc/NUM
// NUM
- if (strncmp(arg, "/proc/", 6) == 0)
+ if (is_prefixed_with(arg, "/proc/"))
arg += 6;
pid = bb_strtou(arg, NULL, 10);
diff --git a/procps/renice.c b/procps/renice.c
index 77f400a..64213c6 100644
--- a/procps/renice.c
+++ b/procps/renice.c
@@ -18,15 +18,26 @@
* options -p, -g, and -u are treated as mode switches for the
* following IDs (if any). Multiple switches are allowed.
*/
+//config:config RENICE
+//config: bool "renice"
+//config: default y
+//config: help
+//config: Renice alters the scheduling priority of one or more running
+//config: processes.
+
+//applet:IF_RENICE(APPLET(renice, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RENICE) += renice.o
//usage:#define renice_trivial_usage
-//usage: "{{-n INCREMENT} | PRIORITY} [[-p | -g | -u] ID...]"
+//usage: "[-n] PRIORITY [[-p | -g | -u] ID...]..."
//usage:#define renice_full_usage "\n\n"
-//usage: "Change scheduling priority for a running process\n"
-//usage: "\n -n Adjust current nice value (smaller is faster)"
-//usage: "\n -p Process id(s) (default)"
-//usage: "\n -g Process group id(s)"
-//usage: "\n -u Process user name(s) and/or id(s)"
+//usage: "Change scheduling priority of a running process\n"
+//usage: "\n -n Add PRIORITY to current nice value"
+//usage: "\n Without -n, nice value is set to PRIORITY"
+//usage: "\n -p Process ids (default)"
+//usage: "\n -g Process group ids"
+//usage: "\n -u Process user names"
#include "libbb.h"
#include <sys/resource.h>
diff --git a/procps/sysctl.c b/procps/sysctl.c
index fe70bfd..16f91dc 100644
--- a/procps/sysctl.c
+++ b/procps/sysctl.c
@@ -10,6 +10,15 @@
* v1.01 - added -p <preload> to preload values from a file
* v1.01.1 - busybox applet aware by <solar@gentoo.org>
*/
+//config:config BB_SYSCTL
+//config: bool "sysctl"
+//config: default y
+//config: help
+//config: Configure kernel parameters at runtime.
+
+//applet:IF_BB_SYSCTL(APPLET(sysctl, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BB_SYSCTL) += sysctl.o
//usage:#define sysctl_trivial_usage
//usage: "[OPTIONS] [KEY[=VALUE]]..."
@@ -129,6 +138,9 @@ static int sysctl_act_on_setting(char *setting)
if (fd < 0) {
switch (errno) {
+ case EACCES:
+ /* Happens for write-only settings, e.g. net.ipv6.route.flush */
+ goto end;
case ENOENT:
if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
bb_error_msg("error: '%s' is an unknown key", outname);
diff --git a/procps/top.c b/procps/top.c
index 51f1c1a..71207ba 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -49,7 +49,6 @@
* cp stat meminfo loadavg proc
* chroot . ./top -bn1 >top1.out
*/
-
//config:config TOP
//config: bool "top"
//config: default y
@@ -104,7 +103,12 @@
//config: help
//config: Enable 's' in top (gives lots of memory info).
+//applet:IF_TOP(APPLET(top, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_TOP) += top.o
+
#include "libbb.h"
+#include "common_bufsiz.h"
typedef struct top_status_t {
@@ -183,11 +187,7 @@ struct globals {
char line_buf[80];
}; //FIX_ALIASING; - large code growth
enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
-#define G (*(struct globals*)&bb_common_bufsiz1)
-struct BUG_bad_size {
- char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
- char BUG_line_buf_too_small[LINE_BUF_SIZE > 80 ? 1 : -1];
-};
+#define G (*(struct globals*)bb_common_bufsiz1)
#define top (G.top )
#define ntop (G.ntop )
#define sort_field (G.sort_field )
@@ -204,7 +204,11 @@ struct BUG_bad_size {
#define num_cpus (G.num_cpus )
#define total_pcpu (G.total_pcpu )
#define line_buf (G.line_buf )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { \
+ setup_common_bufsiz(); \
+ BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
+ BUILD_BUG_ON(LINE_BUF_SIZE <= 80); \
+} while (0)
enum {
OPT_d = (1 << 0),
@@ -264,9 +268,9 @@ static int mult_lvl_cmp(void* a, void* b)
static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
{
#if !ENABLE_FEATURE_TOP_SMP_CPU
- static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
+ static const char fmt[] ALIGN1 = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
#else
- static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
+ static const char fmt[] ALIGN1 = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
#endif
int ret;
@@ -499,85 +503,93 @@ static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
#endif
-static unsigned long display_header(int scr_width, int *lines_rem_p)
-{
- FILE *fp;
- char buf[80];
- char scrbuf[80];
- unsigned long total, used, mfree, shared, buffers, cached;
-
- /* read memory info */
- fp = xfopen_for_read("meminfo");
+enum {
+ MI_MEMTOTAL,
+ MI_MEMFREE,
+ MI_MEMSHARED,
+ MI_SHMEM,
+ MI_BUFFERS,
+ MI_CACHED,
+ MI_SWAPTOTAL,
+ MI_SWAPFREE,
+ MI_DIRTY,
+ MI_WRITEBACK,
+ MI_ANONPAGES,
+ MI_MAPPED,
+ MI_SLAB,
+ MI_MAX
+};
- /*
- * Old kernels (such as 2.4.x) had a nice summary of memory info that
- * we could parse, however this is gone entirely in 2.6. Try parsing
- * the old way first, and if that fails, parse each field manually.
- *
- * First, we read in the first line. Old kernels will have bogus
- * strings we don't care about, whereas new kernels will start right
- * out with MemTotal:
- * -- PFM.
- */
- if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
- fgets(buf, sizeof(buf), fp); /* skip first line */
-
- fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
- &total, &used, &mfree, &shared, &buffers, &cached);
- /* convert to kilobytes */
- used /= 1024;
- mfree /= 1024;
- shared /= 1024;
- buffers /= 1024;
- cached /= 1024;
- total /= 1024;
- } else {
- /*
- * Revert to manual parsing, which incidentally already has the
- * sizes in kilobytes. This should be safe for both 2.4 and
- * 2.6.
- */
- fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
+static void parse_meminfo(unsigned long meminfo[MI_MAX])
+{
+ static const char fields[] ALIGN1 =
+ "MemTotal\0"
+ "MemFree\0"
+ "MemShared\0"
+ "Shmem\0"
+ "Buffers\0"
+ "Cached\0"
+ "SwapTotal\0"
+ "SwapFree\0"
+ "Dirty\0"
+ "Writeback\0"
+ "AnonPages\0"
+ "Mapped\0"
+ "Slab\0";
+ char buf[60]; /* actual lines we expect are ~30 chars or less */
+ FILE *f;
+ int i;
- /*
- * MemShared: is no longer present in 2.6. Report this as 0,
- * to maintain consistent behavior with normal procps.
- */
- if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
- shared = 0;
+ memset(meminfo, 0, sizeof(meminfo[0]) * MI_MAX);
+ f = xfopen_for_read("meminfo");
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *c = strchr(buf, ':');
+ if (!c)
+ continue;
+ *c = '\0';
+ i = index_in_strings(fields, buf);
+ if (i >= 0)
+ meminfo[i] = strtoul(c+1, NULL, 10);
+ }
+ fclose(f);
+}
- fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
- fscanf(fp, "Cached: %lu %s\n", &cached, buf);
+static unsigned long display_header(int scr_width, int *lines_rem_p)
+{
+ char scrbuf[100]; /* [80] was a bit too low on 8Gb ram box */
+ char *buf;
+ unsigned long meminfo[MI_MAX];
- used = total - mfree;
- }
- fclose(fp);
+ parse_meminfo(meminfo);
- /* output memory info */
+ /* Output memory info */
if (scr_width > (int)sizeof(scrbuf))
scr_width = sizeof(scrbuf);
snprintf(scrbuf, scr_width,
"Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
- used, mfree, shared, buffers, cached);
- /* go to top & clear to the end of screen */
+ meminfo[MI_MEMTOTAL] - meminfo[MI_MEMFREE],
+ meminfo[MI_MEMFREE],
+ meminfo[MI_MEMSHARED] + meminfo[MI_SHMEM],
+ meminfo[MI_BUFFERS],
+ meminfo[MI_CACHED]);
+ /* Go to top & clear to the end of screen */
printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf);
(*lines_rem_p)--;
- /* Display CPU time split as percentage of total time
- * This displays either a cumulative line or one line per CPU
+ /* Display CPU time split as percentage of total time.
+ * This displays either a cumulative line or one line per CPU.
*/
display_cpus(scr_width, scrbuf, lines_rem_p);
- /* read load average as a string */
- buf[0] = '\0';
- open_read_close("loadavg", buf, sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = '\n';
- *strchr(buf, '\n') = '\0';
- snprintf(scrbuf, scr_width, "Load average: %s", buf);
+ /* Read load average as a string */
+ buf = stpcpy(scrbuf, "Load average: ");
+ open_read_close("loadavg", buf, sizeof(scrbuf) - sizeof("Load average: "));
+ scrbuf[scr_width - 1] = '\0';
+ strchrnul(buf, '\n')[0] = '\0';
puts(scrbuf);
(*lines_rem_p)--;
- return total;
+ return meminfo[MI_MEMTOTAL];
}
static NOINLINE void display_process_list(int lines_rem, int scr_width)
@@ -719,12 +731,6 @@ static void reset_term(void)
{
if (!OPT_BATCH_MODE)
tcsetattr_stdin_TCSANOW(&initial_settings);
- if (ENABLE_FEATURE_CLEAN_UP) {
- clearmems();
-# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
- free(prev_hist);
-# endif
- }
}
static void sig_catcher(int sig)
@@ -781,64 +787,31 @@ static int topmem_sort(char *a, char *b)
/* display header info (meminfo / loadavg) */
static void display_topmem_header(int scr_width, int *lines_rem_p)
{
- enum {
- TOTAL = 0, MFREE, BUF, CACHE,
- SWAPTOTAL, SWAPFREE, DIRTY,
- MWRITE, ANON, MAP, SLAB,
- NUM_FIELDS
- };
- static const char match[NUM_FIELDS][12] = {
- "\x09" "MemTotal:", // TOTAL
- "\x08" "MemFree:", // MFREE
- "\x08" "Buffers:", // BUF
- "\x07" "Cached:", // CACHE
- "\x0a" "SwapTotal:", // SWAPTOTAL
- "\x09" "SwapFree:", // SWAPFREE
- "\x06" "Dirty:", // DIRTY
- "\x0a" "Writeback:", // MWRITE
- "\x0a" "AnonPages:", // ANON
- "\x07" "Mapped:", // MAP
- "\x05" "Slab:", // SLAB
- };
- char meminfo_buf[4 * 1024];
- const char *Z[NUM_FIELDS];
- unsigned i;
- int sz;
-
- for (i = 0; i < NUM_FIELDS; i++)
- Z[i] = "?";
-
- /* read memory info */
- sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1);
- if (sz >= 0) {
- char *p = meminfo_buf;
- meminfo_buf[sz] = '\0';
- /* Note that fields always appear in the match[] order */
- for (i = 0; i < NUM_FIELDS; i++) {
- char *found = strstr(p, match[i] + 1);
- if (found) {
- /* Cut "NNNN" out of " NNNN kb" */
- char *s = skip_whitespace(found + match[i][0]);
- p = skip_non_whitespace(s);
- *p++ = '\0';
- Z[i] = s;
- }
- }
- }
+ unsigned long meminfo[MI_MAX];
+
+ parse_meminfo(meminfo);
snprintf(line_buf, LINE_BUF_SIZE,
- "Mem total:%s anon:%s map:%s free:%s",
- Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]);
+ "Mem total:%lu anon:%lu map:%lu free:%lu",
+ meminfo[MI_MEMTOTAL],
+ meminfo[MI_ANONPAGES],
+ meminfo[MI_MAPPED],
+ meminfo[MI_MEMFREE]);
printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
snprintf(line_buf, LINE_BUF_SIZE,
- " slab:%s buf:%s cache:%s dirty:%s write:%s",
- Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]);
+ " slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu",
+ meminfo[MI_SLAB],
+ meminfo[MI_BUFFERS],
+ meminfo[MI_CACHED],
+ meminfo[MI_DIRTY],
+ meminfo[MI_WRITEBACK]);
printf("%.*s\n", scr_width, line_buf);
snprintf(line_buf, LINE_BUF_SIZE,
- "Swap total:%s free:%s", // TODO: % used?
- Z[SWAPTOTAL], Z[SWAPFREE]);
+ "Swap total:%lu free:%lu", // TODO: % used?
+ meminfo[MI_SWAPTOTAL],
+ meminfo[MI_SWAPFREE]);
printf("%.*s\n", scr_width, line_buf);
(*lines_rem_p) -= 3;
@@ -855,10 +828,17 @@ static NOINLINE void display_topmem_process_list(int lines_rem, int scr_width)
#define HDR_STR " PID VSZ VSZRW RSS (SHR) DIRTY (SHR) STACK"
#define MIN_WIDTH sizeof(HDR_STR)
const topmem_status_t *s = topmem + G_scroll_ofs;
+ char *cp, ch;
display_topmem_header(scr_width, &lines_rem);
+
strcpy(line_buf, HDR_STR " COMMAND");
- line_buf[11 + sort_field * 6] = "^_"[inverted];
+ /* Mark the ^FIELD^ we sort by */
+ cp = &line_buf[5 + sort_field * 6];
+ ch = "^_"[inverted];
+ cp[6] = ch;
+ do *cp++ = ch; while (*cp == ' ');
+
printf(OPT_BATCH_MODE ? "%.*s" : "\e[7m%.*s\e[0m", scr_width, line_buf);
lines_rem--;
@@ -917,17 +897,12 @@ enum {
#if ENABLE_FEATURE_USE_TERMIOS
static unsigned handle_input(unsigned scan_mask, unsigned interval)
{
- struct pollfd pfd[1];
-
if (option_mask32 & OPT_EOF) {
/* EOF on stdin ("top </dev/null") */
sleep(interval);
return scan_mask;
}
- pfd[0].fd = 0;
- pfd[0].events = POLLIN;
-
while (1) {
int32_t c;
@@ -1067,7 +1042,9 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval)
//usage: "Provide a view of process activity in real time."
//usage: "\n""Read the status of all processes from /proc each SECONDS"
//usage: "\n""and display a screenful of them."
-//usage: "\n""Keys:"
+//usage: "\n"
+//usage: IF_FEATURE_USE_TERMIOS(
+//usage: "Keys:"
//usage: "\n"" N/M"
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/P")
//usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/T")
@@ -1087,6 +1064,7 @@ static unsigned handle_input(unsigned scan_mask, unsigned interval)
//usage: "\n"" Q,^C: exit"
//usage: "\n"
//usage: "\n""Options:"
+//usage: )
//usage: "\n"" -b Batch mode"
//usage: "\n"" -n N Exit after N iterations"
//usage: "\n"" -d N Delay between updates"
@@ -1203,10 +1181,8 @@ int top_main(int argc UNUSED_PARAM, char **argv)
ntop = 0;
while ((p = procps_scan(p, scan_mask)) != NULL) {
int n;
-#if ENABLE_FEATURE_TOPMEM
- if (scan_mask != TOPMEM_MASK)
-#endif
- {
+
+ IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
n = ntop;
top = xrealloc_vector(top, 6, ntop++);
top[n].pid = p->pid;
@@ -1246,7 +1222,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
break;
}
- if (scan_mask != TOPMEM_MASK) {
+ IF_FEATURE_TOPMEM(if (scan_mask != TOPMEM_MASK)) {
#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
if (!prev_hist_count) {
do_stats();
@@ -1260,17 +1236,13 @@ int top_main(int argc UNUSED_PARAM, char **argv)
#else
qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
#endif
+ display_process_list(G.lines, col);
}
#if ENABLE_FEATURE_TOPMEM
else { /* TOPMEM */
qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
- }
-#endif
- if (scan_mask != TOPMEM_MASK)
- display_process_list(G.lines, col);
-#if ENABLE_FEATURE_TOPMEM
- else
display_topmem_process_list(G.lines, col);
+ }
#endif
clearmems();
if (iterations >= 0 && !--iterations)
@@ -1279,12 +1251,18 @@ int top_main(int argc UNUSED_PARAM, char **argv)
sleep(interval);
#else
scan_mask = handle_input(scan_mask, interval);
-#endif /* FEATURE_USE_TERMIOS */
+#endif
} /* end of "while (not Q)" */
bb_putchar('\n');
#if ENABLE_FEATURE_USE_TERMIOS
reset_term();
#endif
+ if (ENABLE_FEATURE_CLEAN_UP) {
+ clearmems();
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+ free(prev_hist);
+#endif
+ }
return EXIT_SUCCESS;
}
diff --git a/procps/uptime.c b/procps/uptime.c
index 778812a..88f896a 100644
--- a/procps/uptime.c
+++ b/procps/uptime.c
@@ -11,9 +11,6 @@
*
* Added FEATURE_UPTIME_UTMP_SUPPORT flag.
*/
-
-/* getopt not needed */
-
//config:config UPTIME
//config: bool "uptime"
//config: default y
@@ -30,6 +27,10 @@
//config: help
//config: Makes uptime display the number of users currently logged on.
+//applet:IF_UPTIME(APPLET(uptime, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UPTIME) += uptime.o
+
//usage:#define uptime_trivial_usage
//usage: ""
//usage:#define uptime_full_usage "\n\n"
@@ -43,6 +44,8 @@
#ifdef __linux__
# include <sys/sysinfo.h>
#endif
+# include <utmp.h>
+
#ifndef FSHIFT
@@ -81,10 +84,10 @@ int uptime_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
#if ENABLE_FEATURE_UPTIME_UTMP_SUPPORT
{
- struct utmp *ut;
+ struct utmpx *ut;
unsigned users = 0;
- while ((ut = getutent()) != NULL) {
- if ((ut->ut_type == USER_PROCESS) && (ut->ut_name[0] != '\0'))
+ while ((ut = getutxent()) != NULL) {
+ if ((ut->ut_type == USER_PROCESS) && (ut->ut_user[0] != '\0'))
users++;
}
printf(", %u users", users);
diff --git a/procps/watch.c b/procps/watch.c
index 0397f21..bb34124 100644
--- a/procps/watch.c
+++ b/procps/watch.c
@@ -11,6 +11,17 @@
/* BB_AUDIT SUSv3 N/A */
/* BB_AUDIT GNU defects -- only option -n is supported. */
+//config:config WATCH
+//config: bool "watch"
+//config: default y
+//config: help
+//config: watch is used to execute a program periodically, showing
+//config: output to the screen.
+
+//applet:IF_WATCH(APPLET(watch, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_WATCH) += watch.o
+
//usage:#define watch_trivial_usage
//usage: "[-n SEC] [-t] PROG ARGS"
//usage:#define watch_full_usage "\n\n"
@@ -51,9 +62,9 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
xopen("/dev/null", O_RDONLY);
#endif
- opt_complementary = "-1:n+"; // at least one param; -n NUM
+ opt_complementary = "-1"; // at least one param; -n NUM
// "+": stop at first non-option (procps 3.x only)
- opt = getopt32(argv, "+dtn:", &period);
+ opt = getopt32(argv, "+dtn:+", &period);
argv += optind;
// watch from both procps 2.x and 3.x does concatenation. Example:
@@ -72,7 +83,7 @@ int watch_main(int argc UNUSED_PARAM, char **argv)
// STDERR_FILENO is procps3 compat:
// "watch ls 2>/dev/null" does not detect tty size
- get_terminal_width_height(STDERR_FILENO, &new_width, NULL);
+ new_width = get_terminal_width(STDERR_FILENO);
if (new_width != width) {
width = new_width;
free(header);
diff --git a/qemu_multiarch_testing/README b/qemu_multiarch_testing/README
new file mode 100644
index 0000000..ecb63f9
--- a/dev/null
+++ b/qemu_multiarch_testing/README
@@ -0,0 +1,63 @@
+How to test build using Aboriginal Linux system images.
+
+* Put a source tree into hdc.dir/.
+For example, this should work:
+git clone git://busybox.net/var/lib/git/busybox.git
+
+* Run ./make-hdc-img.sh: it will generate ext2 image file,
+hdc.img, from hdc.dir/* data. This requires root for loop mount.
+
+* Download and unpack, or build from source and unpack
+one or more system-image-ARCH directories into this directory
+(the one which contains this README).
+
+* Install qemu-system-ARCH. The arch names may differ from
+system-image-ARCH: for example, all ARM flavors (armv4l...armv6l)
+are served by the same qemu - qemu-system-arm. On my machine,
+I needed to install qemu-system-{arm,mips,x86,ppc,sparc,m68k,sh4}.
+
+* Run: ./parallel-build-hdc-img.sh system-image-DIR1 system-image-DIR2...
+(background it if you don't want to see "Waiting to finish" thing).
+This runs build in several qemu virtual machines in parallel.
+
+* Observe system-image-*.log file(s) with growing log of the build.
+
+There is no automated detection of errors for now: you need to examine
+logs yourself.
+
+Log files will also contain uuencoded (or if all else fails, od -tx1'ed)
+binary, if build was successful.
+
+To debug a build problem in one of the sandboxes, change keep_hdb
+to "keep_hdb=true" in parallel-build-hdc-img.sh
+- this preserves system-image-ARCH/hdb.img after the build,
+so you can go into system-image-ARCH and run
+"HDB=hdb.img ./dev-environment.sh" to debug the problem.
+
+You can also run "./parallel-build-hdc-img.sh -s system-image-ARCH"
+- single mode, output is to screen and serial input is from keyboard.
+
+If hdc.dir/bin/busybox-$ARCH exists, it will be used during build
+to supply additional tools (dir with all applets appended to $PATH).
+
+For me, the following system images worked:
+system-image-armv4l
+system-image-armv4tl
+system-image-armv5l
+ od is buggy on arm*:
+ # echo Hello-hello-hello-hello | od -b
+ 0000000 110 145 154 154 157 055 150 145 154 154 157 055 150 145 154 154
+ 0000000 157 055 150 145 154 154 157 012 <= WRONG OFFSET
+ 0000000 (can also be even more bogus like 17767153361)
+system-image-i686
+system-image-mips - od is buggy
+system-image-mipsel - od is buggy
+system-image-x86_64
+system-image-powerpc - qemu 1.2.2 didn't work, 2.4.0 worked; od is buggy
+system-image-sparc - qemu 1.2.2 didn't work, 2.4.0 worked; od is buggy
+
+And these did not:
+system-image-armv6l - hang on "Uncompressing Linux... done, booting the kernel"
+system-image-m68k - my qemu doesn't like "-M q800"
+system-image-mips64 - init dies "Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000a"
+system-image-sh4 - qemu segfaults early in kernel boot
diff --git a/qemu_multiarch_testing/extract_od_binary.sh b/qemu_multiarch_testing/extract_od_binary.sh
new file mode 100755
index 0000000..1006e51
--- a/dev/null
+++ b/qemu_multiarch_testing/extract_od_binary.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Converts textual result of "od -tx1 <FILE"
+# back into a binary FILE
+
+grep -a '^[0-7][0-7][0-7][0-7][0-7][0-7][0-7][0-7]* [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f] [0-9a-f][0-9a-f]' | busybox hexdump -R
diff --git a/qemu_multiarch_testing/hdc.dir/build b/qemu_multiarch_testing/hdc.dir/build
new file mode 100755
index 0000000..a998186
--- a/dev/null
+++ b/qemu_multiarch_testing/hdc.dir/build
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+umount /mnt # optional
+
+test -x "bin/busybox-$HOST" && {
+ echo "Found bin/busybox-$HOST, using it"
+ cp -a "bin/busybox-$HOST" bin/busybox
+ bin/busybox --install -s bin/
+ # Supply missing stuff (e.g. bzip2):
+ PATH="$PATH:$PWD/bin"
+ # Override known-buggy host binaries:
+ cp -af bin/od `which od`
+}
+
+(
+ #set -e -x
+ cd busybox
+
+ make defconfig
+ # Want static build
+ sed 's/^.*CONFIG_STATIC.*$/CONFIG_STATIC=y/' -i .config
+ bzip2 </dev/null >/dev/null || {
+ # Drats, newer Aboriginal Linux has no bzip2
+ sed 's/^.*CONFIG_FEATURE_COMPRESS_USAGE.*$/# CONFIG_FEATURE_COMPRESS_USAGE is not set/' -i .config
+ }
+ test x"`uname -m`" = x"mips" && {
+ # Without this, I get MIPS-I binary instead of MIPS32.
+ # No idea what's the difference, but my router wants MIPS32.
+ sed 's/^.*CONFIG_EXTRA_CFLAGS.*$/CONFIG_EXTRA_CFLAGS="-mips32"/' -i .config
+ }
+ # These won't build because of toolchain/libc breakage:
+ sed 's/^.*CONFIG_FEATURE_SYNC_FANCY.*$/# CONFIG_FEATURE_SYNC_FANCY is not set/' -i .config # no syncfs()
+ sed 's/^.*CONFIG_FEATURE_WTMP.*$/# CONFIG_FEATURE_WTMP is not set/' -i .config
+ sed 's/^.*CONFIG_FEATURE_UTMP.*$/# CONFIG_FEATURE_UTMP is not set/' -i .config
+ sed 's/^.*CONFIG_FEATURE_INETD_RPC.*$/# CONFIG_FEATURE_INETD_RPC is not set/' -i .config
+
+ make #V=1 || sh
+ size busybox
+ ./busybox || echo "Exit code: $?"
+ if uuencode TEST </dev/null >/dev/null && bzip2 </dev/null >/dev/null; then
+ bzip2 <busybox | uuencode busybox.bz2
+ else
+ od -v -tx1 <busybox
+ fi
+ #test "x$FTP_PORT" = x ||
+ # ftpput -P "$FTP_PORT" "$FTP_SERVER" strace
+) 2>&1 | tee build.log
+mount -o remount,ro /home
+sync
+sleep 1
diff --git a/qemu_multiarch_testing/hdc.dir/init b/qemu_multiarch_testing/hdc.dir/init
new file mode 100755
index 0000000..692371d
--- a/dev/null
+++ b/qemu_multiarch_testing/hdc.dir/init
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Emit a msg to let user know this place was reached
+echo "Copying to /home"
+# Had a case where cp SEGVs, let's have diagnostics for it
+cp -a /mnt /home || { echo "cp: $?"; exit 1; }
+cd /home/mnt || { echo "cd: $?"; exit 1; }
+exec ./build
+echo "Failed to exec ./build"
diff --git a/qemu_multiarch_testing/make-hdc-img.sh b/qemu_multiarch_testing/make-hdc-img.sh
new file mode 100755
index 0000000..3c35f4e
--- a/dev/null
+++ b/qemu_multiarch_testing/make-hdc-img.sh
@@ -0,0 +1,30 @@
+#!/bin/sh -ex
+
+mountpoint -q /
+[ ! -e hdc.img.dir ]
+
+cleanup()
+{
+ trap - EXIT
+ if mountpoint -q hdc.img.dir; then
+ umount -d hdc.img.dir
+ fi
+ mountpoint -q hdc.img.dir ||
+ rm -rf hdc.img.dir
+ exit $@
+}
+
+trap 'cleanup $?' EXIT
+trap 'cleanup 1' HUP PIPE INT QUIT TERM
+
+size=$(du -ks hdc.dir | sed -rn 's/^([0-9]+).*/\1/p')
+[ "$size" -gt 0 ]
+
+rm -f hdc.img
+dd if=/dev/zero of=hdc.img count=1 bs=1024 seek=$(($size*2))
+mkfs.ext3 -q -F -b 1024 -i 4096 hdc.img
+tune2fs -c 0 -i 0 hdc.img
+mkdir hdc.img.dir
+mount -o loop hdc.img hdc.img.dir
+cp -a hdc.dir/* hdc.img.dir/
+umount -d hdc.img.dir
diff --git a/qemu_multiarch_testing/parallel-build-hdc-img.sh b/qemu_multiarch_testing/parallel-build-hdc-img.sh
new file mode 100755
index 0000000..9ee54eb
--- a/dev/null
+++ b/qemu_multiarch_testing/parallel-build-hdc-img.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+export HDBMEGS=100
+keep_hdb=false
+
+build_in_dir()
+{
+ cd "$1" || exit 1
+ rm -f hdb.img
+ nice -n10 time ./native-build.sh ../hdc.img
+ $keep_hdb || rm -f hdb.img
+ echo >&3 "Finished: $1"
+}
+
+test "$1" = "-s" && {
+ dir="$2"
+ # single mode: build one directory, show output
+ test -d "$dir" || exit 1
+ test -e "$dir/native-build.sh" || exit 1
+ build_in_dir "$dir"
+ exit $?
+}
+
+started=false
+for dir; do
+ test -d "$dir" || continue
+ test -e "$dir/native-build.sh" || continue
+ echo "Starting: $dir"
+ build_in_dir "$dir" 3>&1 </dev/null >"$dir.log" 2>&1 &
+ started=true
+done
+
+$started || {
+ echo "Give me system-image-ARCH directories on command line"
+ exit 1
+}
+
+echo "Waiting to finish"
+wait
+echo "Done, check the logs"
diff --git a/runit/Config.src b/runit/Config.src
index 9db9740..8cde896 100644
--- a/runit/Config.src
+++ b/runit/Config.src
@@ -7,83 +7,4 @@ menu "Runit Utilities"
INSERT
-config RUNSV
- bool "runsv"
- default y
- help
- runsv starts and monitors a service and optionally an appendant log
- service.
-
-config RUNSVDIR
- bool "runsvdir"
- default y
- help
- runsvdir starts a runsv process for each subdirectory, or symlink to
- a directory, in the services directory dir, up to a limit of 1000
- subdirectories, and restarts a runsv process if it terminates.
-
-config FEATURE_RUNSVDIR_LOG
- bool "Enable scrolling argument log"
- depends on RUNSVDIR
- default n
- help
- Enable feature where second parameter of runsvdir holds last error
- message (viewable via top/ps). Otherwise (feature is off
- or no parameter), error messages go to stderr only.
-
-config SV
- bool "sv"
- default y
- help
- sv reports the current status and controls the state of services
- monitored by the runsv supervisor.
-
-config SV_DEFAULT_SERVICE_DIR
- string "Default directory for services"
- default "/var/service"
- depends on SV
- help
- Default directory for services.
- Defaults to "/var/service"
-
-config SVLOGD
- bool "svlogd"
- default y
- help
- svlogd continuously reads log data from its standard input, optionally
- filters log messages, and writes the data to one or more automatically
- rotated logs.
-
-config CHPST
- bool "chpst"
- default y
- help
- chpst changes the process state according to the given options, and
- execs specified program.
-
-config SETUIDGID
- bool "setuidgid"
- default y
- help
- Sets soft resource limits as specified by options
-
-config ENVUIDGID
- bool "envuidgid"
- default y
- help
- Sets $UID to account's uid and $GID to account's gid
-
-config ENVDIR
- bool "envdir"
- default y
- help
- Sets various environment variables as specified by files
- in the given directory
-
-config SOFTLIMIT
- bool "softlimit"
- default y
- help
- Sets soft resource limits as specified by options
-
endmenu
diff --git a/runit/Kbuild.src b/runit/Kbuild.src
index 0fce955..6b4fb74 100644
--- a/runit/Kbuild.src
+++ b/runit/Kbuild.src
@@ -7,14 +7,3 @@
lib-y:=
INSERT
-
-lib-$(CONFIG_RUNSV) += runsv.o
-lib-$(CONFIG_RUNSVDIR) += runsvdir.o
-lib-$(CONFIG_SV) += sv.o
-lib-$(CONFIG_SVLOGD) += svlogd.o
-lib-$(CONFIG_CHPST) += chpst.o
-
-lib-$(CONFIG_ENVDIR) += chpst.o
-lib-$(CONFIG_ENVUIDGID) += chpst.o
-lib-$(CONFIG_SETUIDGID) += chpst.o
-lib-$(CONFIG_SOFTLIMIT) += chpst.o
diff --git a/runit/chpst.c b/runit/chpst.c
index 71af29f..3769af2 100644
--- a/runit/chpst.c
+++ b/runit/chpst.c
@@ -26,7 +26,50 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* Dependencies on runit_lib.c removed */
+
+//config:config CHPST
+//config: bool "chpst"
+//config: default y
+//config: help
+//config: chpst changes the process state according to the given options, and
+//config: execs specified program.
+//config:
+//config:config SETUIDGID
+//config: bool "setuidgid"
+//config: default y
+//config: help
+//config: Sets soft resource limits as specified by options
+//config:
+//config:config ENVUIDGID
+//config: bool "envuidgid"
+//config: default y
+//config: help
+//config: Sets $UID to account's uid and $GID to account's gid
+//config:
+//config:config ENVDIR
+//config: bool "envdir"
+//config: default y
+//config: help
+//config: Sets various environment variables as specified by files
+//config: in the given directory
+//config:
+//config:config SOFTLIMIT
+//config: bool "softlimit"
+//config: default y
+//config: help
+//config: Sets soft resource limits as specified by options
+
+//applet:IF_CHPST(APPLET(chpst, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_ENVDIR(APPLET_ODDNAME(envdir, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envdir))
+//applet:IF_ENVUIDGID(APPLET_ODDNAME(envuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, envuidgid))
+//applet:IF_SETUIDGID(APPLET_ODDNAME(setuidgid, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, setuidgid))
+//applet:IF_SOFTLIMIT(APPLET_ODDNAME(softlimit, chpst, BB_DIR_USR_BIN, BB_SUID_DROP, softlimit))
+
+//kbuild:lib-$(CONFIG_CHPST) += chpst.o
+//kbuild:lib-$(CONFIG_ENVDIR) += chpst.o
+//kbuild:lib-$(CONFIG_ENVUIDGID) += chpst.o
+//kbuild:lib-$(CONFIG_SETUIDGID) += chpst.o
+//kbuild:lib-$(CONFIG_SOFTLIMIT) += chpst.o
//usage:#define chpst_trivial_usage
//usage: "[-vP012] [-u USER[:GRP]] [-U USER[:GRP]] [-e DIR]\n"
@@ -212,8 +255,7 @@ static NOINLINE void edir(const char *directory_name)
xsetenv(d->d_name, buf);
}
closedir(dir);
- if (fchdir(wdir) == -1)
- bb_perror_msg_and_die("fchdir");
+ xfchdir(wdir);
close(wdir);
}
@@ -258,8 +300,8 @@ int chpst_main(int argc UNUSED_PARAM, char **argv)
// FIXME: can we live with int-sized limits?
// can we live with 40000 days?
// if yes -> getopt converts strings to numbers for us
- opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
- opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
+ opt_complementary = "-1";
+ opt = getopt32(argv, "+a:+c:+d:+f:+l:+m:+o:+p:+r:+s:+t:+u:U:e:"
IF_CHPST("/:n:vP012"),
&limita, &limitc, &limitd, &limitf, &limitl,
&limitm, &limito, &limitp, &limitr, &limits, &limitt,
diff --git a/runit/runsv.c b/runit/runsv.c
index d941e89..e0e3150 100644
--- a/runit/runsv.c
+++ b/runit/runsv.c
@@ -26,7 +26,17 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+//config:config RUNSV
+//config: bool "runsv"
+//config: default y
+//config: help
+//config: runsv starts and monitors a service and optionally an appendant log
+//config: service.
+
+//applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RUNSV) += runsv.o
//usage:#define runsv_trivial_usage
//usage: "DIR"
@@ -35,6 +45,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/file.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#include "runit_lib.h"
#if ENABLE_MONOTONIC_SYSCALL
@@ -49,16 +60,11 @@ static void gettimeofday_ns(struct timespec *ts)
#else
static void gettimeofday_ns(struct timespec *ts)
{
- if (sizeof(struct timeval) == sizeof(struct timespec)
- && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
- ) {
- /* Cheat */
- gettimeofday((void*)ts, NULL);
- ts->tv_nsec *= 1000;
- } else {
- extern void BUG_need_to_implement_gettimeofday_ns(void);
- BUG_need_to_implement_gettimeofday_ns();
- }
+ BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
+ BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
+ /* Cheat */
+ gettimeofday((void*)ts, NULL);
+ ts->tv_nsec *= 1000;
}
#endif
@@ -100,7 +106,7 @@ struct globals {
char *dir;
struct svdir svd[2];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define haslog (G.haslog )
#define sigterm (G.sigterm )
#define pidchanged (G.pidchanged )
@@ -109,12 +115,13 @@ struct globals {
#define dir (G.dir )
#define svd (G.svd )
#define INIT_G() do { \
+ setup_common_bufsiz(); \
pidchanged = 1; \
} while (0)
static void fatal2_cannot(const char *m1, const char *m2)
{
- bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
/* was exiting 111 */
}
static void fatal_cannot(const char *m)
@@ -124,7 +131,7 @@ static void fatal_cannot(const char *m)
}
static void fatal2x_cannot(const char *m1, const char *m2)
{
- bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+ bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
/* was exiting 111 */
}
static void warn_cannot(const char *m)
diff --git a/runit/runsvdir.c b/runit/runsvdir.c
index af7e75b..84916e9 100644
--- a/runit/runsvdir.c
+++ b/runit/runsvdir.c
@@ -26,7 +26,27 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+//config:config RUNSVDIR
+//config: bool "runsvdir"
+//config: default y
+//config: help
+//config: runsvdir starts a runsv process for each subdirectory, or symlink to
+//config: a directory, in the services directory dir, up to a limit of 1000
+//config: subdirectories, and restarts a runsv process if it terminates.
+//config:
+//config:config FEATURE_RUNSVDIR_LOG
+//config: bool "Enable scrolling argument log"
+//config: depends on RUNSVDIR
+//config: default n
+//config: help
+//config: Enable feature where second parameter of runsvdir holds last error
+//config: message (viewable via top/ps). Otherwise (feature is off
+//config: or no parameter), error messages go to stderr only.
+
+//applet:IF_RUNSVDIR(APPLET(runsvdir, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RUNSVDIR) += runsvdir.o
//usage:#define runsvdir_trivial_usage
//usage: "[-P] [-s SCRIPT] DIR"
@@ -37,6 +57,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sys/file.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#include "runit_lib.h"
#define MAXSERVICES 1000
@@ -59,22 +80,20 @@ struct globals {
int svnum;
#if ENABLE_FEATURE_RUNSVDIR_LOG
char *rplog;
- int rploglen;
struct fd_pair logpipe;
struct pollfd pfd[1];
unsigned stamplog;
#endif
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define sv (G.sv )
#define svdir (G.svdir )
#define svnum (G.svnum )
#define rplog (G.rplog )
-#define rploglen (G.rploglen )
#define logpipe (G.logpipe )
#define pfd (G.pfd )
#define stamplog (G.stamplog )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
static void fatal2_cannot(const char *m1, const char *m2)
{
@@ -162,9 +181,9 @@ static NOINLINE int do_rescan(void)
continue;
/* Do we have this service listed already? */
for (i = 0; i < svnum; i++) {
- if ((sv[i].ino == s.st_ino)
+ if (sv[i].ino == s.st_ino
#if CHECK_DEVNO_TOO
- && (sv[i].dev == s.st_dev)
+ && sv[i].dev == s.st_dev
#endif
) {
if (sv[i].pid == 0) /* restart if it has died */
@@ -219,15 +238,12 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
struct stat s;
dev_t last_dev = last_dev; /* for gcc */
ino_t last_ino = last_ino; /* for gcc */
- time_t last_mtime = 0;
- int wstat;
+ time_t last_mtime;
int curdir;
- pid_t pid;
- unsigned deadline;
- unsigned now;
unsigned stampcheck;
int i;
- int need_rescan = 1;
+ int need_rescan;
+ bool i_am_init;
char *opt_s_argv[3];
INIT_G();
@@ -238,18 +254,21 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
getopt32(argv, "Ps:", &opt_s_argv[0]);
argv += optind;
+ i_am_init = (getpid() == 1);
bb_signals(0
| (1 << SIGTERM)
| (1 << SIGHUP)
/* For busybox's init, SIGTERM == reboot,
- * SIGUSR1 == halt
- * SIGUSR2 == poweroff
- * so we need to intercept SIGUSRn too.
+ * SIGUSR1 == halt,
+ * SIGUSR2 == poweroff,
+ * Ctlr-ALt-Del sends SIGINT to init,
+ * so we need to intercept SIGUSRn and SIGINT too.
* Note that we do not implement actual reboot
* (killall(TERM) + umount, etc), we just pause
* respawing and avoid exiting (-> making kernel oops).
- * The user is responsible for the rest. */
- | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
+ * The user is responsible for the rest.
+ */
+ | (i_am_init ? ((1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGINT)) : 0)
, record_signo);
svdir = *argv++;
@@ -257,8 +276,7 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
/* setup log */
if (*argv) {
rplog = *argv;
- rploglen = strlen(rplog);
- if (rploglen < 7) {
+ if (strlen(rplog) < 7) {
warnx("log must have at least seven characters");
} else if (piped_pair(logpipe)) {
warnx("can't create pipe for log");
@@ -287,11 +305,16 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
close_on_exec_on(curdir);
stampcheck = monotonic_sec();
+ need_rescan = 1;
+ last_mtime = 0;
for (;;) {
+ unsigned now;
+ unsigned sig;
+
/* collect children */
for (;;) {
- pid = wait_any_nohang(&wstat);
+ pid_t pid = wait_any_nohang(NULL);
if (pid <= 0)
break;
for (i = 0; i < svnum; i++) {
@@ -345,15 +368,15 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
}
pfd[0].revents = 0;
#endif
- deadline = (need_rescan ? 1 : 5);
- sig_block(SIGCHLD);
+ {
+ unsigned deadline = (need_rescan ? 1 : 5);
#if ENABLE_FEATURE_RUNSVDIR_LOG
- if (rplog)
- poll(pfd, 1, deadline*1000);
- else
+ if (rplog)
+ poll(pfd, 1, deadline*1000);
+ else
#endif
- sleep(deadline);
- sig_unblock(SIGCHLD);
+ sleep(deadline);
+ }
#if ENABLE_FEATURE_RUNSVDIR_LOG
if (pfd[0].revents & POLLIN) {
@@ -361,21 +384,25 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
while (read(logpipe.rd, &ch, 1) > 0) {
if (ch < ' ')
ch = ' ';
- for (i = 6; i < rploglen; i++)
+ for (i = 6; rplog[i] != '\0'; i++)
rplog[i-1] = rplog[i];
- rplog[rploglen-1] = ch;
+ rplog[i-1] = ch;
}
}
#endif
- if (!bb_got_signal)
+ sig = bb_got_signal;
+ if (!sig)
continue;
+ bb_got_signal = 0;
/* -s SCRIPT: useful if we are init.
* In this case typically script never returns,
* it halts/powers off/reboots the system. */
if (opt_s_argv[0]) {
+ pid_t pid;
+
/* Single parameter: signal# */
- opt_s_argv[1] = utoa(bb_got_signal);
+ opt_s_argv[1] = utoa(sig);
pid = spawn(opt_s_argv);
if (pid > 0) {
/* Remembering to wait for _any_ children,
@@ -385,17 +412,16 @@ int runsvdir_main(int argc UNUSED_PARAM, char **argv)
}
}
- if (bb_got_signal == SIGHUP) {
+ if (sig == SIGHUP) {
for (i = 0; i < svnum; i++)
if (sv[i].pid)
kill(sv[i].pid, SIGTERM);
}
/* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
/* Exit unless we are init */
- if (getpid() != 1)
- return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
+ if (!i_am_init)
+ return (SIGHUP == sig) ? 111 : EXIT_SUCCESS;
/* init continues to monitor services forever */
- bb_got_signal = 0;
} /* for (;;) */
}
diff --git a/runit/sv.c b/runit/sv.c
index 825e9d4..9e21322 100644
--- a/runit/sv.c
+++ b/runit/sv.c
@@ -151,26 +151,38 @@ Exit Codes
*/
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* TODO: depends on runit_lib.c - review and reduce/eliminate */
-//usage:#define sv_trivial_usage
-//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
-//usage:#define sv_full_usage "\n\n"
-//usage: "Control services monitored by runsv supervisor.\n"
-//usage: "Commands (only first character is enough):\n"
-//usage: "\n"
-//usage: "status: query service status\n"
-//usage: "up: if service isn't running, start it. If service stops, restart it\n"
-//usage: "once: like 'up', but if service stops, don't restart it\n"
-//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
-//usage: " if it exists. After it stops, don't restart service\n"
-//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
-//usage: " runsv exits too\n"
-//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
-//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
+//config:config SV
+//config: bool "sv"
+//config: default y
+//config: help
+//config: sv reports the current status and controls the state of services
+//config: monitored by the runsv supervisor.
+//config:
+//config:config SV_DEFAULT_SERVICE_DIR
+//config: string "Default directory for services"
+//config: default "/var/service"
+//config: depends on SV
+//config: help
+//config: Default directory for services.
+//config: Defaults to "/var/service"
+//config:
+//config:config SVC
+//config: bool "svc"
+//config: default y
+//config: help
+//config: svc controls the state of services monitored by the runsv supervisor.
+//config: It is comaptible with daemontools command with the same name.
+
+//applet:IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
+//applet:IF_SVC(APPLET(svc, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SV) += sv.o
+//kbuild:lib-$(CONFIG_SVC) += sv.o
#include <sys/file.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#include "runit_lib.h"
struct globals {
@@ -181,17 +193,17 @@ struct globals {
uint64_t tstart, tnow;
svstatus_t svstatus;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define acts (G.acts )
#define service (G.service )
#define rc (G.rc )
#define tstart (G.tstart )
#define tnow (G.tnow )
#define svstatus (G.svstatus )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
-#define str_equal(s,t) (!strcmp((s), (t)))
+#define str_equal(s,t) (strcmp((s), (t)) == 0)
static void fatal_cannot(const char *m1) NORETURN;
@@ -433,8 +445,22 @@ static int control(const char *a)
return 1;
}
-int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int sv_main(int argc UNUSED_PARAM, char **argv)
+//usage:#define sv_trivial_usage
+//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
+//usage:#define sv_full_usage "\n\n"
+//usage: "Control services monitored by runsv supervisor.\n"
+//usage: "Commands (only first character is enough):\n"
+//usage: "\n"
+//usage: "status: query service status\n"
+//usage: "up: if service isn't running, start it. If service stops, restart it\n"
+//usage: "once: like 'up', but if service stops, don't restart it\n"
+//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
+//usage: " if it exists. After it stops, don't restart service\n"
+//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
+//usage: " runsv exits too\n"
+//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
+//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
+static int sv(char **argv)
{
char *x;
char *action;
@@ -455,8 +481,8 @@ int sv_main(int argc UNUSED_PARAM, char **argv)
x = getenv("SVWAIT");
if (x) waitsec = xatou(x);
- opt_complementary = "w+:vv"; /* -w N, -v is a counter */
- getopt32(argv, "w:v", &waitsec, &verbose);
+ opt_complementary = "vv"; /* -w N, -v is a counter */
+ getopt32(argv, "w:+v", &waitsec, &verbose);
argv += optind;
action = *argv++;
if (!action || !*argv) bb_show_usage();
@@ -615,3 +641,72 @@ int sv_main(int argc UNUSED_PARAM, char **argv)
}
return rc > 99 ? 99 : rc;
}
+
+#if ENABLE_SV
+int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sv_main(int argc UNUSED_PARAM, char **argv)
+{
+ return sv(argv);
+}
+#endif
+
+//usage:#define svc_trivial_usage
+//usage: "[-udopchaitkx] SERVICE_DIR..."
+//usage:#define svc_full_usage "\n\n"
+//usage: "Control services monitored by runsv supervisor"
+//usage: "\n"
+//usage: "\n"" -u If service is not running, start it; restart if it stops"
+//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
+//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
+//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
+//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
+#if ENABLE_SVC
+int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int svc_main(int argc UNUSED_PARAM, char **argv)
+{
+ char command[2];
+ const char *optstring;
+ unsigned opts;
+
+ INIT_G();
+
+ optstring = "udopchaitkx";
+ opts = getopt32(argv, optstring);
+ argv += optind;
+ if (!argv[0] || !opts)
+ bb_show_usage();
+
+ argv -= 2;
+ if (optind > 2) {
+ argv--;
+ argv[2] = (char*)"--";
+ }
+ argv[0] = (char*)"sv";
+ argv[1] = command;
+ command[1] = '\0';
+
+ /* getopt32() was already called:
+ * reset the libc getopt() function, which keeps internal state.
+ */
+#ifdef __GLIBC__
+ optind = 0;
+#else /* BSD style */
+ optind = 1;
+ /* optreset = 1; */
+#endif
+
+ do {
+ if (opts & 1) {
+ int r;
+ command[0] = *optstring;
+ r = sv(argv);
+ if (r)
+ return 1;
+ }
+ optstring++;
+ opts >>= 1;
+ } while (opts);
+
+ return 0;
+}
+#endif
diff --git a/runit/svlogd.c b/runit/svlogd.c
index c080b9a..3ed13b6 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -26,7 +26,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
-/* TODO: depends on runit_lib.c - review and reduce/eliminate */
/*
Config files
@@ -125,6 +124,18 @@ log message, you can use a pattern like this instead
-*: *: pid *
*/
+//config:config SVLOGD
+//config: bool "svlogd"
+//config: default y
+//config: help
+//config: svlogd continuously reads log data from its standard input, optionally
+//config: filters log messages, and writes the data to one or more automatically
+//config: rotated logs.
+
+//applet:IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SVLOGD) += svlogd.o
+
//usage:#define svlogd_trivial_usage
//usage: "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
//usage:#define svlogd_full_usage "\n\n"
@@ -144,6 +155,7 @@ log message, you can use a pattern like this instead
#include <sys/file.h>
#include "libbb.h"
+#include "common_bufsiz.h"
#include "runit_lib.h"
#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
@@ -222,7 +234,9 @@ struct globals {
#define blocked_sigset (G.blocked_sigset)
#define fl_flag_0 (G.fl_flag_0 )
#define dirn (G.dirn )
+#define line bb_common_bufsiz1
#define INIT_G() do { \
+ setup_common_bufsiz(); \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
linemax = 1000; \
/*buflen = 1024;*/ \
@@ -230,8 +244,6 @@ struct globals {
replace = ""; \
} while (0)
-#define line bb_common_bufsiz1
-
#define FATAL "fatal: "
#define WARNING "warning: "
@@ -772,7 +784,7 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
case '!':
if (s[1]) {
free(ld->processor);
- ld->processor = wstrdup(s);
+ ld->processor = wstrdup(&s[1]);
}
break;
}
@@ -1034,9 +1046,9 @@ int svlogd_main(int argc, char **argv)
}
if (opt & 2) if (!repl) repl = '_'; // -R
if (opt & 4) { // -l
- linemax = xatou_range(l, 0, BUFSIZ-26);
+ linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
if (linemax == 0)
- linemax = BUFSIZ-26;
+ linemax = COMMON_BUFSIZE-26;
if (linemax < 256)
linemax = 256;
}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 921a528..1bcc1da 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -255,8 +255,9 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ;
ifdef builtin-target
quiet_cmd_link_o_target = LD $@
# If the list of objects to link is empty, just create an empty built-in.o
+# -nostdlib is added to make "make LD=gcc ..." work (some people use that)
cmd_link_o_target = $(if $(strip $(obj-y)),\
- $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\
+ $(LD) -nostdlib $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\
rm -f $@; $(AR) rcs $@)
$(builtin-target): $(obj-y) FORCE
diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c
index b125698..720098a 100644
--- a/scripts/basic/docproc.c
+++ b/scripts/basic/docproc.c
@@ -39,7 +39,7 @@
#include <limits.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <alloca.h>
+//bbox disabled: #include <alloca.h>
/* exitstatus is used to keep track of any failing calls to kernel-doc,
* but execution continues. */
@@ -264,7 +264,7 @@ void singfunc(char * filename, char * line)
vec[idx++] = KERNELDOC;
vec[idx++] = DOCBOOK;
- /* Split line up in individual parameters preceeded by FUNCTION */
+ /* Split line up in individual parameters preceded by FUNCTION */
for (i=0; line[i]; i++) {
if (isspace(line[i])) {
line[i] = '\0';
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 165a8c3..19f82df 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -113,7 +113,7 @@
#include <limits.h>
#include <ctype.h>
#include <arpa/inet.h>
-#include <alloca.h>
+//bbox disabled: #include <alloca.h>
/* bbox: not needed
#define INT_CONF ntohl(0x434f4e46)
diff --git a/scripts/gen_build_files.sh b/scripts/gen_build_files.sh
index d5f7b94..ebee17c 100755
--- a/scripts/gen_build_files.sh
+++ b/scripts/gen_build_files.sh
@@ -17,22 +17,6 @@ status() { printf ' %-8s%s\n' "$1" "$2"; }
gen() { status "GEN" "$@"; }
chk() { status "CHK" "$@"; }
-# On OSX the sed implementation is not compatible with some of the
-# features in this script, so this uses gsed and warns the user if
-# it does not exist.
-UNAME=$(uname -sm)
-case "$UNAME" in
-*Darwin*|*Macintosh*)
- SED_IMPL=$(which gsed)
- if [ $? != 0 ]; then
- echo "GNU sed is required for Darwin builds, please install and add 'gsed' to the path"
- exit 1;
- fi
- ;;
-*)
- SED_IMPL=sed
-esac
-
generate()
{
# NB: data to be inserted at INSERT line is coming on stdin
@@ -43,11 +27,16 @@ generate()
# rules re handling of "\n" in echo params.
printf "%s\n" "${header}"
# print everything up to INSERT line
- $SED_IMPL -n '/^INSERT$/ q; p' "${src}"
+ sed -n '/^INSERT$/ q; p' "${src}"
# copy stdin to stdout
cat
# print everything after INSERT line
- $SED_IMPL -n '/^INSERT$/ { :l; n; p; bl }' "${src}"
+ sed -n '/^INSERT$/ {
+ :l
+ n
+ p
+ bl
+ }' "${src}"
} >"${dst}.tmp"
if ! cmp -s "${dst}" "${dst}.tmp"; then
gen "${dst}"
@@ -58,7 +47,7 @@ generate()
}
# (Re)generate include/applets.h
-$SED_IMPL -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c \
+sed -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c \
| generate \
"$srctree/include/applets.src.h" \
"include/applets.h" \
@@ -68,7 +57,12 @@ $SED_IMPL -n 's@^//applet:@@p' "$srctree"/*/*.c "$srctree"/*/*/*.c \
# We add line continuation backslash after each line,
# and insert empty line before each line which doesn't start
# with space or tab
-$SED_IMPL -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\n\1 \\@p' \
+TAB="$(printf '\tX')"
+TAB="${TAB%X}"
+LF="$(printf '\nX')"
+LF="${LF%X}"
+sed -n -e 's@^//usage:\([ '"$TAB"'].*\)$@\1 \\@p' \
+ -e 's@^//usage:\([^ '"$TAB"'].*\)$@\'"$LF"'\1 \\@p' \
"$srctree"/*/*.c "$srctree"/*/*/*.c \
| generate \
"$srctree/include/usage.src.h" \
@@ -77,7 +71,7 @@ $SED_IMPL -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\
# (Re)generate */Kbuild and */Config.in
# We skip .dotdirs - makes git/svn/etc users happier
-{ cd -- "$srctree" && find . -type d -not '(' -name '.?*' -prune ')'; } \
+{ cd -- "$srctree" && find . -type d ! '(' -name '.?*' -prune ')'; } \
| while read -r d; do
d="${d#./}"
@@ -86,7 +80,7 @@ $SED_IMPL -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\
if test -f "$src"; then
mkdir -p -- "$d" 2>/dev/null
- $SED_IMPL -n 's@^//kbuild:@@p' "$srctree/$d"/*.c \
+ sed -n 's@^//kbuild:@@p' "$srctree/$d"/*.c \
| generate \
"${src}" "${dst}" \
"# DO NOT EDIT. This file is generated from Kbuild.src"
@@ -97,7 +91,7 @@ $SED_IMPL -n -e 's@^//usage:\([ \t].*\)$@\1 \\@p' -e 's@^//usage:\([^ \t].*\)$@\
if test -f "$src"; then
mkdir -p -- "$d" 2>/dev/null
- $SED_IMPL -n 's@^//config:@@p' "$srctree/$d"/*.c \
+ sed -n 's@^//config:@@p' "$srctree/$d"/*.c \
| generate \
"${src}" "${dst}" \
"# DO NOT EDIT. This file is generated from Config.src"
diff --git a/scripts/generate_BUFSIZ.sh b/scripts/generate_BUFSIZ.sh
new file mode 100755
index 0000000..718788e
--- a/dev/null
+++ b/scripts/generate_BUFSIZ.sh
@@ -0,0 +1,137 @@
+#!/bin/sh
+# Called from top-level directory a-la
+#
+# scripts/generate_BUFSIZ.sh include/common_bufsiz.h
+
+. ./.config || exit 1
+
+debug=false
+#debug=true
+
+postcompile=false
+test x"$1" = x"--post" && { postcompile=true; shift; }
+
+common_bufsiz_h=$1
+
+test x"$NM" = x"" && NM="${CONFIG_CROSS_COMPILER_PREFIX}nm"
+test x"$CC" = x"" && CC="${CONFIG_CROSS_COMPILER_PREFIX}gcc"
+
+exitcmd="exit 0"
+
+regenerate() {
+ cat >"$1.$$"
+ test -f "$1" && diff "$1.$$" "$1" >/dev/null && rm "$1.$$" && return
+ mv "$1.$$" "$1"
+}
+
+generate_std_and_exit() {
+ $debug && echo "Configuring: bb_common_bufsiz1[] in bss"
+ {
+ echo "enum { COMMON_BUFSIZE = 1024 };"
+ echo "extern char bb_common_bufsiz1[];"
+ echo "#define setup_common_bufsiz() ((void)0)"
+ } | regenerate "$common_bufsiz_h"
+ echo "std" >"$common_bufsiz_h.method"
+ $exitcmd
+}
+
+generate_big_and_exit() {
+ $debug && echo "Configuring: bb_common_bufsiz1[] in bss, COMMON_BUFSIZE = $1"
+ {
+ echo "enum { COMMON_BUFSIZE = $1 };"
+ echo "extern char bb_common_bufsiz1[];"
+ echo "#define setup_common_bufsiz() ((void)0)"
+ } | regenerate "$common_bufsiz_h"
+ echo "$2" >"$common_bufsiz_h.method"
+ $exitcmd
+}
+
+generate_1k_and_exit() {
+ generate_big_and_exit 1024 "1k"
+}
+
+round_down_COMMON_BUFSIZE() {
+ COMMON_BUFSIZE=1024
+ test "$1" -le 32 && return
+ COMMON_BUFSIZE=$(( ($1-32) & 0x0ffffff0 ))
+ COMMON_BUFSIZE=$(( COMMON_BUFSIZE < 1024 ? 1024 : COMMON_BUFSIZE ))
+}
+
+# User does not want any funky stuff?
+test x"$CONFIG_FEATURE_USE_BSS_TAIL" = x"y" || generate_std_and_exit
+
+# The script is run two times: before compilation, when it needs to
+# (re)generate $common_bufsiz_h, and directly after successful build,
+# when it needs to assess whether the build is ok to use at all (not buggy),
+# and (re)generate $common_bufsiz_h for a future build.
+
+if $postcompile; then
+ # Postcompile needs to create/delete OK/FAIL files
+
+ test -f busybox_unstripped || exit 1
+ test -f "$common_bufsiz_h.method" || exit 1
+
+ # How the build was done?
+ method=`cat -- "$common_bufsiz_h.method"`
+
+ # Get _end address
+ END=`$NM busybox_unstripped | grep ' . _end$'| cut -d' ' -f1`
+ test x"$END" = x"" && generate_std_and_exit
+ $debug && echo "END:0x$END $((0x$END))"
+ END=$((0x$END))
+
+ # Get PAGE_SIZE
+ {
+ echo "#include <sys/user.h>"
+ echo "#if defined(PAGE_SIZE) && PAGE_SIZE > 0"
+ echo "char page_size[PAGE_SIZE];"
+ echo "#endif"
+ } >page_size_$$.c
+ $CC -c "page_size_$$.c" || exit 1
+ PAGE_SIZE=`$NM --size-sort "page_size_$$.o" | cut -d' ' -f1`
+ rm "page_size_$$.c" "page_size_$$.o"
+ test x"$PAGE_SIZE" = x"" && exit 1
+ $debug && echo "PAGE_SIZE:0x$PAGE_SIZE $((0x$PAGE_SIZE))"
+ PAGE_SIZE=$((0x$PAGE_SIZE))
+ test $PAGE_SIZE -lt 1024 && exit 1
+
+ # How much space between _end[] and next page?
+ PAGE_MASK=$((PAGE_SIZE-1))
+ TAIL_SIZE=$(( (-END) & PAGE_MASK ))
+ $debug && echo "TAIL_SIZE:$TAIL_SIZE bytes"
+
+ if test x"$method" = x"1k"; then
+ {
+ echo $TAIL_SIZE
+ md5sum <.config | cut -d' ' -f1
+ stat -c "%Y" .config
+ } >"$common_bufsiz_h.1k.OK"
+ round_down_COMMON_BUFSIZE $((1024 + TAIL_SIZE))
+ # emit message only if COMMON_BUFSIZE is indeed larger
+ test $COMMON_BUFSIZE -gt 1024 \
+ && echo "Rerun make to use larger COMMON_BUFSIZE ($COMMON_BUFSIZE)"
+ test $COMMON_BUFSIZE = 1024 && generate_1k_and_exit
+ generate_big_and_exit $COMMON_BUFSIZE "big"
+ fi
+fi
+
+# Based on past success/fail of 1k build, decide next build type
+
+if test -f "$common_bufsiz_h.1k.OK"; then
+ # previous 1k build succeeded
+ oldcfg=`tail -n2 -- "$common_bufsiz_h.1k.OK"`
+ curcfg=`md5sum <.config | cut -d' ' -f1; stat -c "%Y" .config`
+ # config did not change
+ if test x"$oldcfg" = x"$curcfg"; then
+ # Try bigger COMMON_BUFSIZE if possible
+ TAIL_SIZE=`head -n1 -- "$common_bufsiz_h.1k.OK"`
+ round_down_COMMON_BUFSIZE $((1024 + TAIL_SIZE))
+ test $COMMON_BUFSIZE = 1024 && generate_1k_and_exit
+ generate_big_and_exit $COMMON_BUFSIZE "big"
+ fi
+ # config did change
+ rm -rf -- "$common_bufsiz_h.1k.OK"
+fi
+
+# There was no 1k build yet. Try it.
+generate_1k_and_exit
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index d21d06e..d215d2d 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -124,8 +124,7 @@ int conf_read_simple(const char *name)
case S_INT:
case S_HEX:
case S_STRING:
- if (sym->user.val)
- free(sym->user.val);
+ free(sym->user.val);
default:
sym->user.val = NULL;
sym->user.tri = no;
diff --git a/scripts/kconfig/lxdialog/check-lxdialog.sh b/scripts/kconfig/lxdialog/check-lxdialog.sh
index d34dfd4..5075ebf 100644..100755
--- a/scripts/kconfig/lxdialog/check-lxdialog.sh
+++ b/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -4,7 +4,9 @@
# What library to link
ldflags()
{
- for ext in so a dylib ; do
+ pkg-config --libs ncursesw 2>/dev/null && exit
+ pkg-config --libs ncurses 2>/dev/null && exit
+ for ext in so a dll.a dylib ; do
for lib in ncursesw ncurses curses ; do
$cc -print-file-name=lib${lib}.${ext} | grep -q /
if [ $? -eq 0 ]; then
@@ -19,14 +21,17 @@ ldflags()
# Where is ncurses.h?
ccflags()
{
- if [ -f /usr/include/ncursesw/ncurses.h ]; then
- echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>"'
+ if pkg-config --cflags ncursesw 2>/dev/null; then
+ echo '-DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1'
+ elif pkg-config --cflags ncurses 2>/dev/null; then
+ echo '-DCURSES_LOC="<ncurses.h>"'
elif [ -f /usr/include/ncursesw/curses.h ]; then
- echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
+ echo '-I/usr/include/ncursesw -DCURSES_LOC="<curses.h>"'
+ echo ' -DNCURSES_WIDECHAR=1'
elif [ -f /usr/include/ncurses/ncurses.h ]; then
echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
elif [ -f /usr/include/ncurses/curses.h ]; then
- echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
+ echo '-I/usr/include/ncurses -DCURSES_LOC="<curses.h>"'
elif [ -f /usr/include/ncurses.h ]; then
echo '-DCURSES_LOC="<ncurses.h>"'
else
@@ -40,7 +45,7 @@ trap "rm -f $tmp" 0 1 2 3 15
# Check if we can link to ncurses
check() {
- $cc -xc - -o $tmp 2>/dev/null <<'EOF'
+ $cc -x c - -o $tmp 2>/dev/null <<'EOF'
#include CURSES_LOC
main() {}
EOF
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 2630919..13369e6 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -79,8 +79,7 @@ struct gstr str_assign(const char *s)
/* Free storage for growable string */
void str_free(struct gstr *gs)
{
- if (gs->s)
- free(gs->s);
+ free(gs->s);
gs->s = NULL;
gs->len = 0;
}
diff --git a/scripts/kconfig/zconf.hash.c_shipped b/scripts/kconfig/zconf.hash.c_shipped
index d39cf18..29d9cf6 100644
--- a/scripts/kconfig/zconf.hash.c_shipped
+++ b/scripts/kconfig/zconf.hash.c_shipped
@@ -32,14 +32,7 @@
struct kconf_id;
/* maximum key range = 45, duplicates = 0 */
-#ifdef __GNUC__
-__inline
-#else
-#ifdef __cplusplus
-inline
-#endif
-#endif
-static unsigned int
+unsigned int
kconf_id_hash (register const char *str, register unsigned int len)
{
static unsigned char asso_values[] =
@@ -119,7 +112,7 @@ struct kconf_id_strings_t
char kconf_id_strings_str41[sizeof("choice")];
char kconf_id_strings_str46[sizeof("prompt")];
};
-static struct kconf_id_strings_t kconf_id_strings_contents =
+struct kconf_id_strings_t kconf_id_strings_contents =
{
"if",
"int",
@@ -153,9 +146,6 @@ static struct kconf_id_strings_t kconf_id_strings_contents =
"prompt"
};
#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
-#ifdef __GNUC__
-__inline
-#endif
struct kconf_id *
kconf_id_lookup (register const char *str, register unsigned int len)
{
diff --git a/scripts/randomtest b/scripts/randomtest
index d2b26bc..287f1c7 100755
--- a/scripts/randomtest
+++ b/scripts/randomtest
@@ -52,9 +52,18 @@ echo '# CONFIG_RFKILL is not set' >>.config
if test x"$LIBC" = x"glibc"; then
cat .config \
| grep -v CONFIG_STATIC \
+ \
+ | grep -v CONFIG_FEATURE_2_4_MODULES \
+ | grep -v CONFIG_FEATURE_USE_BSS_TAIL \
+ | grep -v CONFIG_DEBUG_SANITIZE \
>.config.new
mv .config.new .config
echo '# CONFIG_STATIC is not set' >>.config
+ # newer glibc (at least 2.23) no longer supply query_module() ABI.
+ # People who target 2.4 kernels would likely use older glibc (and older bbox).
+ echo '# CONFIG_FEATURE_2_4_MODULES is not set' >>.config
+ echo '# CONFIG_FEATURE_USE_BSS_TAIL is not set' >>.config
+ echo '# CONFIG_DEBUG_SANITIZE is not set' >>.config
fi
# If uclibc, build static, and remove some things
@@ -65,15 +74,28 @@ if test x"$LIBC" = x"uclibc"; then
| grep -v CONFIG_BUILD_LIBBUSYBOX \
| grep -v CONFIG_PIE \
\
- | grep -v CONFIG_FEATURE_TOUCH_NODEREF \
| grep -v CONFIG_FEATURE_2_4_MODULES \
+ | grep -v CONFIG_FEATURE_SYNC_FANCY \
+ | grep -v CONFIG_FEATURE_TOUCH_NODEREF \
+ | grep -v CONFIG_NANDWRITE \
+ | grep -v CONFIG_NANDDUMP \
+ | grep -v CONFIG_BLKDISCARD \
+ | grep -v CONFIG_NSENTER \
+ | grep -v CONFIG_UNSHARE \
>.config.new
mv .config.new .config
echo 'CONFIG_STATIC=y' >>.config
echo '# CONFIG_BUILD_LIBBUSYBOX is not set' >>.config
echo '# CONFIG_PIE is not set' >>.config
echo '# CONFIG_FEATURE_2_4_MODULES is not set' >>.config
+ echo '# CONFIG_FEATURE_SYNC_FANCY is not set' >>.config
echo '# CONFIG_FEATURE_TOUCH_NODEREF is not set' >>.config
+ # My uclibc installation does not support some needed APIs...
+ echo '# CONFIG_NANDWRITE is not set' >>.config
+ echo '# CONFIG_NANDDUMP is not set' >>.config
+ echo '# CONFIG_BLKDISCARD is not set' >>.config
+ echo '# CONFIG_NSENTER is not set' >>.config
+ echo '# CONFIG_UNSHARE is not set' >>.config
fi
# If STATIC, remove some things.
@@ -93,6 +115,8 @@ fi
# Build!
nice -n 10 make $MAKEOPTS 2>&1 | tee make.log
+grep 'Rerun make' make.log \
+&& nice -n 10 make $MAKEOPTS 2>&1 | tee -a make.log
# Return exitcode 1 if busybox executable does not exist
test -x busybox
diff --git a/scripts/randomtest.loop b/scripts/randomtest.loop
index 758a8e8..710f5fd 100755
--- a/scripts/randomtest.loop
+++ b/scripts/randomtest.loop
@@ -33,6 +33,7 @@ while sleep 1; do
)
if test $? != 0; then
echo "Failed runtest in $dir"
+ grep ^FAIL -- "$dir/testsuite/runtest.log"
exit 1 # you may comment this out...
let fail++
continue
diff --git a/scripts/trylink b/scripts/trylink
index e471699..c2a4316 100755
--- a/scripts/trylink
+++ b/scripts/trylink
@@ -46,19 +46,23 @@ try() {
}
check_cc() {
- local tempname="/tmp/temp.$$.$RANDOM"
+ local tempname="$(mktemp tmp.XXXXXXXXXX)"
+ local r
+ echo "int main(int argc,char**argv){return argv?argc:0;}" >"$tempname".c
# Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :(
- # "-xc": C language. "/dev/null" is an empty source file.
- if $CC $1 -shared -xc /dev/null -o "$tempname".o >/dev/null 2>&1; then
- echo "$1";
- else
- echo "$2";
- fi
- rm "$tempname".o 2>/dev/null
+ # Was using "-xc /dev/null", but we need a valid C program.
+ # "eval" may be needed if CFLAGS can contain
+ # '... -D"BB_VER=KBUILD_STR(1.N.M)" ...'
+ # and we need shell to process quotes!
+ $CC $CFLAGS $LDFLAGS $1 "$tempname".c -o "$tempname" >/dev/null 2>&1
+ r=$?
+ rm -f "$tempname" "$tempname".c "$tempname".o
+ return $r
}
check_libc_is_glibc() {
- local tempname="/tmp/temp.$$.$RANDOM"
+ local tempname="$(mktemp tmp.XXXXXXXXXX)"
+ local r
echo "\
#include <stdlib.h>
/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
@@ -66,12 +70,10 @@ check_libc_is_glibc() {
syntax error here
#endif
" >"$tempname".c
- if $CC "$tempname".c -c -o "$tempname".o >/dev/null 2>&1; then
- echo "$2";
- else
- echo "$1";
- fi
- rm "$tempname".c "$tempname".o 2>/dev/null
+ ! $CC $CFLAGS "$tempname".c -c -o "$tempname".o >/dev/null 2>&1
+ r=$?
+ rm -f "$tempname" "$tempname".c "$tempname".o
+ return $r
}
EXE="$1"
@@ -83,32 +85,41 @@ A_FILES="$6"
LDLIBS="$7"
# The --sort-section option is not supported by older versions of ld
-SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""`
+SORT_SECTION="-Wl,--sort-section,alignment"
+if ! check_cc "-Wl,--sort-section,alignment"; then
+ echo "Your linker does not support --sort-section,alignment"
+ SORT_SECTION=""
+fi
START_GROUP="-Wl,--start-group"
END_GROUP="-Wl,--end-group"
INFO_OPTS="-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose"
# gold may not support --sort-common (yet)
-SORT_COMMON=`check_cc "-Wl,--sort-common" ""`
+SORT_COMMON="-Wl,--sort-common"
+if ! check_cc "-Wl,--sort-common"; then
+ echo "Your linker does not support --sort-common"
+ SORT_COMMON=""
+fi
# Static linking against glibc produces buggy executables
# (glibc does not cope well with ld --gc-sections).
# See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
# Note that glibc is unsuitable for static linking anyway.
# We are removing -Wl,--gc-sections from link command line.
-GC_SECTIONS=`(
-. ./.config
-if test x"$CONFIG_STATIC" = x"y"; then
- check_libc_is_glibc "" "-Wl,--gc-sections"
-else
- echo "-Wl,--gc-sections"
+GC_SECTIONS="-Wl,--gc-sections"
+if (. ./.config && test x"$CONFIG_STATIC" = x"y") then
+ if check_libc_is_glibc; then
+ echo "Static linking against glibc, can't use --gc-sections"
+ GC_SECTIONS=""
+ fi
fi
-)`
-
# The --gc-sections option is not supported by older versions of ld
if test -n "$GC_SECTIONS"; then
- GC_SECTIONS=`check_cc "$GC_SECTIONS" ""`
+ if ! check_cc "$GC_SECTIONS"; then
+ echo "Your linker does not support $GC_SECTIONS"
+ GC_SECTIONS=""
+ fi
fi
# Sanitize lib list (dups, extra spaces etc)
@@ -129,6 +140,8 @@ try $CC $CFLAGS $LDFLAGS \
|| {
echo "Failed: $l_list"
cat $EXE.out
+ echo 'Note: if build needs additional libraries, put them in CONFIG_EXTRA_LDLIBS.'
+ echo 'Example: CONFIG_EXTRA_LDLIBS="pthread dl tirpc audit pam"'
exit 1
}
@@ -196,6 +209,16 @@ else
# *(.bss SORT_BY_ALIGNMENT(.bss.*) .gnu.linkonce.b.*)
# This will eliminate most of the padding (~3kb).
# Hmm, "ld --sort-section alignment" should do it too.
+ #
+ # There is a ld hack which is meant to decrease disk usage
+ # at the cost of more RAM usage (??!!) in standard ld script:
+ # /* Adjust the address for the data segment. We want to adjust up to
+ # the same address within the page on the next page up. */
+ # . = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
+ # Replace it with:
+ # . = ALIGN (0x1000); . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
+ # to unconditionally align .data to the next page boundary,
+ # instead of "next page, plus current offset in this page"
try $CC $CFLAGS $LDFLAGS \
-o $EXE \
$SORT_COMMON \
@@ -268,9 +291,9 @@ fi
if test "$CONFIG_FEATURE_INDIVIDUAL" = y; then
echo "Linking individual applets against libbusybox (see $sharedlib_dir/*)"
- gcc -DNAME_MAIN_CNAME -E -include include/autoconf.h include/applets.h \
+ gcc -DNAME_MAIN -E -include include/autoconf.h include/applets.h \
| grep -v "^#" \
- | grep -v "^$" \
+ | grep -v "^ *$" \
> applet_lst.tmp
while read name main junk; do
@@ -300,6 +323,8 @@ int main(int argc, char **argv)
}
rm -- "$sharedlib_dir/applet.c" $EXE.out
$STRIP -s --remove-section=.note --remove-section=.comment $EXE
+ # Let user see that we do something - list the names of created binaries:
+ echo "$EXE"
done <applet_lst.tmp
fi
diff --git a/selinux/Config.src b/selinux/Config.src
index 47d15b6..9cb755a 100644
--- a/selinux/Config.src
+++ b/selinux/Config.src
@@ -8,117 +8,4 @@ menu "SELinux Utilities"
INSERT
-config CHCON
- bool "chcon"
- default n
- depends on SELINUX
- help
- Enable support to change the security context of file.
-
-config FEATURE_CHCON_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on CHCON && LONG_OPTS
- help
- Support long options for the chcon applet.
-
-config GETENFORCE
- bool "getenforce"
- default n
- depends on SELINUX
- help
- Enable support to get the current mode of SELinux.
-
-config GETSEBOOL
- bool "getsebool"
- default n
- depends on SELINUX
- help
- Enable support to get SELinux boolean values.
-
-config LOAD_POLICY
- bool "load_policy"
- default n
- depends on SELINUX
- help
- Enable support to load SELinux policy.
-
-config MATCHPATHCON
- bool "matchpathcon"
- default n
- depends on SELINUX
- help
- Enable support to get default security context of the
- specified path from the file contexts configuration.
-
-config RESTORECON
- bool "restorecon"
- default n
- depends on SELINUX
- help
- Enable support to relabel files. The feature is almost
- the same as setfiles, but usage is a little different.
-
-config RUNCON
- bool "runcon"
- default n
- depends on SELINUX
- help
- Enable support to run command in speficied security context.
-
-config FEATURE_RUNCON_LONG_OPTIONS
- bool "Enable long options"
- default y
- depends on RUNCON && LONG_OPTS
- help
- Support long options for the runcon applet.
-
-config SELINUXENABLED
- bool "selinuxenabled"
- default n
- depends on SELINUX
- help
- Enable support for this command to be used within shell scripts
- to determine if selinux is enabled.
-
-config SETENFORCE
- bool "setenforce"
- default n
- depends on SELINUX
- help
- Enable support to modify the mode SELinux is running in.
-
-config SETFILES
- bool "setfiles"
- default n
- depends on SELINUX
- help
- Enable support to modify to relabel files.
- Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64,
- (It is default in libselinux's Makefile), you _must_ enable
- CONFIG_LFS.
-
-config FEATURE_SETFILES_CHECK_OPTION
- bool "Enable check option"
- default n
- depends on SETFILES
- help
- Support "-c" option (check the validity of the contexts against
- the specified binary policy) for setfiles. Requires libsepol.
-
-config SETSEBOOL
- bool "setsebool"
- default n
- depends on SELINUX
- help
- Enable support for change boolean.
- semanage and -P option is not supported yet.
-
-config SESTATUS
- bool "sestatus"
- default n
- depends on SELINUX
- help
- Displays the status of SELinux.
-
endmenu
diff --git a/selinux/Kbuild.src b/selinux/Kbuild.src
index cdd5f2a..62c9e64 100644
--- a/selinux/Kbuild.src
+++ b/selinux/Kbuild.src
@@ -8,15 +8,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_CHCON) += chcon.o
-lib-$(CONFIG_GETENFORCE) += getenforce.o
-lib-$(CONFIG_GETSEBOOL) += getsebool.o
-lib-$(CONFIG_LOAD_POLICY) += load_policy.o
-lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o
-lib-$(CONFIG_RUNCON) += runcon.o
-lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o
-lib-$(CONFIG_SETENFORCE) += setenforce.o
-lib-$(CONFIG_SETFILES) += setfiles.o
-lib-$(CONFIG_RESTORECON) += setfiles.o
-lib-$(CONFIG_SETSEBOOL) += setsebool.o
-lib-$(CONFIG_SESTATUS) += sestatus.o
diff --git a/selinux/chcon.c b/selinux/chcon.c
index d1e9047..c3a1858 100644
--- a/selinux/chcon.c
+++ b/selinux/chcon.c
@@ -7,6 +7,23 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config CHCON
+//config: bool "chcon"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to change the security context of file.
+//config:
+//config:config FEATURE_CHCON_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on CHCON && LONG_OPTS
+//config: help
+//config: Support long options for the chcon applet.
+
+//applet:IF_CHCON(APPLET(chcon, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_CHCON) += chcon.o
//usage:#define chcon_trivial_usage
//usage: "[OPTIONS] CONTEXT FILE..."
@@ -41,7 +58,8 @@
//usage: )
#include <selinux/context.h>
-
+#include <selinux/selinux.h>
+#include <selinux/label.h>
#include "libbb.h"
#define OPT_RECURSIVE (1<<0) /* 'R' */
diff --git a/selinux/getenforce.c b/selinux/getenforce.c
index 56611d6..3747765 100644
--- a/selinux/getenforce.c
+++ b/selinux/getenforce.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config GETENFORCE
+//config: bool "getenforce"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to get the current mode of SELinux.
+
+//applet:IF_GETENFORCE(APPLET(getenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_GETENFORCE) += getenforce.o
//usage:#define getenforce_trivial_usage NOUSAGE_STR
//usage:#define getenforce_full_usage ""
diff --git a/selinux/getsebool.c b/selinux/getsebool.c
index e8f0fef..fce478f 100644
--- a/selinux/getsebool.c
+++ b/selinux/getsebool.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config GETSEBOOL
+//config: bool "getsebool"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to get SELinux boolean values.
+
+//applet:IF_GETSEBOOL(APPLET(getsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_GETSEBOOL) += getsebool.o
//usage:#define getsebool_trivial_usage
//usage: "-a or getsebool boolean..."
diff --git a/selinux/load_policy.c b/selinux/load_policy.c
index ce139db..f969453 100644
--- a/selinux/load_policy.c
+++ b/selinux/load_policy.c
@@ -4,6 +4,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config LOAD_POLICY
+//config: bool "load_policy"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to load SELinux policy.
+
+//applet:IF_LOAD_POLICY(APPLET(load_policy, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LOAD_POLICY) += load_policy.o
//usage:#define load_policy_trivial_usage NOUSAGE_STR
//usage:#define load_policy_full_usage ""
diff --git a/selinux/matchpathcon.c b/selinux/matchpathcon.c
index f033892..c5cbac6 100644
--- a/selinux/matchpathcon.c
+++ b/selinux/matchpathcon.c
@@ -5,6 +5,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MATCHPATHCON
+//config: bool "matchpathcon"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to get default security context of the
+//config: specified path from the file contexts configuration.
+
+//applet:IF_MATCHPATHCON(APPLET(matchpathcon, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MATCHPATHCON) += matchpathcon.o
//usage:#define matchpathcon_trivial_usage
//usage: "[-n] [-N] [-f file_contexts_file] [-p prefix] [-V]"
diff --git a/selinux/runcon.c b/selinux/runcon.c
index 60d4100..16f1711 100644
--- a/selinux/runcon.c
+++ b/selinux/runcon.c
@@ -28,10 +28,27 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config RUNCON
+//config: bool "runcon"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to run command in specified security context.
+//config:
+//config:config FEATURE_RUNCON_LONG_OPTIONS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on RUNCON && LONG_OPTS
+//config: help
+//config: Support long options for the runcon applet.
+
+//applet:IF_RUNCON(APPLET(runcon, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RUNCON) += runcon.o
//usage:#define runcon_trivial_usage
-//usage: "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] PROG -- ARGS\n"
-//usage: "runcon CONTEXT PROG -- ARGS"
+//usage: "[-c] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] PROG ARGS\n"
+//usage: "runcon CONTEXT PROG ARGS"
//usage:#define runcon_full_usage "\n\n"
//usage: "Run PROG in a different security context\n"
//usage: "\n CONTEXT Complete security context\n"
@@ -51,9 +68,9 @@
//usage: )
#include <selinux/context.h>
-#ifndef ANDROID
-#include <selinux/flask.h>
-#endif
+/* from deprecated <selinux/flask.h>: */
+#undef SECCLASS_PROCESS
+#define SECCLASS_PROCESS 2
#include "libbb.h"
diff --git a/selinux/selinuxenabled.c b/selinux/selinuxenabled.c
index ce830dc..b80c4e7 100644
--- a/selinux/selinuxenabled.c
+++ b/selinux/selinuxenabled.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SELINUXENABLED
+//config: bool "selinuxenabled"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support for this command to be used within shell scripts
+//config: to determine if selinux is enabled.
+
+//applet:IF_SELINUXENABLED(APPLET(selinuxenabled, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SELINUXENABLED) += selinuxenabled.o
//usage:#define selinuxenabled_trivial_usage NOUSAGE_STR
//usage:#define selinuxenabled_full_usage ""
diff --git a/selinux/sestatus.c b/selinux/sestatus.c
index e594318..ad49c9d 100644
--- a/selinux/sestatus.c
+++ b/selinux/sestatus.c
@@ -7,6 +7,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SESTATUS
+//config: bool "sestatus"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Displays the status of SELinux.
+
+//applet:IF_SESTATUS(APPLET(sestatus, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SESTATUS) += sestatus.o
//usage:#define sestatus_trivial_usage
//usage: "[-vb]"
diff --git a/selinux/setenforce.c b/selinux/setenforce.c
index c5bc0a5..8d18abd 100644
--- a/selinux/setenforce.c
+++ b/selinux/setenforce.c
@@ -6,6 +6,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SETENFORCE
+//config: bool "setenforce"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to modify the mode SELinux is running in.
+
+//applet:IF_SETENFORCE(APPLET(setenforce, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETENFORCE) += setenforce.o
//usage:#define setenforce_trivial_usage
//usage: "[Enforcing | Permissive | 1 | 0]"
diff --git a/selinux/setfiles.c b/selinux/setfiles.c
index 1c8ab89..dce4ef6 100644
--- a/selinux/setfiles.c
+++ b/selinux/setfiles.c
@@ -3,6 +3,37 @@
policycoreutils was released under GPL 2.
Port to BusyBox (c) 2007 by Yuichi Nakamura <ynakam@hitachisoft.jp>
*/
+//config:config SETFILES
+//config: bool "setfiles"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to modify to relabel files.
+//config: Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64,
+//config: (It is default in libselinux's Makefile), you _must_ enable
+//config: CONFIG_LFS.
+//config:
+//config:config FEATURE_SETFILES_CHECK_OPTION
+//config: bool "Enable check option"
+//config: default n
+//config: depends on SETFILES
+//config: help
+//config: Support "-c" option (check the validity of the contexts against
+//config: the specified binary policy) for setfiles. Requires libsepol.
+//config:
+//config:config RESTORECON
+//config: bool "restorecon"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support to relabel files. The feature is almost
+//config: the same as setfiles, but usage is a little different.
+
+//applet:IF_SETFILES(APPLET(setfiles, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_RESTORECON(APPLET_ODDNAME(restorecon, setfiles, BB_DIR_SBIN, BB_SUID_DROP, restorecon))
+
+//kbuild:lib-$(CONFIG_SETFILES) += setfiles.o
+//kbuild:lib-$(CONFIG_RESTORECON) += setfiles.o
//usage:#define setfiles_trivial_usage
//usage: "[-dnpqsvW] [-e DIR]... [-o FILE] [-r alt_root_path]"
@@ -17,6 +48,7 @@
//usage: )
//usage: "\n -d Show which specification matched each file"
//usage: "\n -l Log changes in file labels to syslog"
+//TODO: log to syslog is not yet implemented, it goes to stdout only now
//usage: "\n -n Don't change any file labels"
//usage: "\n -q Suppress warnings"
//usage: "\n -r DIR Use an alternate root path"
@@ -45,6 +77,7 @@
//usage: "\n if it has changed"
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
#include <sepol/sepol.h>
#endif
@@ -76,9 +109,10 @@ struct globals {
int nerr;
struct edir excludeArray[MAX_EXCLUDES];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
void BUG_setfiles_globals_too_big(void);
#define INIT_G() do { \
+ setup_common_bufsiz(); \
if (sizeof(G) > COMMON_BUFSIZE) \
BUG_setfiles_globals_too_big(); \
/* memset(&G, 0, sizeof(G)); - already is */ \
@@ -251,7 +285,6 @@ static int match(const char *name, struct stat *sb, char **con)
name = path;
if (excludeCtr > 0 && exclude(name))
goto err;
-
} else {
char *p;
p = realpath(name, path);
@@ -384,16 +417,16 @@ static int restore(const char *file)
* the user has changed but the role and type are the
* same. For "-vv", emit everything. */
if (verbose > 1 || !user_only_changed) {
- bb_info_msg("%s: reset %s context %s->%s",
+ printf("%s: reset %s context %s->%s\n",
applet_name, my_file, context ? context : "", newcon);
}
}
if (FLAG_l_take_log && !user_only_changed) {
if (context)
- bb_info_msg("relabeling %s from %s to %s", my_file, context, newcon);
+ printf("relabeling %s from %s to %s\n", my_file, context, newcon);
else
- bb_info_msg("labeling %s to %s", my_file, newcon);
+ printf("labeling %s to %s\n", my_file, newcon);
}
if (outfile && !user_only_changed)
@@ -576,13 +609,13 @@ int setfiles_main(int argc UNUSED_PARAM, char **argv)
set_matchpathcon_flags(matchpathcon_flags);
- opt_complementary = "e::vv:v--p:p--v:v--q:q--v";
+ opt_complementary = "vv:v--p:p--v:v--q:q--v";
/* Option order must match OPT_x definitions! */
if (applet_name[0] == 'r') { /* restorecon */
- flags = getopt32(argv, "de:f:ilnpqrsvo:FWR",
+ flags = getopt32(argv, "de:*f:ilnpqrsvo:FWR",
&exclude_dir, &input_filename, &out_filename, &verbose);
} else { /* setfiles */
- flags = getopt32(argv, "de:f:ilnpqr:svo:FW"
+ flags = getopt32(argv, "de:*f:ilnpqr:svo:FW"
IF_FEATURE_SETFILES_CHECK_OPTION("c:"),
&exclude_dir, &input_filename, &rootpath, &out_filename,
IF_FEATURE_SETFILES_CHECK_OPTION(&policyfile,)
diff --git a/selinux/setsebool.c b/selinux/setsebool.c
index ec682e5..6a6bd03 100644
--- a/selinux/setsebool.c
+++ b/selinux/setsebool.c
@@ -7,6 +7,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SETSEBOOL
+//config: bool "setsebool"
+//config: default n
+//config: depends on SELINUX
+//config: help
+//config: Enable support for change boolean.
+//config: semanage and -P option is not supported yet.
+
+//applet:IF_SETSEBOOL(APPLET(setsebool, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SETSEBOOL) += setsebool.o
//usage:#define setsebool_trivial_usage
//usage: "boolean value"
diff --git a/shell/Config.src b/shell/Config.src
index b31e62d..7f5f670 100644
--- a/shell/Config.src
+++ b/shell/Config.src
@@ -5,34 +5,30 @@
menu "Shells"
-INSERT
-
choice
prompt "Choose which shell is aliased to 'sh' name"
- default FEATURE_SH_IS_ASH
+ default SH_IS_ASH
help
Choose which shell you want to be executed by 'sh' alias.
The ash shell is the most bash compatible and full featured one.
# note: cannot use "select ASH" here, it breaks "make allnoconfig"
-config FEATURE_SH_IS_ASH
- depends on ASH
- bool "ash"
+config SH_IS_ASH
depends on !NOMMU
+ bool "ash"
-config FEATURE_SH_IS_HUSH
- depends on HUSH
+config SH_IS_HUSH
bool "hush"
-config FEATURE_SH_IS_NONE
+config SH_IS_NONE
bool "none"
endchoice
choice
prompt "Choose which shell is aliased to 'bash' name"
- default FEATURE_BASH_IS_NONE
+ default BASH_IS_NONE
help
Choose which shell you want to be executed by 'bash' alias.
The ash shell is the most bash compatible and full featured one.
@@ -47,32 +43,33 @@ choice
can't be used for running them because it won't recongnize
"bash" as a supported applet name.
-config FEATURE_BASH_IS_ASH
- depends on ASH
- bool "ash"
+config BASH_IS_ASH
depends on !NOMMU
+ bool "ash"
-config FEATURE_BASH_IS_HUSH
- depends on HUSH
+config BASH_IS_HUSH
bool "hush"
-config FEATURE_BASH_IS_NONE
+config BASH_IS_NONE
bool "none"
endchoice
-config SH_MATH_SUPPORT
+INSERT
+
+
+config FEATURE_SH_MATH
bool "POSIX math support"
default y
- depends on ASH || HUSH
+ depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
Enable math support in the shell via $((...)) syntax.
-config SH_MATH_SUPPORT_64
+config FEATURE_SH_MATH_64
bool "Extend POSIX math support to 64 bit"
default y
- depends on SH_MATH_SUPPORT
+ depends on FEATURE_SH_MATH
help
Enable 64-bit math support in the shell. This will make the shell
slightly larger, but will allow computation with very large numbers.
@@ -81,14 +78,14 @@ config SH_MATH_SUPPORT_64
config FEATURE_SH_EXTRA_QUIET
bool "Hide message on interactive shell startup"
default y
- depends on HUSH || ASH
+ depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
Remove the busybox introduction when starting a shell.
config FEATURE_SH_STANDALONE
bool "Standalone shell"
default n
- depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
+ depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
This option causes busybox shells to use busybox applets
in preference to executables in the PATH whenever possible. For
@@ -121,7 +118,7 @@ config FEATURE_SH_STANDALONE
config FEATURE_SH_NOFORK
bool "Run 'nofork' applets directly"
default n
- depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
+ depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
This option causes busybox shells to not execute typical
fork/exec/wait sequence, but call <applet>_main directly,
@@ -139,7 +136,7 @@ config FEATURE_SH_NOFORK
config FEATURE_SH_HISTFILESIZE
bool "Use $HISTFILESIZE"
default y
- depends on HUSH || ASH
+ depends on ASH || HUSH || SH_IS_ASH || BASH_IS_ASH || SH_IS_HUSH || BASH_IS_HUSH
help
This option makes busybox shells to use $HISTFILESIZE variable
to set shell history size. Note that its max value is capped
diff --git a/shell/Kbuild.src b/shell/Kbuild.src
index c00aec9..6bba498 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -8,4 +8,4 @@ lib-y:=
INSERT
-lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
+lib-$(CONFIG_FEATURE_SH_MATH) += math.o
diff --git a/shell/ash.c b/shell/ash.c
index c79ef74..02e33e4 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -187,8 +187,8 @@
//config:
//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh))
-//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, bash))
+//applet:IF_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
+//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
@@ -3738,7 +3738,7 @@ setjobctl(int on)
}
if (pgrp == getpgrp())
break;
- killpg_busybox(0, SIGTTIN);
+ killpg(0, SIGTTIN);
}
initialpgrp = pgrp;
@@ -3851,7 +3851,7 @@ restartjob(struct job *jp, int mode)
pgid = jp->ps[0].ps_pid;
if (mode == FORK_FG)
xtcsetpgrp(ttyfd, pgid);
- killpg_busybox(pgid, SIGCONT);
+ killpg(pgid, SIGCONT);
ps = jp->ps;
i = jp->nprocs;
do {
@@ -5902,7 +5902,7 @@ expbackq(union node *cmd, int quoted, int quotes)
read:
if (in.fd < 0)
break;
- i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
+ i = nonblock_immune_read(in.fd, buf, sizeof(buf));
TRACE(("expbackq: read returns %d\n", i));
if (i <= 0)
break;
@@ -6055,7 +6055,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
c = p[length];
if (c) {
if (!(c & 0x80)
- IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
+ IF_FEATURE_SH_MATH(|| c == CTLENDARI)
) {
/* c == '=' || c == ':' || c == CTLENDARI */
length++;
@@ -9637,7 +9637,7 @@ preadfd(void)
#if ENABLE_FEATURE_EDITING
retry:
if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
- nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
+ nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
else {
int timeout = -1;
# if ENABLE_ASH_IDLE_TIMEOUT
@@ -12868,21 +12868,11 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
}
static int FAST_FUNC
-umaskcmd(int argc UNUSED_PARAM, char **argv)
-{
- static const char permuser[3] ALIGN1 = "ugo";
- static const char permmode[3] ALIGN1 = "rwx";
- static const short permmask[] ALIGN2 = {
- S_IRUSR, S_IWUSR, S_IXUSR,
- S_IRGRP, S_IWGRP, S_IXGRP,
- S_IROTH, S_IWOTH, S_IXOTH
- };
-
- /* TODO: use bb_parse_mode() instead */
+umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+ static const char permuser[3] ALIGN1 = "ogu";
- char *ap;
mode_t mask;
- int i;
int symbolic_mode = 0;
while (nextopt("S") != '\0') {
@@ -12894,45 +12884,43 @@ umaskcmd(int argc UNUSED_PARAM, char **argv)
umask(mask);
INT_ON;
- ap = *argptr;
- if (ap == NULL) {
+ if (*argptr == NULL) {
if (symbolic_mode) {
- char buf[18];
+ char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
char *p = buf;
+ int i;
- for (i = 0; i < 3; i++) {
- int j;
-
+ i = 2;
+ for (;;) {
+ *p++ = ',';
*p++ = permuser[i];
*p++ = '=';
- for (j = 0; j < 3; j++) {
- if ((mask & permmask[3 * i + j]) == 0) {
- *p++ = permmode[j];
- }
- }
- *p++ = ',';
+ /* mask is 0..0uuugggooo. i=2 selects uuu bits */
+ if (!(mask & 0400)) *p++ = 'r';
+ if (!(mask & 0200)) *p++ = 'w';
+ if (!(mask & 0100)) *p++ = 'x';
+ mask <<= 3;
+ if (--i < 0)
+ break;
}
- *--p = 0;
- puts(buf);
+ *p = '\0';
+ puts(buf + 1);
} else {
- out1fmt("%.4o\n", mask);
+ out1fmt("%04o\n", mask);
}
} else {
- if (isdigit((unsigned char) *ap)) {
- mask = 0;
- do {
- if (*ap >= '8' || *ap < '0')
- ash_msg_and_raise_error(msg_illnum, argv[1]);
- mask = (mask << 3) + (*ap - '0');
- } while (*++ap != '\0');
- umask(mask);
- } else {
- mask = ~mask & 0777;
- if (!bb_parse_mode(ap, &mask)) {
- ash_msg_and_raise_error("illegal mode: %s", ap);
- }
- umask(~mask & 0777);
- }
+ char *modestr = *argptr;
+ /* numeric umasks are taken as-is */
+ /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
+ if (!isdigit(modestr[0]))
+ mask ^= 0777;
+ mask = bb_parse_mode(modestr, mask);
+ if ((unsigned)mask > 0777) {
+ ash_msg_and_raise_error("illegal mode: %s", modestr);
+ }
+ if (!isdigit(modestr[0]))
+ mask ^= 0777;
+ umask(mask);
}
return 0;
}
diff --git a/shell/ash_test/.gitignore b/shell/ash_test/.gitignore
new file mode 100644
index 0000000..a1f937c
--- a/dev/null
+++ b/shell/ash_test/.gitignore
@@ -0,0 +1,7 @@
+/ash
+/printenv
+/recho
+/zecho
+
+/*.fail
+*.xx
diff --git a/shell/ash_test/ash-glob/glob1.right b/shell/ash_test/ash-glob/glob1.right
new file mode 100644
index 0000000..f29ab4e
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob1.right
@@ -0,0 +1,2 @@
+glob1.tests
+glob1.tests
diff --git a/shell/ash_test/ash-glob/glob1.tests b/shell/ash_test/ash-glob/glob1.tests
new file mode 100755
index 0000000..f980ce0
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob1.tests
@@ -0,0 +1,2 @@
+echo *glob1?t[e]sts*
+echo "glob1"?'t'[e]s*
diff --git a/shell/ash_test/ash-glob/glob2.right b/shell/ash_test/ash-glob/glob2.right
new file mode 100644
index 0000000..7a70c22
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob2.right
@@ -0,0 +1,18 @@
+Expected Actual
+Z\* : Z\*
+Z* : Z*
+Z\f : Z\f
+Z\* : Z\*
+
+Z\z : Z\z
+Zz : Zz
+Z\z : Z\z
+Z\z : Z\z
+
+Z\ : Z\
+Z\ : Z\
+
+Z\f Zf : Z\f Zf
+Z\f Zf : Z\f Zf
+
+Done: 0
diff --git a/shell/ash_test/ash-glob/glob2.tests b/shell/ash_test/ash-glob/glob2.tests
new file mode 100755
index 0000000..00618b9
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob2.tests
@@ -0,0 +1,27 @@
+# This test demonstrates that in unquoted $v, backslashes expand by this rule:
+# \z -> \\\z; \<eol> -> \\<eol> (for any z, special or not),
+# and subsequently globbing converts \\ to \ and treats \z as literal z
+# even if it is a special char.
+
+>'Zf'
+>'Z\f'
+ echo 'Expected' 'Actual'
+v='\*'; echo 'Z\* :' Z$v # ash is buggy here: prints 'Z\f'
+ echo 'Z* :' Z\*
+ echo 'Z\f :' Z\\*
+ echo 'Z\* :' Z\\\* # NB! only this matches Z$v output
+echo
+v='\z'; echo 'Z\z :' Z$v
+ echo 'Zz :' Z\z
+ echo 'Z\z :' Z\\z
+ echo 'Z\z :' Z\\\z
+echo
+v='\'; echo 'Z\ :' Z$v
+ echo 'Z\ :' Z\\
+echo
+v='*'; echo 'Z\f Zf :' Z$v
+ echo 'Z\f Zf :' Z*
+echo
+
+rm 'Z\f' 'Zf'
+echo Done: $?
diff --git a/shell/ash_test/ash-glob/glob3.right b/shell/ash_test/ash-glob/glob3.right
new file mode 100644
index 0000000..161b589
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob3.right
@@ -0,0 +1,2 @@
+glob3.tests
+./glob3.tests
diff --git a/shell/ash_test/ash-glob/glob3.tests b/shell/ash_test/ash-glob/glob3.tests
new file mode 100755
index 0000000..bdf5400
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob3.tests
@@ -0,0 +1,2 @@
+echo "glob3.test"*
+echo "./glob3.test"*
diff --git a/shell/ash_test/ash-glob/glob_and_assign.right b/shell/ash_test/ash-glob/glob_and_assign.right
new file mode 100644
index 0000000..d46e443
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_and_assign.right
@@ -0,0 +1,6 @@
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+*.tmp
+ZVAR=z.tmp z.tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
diff --git a/shell/ash_test/ash-glob/glob_and_assign.tests b/shell/ash_test/ash-glob/glob_and_assign.tests
new file mode 100755
index 0000000..0b158f2
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_and_assign.tests
@@ -0,0 +1,10 @@
+>ZVAR=z.tmp
+>z.tmp
+ZVAR=*.tmp echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+ZVAR=*.tmp /bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+ZVAR=*.tmp
+echo "$ZVAR"
+echo $ZVAR
+echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+/bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+rm ZVAR=z.tmp z.tmp
diff --git a/shell/ash_test/ash-glob/glob_dir.right b/shell/ash_test/ash-glob/glob_dir.right
new file mode 100644
index 0000000..aa90514
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_dir.right
@@ -0,0 +1,19 @@
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+
+*/z.tmp
+*/z.*
+*/?.*
+*/z*p
+d*r*e*t/z*p
+*\/z.tmp
+*/z.*
+*/z*p
+d*r*e*t/z*p
diff --git a/shell/ash_test/ash-glob/glob_dir.tests b/shell/ash_test/ash-glob/glob_dir.tests
new file mode 100755
index 0000000..dc4c4fd
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_dir.tests
@@ -0,0 +1,25 @@
+mkdir dirtest
+ >dirtest/z.tmp
+
+echo */z.tmp
+echo */z.*
+echo */?.*
+echo */z*p
+echo d*r*e*t/z*p
+echo *"/z.t"mp
+echo */z"."*
+echo *"/z"*"p"
+echo "d"*r*e*t"/"z*p
+echo
+echo \*/z.tmp
+echo "*"/z.*
+echo */"?".*
+echo */z"*p"
+echo d*r*e\*t/z*p
+echo *"\\/z.t"mp
+echo */z".*"
+echo *"/z"\*"p"
+echo "d*"r*e*t"/"z*p
+
+rm dirtest/z.tmp
+rmdir dirtest
diff --git a/shell/ash_test/ash-glob/glob_redir.right b/shell/ash_test/ash-glob/glob_redir.right
new file mode 100644
index 0000000..fbd0309
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_redir.right
@@ -0,0 +1,2 @@
+z.tmp:
+?.tmp: TEST
diff --git a/shell/ash_test/ash-glob/glob_redir.tests b/shell/ash_test/ash-glob/glob_redir.tests
new file mode 100755
index 0000000..621d120
--- a/dev/null
+++ b/shell/ash_test/ash-glob/glob_redir.tests
@@ -0,0 +1,9 @@
+# Redirections are not globbed.
+# bash:
+# if run as "sh", they are not globbed, but
+# if run as "bash", they are!
+>z.tmp
+echo TEST >?.tmp
+echo 'z.tmp:' `cat 'z.tmp'`
+echo '?.tmp:' `cat '?.tmp'`
+rm 'z.tmp' '?.tmp'
diff --git a/shell/ash_test/ash-heredoc/heredoc1.right b/shell/ash_test/ash-heredoc/heredoc1.right
new file mode 100644
index 0000000..40aa5a5
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc1.right
@@ -0,0 +1 @@
+./heredoc1.tests: line 3: syntax error: unexpected "then"
diff --git a/shell/ash_test/ash-heredoc/heredoc1.tests b/shell/ash_test/ash-heredoc/heredoc1.tests
new file mode 100755
index 0000000..a912a67
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc1.tests
@@ -0,0 +1,3 @@
+# We used to SEGV on this:
+
+<<EOF; then <W
diff --git a/shell/ash_test/ash-heredoc/heredoc2.right b/shell/ash_test/ash-heredoc/heredoc2.right
new file mode 100644
index 0000000..a486f1a
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc2.right
@@ -0,0 +1,2 @@
+bar
+bar
diff --git a/shell/ash_test/ash-heredoc/heredoc2.tests b/shell/ash_test/ash-heredoc/heredoc2.tests
new file mode 100755
index 0000000..6d9ccb6
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc2.tests
@@ -0,0 +1,7 @@
+foo () {
+cat <<EOF && { echo "$1" ; }
+$1
+EOF
+}
+
+foo "bar"
diff --git a/shell/ash_test/ash-heredoc/heredoc3.right b/shell/ash_test/ash-heredoc/heredoc3.right
new file mode 100644
index 0000000..ce01362
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc3.right
@@ -0,0 +1 @@
+hello
diff --git a/shell/ash_test/ash-heredoc/heredoc3.tests b/shell/ash_test/ash-heredoc/heredoc3.tests
new file mode 100755
index 0000000..96c227c
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc3.tests
@@ -0,0 +1,9 @@
+echo hello >greeting
+cat <<EOF &&
+$(cat greeting)
+EOF
+{
+ echo $?
+ cat greeting
+} >/dev/null
+rm greeting
diff --git a/shell/ash_test/ash-heredoc/heredoc4.right b/shell/ash_test/ash-heredoc/heredoc4.right
new file mode 100644
index 0000000..371b092
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc4.right
@@ -0,0 +1 @@
+'$'
diff --git a/shell/ash_test/ash-heredoc/heredoc4.tests b/shell/ash_test/ash-heredoc/heredoc4.tests
new file mode 100755
index 0000000..642ddb3
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc4.tests
@@ -0,0 +1,3 @@
+cat <<EOF
+'$'
+EOF
diff --git a/shell/hush_test/hush-misc/heredoc2.right b/shell/ash_test/ash-heredoc/heredoc5.right
index 74110e3..74110e3 100644
--- a/shell/hush_test/hush-misc/heredoc2.right
+++ b/shell/ash_test/ash-heredoc/heredoc5.right
diff --git a/shell/hush_test/hush-misc/heredoc2.tests b/shell/ash_test/ash-heredoc/heredoc5.tests
index e619bde..e619bde 100755
--- a/shell/hush_test/hush-misc/heredoc2.tests
+++ b/shell/ash_test/ash-heredoc/heredoc5.tests
diff --git a/shell/ash_test/ash-heredoc/heredoc6.right b/shell/ash_test/ash-heredoc/heredoc6.right
new file mode 100644
index 0000000..5d0f077
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc6.right
@@ -0,0 +1,2 @@
+test
+OK:0
diff --git a/shell/ash_test/ash-heredoc/heredoc6.tests b/shell/ash_test/ash-heredoc/heredoc6.tests
new file mode 100755
index 0000000..346f594
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc6.tests
@@ -0,0 +1,4 @@
+eval 'cat <<- NOT
+test
+NOT'
+echo OK:$?
diff --git a/shell/ash_test/ash-heredoc/heredoc7.right b/shell/ash_test/ash-heredoc/heredoc7.right
new file mode 100644
index 0000000..5d9c6c6
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc7.right
@@ -0,0 +1 @@
+_ASBOX
diff --git a/shell/ash_test/ash-heredoc/heredoc7.tests b/shell/ash_test/ash-heredoc/heredoc7.tests
new file mode 100755
index 0000000..abd5941
--- a/dev/null
+++ b/shell/ash_test/ash-heredoc/heredoc7.tests
@@ -0,0 +1,3 @@
+cat << _ACEOF
+_ASBOX
+_ACEOF
diff --git a/shell/hush_test/hush-misc/heredoc_huge.right b/shell/ash_test/ash-heredoc/heredoc_huge.right
index 11740f6..11740f6 100644
--- a/shell/hush_test/hush-misc/heredoc_huge.right
+++ b/shell/ash_test/ash-heredoc/heredoc_huge.right
diff --git a/shell/hush_test/hush-misc/heredoc_huge.tests b/shell/ash_test/ash-heredoc/heredoc_huge.tests
index c2ec281..c2ec281 100755
--- a/shell/hush_test/hush-misc/heredoc_huge.tests
+++ b/shell/ash_test/ash-heredoc/heredoc_huge.tests
diff --git a/shell/ash_test/ash-misc/and-or.right b/shell/ash_test/ash-misc/and-or.right
new file mode 100644
index 0000000..f9fa5fb
--- a/dev/null
+++ b/shell/ash_test/ash-misc/and-or.right
@@ -0,0 +1,18 @@
+a1
+a4
+b1
+b3
+b4
+b6
+c4
+c5
+c7
+c8
+ff1
+ff3
+ft2
+ft3
+tf2
+tf3
+tt2
+tt4
diff --git a/shell/ash_test/ash-misc/and-or.tests b/shell/ash_test/ash-misc/and-or.tests
new file mode 100755
index 0000000..485458a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/and-or.tests
@@ -0,0 +1,34 @@
+false || echo a1
+false && echo a2
+true || echo a3
+true && echo a4
+
+false || echo b1 || echo b2
+false || echo b3 && echo b4
+false && echo b5 || echo b6
+false && echo b7 && echo b8
+
+true || echo c1 || echo c2
+true || echo c3 && echo c4
+true && echo c5 || echo c6
+true && echo c7 && echo c8
+
+false || false || echo ff1
+false || false && echo ff2
+false && false || echo ff3
+false && false && echo ff4
+
+false || true || echo ft1
+false || true && echo ft2
+false && true || echo ft3
+false && true && echo ft4
+
+true || false || echo tf1
+true || false && echo tf2
+true && false || echo tf3
+true && false && echo tf4
+
+true || true || echo tt1
+true || true && echo tt2
+true && true || echo tt3
+true && true && echo tt4
diff --git a/shell/ash_test/ash-misc/assignment1.right b/shell/ash_test/ash-misc/assignment1.right
new file mode 100644
index 0000000..d0a13d3
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment1.right
@@ -0,0 +1,9 @@
+if1:0
+while1:0
+until1:0
+if2:0
+while2:0
+until2:0
+if3:0
+while3:0
+until3:0
diff --git a/shell/ash_test/ash-misc/assignment1.tests b/shell/ash_test/ash-misc/assignment1.tests
new file mode 100755
index 0000000..033b352
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment1.tests
@@ -0,0 +1,42 @@
+# Assignments after some keywords should still work
+
+if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi
+echo if1:$?
+while a=1 true; do a=1 true; break; done
+echo while1:$?
+until a=1 false; do a=1 true; break; done
+echo until1:$?
+
+if a=1 true
+ then a=1 true
+ elif a=1 true
+ then a=1 true
+ else a=1 true
+ fi
+echo if2:$?
+while a=1 true
+ do a=1 true
+ break
+ done
+echo while2:$?
+until a=1 false
+ do a=1 true
+ break
+ done
+echo until2:$?
+
+if
+ a=1 true; then
+ a=1 true; elif
+ a=1 true; then
+ a=1 true; else
+ a=1 true; fi
+echo if3:$?
+while
+ a=1 true; do
+ a=1 true; break; done
+echo while3:$?
+until
+ a=1 false; do
+ a=1 true; break; done
+echo until3:$?
diff --git a/shell/ash_test/ash-misc/assignment3.right b/shell/ash_test/ash-misc/assignment3.right
new file mode 100644
index 0000000..0f02d7c
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment3.right
@@ -0,0 +1,2 @@
+Done:0
+abc=123
diff --git a/shell/ash_test/ash-misc/assignment3.tests b/shell/ash_test/ash-misc/assignment3.tests
new file mode 100755
index 0000000..790129b
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment3.tests
@@ -0,0 +1,5 @@
+# This must be interpreted as assignments
+a=1 b\
+=2 c=3
+echo Done:$?
+echo abc=$a$b$c
diff --git a/shell/ash_test/ash-misc/assignment4.right b/shell/ash_test/ash-misc/assignment4.right
new file mode 100644
index 0000000..31c896f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment4.right
@@ -0,0 +1 @@
+Done:0
diff --git a/shell/ash_test/ash-misc/assignment4.tests b/shell/ash_test/ash-misc/assignment4.tests
new file mode 100755
index 0000000..6f46d0a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/assignment4.tests
@@ -0,0 +1,3 @@
+# There was a bug where we misinterpreted assignments after 'do':
+for i in 1; do eval b=; done
+echo Done:$?
diff --git a/shell/ash_test/ash-misc/break1.right b/shell/ash_test/ash-misc/break1.right
new file mode 100644
index 0000000..04a4b17
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break1.right
@@ -0,0 +1,2 @@
+A
+OK:0
diff --git a/shell/ash_test/ash-misc/break1.tests b/shell/ash_test/ash-misc/break1.tests
new file mode 100755
index 0000000..3a6b060
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break1.tests
@@ -0,0 +1,2 @@
+while true; do echo A; break; echo B; done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/break2.right b/shell/ash_test/ash-misc/break2.right
new file mode 100644
index 0000000..8a15cb9
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break2.right
@@ -0,0 +1,3 @@
+A
+AA
+OK:0
diff --git a/shell/ash_test/ash-misc/break2.tests b/shell/ash_test/ash-misc/break2.tests
new file mode 100755
index 0000000..7da9faf
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break2.tests
@@ -0,0 +1,6 @@
+while true; do
+ echo A
+ while true; do echo AA; break 2; echo BB; done
+ echo B
+done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/break3.right b/shell/ash_test/ash-misc/break3.right
new file mode 100644
index 0000000..04a4b17
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break3.right
@@ -0,0 +1,2 @@
+A
+OK:0
diff --git a/shell/ash_test/ash-misc/break3.tests b/shell/ash_test/ash-misc/break3.tests
new file mode 100755
index 0000000..d138dca
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break3.tests
@@ -0,0 +1,2 @@
+v=break; while true; do echo A; $v; echo B; break; echo C; done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/break4.right b/shell/ash_test/ash-misc/break4.right
new file mode 100644
index 0000000..6f41c14
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break4.right
@@ -0,0 +1,6 @@
+A
+AA
+TRUE
+A
+AA
+OK:0
diff --git a/shell/ash_test/ash-misc/break4.tests b/shell/ash_test/ash-misc/break4.tests
new file mode 100755
index 0000000..67da288
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break4.tests
@@ -0,0 +1,12 @@
+cond=true
+while $cond; do
+ echo A
+ if test "$cond" = true; then
+ cond='echo TRUE'
+ else
+ cond=false
+ fi
+ while true; do echo AA; continue 2; echo BB; done
+ echo B
+done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/break5.right b/shell/ash_test/ash-misc/break5.right
new file mode 100644
index 0000000..0b9df2a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break5.right
@@ -0,0 +1,13 @@
+A
+B
+0
+A:a
+B
+D
+A:b
+B
+D
+A:c
+B
+D
+0
diff --git a/shell/ash_test/ash-misc/break5.tests b/shell/ash_test/ash-misc/break5.tests
new file mode 100755
index 0000000..273e040
--- a/dev/null
+++ b/shell/ash_test/ash-misc/break5.tests
@@ -0,0 +1,4 @@
+while true; do echo A; { echo B; break; echo C; }; echo D; done
+echo $?
+for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done
+echo $?
diff --git a/shell/ash_test/ash-misc/builtin1.right b/shell/ash_test/ash-misc/builtin1.right
new file mode 100644
index 0000000..2e55ecb
--- a/dev/null
+++ b/shell/ash_test/ash-misc/builtin1.right
@@ -0,0 +1,2 @@
+VARIABLE=export
+OK:0
diff --git a/shell/ash_test/ash-misc/builtin1.tests b/shell/ash_test/ash-misc/builtin1.tests
new file mode 100755
index 0000000..1a2941f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/builtin1.tests
@@ -0,0 +1,6 @@
+# builtins, unlike keywords like "while", can be constructed
+# with substitutions
+VARIABLE=export
+$VARIABLE VARIABLE
+env | grep ^VARIABLE
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/case1.right b/shell/ash_test/ash-misc/case1.right
new file mode 100644
index 0000000..4afb2f5
--- a/dev/null
+++ b/shell/ash_test/ash-misc/case1.right
@@ -0,0 +1,22 @@
+OK_1
+OK_1
+OK_21
+OK_22
+OK_23
+OK_31
+OK_32
+OK_41
+OK_42
+OK_43
+OK_44
+OK_51
+OK_52
+OK_53
+OK_sub1
+OK_sub2
+OK_sub3
+OK_sub4
+OK_sub5
+OK_sub6
+OK_esac1
+Done
diff --git a/shell/ash_test/ash-misc/case1.tests b/shell/ash_test/ash-misc/case1.tests
new file mode 100755
index 0000000..d72b57f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/case1.tests
@@ -0,0 +1,40 @@
+case w in a) echo SKIP;; w) echo OK_1;; w) echo WRONG;; esac
+
+case w in
+ a) echo SKIP;;
+ w)echo OK_1 ;;
+ w)
+ echo WRONG
+ ;;
+esac
+
+t=w
+case $t in a) echo SKIP;; w) echo OK_21;; w) echo WRONG;; esac;
+case "$t" in a) echo SKIP;; w) echo OK_22;; w) echo WRONG;; esac;
+case w in a) echo SKIP;; $t) echo OK_23;; "$t") echo WRONG;; esac;
+
+case '' in a) echo SKIP;; w) echo WRONG;; *) echo OK_31;; esac;
+case '' in a) echo SKIP;; '') echo OK_32;; *) echo WRONG;; esac;
+
+case `echo w` in a) echo SKIP;; w) echo OK_41;; w) echo WRONG;; esac;
+case "`echo w`" in a) echo SKIP;; w) echo OK_42;; w) echo WRONG;; esac;
+case `echo w w` in a) echo SKIP;; w) echo WRONG;; 'w w') echo OK_43;; esac;
+case `echo w w` in a) echo SKIP;; w) echo WRONG;; w*) echo OK_44;; esac;
+
+case w in `echo w`) echo OK_51;; `echo WRONG >&2`w) echo WRONG;; esac;
+case w in `echo OK_52 >&2`) echo SKIP;; `echo`w) echo OK_53;; esac;
+
+# parsing cases in subshells can easily get messy
+ case m in m) echo OK_sub1;; esac
+ case m in (m) echo OK_sub2;; esac
+(case m in m) echo OK_sub3;; esac)
+(case m in (m) echo OK_sub4;; esac)
+(
+ case m in m) echo OK_sub5;; esac
+)
+(
+ case m in (m) echo OK_sub6;; esac
+)
+(case esac in "esac") echo OK_esac1;; esac)
+
+echo Done
diff --git a/shell/ash_test/ash-misc/colon.right b/shell/ash_test/ash-misc/colon.right
new file mode 100644
index 0000000..2a87d02
--- a/dev/null
+++ b/shell/ash_test/ash-misc/colon.right
@@ -0,0 +1,2 @@
+0
+OK: 0
diff --git a/shell/ash_test/ash-misc/colon.tests b/shell/ash_test/ash-misc/colon.tests
new file mode 100755
index 0000000..cb8ab53
--- a/dev/null
+++ b/shell/ash_test/ash-misc/colon.tests
@@ -0,0 +1,5 @@
+false
+:
+echo $?
+(while :; do exit; done)
+echo OK: $?
diff --git a/shell/ash_test/ash-misc/command.right b/shell/ash_test/ash-misc/command.right
new file mode 100644
index 0000000..7f746d9
--- a/dev/null
+++ b/shell/ash_test/ash-misc/command.right
@@ -0,0 +1 @@
+recho: not found
diff --git a/shell/ash_test/ash-misc/command.tests b/shell/ash_test/ash-misc/command.tests
new file mode 100755
index 0000000..5d445af
--- a/dev/null
+++ b/shell/ash_test/ash-misc/command.tests
@@ -0,0 +1 @@
+command -p -V recho
diff --git a/shell/ash_test/ash-misc/command2.right b/shell/ash_test/ash-misc/command2.right
new file mode 100644
index 0000000..8d2165f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/command2.right
@@ -0,0 +1,2 @@
+test1
+./command2.tests: ./test1.sh: line 1: ./test2.sh: Permission denied
diff --git a/shell/ash_test/ash-misc/command2.tests b/shell/ash_test/ash-misc/command2.tests
new file mode 100755
index 0000000..9d9de9a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/command2.tests
@@ -0,0 +1,6 @@
+echo "echo test1; ./test2.sh" >test1.sh
+echo "echo test2" >test2.sh
+
+command . ./test1.sh
+
+rm -f test1.sh test2.sh
diff --git a/shell/ash_test/ash-misc/compound.right b/shell/ash_test/ash-misc/compound.right
new file mode 100644
index 0000000..757d42f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/compound.right
@@ -0,0 +1,14 @@
+new group
+0
+1
+2
+3
+4
+5
+6
+new group
+new group
+0
+1
+2
+3
diff --git a/shell/ash_test/ash-misc/compound.tests b/shell/ash_test/ash-misc/compound.tests
new file mode 100755
index 0000000..a5e85c3
--- a/dev/null
+++ b/shell/ash_test/ash-misc/compound.tests
@@ -0,0 +1,21 @@
+echo new group
+echo 0; { :; }
+echo 1; { : ;}
+echo 2; ({ :; })
+echo 3; ({ : ;})
+echo 4; ( : )
+echo 5; ( :; )
+echo 6; ( : ;)
+# not sure if POSIX requires these, but bash accepts them ...
+#echo 7; {( : )}
+#echo 8; {( :; )}
+#echo 9; {( : ;)}
+
+echo new group
+#echo 0; {(:);}
+
+echo new group
+echo 0; (:)
+echo 1; (:;)
+echo 2; (:);
+echo 3; (:;);
diff --git a/shell/ash_test/ash-misc/continue1.right b/shell/ash_test/ash-misc/continue1.right
new file mode 100644
index 0000000..c4a5565
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue1.right
@@ -0,0 +1,8 @@
+A:a
+A:b
+A:c
+OK1
+A:a
+A:b
+A:c
+OK2
diff --git a/shell/ash_test/ash-misc/continue1.tests b/shell/ash_test/ash-misc/continue1.tests
new file mode 100755
index 0000000..72d3566
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue1.tests
@@ -0,0 +1,4 @@
+for v in a b c; do echo A:$v; continue 666; done
+echo OK1
+for v in a b c; do echo A:$v; continue 666; done
+echo OK2
diff --git a/shell/ash_test/ash-misc/continue2.right b/shell/ash_test/ash-misc/continue2.right
new file mode 100644
index 0000000..49d3ebd
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue2.right
@@ -0,0 +1 @@
+Ok:1
diff --git a/shell/ash_test/ash-misc/continue2.tests b/shell/ash_test/ash-misc/continue2.tests
new file mode 100755
index 0000000..c2df071
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue2.tests
@@ -0,0 +1,3 @@
+e=''
+(while test $e && exit 1; true; do e=1; continue; done)
+echo Ok:$?
diff --git a/shell/ash_test/ash-misc/continue3.right b/shell/ash_test/ash-misc/continue3.right
new file mode 100644
index 0000000..aa47d0d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue3.right
@@ -0,0 +1,2 @@
+0
+0
diff --git a/shell/ash_test/ash-misc/continue3.tests b/shell/ash_test/ash-misc/continue3.tests
new file mode 100755
index 0000000..0aff867
--- a/dev/null
+++ b/shell/ash_test/ash-misc/continue3.tests
@@ -0,0 +1,3 @@
+# Test that "continue" does affect exitcode (sets to 0)
+e=''
+while echo $?; test $e && exit; true; do e=1; false; continue; done
diff --git a/shell/ash_test/ash-misc/echo_write_error.tests b/shell/ash_test/ash-misc/echo_write_error.tests
index 0a40c9f..0a40c9f 100644..100755
--- a/shell/ash_test/ash-misc/echo_write_error.tests
+++ b/shell/ash_test/ash-misc/echo_write_error.tests
diff --git a/shell/ash_test/ash-misc/empty_for.right b/shell/ash_test/ash-misc/empty_for.right
new file mode 100644
index 0000000..290d39b
--- a/dev/null
+++ b/shell/ash_test/ash-misc/empty_for.right
@@ -0,0 +1 @@
+OK: 0
diff --git a/shell/ash_test/ash-misc/empty_for.tests b/shell/ash_test/ash-misc/empty_for.tests
new file mode 100755
index 0000000..0cb52e8
--- a/dev/null
+++ b/shell/ash_test/ash-misc/empty_for.tests
@@ -0,0 +1,3 @@
+false
+for a in; do echo "HELLO"; done
+echo OK: $?
diff --git a/shell/ash_test/ash-misc/empty_for2.right b/shell/ash_test/ash-misc/empty_for2.right
new file mode 100644
index 0000000..1acee9e
--- a/dev/null
+++ b/shell/ash_test/ash-misc/empty_for2.right
@@ -0,0 +1,4 @@
+PARAM:abc
+PARAM:d e
+PARAM:123
+OK: 0
diff --git a/shell/ash_test/ash-misc/empty_for2.tests b/shell/ash_test/ash-misc/empty_for2.tests
new file mode 100755
index 0000000..2b12ec2
--- a/dev/null
+++ b/shell/ash_test/ash-misc/empty_for2.tests
@@ -0,0 +1,6 @@
+if test $# = 0; then
+ exec "$THIS_SH" $0 abc "d e" 123
+fi
+false
+for v; do echo "PARAM:$v"; done
+echo OK: $?
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/ash_test/ash-misc/errexit1.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/ash_test/ash-misc/errexit1.right
diff --git a/shell/ash_test/ash-misc/errexit1.tests b/shell/ash_test/ash-misc/errexit1.tests
new file mode 100755
index 0000000..7b4a156
--- a/dev/null
+++ b/shell/ash_test/ash-misc/errexit1.tests
@@ -0,0 +1,5 @@
+set -e
+(true)
+echo OK
+(false)
+echo FAIL
diff --git a/shell/ash_test/ash-misc/eval1.right b/shell/ash_test/ash-misc/eval1.right
new file mode 100644
index 0000000..7b24a35
--- a/dev/null
+++ b/shell/ash_test/ash-misc/eval1.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/ash_test/ash-misc/eval1.tests b/shell/ash_test/ash-misc/eval1.tests
new file mode 100755
index 0000000..b78c6cc
--- a/dev/null
+++ b/shell/ash_test/ash-misc/eval1.tests
@@ -0,0 +1,4 @@
+# empty eval nevertheless sets $? = 0
+false
+eval
+echo Ok:$?
diff --git a/shell/ash_test/ash-misc/eval2.right b/shell/ash_test/ash-misc/eval2.right
new file mode 100644
index 0000000..a7ce6cc
--- a/dev/null
+++ b/shell/ash_test/ash-misc/eval2.right
@@ -0,0 +1,3 @@
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/ash_test/ash-misc/eval2.tests b/shell/ash_test/ash-misc/eval2.tests
new file mode 100755
index 0000000..6bfb87a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/eval2.tests
@@ -0,0 +1,4 @@
+false; eval; echo Zero:$?
+false; eval ""; echo Zero:$?
+false; eval "
+"; echo Zero:$?
diff --git a/shell/ash_test/ash-misc/exec.right b/shell/ash_test/ash-misc/exec.right
new file mode 100644
index 0000000..1741a38
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exec.right
@@ -0,0 +1,2 @@
+./exec.tests: exec: line 2: ./test1.sh: not found
+127
diff --git a/shell/ash_test/ash-misc/exec.tests b/shell/ash_test/ash-misc/exec.tests
new file mode 100755
index 0000000..624915d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exec.tests
@@ -0,0 +1,3 @@
+rm -f test1.sh
+(exec ./test1.sh)
+echo $?
diff --git a/shell/ash_test/ash-misc/exit1.right b/shell/ash_test/ash-misc/exit1.right
new file mode 100644
index 0000000..dd2cfc2
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exit1.right
@@ -0,0 +1 @@
+Once
diff --git a/shell/ash_test/ash-misc/exit1.tests b/shell/ash_test/ash-misc/exit1.tests
new file mode 100755
index 0000000..41e0d09
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exit1.tests
@@ -0,0 +1,4 @@
+trap "echo Not shown" EXIT
+(exit) # must be silent
+trap "echo Once; exit" EXIT
+{ exit; }
diff --git a/shell/ash_test/ash-misc/exitcode1.right b/shell/ash_test/ash-misc/exitcode1.right
new file mode 100644
index 0000000..e5fefef
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.right
@@ -0,0 +1,2 @@
+One:1
+Zero:0
diff --git a/shell/ash_test/ash-misc/exitcode1.tests b/shell/ash_test/ash-misc/exitcode1.tests
new file mode 100755
index 0000000..dc8619d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode1.tests
@@ -0,0 +1,2 @@
+false || case a in a) echo One:$?;; esac
+echo Zero:$?
diff --git a/shell/ash_test/ash-misc/exitcode2.right b/shell/ash_test/ash-misc/exitcode2.right
new file mode 100644
index 0000000..f7cb983
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode2.right
@@ -0,0 +1,4 @@
+./test.sh: line 1: syntax error: unexpected ")"
+Done:2
+./exitcode2.tests: line 11: can't open does_not_exist: no such file
+Done:1
diff --git a/shell/ash_test/ash-misc/exitcode2.tests b/shell/ash_test/ash-misc/exitcode2.tests
new file mode 100755
index 0000000..79a6ebd
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode2.tests
@@ -0,0 +1,12 @@
+# syntax error should return status 2
+cat >test.sh <<EOF
+)
+EOF
+chmod +x test.sh
+$THIS_SH ./test.sh
+echo Done:$?
+rm -f test.sh
+
+# redirection error with special builtin should return status 1
+(eval cat <does_not_exist)
+echo Done:$?
diff --git a/shell/ash_test/ash-misc/exitcode_EACCES.right b/shell/ash_test/ash-misc/exitcode_EACCES.right
new file mode 100644
index 0000000..50719c9
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode_EACCES.right
@@ -0,0 +1,2 @@
+./exitcode_EACCES.tests: line 1: ./: Permission denied
+126
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.tests b/shell/ash_test/ash-misc/exitcode_EACCES.tests
index 26b5c61..26b5c61 100755
--- a/shell/msh_test/msh-execution/exitcode_EACCES.tests
+++ b/shell/ash_test/ash-misc/exitcode_EACCES.tests
diff --git a/shell/ash_test/ash-misc/exitcode_ENOENT.right b/shell/ash_test/ash-misc/exitcode_ENOENT.right
new file mode 100644
index 0000000..51a4f11
--- a/dev/null
+++ b/shell/ash_test/ash-misc/exitcode_ENOENT.right
@@ -0,0 +1,2 @@
+./exitcode_ENOENT.tests: line 1: ./does_not_exist_for_sure: not found
+127
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.tests b/shell/ash_test/ash-misc/exitcode_ENOENT.tests
index 7f1b88a..7f1b88a 100755
--- a/shell/msh_test/msh-execution/exitcode_ENOENT.tests
+++ b/shell/ash_test/ash-misc/exitcode_ENOENT.tests
diff --git a/shell/msh_test/msh-parsing/argv0.right b/shell/ash_test/ash-misc/for.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-parsing/argv0.right
+++ b/shell/ash_test/ash-misc/for.right
diff --git a/shell/ash_test/ash-misc/for.tests b/shell/ash_test/ash-misc/for.tests
new file mode 100755
index 0000000..4889a9f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/for.tests
@@ -0,0 +1,5 @@
+for i
+in OK
+do
+ echo $i
+done
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.right b/shell/ash_test/ash-misc/for_with_bslashes.right
new file mode 100644
index 0000000..02d9669
--- a/dev/null
+++ b/shell/ash_test/ash-misc/for_with_bslashes.right
@@ -0,0 +1,8 @@
+a
+b\c
+b\\c
+b"c
+b'c
+b$c
+b`true`c
+Zero:0
diff --git a/shell/ash_test/ash-misc/for_with_bslashes.tests b/shell/ash_test/ash-misc/for_with_bslashes.tests
new file mode 100755
index 0000000..363f3d8
--- a/dev/null
+++ b/shell/ash_test/ash-misc/for_with_bslashes.tests
@@ -0,0 +1,10 @@
+# UNFIXED BUG.
+# commented-out words contain ^C character.
+# It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it.
+# When it is fixed, update this test.
+
+for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c'
+do
+ echo $a
+done
+echo Zero:$?
diff --git a/shell/ash_test/ash-misc/for_with_keywords.right b/shell/ash_test/ash-misc/for_with_keywords.right
new file mode 100644
index 0000000..eb04e9a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/for_with_keywords.right
@@ -0,0 +1,4 @@
+do
+done
+then
+OK: 0
diff --git a/shell/ash_test/ash-misc/for_with_keywords.tests b/shell/ash_test/ash-misc/for_with_keywords.tests
new file mode 100755
index 0000000..a8b8e42
--- a/dev/null
+++ b/shell/ash_test/ash-misc/for_with_keywords.tests
@@ -0,0 +1,2 @@
+for if in do done then; do echo $if; done
+echo OK: $?
diff --git a/shell/ash_test/ash-misc/func1.right b/shell/ash_test/ash-misc/func1.right
new file mode 100644
index 0000000..e21665a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func1.right
@@ -0,0 +1,6 @@
+Hello
+Zero: 0
+One: 1 Param1: World
+Zero: 0 Param1: Restored
+Multi line function
+One: 1
diff --git a/shell/ash_test/ash-misc/func1.tests b/shell/ash_test/ash-misc/func1.tests
new file mode 100755
index 0000000..ffb269f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func1.tests
@@ -0,0 +1,16 @@
+f() { echo Hello; }
+g () { echo One: $# Param1: $1; }
+h ( )
+{
+ echo -n 'Multi ' && echo -n 'line '
+ echo function
+ false
+}
+
+f
+echo Zero: $?
+set -- Restored
+{ g World; }
+echo Zero: $? Param1: $1
+( h )
+echo One: $?
diff --git a/shell/ash_test/ash-misc/func2.right b/shell/ash_test/ash-misc/func2.right
new file mode 100644
index 0000000..f2a041d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func2.right
@@ -0,0 +1,5 @@
+First 0
+Second 0
+First 1
+Second 1
+Done
diff --git a/shell/ash_test/ash-misc/func2.tests b/shell/ash_test/ash-misc/func2.tests
new file mode 100755
index 0000000..763203f
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func2.tests
@@ -0,0 +1,9 @@
+i=0
+while test $i != 2; do
+ f() { echo First $i; }
+ f
+ f() { echo Second $i; }
+ f
+ : $((i++))
+done
+echo Done
diff --git a/shell/ash_test/ash-misc/func3.right b/shell/ash_test/ash-misc/func3.right
new file mode 100644
index 0000000..b6d7345
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func3.right
@@ -0,0 +1,4 @@
+One:1
+Zero:0
+One:1
+Five:5
diff --git a/shell/ash_test/ash-misc/func3.tests b/shell/ash_test/ash-misc/func3.tests
new file mode 100755
index 0000000..fa6f26a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func3.tests
@@ -0,0 +1,8 @@
+f() { false; return; echo BAD; };
+{ f; echo One:$?; }; echo Zero:$?
+
+f() { false; return; };
+f; echo One:$?
+
+f() { return 5; };
+f; echo Five:$?
diff --git a/shell/ash_test/ash-misc/func4.right b/shell/ash_test/ash-misc/func4.right
new file mode 100644
index 0000000..0c87e31
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func4.right
@@ -0,0 +1,2 @@
+24
+Done
diff --git a/shell/ash_test/ash-misc/func4.tests b/shell/ash_test/ash-misc/func4.tests
new file mode 100755
index 0000000..74c1b9a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func4.tests
@@ -0,0 +1,7 @@
+func() {
+ eval "echo \"\${val_${1}}\""
+}
+
+val_x=24
+(func x)
+echo Done
diff --git a/shell/ash_test/ash-misc/func5.right b/shell/ash_test/ash-misc/func5.right
new file mode 100644
index 0000000..2c9d316
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func5.right
@@ -0,0 +1,6 @@
+1
+2
+3
+1
+2
+3
diff --git a/shell/ash_test/ash-misc/func5.tests b/shell/ash_test/ash-misc/func5.tests
new file mode 100755
index 0000000..e967208
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func5.tests
@@ -0,0 +1,13 @@
+f() { echo $1; }
+f 1
+
+f() ( echo $1; )
+f 2
+
+f() ( echo $1 )
+f 3
+
+f() for i in 1 2 3; do
+ echo $i
+done
+f
diff --git a/shell/ash_test/ash-misc/func6.right b/shell/ash_test/ash-misc/func6.right
new file mode 100644
index 0000000..0ebd8e5
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func6.right
@@ -0,0 +1,2 @@
+Two:2
+Two:2
diff --git a/shell/ash_test/ash-misc/func6.tests b/shell/ash_test/ash-misc/func6.tests
new file mode 100755
index 0000000..029c3e8
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func6.tests
@@ -0,0 +1,11 @@
+f1() {
+ while return 2; do :; done
+}
+f1
+echo Two:$?
+
+f2() {
+ while :; do return 2; done
+}
+f2
+echo Two:$?
diff --git a/shell/ash_test/ash-misc/func_args1.right b/shell/ash_test/ash-misc/func_args1.right
new file mode 100644
index 0000000..2dfb962
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_args1.right
@@ -0,0 +1,5 @@
+params: a b c
+'f 1 2 3' called
+params: a b c
+'f 1 2 3' called
+params: a b c
diff --git a/shell/ash_test/ash-misc/func_args1.tests b/shell/ash_test/ash-misc/func_args1.tests
new file mode 100755
index 0000000..d394c63
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_args1.tests
@@ -0,0 +1,8 @@
+f() { echo "'f $1 $2 $3' called"; }
+
+set -- a b c
+echo "params: $1 $2 $3"
+f 1 2 3
+echo "params: $1 $2 $3"
+true | f 1 2 3
+echo "params: $1 $2 $3"
diff --git a/shell/ash_test/ash-misc/func_bash1.right b/shell/ash_test/ash-misc/func_bash1.right
new file mode 100644
index 0000000..41bf882
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_bash1.right
@@ -0,0 +1,12 @@
+1
+2
+3
+1
+2
+3
+1
+2
+3
+1
+2
+3
diff --git a/shell/ash_test/ash-misc/func_bash1.tests b/shell/ash_test/ash-misc/func_bash1.tests
new file mode 100755
index 0000000..2cc0970
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_bash1.tests
@@ -0,0 +1,28 @@
+function f() { echo $1; }
+f 1
+
+function f() ( echo $1; )
+f 2
+
+function f() ( echo $1 )
+f 3
+
+function f() for i in 1 2 3; do
+ echo $i
+done
+f
+
+function f { echo $1; }
+f 1
+
+# the next two don't work
+#function f ( echo $1; )
+f 2
+
+#function f ( echo $1 )
+f 3
+
+function f for i in 1 2 3; do
+ echo $i
+done
+f
diff --git a/shell/ash_test/ash-misc/func_local1.right b/shell/ash_test/ash-misc/func_local1.right
new file mode 100644
index 0000000..3121783
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_local1.right
@@ -0,0 +1,3 @@
+z=a
+z=z
+Done
diff --git a/shell/ash_test/ash-misc/func_local1.tests b/shell/ash_test/ash-misc/func_local1.tests
new file mode 100755
index 0000000..1d594e2
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_local1.tests
@@ -0,0 +1,5 @@
+export z=z
+f() { local z=a; env | grep ^z; }
+f
+env | grep ^z
+echo Done
diff --git a/shell/ash_test/ash-misc/func_local2.right b/shell/ash_test/ash-misc/func_local2.right
new file mode 100644
index 0000000..fe9343a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_local2.right
@@ -0,0 +1,14 @@
+1
+2
+1
+2
+1
+1
+2
+2
+3
+2
+2
+3
+1
+Done
diff --git a/shell/ash_test/ash-misc/func_local2.tests b/shell/ash_test/ash-misc/func_local2.tests
new file mode 100755
index 0000000..1a9ae55
--- a/dev/null
+++ b/shell/ash_test/ash-misc/func_local2.tests
@@ -0,0 +1,7 @@
+x=1
+f() { echo $x; local x=$((x+1)); echo $x; }
+g() { f; echo $x; f; local x=$((x+1)); f; echo $x; f; }
+f
+g
+echo $x
+echo Done
diff --git a/shell/ash_test/ash-misc/group_in_braces.right b/shell/ash_test/ash-misc/group_in_braces.right
new file mode 100644
index 0000000..a706449
--- a/dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.right
@@ -0,0 +1,5 @@
+Zero:0
+Zero:0
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/ash_test/ash-misc/group_in_braces.tests b/shell/ash_test/ash-misc/group_in_braces.tests
new file mode 100755
index 0000000..f6571c3
--- a/dev/null
+++ b/shell/ash_test/ash-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
+# Test cases where { cmd } does not require semicolon after "cmd"
+(exit 2); { { true; } }
+echo Zero:$?
+(exit 2); {(true)}
+echo Zero:$?
+(exit 2); { true | { true; } }
+echo Zero:$?
+(exit 2); { while false; do :; done }
+echo Zero:$?
+(exit 2); { case a in b) ;; esac }
+echo Zero:$?
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.right b/shell/ash_test/ash-misc/if_false_exitcode.right
new file mode 100644
index 0000000..7b24a35
--- a/dev/null
+++ b/shell/ash_test/ash-misc/if_false_exitcode.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/ash_test/ash-misc/if_false_exitcode.tests b/shell/ash_test/ash-misc/if_false_exitcode.tests
new file mode 100755
index 0000000..01b36b1
--- a/dev/null
+++ b/shell/ash_test/ash-misc/if_false_exitcode.tests
@@ -0,0 +1,2 @@
+if false; then echo Bad; fi
+echo Ok:$?
diff --git a/shell/ash_test/ash-misc/local1.right b/shell/ash_test/ash-misc/local1.right
new file mode 100644
index 0000000..a2d121d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/local1.right
@@ -0,0 +1,4 @@
+A1:'A'
+A2:''
+A3:''
+A4:'A'
diff --git a/shell/ash_test/ash-misc/local1.tests b/shell/ash_test/ash-misc/local1.tests
new file mode 100755
index 0000000..b1e6750
--- a/dev/null
+++ b/shell/ash_test/ash-misc/local1.tests
@@ -0,0 +1,11 @@
+a=A
+f() {
+ local a
+ # the above line unsets $a
+ echo "A2:'$a'"
+ unset a
+ echo "A3:'$a'"
+}
+echo "A1:'$a'"
+f
+echo "A4:'$a'"
diff --git a/shell/ash_test/ash-misc/local2.right b/shell/ash_test/ash-misc/local2.right
new file mode 100644
index 0000000..630ef79
--- a/dev/null
+++ b/shell/ash_test/ash-misc/local2.right
@@ -0,0 +1 @@
+./local2.tests: local: line 1: not in a function
diff --git a/shell/ash_test/ash-misc/local2.tests b/shell/ash_test/ash-misc/local2.tests
new file mode 100755
index 0000000..8e14037
--- a/dev/null
+++ b/shell/ash_test/ash-misc/local2.tests
@@ -0,0 +1 @@
+local x=1
diff --git a/shell/ash_test/ash-misc/nommu1.right b/shell/ash_test/ash-misc/nommu1.right
new file mode 100644
index 0000000..d206a85
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu1.right
@@ -0,0 +1,7 @@
+Ok
+Ok
+Ok
+Ok
+Ok
+Ok
+Done
diff --git a/shell/ash_test/ash-misc/nommu1.tests b/shell/ash_test/ash-misc/nommu1.tests
new file mode 100755
index 0000000..e14ada5
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu1.tests
@@ -0,0 +1,12 @@
+(echo \
+Ok)
+( (echo \
+Ok) )
+( ( (echo \
+Ok) ) )
+
+(echo \Ok)
+( (echo \Ok) )
+( ( (echo \Ok) ) )
+
+echo Done
diff --git a/shell/ash_test/ash-misc/nommu2.right b/shell/ash_test/ash-misc/nommu2.right
new file mode 100644
index 0000000..fb8ba8b
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu2.right
@@ -0,0 +1,5 @@
+Ok
+Ok
+Ok
+Ok
+Done
diff --git a/shell/ash_test/ash-misc/nommu2.tests b/shell/ash_test/ash-misc/nommu2.tests
new file mode 100755
index 0000000..61ed5ce
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu2.tests
@@ -0,0 +1,5 @@
+echo Not shown | if true; then echo $(echo Ok); fi
+echo Not shown | if true; then echo `echo Ok`; fi
+echo Not shown | ( if true; then echo $(echo Ok); fi )
+echo Not shown | ( if true; then echo `echo Ok`; fi )
+echo Done
diff --git a/shell/ash_test/ash-misc/nommu3.right b/shell/ash_test/ash-misc/nommu3.right
new file mode 100644
index 0000000..da1534b
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu3.right
@@ -0,0 +1,2 @@
+Ok
+0
diff --git a/shell/ash_test/ash-misc/nommu3.tests b/shell/ash_test/ash-misc/nommu3.tests
new file mode 100755
index 0000000..ac82a6a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/nommu3.tests
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+func()
+{
+ while read p; do echo "$p"; done
+}
+
+pipe_to_func()
+{
+ # We had a NOMMU bug which caused "echo Ok |" part to be lost
+ echo Ok | func
+}
+
+pipe_to_func | cat
+echo $?
diff --git a/shell/ash_test/ash-misc/opts1.right b/shell/ash_test/ash-misc/opts1.right
new file mode 100644
index 0000000..4da7573
--- a/dev/null
+++ b/shell/ash_test/ash-misc/opts1.right
@@ -0,0 +1,2 @@
+Param1: >-10qwertyuiop<
+Done
diff --git a/shell/ash_test/ash-misc/opts1.tests b/shell/ash_test/ash-misc/opts1.tests
new file mode 100755
index 0000000..45a23d6
--- a/dev/null
+++ b/shell/ash_test/ash-misc/opts1.tests
@@ -0,0 +1,5 @@
+if test $# = 0; then
+ exec "$THIS_SH" $0 -10qwertyuiop
+fi
+echo "Param1: >$1<"
+echo Done
diff --git a/shell/ash_test/ash-misc/pid.right b/shell/ash_test/ash-misc/pid.right
new file mode 100644
index 0000000..573541a
--- a/dev/null
+++ b/shell/ash_test/ash-misc/pid.right
@@ -0,0 +1 @@
+0
diff --git a/shell/ash_test/ash-misc/pid.tests b/shell/ash_test/ash-misc/pid.tests
new file mode 100755
index 0000000..eaeaa71
--- a/dev/null
+++ b/shell/ash_test/ash-misc/pid.tests
@@ -0,0 +1 @@
+test `(echo $$)` = `echo $$`; echo $?
diff --git a/shell/ash_test/ash-misc/pipefail.right b/shell/ash_test/ash-misc/pipefail.right
new file mode 100644
index 0000000..5845d89
--- a/dev/null
+++ b/shell/ash_test/ash-misc/pipefail.right
@@ -0,0 +1,40 @@
+Default:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+0
+1
+exit 2 | exit 3 | exit 4:
+4
+0
+Pipefail on:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+1
+0
+exit 2 | exit 3 | exit 4:
+4
+0
+Pipefail off:
+true | true:
+0
+1
+true | false:
+1
+0
+false | true:
+0
+1
+exit 2 | exit 3 | exit 4:
+4
+0
+Done
diff --git a/shell/ash_test/ash-misc/pipefail.tests b/shell/ash_test/ash-misc/pipefail.tests
new file mode 100755
index 0000000..9df8418
--- a/dev/null
+++ b/shell/ash_test/ash-misc/pipefail.tests
@@ -0,0 +1,45 @@
+echo Default:
+echo "true | true:"
+ true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+ true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+ false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+ exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Pipefail on:
+set -o pipefail
+echo "true | true:"
+ true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+ true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+ false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+ exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Pipefail off:
+set +o pipefail
+echo "true | true:"
+ true | true; echo $?
+! true | true; echo $?
+echo "true | false:"
+ true | false; echo $?
+! true | false; echo $?
+echo "false | true:"
+ false | true; echo $?
+! false | true; echo $?
+echo "exit 2 | exit 3 | exit 4:"
+ exit 2 | exit 3 | exit 4; echo $?
+! exit 2 | exit 3 | exit 4; echo $?
+
+echo Done
diff --git a/shell/msh_test/msh-bugs/read.right b/shell/ash_test/ash-misc/read.right
index 0e50e2a..0e50e2a 100644
--- a/shell/msh_test/msh-bugs/read.right
+++ b/shell/ash_test/ash-misc/read.right
diff --git a/shell/msh_test/msh-bugs/read.tests b/shell/ash_test/ash-misc/read.tests
index ff1acbd..ff1acbd 100755
--- a/shell/msh_test/msh-bugs/read.tests
+++ b/shell/ash_test/ash-misc/read.tests
diff --git a/shell/ash_test/ash-misc/return1.right b/shell/ash_test/ash-misc/return1.right
new file mode 100644
index 0000000..7b24a35
--- a/dev/null
+++ b/shell/ash_test/ash-misc/return1.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/ash_test/ash-misc/return1.tests b/shell/ash_test/ash-misc/return1.tests
new file mode 100755
index 0000000..eeb92ef
--- a/dev/null
+++ b/shell/ash_test/ash-misc/return1.tests
@@ -0,0 +1,4 @@
+echo "true && return; echo Should not be printed" >return_sourced
+. ./return_sourced
+rm return_sourced
+echo Ok:$?
diff --git a/shell/msh_test/msh-bugs/shift.right b/shell/ash_test/ash-misc/shift.right
index d281e35..d281e35 100644
--- a/shell/msh_test/msh-bugs/shift.right
+++ b/shell/ash_test/ash-misc/shift.right
diff --git a/shell/msh_test/msh-bugs/shift.tests b/shell/ash_test/ash-misc/shift.tests
index 53ef249..53ef249 100755
--- a/shell/msh_test/msh-bugs/shift.tests
+++ b/shell/ash_test/ash-misc/shift.tests
diff --git a/shell/ash_test/ash-misc/sigint1.right b/shell/ash_test/ash-misc/sigint1.right
new file mode 100644
index 0000000..a9094b0
--- a/dev/null
+++ b/shell/ash_test/ash-misc/sigint1.right
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/shell/ash_test/ash-misc/sigint1.tests b/shell/ash_test/ash-misc/sigint1.tests
new file mode 100755
index 0000000..3d483d3
--- a/dev/null
+++ b/shell/ash_test/ash-misc/sigint1.tests
@@ -0,0 +1,41 @@
+# What should happen if non-interactive shell gets SIGINT?
+
+(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
+
+# We create a child which exits with 0 even on SIGINT
+# (The complex command is necessary only if SIGINT is generated by ^C,
+# in this testcase even bare "sleep 2" would do because
+# in the testcase we don't send SIGINT *to the child*...)
+$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
+
+# In one second, we (main shell) get SIGINT here.
+# The question is whether we should, or should not, exit.
+
+# bash will not stop here. It will execute next command(s).
+
+# The rationale for this is described here:
+# http://www.cons.org/cracauer/sigint.html
+#
+# Basically, bash will not exit on SIGINT immediately if it waits
+# for a child. It will wait for the child to exit.
+# If child exits NOT by dying on SIGINT, then bash will not exit.
+#
+# The idea is that the following script:
+# | emacs file.txt
+# | more cmds
+# User may use ^C to interrupt editor's ops like search. But then
+# emacs exits normally. User expects that script doesn't stop.
+#
+# This is a nice idea, but detecting "did process really exit
+# with SIGINT?" is racy. Consider:
+# | bash -c 'while true; do /bin/true; done'
+# When ^C is pressed while bash waits for /bin/true to exit,
+# it may happen that /bin/true exits with exitcode 0 before
+# ^C is delivered to it as SIGINT. bash will see SIGINT, then
+# it will see that child exited with 0, and bash will NOT EXIT.
+
+# Therefore we do not implement bash behavior.
+# I'd say that emacs need to put itself into a separate pgrp
+# to isolate shell from getting stray SIGINTs from ^C.
+
+echo Next command after SIGINT was executed
diff --git a/shell/ash_test/ash-misc/source3.right b/shell/ash_test/ash-misc/source3.right
new file mode 100644
index 0000000..bdf9001
--- a/dev/null
+++ b/shell/ash_test/ash-misc/source3.right
@@ -0,0 +1,2 @@
+Zero:0
+Zero:0
diff --git a/shell/ash_test/ash-misc/source3.tests b/shell/ash_test/ash-misc/source3.tests
new file mode 100755
index 0000000..1abf156
--- a/dev/null
+++ b/shell/ash_test/ash-misc/source3.tests
@@ -0,0 +1,6 @@
+# Test both empty file, and one-empty-line file
+echo >sourced1
+true >sourced2
+false; . ./sourced1; echo Zero:$?
+false; . ./sourced2; echo Zero:$?
+rm sourced1 sourced2
diff --git a/shell/ash_test/ash-misc/source5.right b/shell/ash_test/ash-misc/source5.right
new file mode 100644
index 0000000..0587bad
--- a/dev/null
+++ b/shell/ash_test/ash-misc/source5.right
@@ -0,0 +1,4 @@
+0:arg0 1:arg1 2:arg2
+Ok1:0
+0:arg0 1:q 2:w
+Ok2:0
diff --git a/shell/ash_test/ash-misc/source5.tests b/shell/ash_test/ash-misc/source5.tests
new file mode 100755
index 0000000..40b6b83
--- a/dev/null
+++ b/shell/ash_test/ash-misc/source5.tests
@@ -0,0 +1,8 @@
+echo 'echo "0:$0 1:$1 2:$2"' >sourced1
+set -- 1 2 3
+"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
+echo Ok1:$?
+"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
+echo Ok2:$?
+
+rm sourced1
diff --git a/shell/ash_test/ash-misc/tickquote1.right b/shell/ash_test/ash-misc/tickquote1.right
new file mode 100644
index 0000000..2e661bf
--- a/dev/null
+++ b/shell/ash_test/ash-misc/tickquote1.right
@@ -0,0 +1 @@
+./tickquote1.tests: line 1: syntax error: unterminated quoted string
diff --git a/shell/ash_test/ash-misc/tickquote1.tests b/shell/ash_test/ash-misc/tickquote1.tests
new file mode 100755
index 0000000..90d5bbc
--- a/dev/null
+++ b/shell/ash_test/ash-misc/tickquote1.tests
@@ -0,0 +1 @@
+echo `"pwd`
diff --git a/shell/ash_test/ash-misc/unicode1.right b/shell/ash_test/ash-misc/unicode1.right
new file mode 100644
index 0000000..d3bbbf6
--- a/dev/null
+++ b/shell/ash_test/ash-misc/unicode1.right
@@ -0,0 +1,3 @@
+1
+1
+Ok
diff --git a/shell/ash_test/ash-misc/unicode1.tests b/shell/ash_test/ash-misc/unicode1.tests
new file mode 100755
index 0000000..8788ba9
--- a/dev/null
+++ b/shell/ash_test/ash-misc/unicode1.tests
@@ -0,0 +1,13 @@
+LANG=en_US.UTF-8
+
+# A combining character U+300
+a=`printf "\xcc\x80"`
+# Should print 1
+echo ${#a}
+
+# A Japanese katakana charachter U+30a3
+a=`printf "\xe3\x82\xa3"`
+# Should print 1
+echo ${#a}
+
+echo Ok
diff --git a/shell/ash_test/ash-misc/until1.right b/shell/ash_test/ash-misc/until1.right
new file mode 100644
index 0000000..be2daad
--- a/dev/null
+++ b/shell/ash_test/ash-misc/until1.right
@@ -0,0 +1,3 @@
+1
+1
+Ok:0
diff --git a/shell/ash_test/ash-misc/until1.tests b/shell/ash_test/ash-misc/until1.tests
new file mode 100755
index 0000000..10ab283
--- a/dev/null
+++ b/shell/ash_test/ash-misc/until1.tests
@@ -0,0 +1,11 @@
+x=1
+until test "$x" = 4; do echo $x; x=4; done
+
+# We had a bug in multi-line form
+x=1
+until test "$x" = 4; do
+ echo $x
+ x=4
+done
+
+echo Ok:$?
diff --git a/shell/ash_test/ash-misc/wait4.right b/shell/ash_test/ash-misc/wait4.right
new file mode 100644
index 0000000..f7987db
--- a/dev/null
+++ b/shell/ash_test/ash-misc/wait4.right
@@ -0,0 +1 @@
+Three:3
diff --git a/shell/ash_test/ash-misc/wait4.tests b/shell/ash_test/ash-misc/wait4.tests
new file mode 100755
index 0000000..cc34059
--- a/dev/null
+++ b/shell/ash_test/ash-misc/wait4.tests
@@ -0,0 +1,2 @@
+sleep 1 | (sleep 1;exit 3) & wait %1
+echo Three:$?
diff --git a/shell/ash_test/ash-misc/wait5.right b/shell/ash_test/ash-misc/wait5.right
new file mode 100644
index 0000000..82c9d56
--- a/dev/null
+++ b/shell/ash_test/ash-misc/wait5.right
@@ -0,0 +1,2 @@
+Zero:0
+Three:3
diff --git a/shell/ash_test/ash-misc/wait5.tests b/shell/ash_test/ash-misc/wait5.tests
new file mode 100755
index 0000000..1b4762d
--- a/dev/null
+++ b/shell/ash_test/ash-misc/wait5.tests
@@ -0,0 +1,5 @@
+sleep 0 | (sleep 0;exit 3) &
+sleep 1
+echo Zero:$?
+wait %1
+echo Three:$?
diff --git a/shell/ash_test/ash-misc/while1.right b/shell/ash_test/ash-misc/while1.right
new file mode 100644
index 0000000..7c4d7be
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while1.right
@@ -0,0 +1 @@
+OK:0
diff --git a/shell/ash_test/ash-misc/while1.tests b/shell/ash_test/ash-misc/while1.tests
new file mode 100755
index 0000000..11e201e
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while1.tests
@@ -0,0 +1,2 @@
+while false; do echo NOT SHOWN; done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/while2.right b/shell/ash_test/ash-misc/while2.right
new file mode 100644
index 0000000..07207cc
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while2.right
@@ -0,0 +1,2 @@
+Hello
+OK:0
diff --git a/shell/ash_test/ash-misc/while2.tests b/shell/ash_test/ash-misc/while2.tests
new file mode 100755
index 0000000..2247adc
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while2.tests
@@ -0,0 +1,2 @@
+while echo Hello; false; do echo NOT SHOWN; done
+echo OK:$?
diff --git a/shell/ash_test/ash-misc/while4.right b/shell/ash_test/ash-misc/while4.right
new file mode 100644
index 0000000..7b24a35
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while4.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/ash_test/ash-misc/while4.tests b/shell/ash_test/ash-misc/while4.tests
new file mode 100755
index 0000000..ba80e60
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while4.tests
@@ -0,0 +1,6 @@
+false
+while false && echo Not reached; do
+ echo BUG
+ break
+done
+echo Ok:$?
diff --git a/shell/ash_test/ash-misc/while_in_subshell.right b/shell/ash_test/ash-misc/while_in_subshell.right
new file mode 100644
index 0000000..290d39b
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while_in_subshell.right
@@ -0,0 +1 @@
+OK: 0
diff --git a/shell/ash_test/ash-misc/while_in_subshell.tests b/shell/ash_test/ash-misc/while_in_subshell.tests
new file mode 100755
index 0000000..def8e09
--- a/dev/null
+++ b/shell/ash_test/ash-misc/while_in_subshell.tests
@@ -0,0 +1,2 @@
+(while true; do exit; done)
+echo OK: $?
diff --git a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
index 3fa2f18..74dca1c 100755
--- a/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
+++ b/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
@@ -1,5 +1,5 @@
# The bug here was triggered by:
-# * performin pathname expansion because we see [
+# * performing pathname expansion because we see [
# * replace operator did not escape \ in replace string
IP=192.168.0.1
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash1.right b/shell/ash_test/ash-quoting/dollar_squote_bash1.right
index 57536b1..9f4e25e 100644
--- a/shell/ash_test/ash-quoting/dollar_squote_bash1.right
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash1.right
@@ -1,4 +1,5 @@
a b
+$'a\tb'
a
b c
def
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash1.tests b/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
index 93a56ca..6fc411b 100755
--- a/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
@@ -1,4 +1,5 @@
echo $'a\tb'
+echo "$'a\tb'"
echo $'a\nb' $'c\nd''ef'
echo $'a\'b' $'c\"d' $'e\\f'
echo $'a\63b' $'c\063b' $'e\0633f'
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.right b/shell/ash_test/ash-quoting/dollar_squote_bash2.right
new file mode 100644
index 0000000..f7a1731
--- a/dev/null
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.right
@@ -0,0 +1,6 @@
+strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
+strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
+80:\€
+81:\
+82:\‚
+Done:0
diff --git a/shell/ash_test/ash-quoting/dollar_squote_bash2.tests b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests
new file mode 100755
index 0000000..4497728
--- a/dev/null
+++ b/shell/ash_test/ash-quoting/dollar_squote_bash2.tests
@@ -0,0 +1,10 @@
+# Embedded NULs
+echo $'str\x00'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
+echo $'str\000'strstrstrstrstrstrstrstrstrstrstrstrstrstrstrstr
+
+# The chars after '\' are hex 0x80,81,82...
+echo 80:$'\€'
+echo 81:$'\'
+echo 82:$'\‚'
+
+echo Done:$?
diff --git a/shell/ash_test/ash-read/read_r.tests b/shell/ash_test/ash-read/read_r.tests
index 2c4cc61..1f0a18a 100755
--- a/shell/ash_test/ash-read/read_r.tests
+++ b/shell/ash_test/ash-read/read_r.tests
@@ -1,2 +1,4 @@
-echo -e 'test\\\nbest' | (read reply; echo "$reply")
-echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
+echo 'test\
+best' | (read reply; echo "$reply")
+echo 'test\
+best' | (read -r reply; echo "$reply")
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/ash_test/ash-redir/redir1.right
index 15515d1..15515d1 100644
--- a/shell/hush_test/hush-misc/redir1.right
+++ b/shell/ash_test/ash-redir/redir1.right
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/ash_test/ash-redir/redir1.tests
index ef2fbfb..ef2fbfb 100755
--- a/shell/hush_test/hush-misc/redir1.tests
+++ b/shell/ash_test/ash-redir/redir1.tests
diff --git a/shell/ash_test/ash-redir/redir7.tests b/shell/ash_test/ash-redir/redir7.tests
index ca3979a..e873a46 100755
--- a/shell/ash_test/ash-redir/redir7.tests
+++ b/shell/ash_test/ash-redir/redir7.tests
@@ -4,9 +4,9 @@
# was out of sync for redirect filenames.
>unicode.sh
-echo -e 'echo Ok >uni\x81code' >>unicode.sh
-echo -e 'cat uni\x81code' >>unicode.sh
-echo -e 'cat uni?code' >>unicode.sh
+printf 'echo Ok >uni\x81code\n' >>unicode.sh
+printf 'cat uni\x81code\n' >>unicode.sh
+printf 'cat uni?code\n' >>unicode.sh
. ./unicode.sh
rm uni*code*
echo Done
diff --git a/shell/ash_test/ash-redir/redir8.tests b/shell/ash_test/ash-redir/redir8.tests
index 8cb42c0..2bd3867 100755
--- a/shell/ash_test/ash-redir/redir8.tests
+++ b/shell/ash_test/ash-redir/redir8.tests
@@ -6,10 +6,10 @@
# Subcase when redirect filename is specified in a variable.
>unicode.sh
-echo -e 'v=uni\x81code' >>unicode.sh
-echo -e 'echo Ok >"$v"' >>unicode.sh
-echo -e 'cat uni\x81code' >>unicode.sh
-echo -e 'cat uni?code' >>unicode.sh
+printf 'v=uni\x81code\n' >>unicode.sh
+printf 'echo Ok >"$v"\n' >>unicode.sh
+printf 'cat uni\x81code\n' >>unicode.sh
+printf 'cat uni?code\n' >>unicode.sh
. ./unicode.sh
rm uni*code*
echo Done
diff --git a/shell/hush_test/hush-misc/redir2.right b/shell/ash_test/ash-redir/redir_escapednum.right
index 7326d96..7326d96 100644
--- a/shell/hush_test/hush-misc/redir2.right
+++ b/shell/ash_test/ash-redir/redir_escapednum.right
diff --git a/shell/hush_test/hush-misc/redir2.tests b/shell/ash_test/ash-redir/redir_escapednum.tests
index 81983ca..81983ca 100755
--- a/shell/hush_test/hush-misc/redir2.tests
+++ b/shell/ash_test/ash-redir/redir_escapednum.tests
diff --git a/shell/hush_test/hush-misc/redir4.right b/shell/ash_test/ash-redir/redir_expand.right
index ead25f6..ead25f6 100644
--- a/shell/hush_test/hush-misc/redir4.right
+++ b/shell/ash_test/ash-redir/redir_expand.right
diff --git a/shell/hush_test/hush-misc/redir4.tests b/shell/ash_test/ash-redir/redir_expand.tests
index c50b8ce..c50b8ce 100755
--- a/shell/hush_test/hush-misc/redir4.tests
+++ b/shell/ash_test/ash-redir/redir_expand.tests
diff --git a/shell/ash_test/ash-redir/redir_leak.right b/shell/ash_test/ash-redir/redir_leak.right
new file mode 100644
index 0000000..b1c4829
--- a/dev/null
+++ b/shell/ash_test/ash-redir/redir_leak.right
@@ -0,0 +1,6 @@
+4
+4
+4
+4
+4
+4
diff --git a/shell/ash_test/ash-redir/redir_leak.tests b/shell/ash_test/ash-redir/redir_leak.tests
new file mode 100755
index 0000000..c8a9c63
--- a/dev/null
+++ b/shell/ash_test/ash-redir/redir_leak.tests
@@ -0,0 +1,10 @@
+# Each of these should show only four lines:
+# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls.
+# This test detects bugs where redirects leave stray open fds.
+
+ls -1 /proc/self/fd | wc -l
+ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l
+ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l
+echo "`ls -1 /proc/self/fd `" | wc -l
+echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l
+echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l
diff --git a/shell/hush_test/hush-misc/redir6.right b/shell/ash_test/ash-redir/redir_multi.right
index a97c4bd..a97c4bd 100644
--- a/shell/hush_test/hush-misc/redir6.right
+++ b/shell/ash_test/ash-redir/redir_multi.right
diff --git a/shell/hush_test/hush-misc/redir6.tests b/shell/ash_test/ash-redir/redir_multi.tests
index c639ebb..c639ebb 100755
--- a/shell/hush_test/hush-misc/redir6.tests
+++ b/shell/ash_test/ash-redir/redir_multi.tests
diff --git a/shell/ash_test/ash-redir/redir_script.right b/shell/ash_test/ash-redir/redir_script.right
new file mode 100644
index 0000000..6694ed3
--- a/dev/null
+++ b/shell/ash_test/ash-redir/redir_script.right
@@ -0,0 +1 @@
+Ok: script fd is not closed
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests
new file mode 100755
index 0000000..ccc497d
--- a/dev/null
+++ b/shell/ash_test/ash-redir/redir_script.tests
@@ -0,0 +1,29 @@
+# Builds a " 3>&- 4>&-" string.
+# Note: one of these fds is a directory opened to /proc/self/fd
+# for globbing. It is unwanted, but I don't know how to filter it out.
+find_fds() {
+ fds=""
+ for f in /proc/self/fd/*; do
+ test "$f" = "/proc/self/fd/0" && continue
+ test "$f" = "/proc/self/fd/1" && continue
+ test "$f" = "/proc/self/fd/2" && continue
+ fds="$fds ${f##*/}>&-"
+ done
+}
+
+find_fds
+fds1="$fds"
+
+# One of the fds is open to the script body
+# Close it while executing something.
+eval "find_fds $fds"
+
+# Shell should not lose that fd. Did it?
+find_fds
+test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
+
+echo "Bug: script fd is closed"
+echo "fds1:$fds1"
+echo "fds2:$fds"
+exit 1
+
diff --git a/shell/hush_test/hush-parsing/redir_space.right b/shell/ash_test/ash-redir/redir_space.right
index 0842952..0842952 100644
--- a/shell/hush_test/hush-parsing/redir_space.right
+++ b/shell/ash_test/ash-redir/redir_space.right
diff --git a/shell/hush_test/hush-parsing/redir_space.tests b/shell/ash_test/ash-redir/redir_space.tests
index c0b5430..c0b5430 100755
--- a/shell/hush_test/hush-parsing/redir_space.tests
+++ b/shell/ash_test/ash-redir/redir_space.tests
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.right b/shell/ash_test/ash-signals/continue_and_trap1.right
new file mode 100644
index 0000000..d2dd0af
--- a/dev/null
+++ b/shell/ash_test/ash-signals/continue_and_trap1.right
@@ -0,0 +1 @@
+Exiting
diff --git a/shell/ash_test/ash-signals/continue_and_trap1.tests b/shell/ash_test/ash-signals/continue_and_trap1.tests
new file mode 100755
index 0000000..2a5c147
--- a/dev/null
+++ b/shell/ash_test/ash-signals/continue_and_trap1.tests
@@ -0,0 +1,7 @@
+trap "echo Exiting; exit" INT
+
+(sleep 1; kill -s INT $$) &
+
+while continue; do
+ continue;
+done
diff --git a/shell/ash_test/ash-signals/return_in_trap1.right b/shell/ash_test/ash-signals/return_in_trap1.right
new file mode 100644
index 0000000..a6e6378
--- a/dev/null
+++ b/shell/ash_test/ash-signals/return_in_trap1.right
@@ -0,0 +1,4 @@
+a:2
+b:0
+Trap
+d:3
diff --git a/shell/ash_test/ash-signals/return_in_trap1.tests b/shell/ash_test/ash-signals/return_in_trap1.tests
new file mode 100755
index 0000000..4c0d53b
--- a/dev/null
+++ b/shell/ash_test/ash-signals/return_in_trap1.tests
@@ -0,0 +1,18 @@
+a() {
+ (exit 2)
+ echo a:$?
+ (kill -s USR1 $$; echo b:$?; exit 3)
+ echo c:$? # does not execute
+ (exit 4)
+}
+
+trap "echo Trap; return" USR1
+a
+
+echo d:$?
+# It's debatable what is the correct value above.
+# Does 'return' in trap see $? == 2 or $? == 3?
+# IOW: after (kill..), does shell first wait for its completion
+# and sets $?, then checks pending signals and runs a trap handler,
+# or does it first check pending signals and runs handler?
+# hush does the former, and prints 3.
diff --git a/shell/hush_test/hush-trap/save-ret.right b/shell/ash_test/ash-signals/save-ret.right
index a3e12ce..a3e12ce 100644
--- a/shell/hush_test/hush-trap/save-ret.right
+++ b/shell/ash_test/ash-signals/save-ret.right
diff --git a/shell/hush_test/hush-trap/save-ret.tests b/shell/ash_test/ash-signals/save-ret.tests
index 0786b6d..0786b6d 100755
--- a/shell/hush_test/hush-trap/save-ret.tests
+++ b/shell/ash_test/ash-signals/save-ret.tests
diff --git a/shell/ash_test/ash-signals/signal1.tests b/shell/ash_test/ash-signals/signal1.tests
index 28bfc6a..6194346 100755
--- a/shell/ash_test/ash-signals/signal1.tests
+++ b/shell/ash_test/ash-signals/signal1.tests
@@ -19,7 +19,7 @@ while $sleeping; do
if wait %%; then
echo "sleep completed"
sleeping=false
- elif [ $? == 127 ]; then
+ elif [ $? = 127 ]; then
echo "BUG: no processes to wait for?!"
sleeping=false
else
diff --git a/shell/ash_test/ash-signals/sigquit_exec.right b/shell/ash_test/ash-signals/sigquit_exec.right
new file mode 100644
index 0000000..a804192
--- a/dev/null
+++ b/shell/ash_test/ash-signals/sigquit_exec.right
@@ -0,0 +1,2 @@
+SigIgn: 0000000000000000
+SigIgn: 0000000000000000
diff --git a/shell/ash_test/ash-signals/sigquit_exec.tests b/shell/ash_test/ash-signals/sigquit_exec.tests
new file mode 100755
index 0000000..24bda69
--- a/dev/null
+++ b/shell/ash_test/ash-signals/sigquit_exec.tests
@@ -0,0 +1,4 @@
+# Should show no masked signals in both cases.
+# We had a bug where SIGQUIT was masked on exec.
+grep SigIgn: /proc/self/status
+exec grep SigIgn: /proc/self/status
diff --git a/shell/ash_test/ash-vars/empty.right b/shell/ash_test/ash-vars/empty.right
new file mode 100644
index 0000000..2cb3c70
--- a/dev/null
+++ b/shell/ash_test/ash-vars/empty.right
@@ -0,0 +1,3 @@
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
diff --git a/shell/ash_test/ash-vars/empty.tests b/shell/ash_test/ash-vars/empty.tests
new file mode 100755
index 0000000..a9c247e
--- a/dev/null
+++ b/shell/ash_test/ash-vars/empty.tests
@@ -0,0 +1,5 @@
+e=
+
+echo a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+echo a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
+echo $e a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
diff --git a/shell/ash_test/ash-vars/glob_and_vars.right b/shell/ash_test/ash-vars/glob_and_vars.right
new file mode 100644
index 0000000..3ac7ec5
--- a/dev/null
+++ b/shell/ash_test/ash-vars/glob_and_vars.right
@@ -0,0 +1 @@
+./glob_and_vars.right ./glob_and_vars.tests
diff --git a/shell/ash_test/ash-vars/glob_and_vars.tests b/shell/ash_test/ash-vars/glob_and_vars.tests
new file mode 100755
index 0000000..482cf9d
--- a/dev/null
+++ b/shell/ash_test/ash-vars/glob_and_vars.tests
@@ -0,0 +1,2 @@
+v=.
+echo $v/glob_and_vars.[tr]*
diff --git a/shell/ash_test/ash-vars/param_expand_len.right b/shell/ash_test/ash-vars/param_expand_len.right
new file mode 100644
index 0000000..48d01d2
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len.right
@@ -0,0 +1,12 @@
+0
+0
+1
+Make sure len parsing doesnt break arg count
+0 0
+4 4
+Testing len op
+4 3 2 1 0 0
+0 3 0
+Nothing:
+Nothing:
+One:1
diff --git a/shell/ash_test/ash-vars/param_expand_len.tests b/shell/ash_test/ash-vars/param_expand_len.tests
new file mode 100755
index 0000000..369c8d4
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_expand_len.tests
@@ -0,0 +1,24 @@
+"$THIS_SH" -c 'echo $#'
+"$THIS_SH" -c 'echo $#' arg0
+"$THIS_SH" -c 'echo $#' arg0 arg1
+
+echo Make sure len parsing doesnt break arg count
+set --
+echo $# ${#}
+set -- aaaa bbb cc d
+echo $# ${#}
+
+echo Testing len op
+echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6}
+
+unset e
+f=abc
+g=
+echo ${#e} ${#f} ${#g}
+
+set -- a
+# This must be interpreted as: $# ("1"), then remove trailing "1".
+# IOW: empty result.
+echo Nothing:${##1}
+echo Nothing:${#%1}
+echo One:${##x}
diff --git a/shell/ash_test/ash-vars/param_glob.right b/shell/ash_test/ash-vars/param_glob.right
new file mode 100644
index 0000000..bdee8fe
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_glob.right
@@ -0,0 +1,4 @@
+param_glob.tests
+param_glob.tests
+param_glob.t*
+param_glob.t*
diff --git a/shell/ash_test/ash-vars/param_glob.tests b/shell/ash_test/ash-vars/param_glob.tests
new file mode 100755
index 0000000..4d74fee
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_glob.tests
@@ -0,0 +1,9 @@
+if test $# = 0; then
+ exec "$THIS_SH" "$0" 'param_glob.t*'
+ echo NOT SHOWN
+ exit
+fi
+echo $*
+echo $@
+echo "$*"
+echo "$@"
diff --git a/shell/ash_test/ash-vars/param_subshell.right b/shell/ash_test/ash-vars/param_subshell.right
new file mode 100644
index 0000000..f3c3767
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_subshell.right
@@ -0,0 +1,7 @@
+1=1
+2=2
+3=3
+4=4
+5=5
+6=6
+7=7
diff --git a/shell/ash_test/ash-vars/param_subshell.tests b/shell/ash_test/ash-vars/param_subshell.tests
new file mode 100755
index 0000000..27fdc5b
--- a/dev/null
+++ b/shell/ash_test/ash-vars/param_subshell.tests
@@ -0,0 +1,15 @@
+if test $# = 0; then
+ "$THIS_SH" "$0" 1 2 3 4 5 6 7 8 9
+ exit
+fi
+echo 1=$1
+{ echo 2=$2; }
+{ echo 3=$3; } &
+# cant use usleep as it isnt standard in $PATH --
+# we fail when testing busybox compiled solely as "hush"
+wait
+( echo 4=$4 )
+( echo 5=$5 ) &
+wait
+true | echo 6=$6 | cat
+true | { echo 7=$7; } | cat
diff --git a/shell/ash_test/ash-vars/readonly1.right b/shell/ash_test/ash-vars/readonly1.right
new file mode 100644
index 0000000..2b363e3
--- a/dev/null
+++ b/shell/ash_test/ash-vars/readonly1.right
@@ -0,0 +1,2 @@
+One:1
+One:1
diff --git a/shell/ash_test/ash-vars/readonly1.tests b/shell/ash_test/ash-vars/readonly1.tests
new file mode 100755
index 0000000..81b461f
--- a/dev/null
+++ b/shell/ash_test/ash-vars/readonly1.tests
@@ -0,0 +1,7 @@
+readonly bla=123
+# Bare "eval bla=123" should abort ("eval" is a special builtin):
+(eval bla=123 2>/dev/null; echo BUG)
+echo One:$?
+# "command BLTIN" disables "special-ness", should not abort:
+command eval bla=123 2>/dev/null
+echo One:$?
diff --git a/shell/msh_test/msh-vars/star.right b/shell/ash_test/ash-vars/star.right
index 0ecc55b..0ecc55b 100644
--- a/shell/msh_test/msh-vars/star.right
+++ b/shell/ash_test/ash-vars/star.right
diff --git a/shell/msh_test/msh-vars/star.tests b/shell/ash_test/ash-vars/star.tests
index 5554c40..5554c40 100755
--- a/shell/msh_test/msh-vars/star.tests
+++ b/shell/ash_test/ash-vars/star.tests
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
new file mode 100644
index 0000000..81a1585
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
@@ -0,0 +1,2 @@
+12
+9
diff --git a/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
new file mode 100755
index 0000000..e97a08a
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
@@ -0,0 +1,3 @@
+unset a
+echo $((3 + ${a:=$((4 + 5))}))
+echo $a
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
new file mode 100644
index 0000000..4b9b4f0
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
@@ -0,0 +1 @@
+~root
diff --git a/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
new file mode 100755
index 0000000..d8eb8fc
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
@@ -0,0 +1,2 @@
+unset a
+echo "${a:-~root}"
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
new file mode 100644
index 0000000..030ebde
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
@@ -0,0 +1 @@
+/b/c/
diff --git a/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
new file mode 100755
index 0000000..fb93714
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
@@ -0,0 +1,3 @@
+a=/b/c/*
+b=\\
+echo ${a%$b*}
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right
new file mode 100644
index 0000000..2357750
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.right
@@ -0,0 +1 @@
+:/root
diff --git a/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests
new file mode 100755
index 0000000..6605315
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-expand-tilde-in-parameter-expansion.tests
@@ -0,0 +1,2 @@
+a=~root:~root
+echo ${a#~root}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right
new file mode 100644
index 0000000..2da3272
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.right
@@ -0,0 +1 @@
+a_\_z_c
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests
new file mode 100755
index 0000000..e4529c6
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-1.tests
@@ -0,0 +1,2 @@
+v="a\bc"
+echo ${v/\\b/_\\_\z_}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right
new file mode 100644
index 0000000..7447c0a
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.right
@@ -0,0 +1 @@
+ax/yc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests
new file mode 100755
index 0000000..2db1db8
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-2.tests
@@ -0,0 +1,2 @@
+v="abc"
+echo ${v/b/x/y}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right
new file mode 100644
index 0000000..5ea5ff8
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.right
@@ -0,0 +1 @@
+axcabc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests
new file mode 100755
index 0000000..0935e45
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-3.tests
@@ -0,0 +1,2 @@
+v="abcabc"
+echo ${v/b/x}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right
new file mode 100644
index 0000000..46dd750
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.right
@@ -0,0 +1 @@
+axcaxc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests
new file mode 100755
index 0000000..d8de843
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-4.tests
@@ -0,0 +1,2 @@
+v="abcabc"
+echo ${v//b/x}
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right
new file mode 100644
index 0000000..699b27b
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.right
@@ -0,0 +1 @@
+axc
diff --git a/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests
new file mode 100755
index 0000000..5523888
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-pattern-replacement-in-parameter-expansion-5.tests
@@ -0,0 +1,2 @@
+v="ab/c"
+echo ${v/b\//x}
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.right b/shell/ash_test/ash-vars/var-runtime-quote-detection.right
new file mode 100644
index 0000000..b554d9e
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.right
@@ -0,0 +1 @@
+<>
diff --git a/shell/ash_test/ash-vars/var-runtime-quote-detection.tests b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests
new file mode 100755
index 0000000..e570631
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-runtime-quote-detection.tests
@@ -0,0 +1 @@
+foo=\\ echo "<${foo#[\\]}>"
diff --git a/shell/ash_test/ash-vars/var-utf8-length.right b/shell/ash_test/ash-vars/var-utf8-length.right
new file mode 100644
index 0000000..6f4247a
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-utf8-length.right
@@ -0,0 +1 @@
+26
diff --git a/shell/ash_test/ash-vars/var-utf8-length.tests b/shell/ash_test/ash-vars/var-utf8-length.tests
new file mode 100755
index 0000000..d04b2cb
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var-utf8-length.tests
@@ -0,0 +1,2 @@
+X=abcdÉfghÃjklmnÓpqrstÚvwcyz
+echo ${#X}
diff --git a/shell/ash_test/ash-vars/var1.right b/shell/ash_test/ash-vars/var1.right
index 2a01291..194e7db 100644
--- a/shell/ash_test/ash-vars/var1.right
+++ b/shell/ash_test/ash-vars/var1.right
@@ -1,6 +1,4 @@
-a=a A=a
-a=a A=a
-a= A=
-a= A=
-a=a A=a
-a=a A=a
+http://busybox.net
+http://busybox.net_abc
+1 1
+1 1
diff --git a/shell/ash_test/ash-vars/var1.tests b/shell/ash_test/ash-vars/var1.tests
index 802e489..48a6782 100755
--- a/shell/ash_test/ash-vars/var1.tests
+++ b/shell/ash_test/ash-vars/var1.tests
@@ -1,14 +1,9 @@
-# check that first assignment has proper effect on second one
+URL=http://busybox.net
-(
-a=a A=$a
-echo a=$a A=$A
-)
-(a=a A=$a; echo a=$a A=$A)
-(a=a A=$a echo a=$a A=$A)
-(a=a A=$a /bin/echo a=$a A=$A)
+echo $URL
+echo ${URL}_abc
-f() { echo a=$a A=$A; }
-
-(a=a A=$a f)
-(a=a A=$a; f)
+true
+false; echo $? ${?}
+true
+{ false; echo $? ${?}; }
diff --git a/shell/ash_test/ash-vars/var2.right b/shell/ash_test/ash-vars/var2.right
index 8fed138..40bf4bf 100644
--- a/shell/ash_test/ash-vars/var2.right
+++ b/shell/ash_test/ash-vars/var2.right
@@ -1 +1,2 @@
-bus/usb/1/2
+http://busybox.net
+http://busybox.net_abc
diff --git a/shell/ash_test/ash-vars/var2.tests b/shell/ash_test/ash-vars/var2.tests
index 07feaeb..1292410 100755
--- a/shell/ash_test/ash-vars/var2.tests
+++ b/shell/ash_test/ash-vars/var2.tests
@@ -1 +1,4 @@
-X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
+_1=http://busybox.net
+
+echo $_1
+echo ${_1}_abc
diff --git a/shell/ash_test/ash-vars/var3.right b/shell/ash_test/ash-vars/var3.right
new file mode 100644
index 0000000..8eb0e33
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var3.right
@@ -0,0 +1,5 @@
+1
+1
+
+
+0
diff --git a/shell/ash_test/ash-vars/var3.tests b/shell/ash_test/ash-vars/var3.tests
new file mode 100755
index 0000000..97b102c
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var3.tests
@@ -0,0 +1 @@
+x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
diff --git a/shell/ash_test/ash-vars/var4.right b/shell/ash_test/ash-vars/var4.right
new file mode 100644
index 0000000..8fed138
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var4.right
@@ -0,0 +1 @@
+bus/usb/1/2
diff --git a/shell/ash_test/ash-vars/var4.tests b/shell/ash_test/ash-vars/var4.tests
new file mode 100755
index 0000000..07feaeb
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var4.tests
@@ -0,0 +1 @@
+X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
diff --git a/shell/ash_test/ash-vars/var5.right b/shell/ash_test/ash-vars/var5.right
new file mode 100644
index 0000000..2a01291
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var5.right
@@ -0,0 +1,6 @@
+a=a A=a
+a=a A=a
+a= A=
+a= A=
+a=a A=a
+a=a A=a
diff --git a/shell/ash_test/ash-vars/var5.tests b/shell/ash_test/ash-vars/var5.tests
new file mode 100755
index 0000000..802e489
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var5.tests
@@ -0,0 +1,14 @@
+# check that first assignment has proper effect on second one
+
+(
+a=a A=$a
+echo a=$a A=$A
+)
+(a=a A=$a; echo a=$a A=$A)
+(a=a A=$a echo a=$a A=$A)
+(a=a A=$a /bin/echo a=$a A=$A)
+
+f() { echo a=$a A=$A; }
+
+(a=a A=$a f)
+(a=a A=$a; f)
diff --git a/shell/ash_test/ash-vars/var_bash1a.right b/shell/ash_test/ash-vars/var_bash1a.right
new file mode 100644
index 0000000..1965b5c
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_bash1a.right
@@ -0,0 +1,6 @@
+parameter 'abcdef'
+varoffset2 'cdef'
+varoffset-2 'ef'
+literal '2' 'cdef'
+literal '-2' 'abcdef'
+literal ' -2' 'ef'
diff --git a/shell/ash_test/ash-vars/var_bash1a.tests b/shell/ash_test/ash-vars/var_bash1a.tests
new file mode 100755
index 0000000..551dd9a
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_bash1a.tests
@@ -0,0 +1,11 @@
+parameter=abcdef
+offset=2
+noffset=-2
+echo "parameter '${parameter}'"
+echo "varoffset2 '${parameter:${offset}}'"
+echo "varoffset-2 '${parameter:${noffset}}'"
+echo "literal '2' '${parameter:2}'"
+# This is not inrpreted as ${VAR:POS{:LEN}},
+# but as ${VAR:=WORD} - if VAR is unset or null, substitute WORD
+echo "literal '-2' '${parameter:-2}'"
+echo "literal ' -2' '${parameter: -2}'"
diff --git a/shell/ash_test/ash-vars/var_bash4.right b/shell/ash_test/ash-vars/var_bash4.right
index 600e853..0ef1bf6 100644
--- a/shell/ash_test/ash-vars/var_bash4.right
+++ b/shell/ash_test/ash-vars/var_bash4.right
@@ -1,23 +1,40 @@
Source: a*b\*c
Replace str: _\\_\z_
Pattern: single backslash and star: "replace literal star"
-In assignment: a_\_z_b\*c
Unquoted: a_\_z_b\*c
+Unquoted =: a_\_z_b\*c
Quoted: a_\_\z_b\*c
+Quoted =: a_\_\z_b\*c
Pattern: double backslash and star: "replace backslash and everything after it"
-In assignment: a*b_\_z_
Unquoted: a*b_\_z_
+Unquoted =: a*b_\_z_
Quoted: a*b_\_\z_
+Quoted =: a*b_\_\z_
Source: a\bc
Replace str: _\\_\z_
Pattern: single backslash and b: "replace b"
-In assignment: a\_\_z_c
Unquoted: a\_\_z_c
+Unquoted =: a\_\_z_c
Quoted: a\_\_\z_c
+Quoted =: a\_\_\z_c
Pattern: double backslash and b: "replace backslash and b"
-In assignment: a_\_z_c
Unquoted: a_\_z_c
+Unquoted =: a_\_z_c
Quoted: a_\_\z_c
+Quoted =: a_\_\z_c
+
+Source: a\bc
+Replace str: _\\_\z_ (as variable $s)
+Pattern: single backslash and b: "replace b"
+Unquoted: a\_\\_\z_c
+Unquoted =: a\_\\_\z_c
+Quoted: a\_\\_\z_c
+Quoted =: a\_\\_\z_c
+Pattern: double backslash and b: "replace backslash and b"
+Unquoted: a_\\_\z_c
+Unquoted =: a_\\_\z_c
+Quoted: a_\\_\z_c
+Quoted =: a_\\_\z_c
Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash4.tests b/shell/ash_test/ash-vars/var_bash4.tests
index d547061..32aa2b3 100755
--- a/shell/ash_test/ash-vars/var_bash4.tests
+++ b/shell/ash_test/ash-vars/var_bash4.tests
@@ -6,23 +6,30 @@
# even in quotes.
#
# bash4 (and probably bash3 too): "Quoted:" results are different from
-# unquoted and assignment expansions - they have a backslash before z.
+# unquoted expansions - they have a backslash before z.
+#
+# The difference only exists if repl is a literal. If it is a variable:
+# ${v/.../$s}, then all backslashes are preserved in both cases.
v='a*b\*c'
echo 'Source: ' "$v"
echo 'Replace str: ' '_\\_\z_'
echo 'Pattern: ' 'single backslash and star: "replace literal star"'
-r=${v/\*/_\\_\z_}
-echo 'In assignment:' "$r"
echo 'Unquoted: ' ${v/\*/_\\_\z_}
+r=${v/\*/_\\_\z_}
+echo 'Unquoted =: ' "$r"
echo 'Quoted: ' "${v/\*/_\\_\z_}"
+r="${v/\*/_\\_\z_}"
+echo 'Quoted =: ' "$r"
echo 'Pattern: ' 'double backslash and star: "replace backslash and everything after it"'
-r=${v/\\*/_\\_\z_}
-echo 'In assignment:' "$r"
echo 'Unquoted: ' ${v/\\*/_\\_\z_}
+r=${v/\\*/_\\_\z_}
+echo 'Unquoted =: ' "$r"
echo 'Quoted: ' "${v/\\*/_\\_\z_}"
+r="${v/\\*/_\\_\z_}"
+echo 'Quoted =: ' "$r"
echo
@@ -31,16 +38,43 @@ echo 'Source: ' "$v"
echo 'Replace str: ' '_\\_\z_'
echo 'Pattern: ' 'single backslash and b: "replace b"'
-r=${v/\b/_\\_\z_}
-echo 'In assignment:' "$r"
echo 'Unquoted: ' ${v/\b/_\\_\z_}
+r=${v/\b/_\\_\z_}
+echo 'Unquoted =: ' "$r"
echo 'Quoted: ' "${v/\b/_\\_\z_}"
+r="${v/\b/_\\_\z_}"
+echo 'Quoted =: ' "$r"
echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
-r=${v/\\b/_\\_\z_}
-echo 'In assignment:' "$r"
echo 'Unquoted: ' ${v/\\b/_\\_\z_}
+r=${v/\\b/_\\_\z_}
+echo 'Unquoted =: ' "$r"
echo 'Quoted: ' "${v/\\b/_\\_\z_}"
+r="${v/\\b/_\\_\z_}"
+echo 'Quoted =: ' "$r"
+
+echo
+
+v='a\bc'
+s='_\\_\z_'
+echo 'Source: ' "$v"
+echo 'Replace str: ' "$s" '(as variable $s)'
+
+echo 'Pattern: ' 'single backslash and b: "replace b"'
+echo 'Unquoted: ' ${v/\b/$s}
+r=${v/\b/$s}
+echo 'Unquoted =: ' "$r"
+echo 'Quoted: ' "${v/\b/$s}"
+r="${v/\b/$s}"
+echo 'Quoted =: ' "$r"
+
+echo 'Pattern: ' 'double backslash and b: "replace backslash and b"'
+echo 'Unquoted: ' ${v/\\b/$s}
+r=${v/\\b/$s}
+echo 'Unquoted =: ' "$r"
+echo 'Quoted: ' "${v/\\b/$s}"
+r="${v/\\b/$s}"
+echo 'Quoted =: ' "$r"
echo
diff --git a/shell/ash_test/ash-vars/var_bash5.right b/shell/ash_test/ash-vars/var_bash5.right
index 278ed32..1990902 100644
--- a/shell/ash_test/ash-vars/var_bash5.right
+++ b/shell/ash_test/ash-vars/var_bash5.right
@@ -1,4 +1,11 @@
-a/
-a/d
-a/e/f
+1 a/
+2 a/d
+3 a/e/f
+4 a\
+5 a\d
+6 a\e\f
+7 a\\
+8 a\\d
+9 a\\e\\f
+a ab
Done: 0
diff --git a/shell/ash_test/ash-vars/var_bash5.tests b/shell/ash_test/ash-vars/var_bash5.tests
index 7f482a5..5748b4a 100755
--- a/shell/ash_test/ash-vars/var_bash5.tests
+++ b/shell/ash_test/ash-vars/var_bash5.tests
@@ -4,8 +4,26 @@
v='a/b/c'
s='b/c'
r='e/f'
-echo "${v/$s}"
-echo "${v/$s/d}"
-echo "${v/$s/$r}"
+echo "1 ${v/$s}"
+echo "2 ${v/$s/d}"
+echo "3 ${v/$s/$r}"
+
+v='a\b\c'
+s='b\\c'
+r='e\f'
+echo "4 ${v/$s}"
+echo "5 ${v/$s/d}"
+echo "6 ${v/$s/$r}"
+
+v='a\\b\\c'
+s='b\\\\c'
+r='e\\f'
+echo "7 ${v/$s}"
+echo "8 ${v/$s/d}"
+echo "9 ${v/$s/$r}"
+
+v='a-$a-\t-\\-\"-\`-\--\z-\*-\?-b'
+s='-$a-\\t-\\\\-\\"-\\`-\\--\\z-\\\*-\\\?-'
+echo "a ${v/$s}"
echo Done: $?
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.right b/shell/ash_test/ash-vars/var_expand_in_assign.right
index 352210d..352210d 100644
--- a/shell/msh_test/msh-bugs/var_expand_in_assign.right
+++ b/shell/ash_test/ash-vars/var_expand_in_assign.right
diff --git a/shell/msh_test/msh-bugs/var_expand_in_assign.tests b/shell/ash_test/ash-vars/var_expand_in_assign.tests
index 18cdc74..18cdc74 100755
--- a/shell/msh_test/msh-bugs/var_expand_in_assign.tests
+++ b/shell/ash_test/ash-vars/var_expand_in_assign.tests
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.right b/shell/ash_test/ash-vars/var_expand_in_redir.right
index 423299c..423299c 100644
--- a/shell/msh_test/msh-bugs/var_expand_in_redir.right
+++ b/shell/ash_test/ash-vars/var_expand_in_redir.right
diff --git a/shell/msh_test/msh-bugs/var_expand_in_redir.tests b/shell/ash_test/ash-vars/var_expand_in_redir.tests
index bda6bdd..bda6bdd 100755
--- a/shell/msh_test/msh-bugs/var_expand_in_redir.tests
+++ b/shell/ash_test/ash-vars/var_expand_in_redir.tests
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.right b/shell/ash_test/ash-vars/var_expand_on_ifs.right
new file mode 100644
index 0000000..2ed2069
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_expand_on_ifs.right
@@ -0,0 +1,9 @@
+1 a b c
+2 a + b c
+3 a b c
+4 a b c
+5 a b c
+6 a b + c
+7 a b c
+8 a b c
+9 a b c
diff --git a/shell/ash_test/ash-vars/var_expand_on_ifs.tests b/shell/ash_test/ash-vars/var_expand_on_ifs.tests
new file mode 100755
index 0000000..a12ff8e
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_expand_on_ifs.tests
@@ -0,0 +1,11 @@
+b=' b '
+e=''
+echo 1 a $b c
+echo 2 a +$b c
+echo 3 a $e$b c
+echo 4 a "$e"$b c
+echo 5 a ""$b c
+echo 6 a $b+ c
+echo 7 a $b$e c
+echo 8 a $b"$e" c
+echo 9 a $b"" c
diff --git a/shell/ash_test/ash-vars/var_in_pipes.right b/shell/ash_test/ash-vars/var_in_pipes.right
new file mode 100644
index 0000000..faf65be
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_in_pipes.right
@@ -0,0 +1,6 @@
+b=1
+b=2
+b=3
+b=4
+b=5
+b=6
diff --git a/shell/ash_test/ash-vars/var_in_pipes.tests b/shell/ash_test/ash-vars/var_in_pipes.tests
new file mode 100755
index 0000000..3f8cd27
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_in_pipes.tests
@@ -0,0 +1,7 @@
+b=1 env | grep ^b=
+true | b=2 env | grep ^b=
+a=1 true | b=3 env | grep ^b=
+
+(b=4 env) | grep ^b=
+(true | b=5 env) | grep ^b=
+(a=1 true | b=6 env) | grep ^b=
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/ash_test/ash-vars/var_leaks.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/ash_test/ash-vars/var_leaks.right
diff --git a/shell/ash_test/ash-vars/var_leaks.tests b/shell/ash_test/ash-vars/var_leaks.tests
new file mode 100755
index 0000000..27c8c65
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_leaks.tests
@@ -0,0 +1,14 @@
+# external program
+a=b /bin/true
+env | grep ^a=
+
+# builtin
+a=b true
+env | grep ^a=
+
+# exec with redirection only
+# in bash, this leaks!
+a=b exec 1>&1
+env | grep ^a=
+
+echo OK
diff --git a/shell/ash_test/ash-vars/var_posix1.right b/shell/ash_test/ash-vars/var_posix1.right
index 55f3579..7ff618a 100644
--- a/shell/ash_test/ash-vars/var_posix1.right
+++ b/shell/ash_test/ash-vars/var_posix1.right
@@ -1,3 +1,19 @@
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
abcdcd
abcdcd
abcdcd
@@ -5,7 +21,9 @@ cdcd
babcdcd
babcdcd
ababcdcd
-
+Empty:
+ababcdcd}_tail
+ababcdcd_tail
ababcd
ababcd
ababcd
@@ -13,5 +31,11 @@ abab
ababcdc
ababcdc
ababcdcd
-
-end
+Empty:
+ababcdcd}_tail
+ababcdcd_tail
+ababcdcd
+ab
+ab
+ab
+End
diff --git a/shell/ash_test/ash-vars/var_posix1.tests b/shell/ash_test/ash-vars/var_posix1.tests
index 4139e2c..82abe81 100755
--- a/shell/ash_test/ash-vars/var_posix1.tests
+++ b/shell/ash_test/ash-vars/var_posix1.tests
@@ -1,3 +1,25 @@
+unset var
+
+echo Empty:${var#}
+echo Empty:${var##}
+echo Empty:${var#*}
+echo Empty:${var##*}
+echo Empty:${var%}
+echo Empty:${var%%}
+echo Empty:${var%*}
+echo Empty:${var%%*}
+
+var=
+
+echo Empty:${var#}
+echo Empty:${var##}
+echo Empty:${var#*}
+echo Empty:${var##*}
+echo Empty:${var%}
+echo Empty:${var%%}
+echo Empty:${var%*}
+echo Empty:${var%%*}
+
var=ababcdcd
echo ${var#ab}
@@ -7,7 +29,9 @@ echo ${var##a*b}
echo ${var#?}
echo ${var##?}
echo ${var#*}
-echo ${var##*}
+echo Empty:${var##*}
+echo ${var#}}_tail
+echo ${var#\}}_tail
echo ${var%cd}
echo ${var%%cd}
@@ -16,6 +40,13 @@ echo ${var%%c*d}
echo ${var%?}
echo ${var%%?}
echo ${var%*}
-echo ${var%%*}
+echo Empty:${var%%*}
+echo ${var#}}_tail
+echo ${var#\}}_tail
+echo ${var%\\*}
+
+a=ab}; echo ${a%\}};
+a=abc; c=c; echo ${a%${c}}
+a=ab{{c; echo ${a%`echo {{c`}
-echo end
+echo End
diff --git a/shell/ash_test/ash-vars/var_serial.right b/shell/ash_test/ash-vars/var_serial.right
new file mode 100644
index 0000000..42aa330
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_serial.right
@@ -0,0 +1,5 @@
+Assignments only: c=a
+Assignments and a command: c=a
+Assignments and a builtin: c=a
+Assignments and a function: c=a
+Done
diff --git a/shell/ash_test/ash-vars/var_serial.tests b/shell/ash_test/ash-vars/var_serial.tests
new file mode 100755
index 0000000..6b4a4cd
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_serial.tests
@@ -0,0 +1,22 @@
+a=a
+
+b=b
+c=c
+# Second assignment depends on the first:
+b=$a c=$b
+echo Assignments only: c=$c
+
+b=b
+c=c
+b=$a c=$b "$THIS_SH" -c 'echo Assignments and a command: c=$c'
+
+b=b
+c=c
+b=$a c=$b eval 'echo Assignments and a builtin: c=$c'
+
+b=b
+c=c
+f() { echo Assignments and a function: c=$c; }
+b=$a c=$b f
+
+echo Done
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.right b/shell/ash_test/ash-vars/var_subst_in_for.right
index c8aca1c..c8aca1c 100644
--- a/shell/msh_test/msh-vars/var_subst_in_for.right
+++ b/shell/ash_test/ash-vars/var_subst_in_for.right
diff --git a/shell/ash_test/ash-vars/var_subst_in_for.tests b/shell/ash_test/ash-vars/var_subst_in_for.tests
new file mode 100755
index 0000000..433c606
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_subst_in_for.tests
@@ -0,0 +1,40 @@
+if test $# = 0; then
+ exec "$THIS_SH" "$0" abc "d e"
+fi
+
+echo 'Testing: in x y z'
+for a in x y z; do echo ".$a."; done
+
+echo 'Testing: in u $empty v'
+empty=''
+for a in u $empty v; do echo ".$a."; done
+
+echo 'Testing: in u " $empty" v'
+empty=''
+for a in u " $empty" v; do echo ".$a."; done
+
+echo 'Testing: in u $empty $empty$a v'
+a='a'
+for a in u $empty $empty$a v; do echo ".$a."; done
+
+echo 'Testing: in $a_b'
+a_b='a b'
+for a in $a_b; do echo ".$a."; done
+
+echo 'Testing: in $*'
+for a in $*; do echo ".$a."; done
+
+echo 'Testing: in $@'
+for a in $@; do echo ".$a."; done
+
+echo 'Testing: in -$*-'
+for a in -$*-; do echo ".$a."; done
+
+echo 'Testing: in -$@-'
+for a in -$@-; do echo ".$a."; done
+
+echo 'Testing: in $a_b -$a_b-'
+a_b='a b'
+for a in $a_b -$a_b-; do echo ".$a."; done
+
+echo Finished
diff --git a/shell/ash_test/ash-vars/var_unbackslash.right b/shell/ash_test/ash-vars/var_unbackslash.right
new file mode 100644
index 0000000..8bc8347
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash.right
@@ -0,0 +1,11 @@
+b1=-qwerty-t-\-"-`---z-*-?-
+b1=-qwerty-t-\-"-`---z-*-?-
+b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
+b2=-qwerty-\t-\-"-`-\--\z-\*-\?-
+b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+b3=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+c=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+Done: 0
diff --git a/shell/ash_test/ash-vars/var_unbackslash.tests b/shell/ash_test/ash-vars/var_unbackslash.tests
new file mode 100755
index 0000000..bb52af3
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash.tests
@@ -0,0 +1,23 @@
+# Test for correct handling of backslashes
+a=qwerty
+
+b=-$a-\t-\\-\"-\`-\--\z-\*-\?-
+echo b1=$b
+echo "b1=$b"
+b="-$a-\t-\\-\"-\`-\--\z-\*-\?-"
+echo b2=$b
+echo "b2=$b"
+b='-$a-\t-\\-\"-\`-\--\z-\*-\?-'
+echo b3=$b
+echo "b3=$b"
+
+c=$b
+echo "c=$c"
+c=${b}
+echo "c=$c"
+c="$b"
+echo "c=$c"
+c="${b}"
+echo "c=$c"
+
+echo "Done: $?"
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.right b/shell/ash_test/ash-vars/var_unbackslash1.right
new file mode 100644
index 0000000..3e0c0e2
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash1.right
@@ -0,0 +1,7 @@
+Ok
+Ba d
+Ok
+Ok
+Ok
+Forty two:42
+Forty two:42
diff --git a/shell/ash_test/ash-vars/var_unbackslash1.tests b/shell/ash_test/ash-vars/var_unbackslash1.tests
new file mode 100755
index 0000000..a4665e4
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_unbackslash1.tests
@@ -0,0 +1,35 @@
+ad="Ok"
+a="Ba"
+
+# "Ok"
+echo $a\
+d
+
+# This variable contains backslash+newline!
+e='echo $a\
+d'
+
+# "Ba d"
+eval $e
+# "Ok"
+eval "$e"
+
+echo $\
+(echo Ok\
+)
+echo "$\
+(echo Ok\
+)"
+
+echo Forty two:$\
+(\
+(\
+42\
+)\
+)
+echo "Forty two:$\
+(\
+(\
+42\
+)\
+)"
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.right b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right
new file mode 100644
index 0000000..cf583d0
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.right
@@ -0,0 +1,41 @@
+Testing: !IFS $*
+.abc.
+.d.
+.e.
+Testing: !IFS $@
+.abc.
+.d.
+.e.
+Testing: !IFS "$*"
+.abc d e.
+Testing: !IFS "$@"
+.abc.
+.d e.
+Testing: IFS="" $*
+.abc.
+.d e.
+Testing: IFS="" $@
+.abc.
+.d e.
+Testing: IFS="" "$*"
+.abcd e.
+Testing: IFS="" "$@"
+.abc.
+.d e.
+Testing: !IFS v=$*
+v='abc d e'
+Testing: !IFS v=$@
+v='abc d e'
+Testing: !IFS v="$*"
+v='abc d e'
+Testing: !IFS v="$@"
+v='abc d e'
+Testing: IFS="" v=$*
+v='abcd e'
+Testing: IFS="" v=$@
+v='abcd e'
+Testing: IFS="" v="$*"
+v='abcd e'
+Testing: IFS="" v="$@"
+v='abcd e'
+Finished
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests
new file mode 100755
index 0000000..a62afc6
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs1.tests
@@ -0,0 +1,42 @@
+set -- abc "d e"
+
+echo 'Testing: !IFS $*'
+unset IFS; for a in $*; do echo ".$a."; done
+echo 'Testing: !IFS $@'
+unset IFS; for a in $@; do echo ".$a."; done
+echo 'Testing: !IFS "$*"'
+unset IFS; for a in "$*"; do echo ".$a."; done
+echo 'Testing: !IFS "$@"'
+unset IFS; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: IFS="" $*'
+IFS=""; for a in $*; do echo ".$a."; done
+echo 'Testing: IFS="" $@'
+IFS=""; for a in $@; do echo ".$a."; done
+echo 'Testing: IFS="" "$*"'
+IFS=""; for a in "$*"; do echo ".$a."; done
+echo 'Testing: IFS="" "$@"'
+IFS=""; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: !IFS v=$*'
+unset IFS; v=$*; echo "v='$v'"
+echo 'Testing: !IFS v=$@'
+unset IFS; v=$@; echo "v='$v'"
+echo 'Testing: !IFS v="$*"'
+unset IFS; v="$*"; echo "v='$v'"
+echo 'Testing: !IFS v="$@"'
+unset IFS; v="$@"; echo "v='$v'"
+
+echo 'Testing: IFS="" v=$*'
+IFS=""; v=$*; echo "v='$v'"
+echo 'Testing: IFS="" v=$@'
+IFS=""; v=$@; echo "v='$v'"
+echo 'Testing: IFS="" v="$*"'
+IFS=""; v="$*"; echo "v='$v'"
+echo 'Testing: IFS="" v="$@"'
+IFS=""; v="$@"; echo "v='$v'"
+
+# Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e"
+# We produce "abcd e"
+
+echo Finished
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.right b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right
new file mode 100644
index 0000000..c234193
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.right
@@ -0,0 +1,3 @@
+Unquoted:<1>
+Unquoted:<3>
+Quoted:<123>
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests
new file mode 100755
index 0000000..4752354
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs2.tests
@@ -0,0 +1,13 @@
+# 123 chars long
+a="\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+0123456789\
+0123456789\
+012"
+
+IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done
+IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.right b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right
new file mode 100644
index 0000000..5ab72e1
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.right
@@ -0,0 +1,12 @@
+Unquoted%:<q>
+Unquoted%:<w>
+Unquoted%:<e>
+Unquoted%:<r>
+Unquoted%:<t>
+Unquoted#:<w>
+Unquoted#:<e>
+Unquoted#:<r>
+Unquoted#:<t>
+Unquoted#:<y>
+Quoted%:<q w e r t >
+Quoted#:< w e r t y>
diff --git a/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests
new file mode 100755
index 0000000..4aa6557
--- a/dev/null
+++ b/shell/ash_test/ash-vars/var_wordsplit_ifs3.tests
@@ -0,0 +1,5 @@
+a="q w e r t y"
+for v in ${a%y}; do echo Unquoted%:"<$v>"; done
+for v in ${a#q}; do echo Unquoted#:"<$v>"; done
+for v in "${a%y}"; do echo Quoted%:"<$v>"; done
+for v in "${a#q}"; do echo Quoted#:"<$v>"; done
diff --git a/shell/ash_test/printenv.c b/shell/ash_test/printenv.c
index c4ccda8..c0c5e19 100644
--- a/shell/ash_test/printenv.c
+++ b/shell/ash_test/printenv.c
@@ -24,6 +24,7 @@
with Bash; see the file COPYING. If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/shell/ash_test/run-all b/shell/ash_test/run-all
index ad93e25..354cc1f 100755
--- a/shell/ash_test/run-all
+++ b/shell/ash_test/run-all
@@ -31,8 +31,9 @@ do_test()
*.orig|*~) ;;
#*) echo $x ; sh $x ;;
*)
+ echo -n "$1/$x: "
sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
- { echo "$1/$x: ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "$1/$x: fail";
+ { echo "ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "fail";
;;
esac
done
@@ -42,11 +43,12 @@ do_test()
test -x "$x" || continue
name="${x%%.tests}"
test -f "$name.right" || continue
+ echo -n "$1/$x: "
{
"$THIS_SH" "./$x" >"$name.xx" 2>&1
diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
&& rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
- } && echo "$1/$x: ok" || echo "$1/$x: fail"
+ } && echo "ok" || echo "fail"
done
)
}
diff --git a/shell/hush.c b/shell/hush.c
index 8a4215d..4ec2d0a 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,6 +44,7 @@
* special variables (done: PWD, PPID, RANDOM)
* tilde expansion
* aliases
+ * kill %jobspec
* follow IFS rules more precisely, including update semantics
* builtins mandated by standards we don't support:
* [un]alias, command, fc, getopts, newgrp, readonly, times
@@ -81,31 +82,6 @@
* $ "export" i=`echo 'aaa bbb'`; echo "$i"
* aaa
*/
-#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
- || defined(__APPLE__) \
- )
-# include <malloc.h> /* for malloc_trim */
-#endif
-#include <glob.h>
-/* #include <dmalloc.h> */
-#if ENABLE_HUSH_CASE
-# include <fnmatch.h>
-#endif
-
-#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
-#include "unicode.h"
-#include "shell_common.h"
-#include "math.h"
-#include "match.h"
-#if ENABLE_HUSH_RANDOM_SUPPORT
-# include "random.h"
-#else
-# define CLEAR_RANDOM_T(rnd) ((void)0)
-#endif
-#ifndef PIPE_BUF
-# define PIPE_BUF 4096 /* amount of buffering in a pipe */
-#endif
-
//config:config HUSH
//config: bool "hush"
//config: default y
@@ -123,7 +99,7 @@
//config:config HUSH_BASH_COMPAT
//config: bool "bash-compatible extensions"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable bash-compatible extensions.
//config:
@@ -137,14 +113,14 @@
//config:config HUSH_HELP
//config: bool "help builtin"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable help builtin in hush. Code size + ~1 kbyte.
//config:
//config:config HUSH_INTERACTIVE
//config: bool "Interactive mode"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable interactive mode (prompt and command editing).
//config: Without this, hush simply reads and executes commands
@@ -172,35 +148,35 @@
//config:config HUSH_TICK
//config: bool "Process substitution"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable process substitution `command` and $(command) in hush.
//config:
//config:config HUSH_IF
//config: bool "Support if/then/elif/else/fi"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable if/then/elif/else/fi in hush.
//config:
//config:config HUSH_LOOPS
//config: bool "Support for, while and until loops"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable for, while and until loops in hush.
//config:
//config:config HUSH_CASE
//config: bool "Support case ... esac statement"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable case ... esac statement in hush. +400 bytes.
//config:
//config:config HUSH_FUNCTIONS
//config: bool "Support funcname() { commands; } syntax"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable support for shell functions in hush. +800 bytes.
//config:
@@ -214,7 +190,7 @@
//config:config HUSH_RANDOM_SUPPORT
//config: bool "Pseudorandom generator and $RANDOM variable"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
//config: Each read of "$RANDOM" will generate a new pseudorandom value.
@@ -222,14 +198,14 @@
//config:config HUSH_EXPORT_N
//config: bool "Support 'export -n' option"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: export -n unexports variables. It is a bash extension.
//config:
//config:config HUSH_MODE_X
//config: bool "Support 'hush -x' option and 'set -x' command"
//config: default y
-//config: depends on HUSH
+//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH
//config: help
//config: This instructs hush to print commands before execution.
//config: Adds ~300 bytes.
@@ -240,14 +216,15 @@
//config: select HUSH
//config: help
//config: msh is deprecated and will be removed, please migrate to hush.
-//config:
//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_MSH(APPLET(msh, BB_DIR_BIN, BB_SUID_DROP))
-//applet:IF_FEATURE_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, sh))
-//applet:IF_FEATURE_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, bash))
+//applet:IF_MSH(APPLET_ODDNAME(msh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
+//applet:IF_SH_IS_HUSH(APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
+//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
//kbuild:lib-$(CONFIG_HUSH) += hush.o match.o shell_common.o
+//kbuild:lib-$(CONFIG_SH_IS_HUSH) += hush.o match.o shell_common.o
+//kbuild:lib-$(CONFIG_BASH_IS_HUSH) += hush.o match.o shell_common.o
//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
/* -i (interactive) and -s (read stdin) are also accepted,
@@ -260,17 +237,34 @@
//usage:#define hush_full_usage "\n\n"
//usage: "Unix shell interpreter"
-//usage:#define msh_trivial_usage hush_trivial_usage
-//usage:#define msh_full_usage hush_full_usage
+#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+ || defined(__APPLE__) \
+ )
+# include <malloc.h> /* for malloc_trim */
+#endif
+#include <glob.h>
+/* #include <dmalloc.h> */
+#if ENABLE_HUSH_CASE
+# include <fnmatch.h>
+#endif
+#include <sys/utsname.h> /* for setting $HOSTNAME */
-//usage:#if ENABLE_FEATURE_SH_IS_HUSH
-//usage:# define sh_trivial_usage hush_trivial_usage
-//usage:# define sh_full_usage hush_full_usage
-//usage:#endif
-//usage:#if ENABLE_FEATURE_BASH_IS_HUSH
-//usage:# define bash_trivial_usage hush_trivial_usage
-//usage:# define bash_full_usage hush_full_usage
-//usage:#endif
+#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
+#include "unicode.h"
+#include "shell_common.h"
+#include "math.h"
+#include "match.h"
+#if ENABLE_HUSH_RANDOM_SUPPORT
+# include "random.h"
+#else
+# define CLEAR_RANDOM_T(rnd) ((void)0)
+#endif
+#ifndef F_DUPFD_CLOEXEC
+# define F_DUPFD_CLOEXEC F_DUPFD
+#endif
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096 /* amount of buffering in a pipe */
+#endif
/* Build knobs */
@@ -441,7 +435,7 @@ enum {
MAYBE_ASSIGNMENT = 0,
DEFINITELY_ASSIGNMENT = 1,
NOT_ASSIGNMENT = 2,
- /* Not an assigment, but next word may be: "if v=xyz cmd;" */
+ /* Not an assignment, but next word may be: "if v=xyz cmd;" */
WORD_IS_KEYWORD = 3,
};
/* Used for initialization: o_string foo = NULL_O_STRING; */
@@ -458,19 +452,13 @@ static const char *const assignment_flag[] = {
typedef struct in_str {
const char *p;
- /* eof_flag=1: last char in ->p is really an EOF */
- char eof_flag; /* meaningless if ->p == NULL */
- char peek_buf[2];
#if ENABLE_HUSH_INTERACTIVE
smallint promptmode; /* 0: PS1, 1: PS2 */
#endif
+ int peek_buf[2];
int last_char;
FILE *file;
- int (*get) (struct in_str *) FAST_FUNC;
- int (*peek) (struct in_str *) FAST_FUNC;
} in_str;
-#define i_getch(input) ((input)->get(input))
-#define i_peek(input) ((input)->peek(input))
/* The descrip member of this structure is only used to make
* debugging output pretty */
@@ -585,10 +573,10 @@ struct pipe {
IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
};
typedef enum pipe_style {
- PIPE_SEQ = 1,
- PIPE_AND = 2,
- PIPE_OR = 3,
- PIPE_BG = 4,
+ PIPE_SEQ = 0,
+ PIPE_AND = 1,
+ PIPE_OR = 2,
+ PIPE_BG = 3,
} pipe_style;
/* Is there anything in this pipe at all? */
#define IS_NULL_PIPE(pi) \
@@ -710,6 +698,13 @@ enum {
};
+struct FILE_list {
+ struct FILE_list *next;
+ FILE *fp;
+ int fd;
+};
+
+
/* "Globals" within this file */
/* Sorted roughly by size (smaller offsets == smaller code) */
struct globals {
@@ -768,6 +763,9 @@ struct globals {
* 1: return is invoked, skip all till end of func
*/
smallint flag_return_in_progress;
+# define G_flag_return_in_progress (G.flag_return_in_progress)
+#else
+# define G_flag_return_in_progress 0
#endif
smallint exiting; /* used to prevent EXIT trap recursion */
/* These four support $?, $#, and $1 */
@@ -801,6 +799,7 @@ struct globals {
unsigned handled_SIGCHLD;
smallint we_have_children;
#endif
+ struct FILE_list *FILE_list;
/* Which signals have non-DFL handler (even with no traps set)?
* Set at the start to:
* (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
@@ -822,7 +821,9 @@ struct globals {
int debug_indent;
#endif
struct sigaction sa;
- char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2];
+#if ENABLE_FEATURE_EDITING
+ char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
+#endif
};
#define G (*ptr_to_globals)
/* Not #defining name to G.name - this quickly gets unwieldy
@@ -944,6 +945,7 @@ static const struct built_in_command bltins1[] = {
BLTIN("source" , builtin_source , "Run commands in a file"),
#endif
BLTIN("trap" , builtin_trap , "Trap signals"),
+ BLTIN("true" , builtin_true , NULL),
BLTIN("type" , builtin_type , "Show command type"),
BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"),
BLTIN("umask" , builtin_umask , "Set file creation mask"),
@@ -1135,6 +1137,9 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
char msg[2];
msg[0] = ch;
msg[1] = '\0';
+#if HUSH_DEBUG >= 2
+ bb_error_msg("hush.c:%u", lineno);
+#endif
bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
}
@@ -1250,6 +1255,91 @@ static void free_strings(char **strings)
}
+static int xdup_and_close(int fd, int F_DUPFD_maybe_CLOEXEC)
+{
+ /* We avoid taking stdio fds. Mimicking ash: use fds above 9 */
+ int newfd = fcntl(fd, F_DUPFD_maybe_CLOEXEC, 10);
+ if (newfd < 0) {
+ /* fd was not open? */
+ if (errno == EBADF)
+ return fd;
+ xfunc_die();
+ }
+ close(fd);
+ return newfd;
+}
+
+
+/* Manipulating the list of open FILEs */
+static FILE *remember_FILE(FILE *fp)
+{
+ if (fp) {
+ struct FILE_list *n = xmalloc(sizeof(*n));
+ n->next = G.FILE_list;
+ G.FILE_list = n;
+ n->fp = fp;
+ n->fd = fileno(fp);
+ close_on_exec_on(n->fd);
+ }
+ return fp;
+}
+static void fclose_and_forget(FILE *fp)
+{
+ struct FILE_list **pp = &G.FILE_list;
+ while (*pp) {
+ struct FILE_list *cur = *pp;
+ if (cur->fp == fp) {
+ *pp = cur->next;
+ free(cur);
+ break;
+ }
+ pp = &cur->next;
+ }
+ fclose(fp);
+}
+static int save_FILEs_on_redirect(int fd)
+{
+ struct FILE_list *fl = G.FILE_list;
+ while (fl) {
+ if (fd == fl->fd) {
+ /* We use it only on script files, they are all CLOEXEC */
+ fl->fd = xdup_and_close(fd, F_DUPFD_CLOEXEC);
+ return 1;
+ }
+ fl = fl->next;
+ }
+ return 0;
+}
+static void restore_redirected_FILEs(void)
+{
+ struct FILE_list *fl = G.FILE_list;
+ while (fl) {
+ int should_be = fileno(fl->fp);
+ if (fl->fd != should_be) {
+ xmove_fd(fl->fd, should_be);
+ fl->fd = should_be;
+ }
+ fl = fl->next;
+ }
+}
+#if ENABLE_FEATURE_SH_STANDALONE
+static void close_all_FILE_list(void)
+{
+ struct FILE_list *fl = G.FILE_list;
+ while (fl) {
+ /* fclose would also free FILE object.
+ * It is disastrous if we share memory with a vforked parent.
+ * I'm not sure we never come here after vfork.
+ * Therefore just close fd, nothing more.
+ */
+ /*fclose(fl->fp); - unsafe */
+ close(fl->fd);
+ fl = fl->next;
+ }
+}
+#endif
+
+
/* Helpers for setting new $n and restoring them back
*/
typedef struct save_arg_t {
@@ -1475,18 +1565,50 @@ static sighandler_t install_sighandler(int sig, sighandler_t handler)
return old_sa.sa_handler;
}
+static void hush_exit(int exitcode) NORETURN;
+
+static void restore_ttypgrp_and__exit(void) NORETURN;
+static void restore_ttypgrp_and__exit(void)
+{
+ /* xfunc has failed! die die die */
+ /* no EXIT traps, this is an escape hatch! */
+ G.exiting = 1;
+ hush_exit(xfunc_error_retval);
+}
+
#if ENABLE_HUSH_JOB
+/* Needed only on some libc:
+ * It was observed that on exit(), fgetc'ed buffered data
+ * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
+ * With the net effect that even after fork(), not vfork(),
+ * exit() in NOEXECed applet in "sh SCRIPT":
+ * noexec_applet_here
+ * echo END_OF_SCRIPT
+ * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
+ * This makes "echo END_OF_SCRIPT" executed twice.
+ * Similar problems can be seen with die_if_script() -> xfunc_die()
+ * and in `cmd` handling.
+ * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
+ */
+static void fflush_and__exit(void) NORETURN;
+static void fflush_and__exit(void)
+{
+ fflush_all();
+ _exit(xfunc_error_retval);
+}
+
/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
-# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0)
+# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
/* After [v]fork, in parent: restore tty pgrp on xfunc death */
-# define enable_restore_tty_pgrp_on_exit() (die_sleep = -1)
+# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
/* Restores tty foreground process group, and exits.
* May be called as signal handler for fatal signal
* (will resend signal to itself, producing correct exit state)
* or called directly with -EXITCODE.
- * We also call it if xfunc is exiting. */
+ * We also call it if xfunc is exiting.
+ */
static void sigexit(int sig) NORETURN;
static void sigexit(int sig)
{
@@ -1541,7 +1663,6 @@ static sighandler_t pick_sighandler(unsigned sig)
}
/* Restores tty foreground process group, and exits. */
-static void hush_exit(int exitcode) NORETURN;
static void hush_exit(int exitcode)
{
#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
@@ -1577,11 +1698,11 @@ static void hush_exit(int exitcode)
}
#endif
-#if ENABLE_HUSH_JOB
fflush_all();
+#if ENABLE_HUSH_JOB
sigexit(- (exitcode & 0xff));
#else
- exit(exitcode);
+ _exit(exitcode);
#endif
}
@@ -1607,6 +1728,7 @@ static int check_and_run_traps(void)
break;
got_sig:
if (G.traps && G.traps[sig]) {
+ debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
if (G.traps[sig][0]) {
/* We have user-defined handler */
smalluint save_rcode;
@@ -1616,6 +1738,7 @@ static int check_and_run_traps(void)
argv[2] = NULL;
save_rcode = G.last_exitcode;
builtin_eval(argv);
+//FIXME: shouldn't it be set to 128 + sig instead?
G.last_exitcode = save_rcode;
last_sig = sig;
} /* else: "" trap, ignoring signal */
@@ -1624,14 +1747,14 @@ static int check_and_run_traps(void)
/* not a trap: special action */
switch (sig) {
case SIGINT:
- /* Builtin was ^C'ed, make it look prettier: */
- bb_putchar('\n');
+ debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
G.flag_SIGINT = 1;
last_sig = sig;
break;
#if ENABLE_HUSH_JOB
case SIGHUP: {
struct pipe *job;
+ debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
/* bash is observed to signal whole process groups,
* not individual processes */
for (job = G.job_list; job; job = job->next) {
@@ -1646,6 +1769,7 @@ static int check_and_run_traps(void)
#endif
#if ENABLE_HUSH_FAST
case SIGCHLD:
+ debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
G.count_SIGCHLD++;
//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
/* Note:
@@ -1655,6 +1779,7 @@ static int check_and_run_traps(void)
break;
#endif
default: /* ignored: */
+ debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
/* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
/* Note:
* We dont do 'last_sig = sig' here -> NOT returning this sig.
@@ -1754,6 +1879,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
{
struct variable **var_pp;
struct variable *cur;
+ char *free_me = NULL;
char *eq_sign;
int name_len;
@@ -1770,6 +1896,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
var_pp = &cur->next;
continue;
}
+
/* We found an existing var with this name */
if (cur->flg_read_only) {
#if !BB_MMU
@@ -1818,12 +1945,17 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
strcpy(cur->varstr, str);
goto free_and_exp;
}
- } else {
- /* max_len == 0 signifies "malloced" var, which we can
- * (and has to) free */
- free(cur->varstr);
- }
- cur->max_len = 0;
+ /* Can't reuse */
+ cur->max_len = 0;
+ goto set_str_and_exp;
+ }
+ /* max_len == 0 signifies "malloced" var, which we can
+ * (and have to) free. But we can't free(cur->varstr) here:
+ * if cur->flg_export is 1, it is in the environment.
+ * We should either unsetenv+free, or wait until putenv,
+ * then putenv(new)+free(old).
+ */
+ free_me = cur->varstr;
goto set_str_and_exp;
}
@@ -1850,10 +1982,15 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
cur->flg_export = 0;
/* unsetenv was already done */
} else {
+ int i;
debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
- return putenv(cur->varstr);
+ i = putenv(cur->varstr);
+ /* only now we can free old exported malloced string */
+ free(free_me);
+ return i;
}
}
+ free(free_me);
return 0;
}
@@ -1975,26 +2112,37 @@ static struct variable *set_vars_and_save_old(char **strings)
/*
- * in_str support
+ * Unicode helper
*/
-static int FAST_FUNC static_get(struct in_str *i)
+static void reinit_unicode_for_hush(void)
{
- int ch = *i->p;
- if (ch != '\0') {
- i->p++;
- i->last_char = ch;
- return ch;
+ /* Unicode support should be activated even if LANG is set
+ * _during_ shell execution, not only if it was set when
+ * shell was started. Therefore, re-check LANG every time:
+ */
+ if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
+ || ENABLE_UNICODE_USING_LOCALE
+ ) {
+ const char *s = get_local_var_value("LC_ALL");
+ if (!s) s = get_local_var_value("LC_CTYPE");
+ if (!s) s = get_local_var_value("LANG");
+ reinit_unicode(s);
}
- return EOF;
}
-static int FAST_FUNC static_peek(struct in_str *i)
-{
- return *i->p;
-}
+/*
+ * in_str support (strings, and "strings" read from files).
+ */
#if ENABLE_HUSH_INTERACTIVE
-
+/* To test correct lineedit/interactive behavior, type from command line:
+ * echo $P\
+ * \
+ * AT\
+ * H\
+ * \
+ * It excercises a lot of corner cases.
+ */
static void cmdedit_update_prompt(void)
{
if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
@@ -2008,7 +2156,6 @@ static void cmdedit_update_prompt(void)
if (G.PS2 == NULL)
G.PS2 = "> ";
}
-
static const char *setup_prompt_string(int promptmode)
{
const char *prompt_str;
@@ -2026,43 +2173,50 @@ static const char *setup_prompt_string(int promptmode)
prompt_str = G.PS2;
} else
prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
- debug_printf("result '%s'\n", prompt_str);
+ debug_printf("prompt_str '%s'\n", prompt_str);
return prompt_str;
}
-
-static void get_user_input(struct in_str *i)
+static int get_user_input(struct in_str *i)
{
int r;
const char *prompt_str;
prompt_str = setup_prompt_string(i->promptmode);
# if ENABLE_FEATURE_EDITING
- /* Enable command line editing only while a command line
- * is actually being read */
- do {
- /* Unicode support should be activated even if LANG is set
- * _during_ shell execution, not only if it was set when
- * shell was started. Therefore, re-check LANG every time:
- */
- const char *s = get_local_var_value("LC_ALL");
- if (!s) s = get_local_var_value("LC_CTYPE");
- if (!s) s = get_local_var_value("LANG");
- reinit_unicode(s);
-
- G.flag_SIGINT = 0;
+ for (;;) {
+ reinit_unicode_for_hush();
+ if (G.flag_SIGINT) {
+ /* There was ^C'ed, make it look prettier: */
+ bb_putchar('\n');
+ G.flag_SIGINT = 0;
+ }
/* buglet: SIGINT will not make new prompt to appear _at once_,
- * only after <Enter>. (^C will work) */
- r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
- /* catch *SIGINT* etc (^C is handled by read_line_input) */
+ * only after <Enter>. (^C works immediately) */
+ r = read_line_input(G.line_input_state, prompt_str,
+ G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1,
+ /*timeout*/ -1
+ );
+ /* read_line_input intercepts ^C, "convert" it to SIGINT */
+ if (r == 0) {
+ write(STDOUT_FILENO, "^C", 2);
+ raise(SIGINT);
+ }
check_and_run_traps();
- } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
- i->eof_flag = (r < 0);
- if (i->eof_flag) { /* EOF/error detected */
- G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
- G.user_input_buf[1] = '\0';
+ if (r != 0 && !G.flag_SIGINT)
+ break;
+ /* ^C or SIGINT: repeat */
+ G.last_exitcode = 128 + SIGINT;
}
+ if (r < 0) {
+ /* EOF/error detected */
+ i->p = NULL;
+ i->peek_buf[0] = r = EOF;
+ return r;
+ }
+ i->p = G.user_input_buf;
+ return (unsigned char)*i->p++;
# else
- do {
+ for (;;) {
G.flag_SIGINT = 0;
if (i->last_char == '\0' || i->last_char == '\n') {
/* Why check_and_run_traps here? Try this interactively:
@@ -2074,76 +2228,161 @@ static void get_user_input(struct in_str *i)
fputs(prompt_str, stdout);
}
fflush_all();
- G.user_input_buf[0] = r = fgetc(i->file);
- /*G.user_input_buf[1] = '\0'; - already is and never changed */
- } while (G.flag_SIGINT);
- i->eof_flag = (r == EOF);
+//FIXME: here ^C or SIGINT will have effect only after <Enter>
+ r = fgetc(i->file);
+ /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
+ * no ^C masking happens during fgetc, no special code for ^C:
+ * it generates SIGINT as usual.
+ */
+ check_and_run_traps();
+ if (G.flag_SIGINT)
+ G.last_exitcode = 128 + SIGINT;
+ if (r != '\0')
+ break;
+ }
+ return r;
# endif
- i->p = G.user_input_buf;
}
-
-#endif /* INTERACTIVE */
-
/* This is the magic location that prints prompts
* and gets data back from the user */
-static int FAST_FUNC file_get(struct in_str *i)
+static int fgetc_interactive(struct in_str *i)
{
int ch;
-
- /* If there is data waiting, eat it up */
- if (i->p && *i->p) {
-#if ENABLE_HUSH_INTERACTIVE
- take_cached:
-#endif
- ch = *i->p++;
- if (i->eof_flag && !*i->p)
- ch = EOF;
- /* note: ch is never NUL */
+ /* If it's interactive stdin, get new line. */
+ if (G_interactive_fd && i->file == stdin) {
+ /* Returns first char (or EOF), the rest is in i->p[] */
+ ch = get_user_input(i);
+ i->promptmode = 1; /* PS2 */
} else {
- /* need to double check i->file because we might be doing something
- * more complicated by now, like sourcing or substituting. */
-#if ENABLE_HUSH_INTERACTIVE
- if (G_interactive_fd && i->file == stdin) {
- do {
- get_user_input(i);
- } while (!*i->p); /* need non-empty line */
- i->promptmode = 1; /* PS2 */
- goto take_cached;
+ /* Not stdin: script file, sourced file, etc */
+ do ch = fgetc(i->file); while (ch == '\0');
+ }
+ return ch;
+}
+#else
+static inline int fgetc_interactive(struct in_str *i)
+{
+ int ch;
+ do ch = fgetc(i->file); while (ch == '\0');
+ return ch;
+}
+#endif /* INTERACTIVE */
+
+static int i_getch(struct in_str *i)
+{
+ int ch;
+
+ if (!i->file) {
+ /* string-based in_str */
+ ch = (unsigned char)*i->p;
+ if (ch != '\0') {
+ i->p++;
+ i->last_char = ch;
+ return ch;
}
+ return EOF;
+ }
+
+ /* FILE-based in_str */
+
+#if ENABLE_FEATURE_EDITING
+ /* This can be stdin, check line editing char[] buffer */
+ if (i->p && *i->p != '\0') {
+ ch = (unsigned char)*i->p++;
+ goto out;
+ }
#endif
- do ch = fgetc(i->file); while (ch == '\0');
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ ch = i->peek_buf[0];
+ if (ch != 0) {
+ int ch2 = i->peek_buf[1];
+ i->peek_buf[0] = ch2;
+ if (ch2 == 0) /* very likely, avoid redundant write */
+ goto out;
+ i->peek_buf[1] = 0;
+ goto out;
}
+
+ ch = fgetc_interactive(i);
+ out:
debug_printf("file_get: got '%c' %d\n", ch, ch);
i->last_char = ch;
return ch;
}
-/* All callers guarantee this routine will never
- * be used right after a newline, so prompting is not needed.
- */
-static int FAST_FUNC file_peek(struct in_str *i)
+static int i_peek(struct in_str *i)
{
int ch;
- if (i->p && *i->p) {
- if (i->eof_flag && !i->p[1])
- return EOF;
- return *i->p;
- /* note: ch is never NUL */
+
+ if (!i->file) {
+ /* string-based in_str */
+ /* Doesn't report EOF on NUL. None of the callers care. */
+ return (unsigned char)*i->p;
}
- do ch = fgetc(i->file); while (ch == '\0');
- i->eof_flag = (ch == EOF);
- i->peek_buf[0] = ch;
- i->peek_buf[1] = '\0';
- i->p = i->peek_buf;
+
+ /* FILE-based in_str */
+
+#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
+ /* This can be stdin, check line editing char[] buffer */
+ if (i->p && *i->p != '\0')
+ return (unsigned char)*i->p;
+#endif
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ ch = i->peek_buf[0];
+ if (ch != 0)
+ return ch;
+
+ /* Need to get a new char */
+ ch = fgetc_interactive(i);
debug_printf("file_peek: got '%c' %d\n", ch, ch);
+
+ /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
+#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
+ if (i->p) {
+ i->p -= 1;
+ return ch;
+ }
+#endif
+ i->peek_buf[0] = ch;
+ /*i->peek_buf[1] = 0; - already is */
+ return ch;
+}
+
+/* Only ever called if i_peek() was called, and did not return EOF.
+ * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
+ * not end-of-line. Therefore we never need to read a new editing line here.
+ */
+static int i_peek2(struct in_str *i)
+{
+ int ch;
+
+ /* There are two cases when i->p[] buffer exists.
+ * (1) it's a string in_str.
+ * (2) It's a file, and we have a saved line editing buffer.
+ * In both cases, we know that i->p[0] exists and not NUL, and
+ * the peek2 result is in i->p[1].
+ */
+ if (i->p)
+ return (unsigned char)i->p[1];
+
+ /* Now we know it is a file-based in_str. */
+
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ /* Is there 2nd char? */
+ ch = i->peek_buf[1];
+ if (ch == 0) {
+ /* We did not read it yet, get it now */
+ do ch = fgetc(i->file); while (ch == '\0');
+ i->peek_buf[1] = ch;
+ }
+
+ debug_printf("file_peek2: got '%c' %d\n", ch, ch);
return ch;
}
static void setup_file_in_str(struct in_str *i, FILE *f)
{
memset(i, 0, sizeof(*i));
- i->peek = file_peek;
- i->get = file_get;
/* i->promptmode = 0; - PS1 (memset did it) */
i->file = f;
/* i->p = NULL; */
@@ -2152,11 +2391,9 @@ static void setup_file_in_str(struct in_str *i, FILE *f)
static void setup_string_in_str(struct in_str *i, const char *s)
{
memset(i, 0, sizeof(*i));
- i->peek = static_peek;
- i->get = static_get;
/* i->promptmode = 0; - PS1 (memset did it) */
+ /*i->file = NULL */;
i->p = s;
- /* i->eof_flag = 0; */
}
@@ -2187,7 +2424,7 @@ static ALWAYS_INLINE void o_free_unsafe(o_string *o)
static void o_grow_by(o_string *o, int len)
{
if (o->length + len > o->maxlen) {
- o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK);
+ o->maxlen += (2 * len) | (B_CHUNK-1);
o->data = xrealloc(o->data, 1 + o->maxlen);
}
}
@@ -2195,11 +2432,26 @@ static void o_grow_by(o_string *o, int len)
static void o_addchr(o_string *o, int ch)
{
debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
+ if (o->length < o->maxlen) {
+ /* likely. avoid o_grow_by() call */
+ add:
+ o->data[o->length] = ch;
+ o->length++;
+ o->data[o->length] = '\0';
+ return;
+ }
o_grow_by(o, 1);
- o->data[o->length] = ch;
- o->length++;
+ goto add;
+}
+
+#if 0
+/* Valid only if we know o_string is not empty */
+static void o_delchr(o_string *o)
+{
+ o->length--;
o->data[o->length] = '\0';
}
+#endif
static void o_addblock(o_string *o, const char *str, int len)
{
@@ -2897,7 +3149,6 @@ static struct pipe *new_pipe(void)
{
struct pipe *pi;
pi = xzalloc(sizeof(struct pipe));
- /*pi->followup = 0; - deliberately invalid value */
/*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
return pi;
}
@@ -2913,6 +3164,14 @@ static int done_command(struct parse_context *ctx)
struct pipe *pi = ctx->pipe;
struct command *command = ctx->command;
+#if 0 /* Instead we emit error message at run time */
+ if (ctx->pending_redirect) {
+ /* For example, "cmd >" (no filename to redirect to) */
+ die_if_script("syntax error: %s", "invalid redirect");
+ ctx->pending_redirect = NULL;
+ }
+#endif
+
if (command) {
if (IS_NULL_CMD(command)) {
debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
@@ -3147,11 +3406,29 @@ static int reserved_word(o_string *word, struct parse_context *ctx)
old->command->group = ctx->list_head;
old->command->cmd_type = CMD_NORMAL;
# if !BB_MMU
- o_addstr(&old->as_string, ctx->as_string.data);
- o_free_unsafe(&ctx->as_string);
- old->command->group_as_string = xstrdup(old->as_string.data);
- debug_printf_parse("pop, remembering as:'%s'\n",
- old->command->group_as_string);
+ /* At this point, the compound command's string is in
+ * ctx->as_string... except for the leading keyword!
+ * Consider this example: "echo a | if true; then echo a; fi"
+ * ctx->as_string will contain "true; then echo a; fi",
+ * with "if " remaining in old->as_string!
+ */
+ {
+ char *str;
+ int len = old->as_string.length;
+ /* Concatenate halves */
+ o_addstr(&old->as_string, ctx->as_string.data);
+ o_free_unsafe(&ctx->as_string);
+ /* Find where leading keyword starts in first half */
+ str = old->as_string.data + len;
+ if (str > old->as_string.data)
+ str--; /* skip whitespace after keyword */
+ while (str > old->as_string.data && isalpha(str[-1]))
+ str--;
+ /* Ugh, we're done with this horrid hack */
+ old->command->group_as_string = xstrdup(str);
+ debug_printf_parse("pop, remembering as:'%s'\n",
+ old->command->group_as_string);
+ }
# endif
*ctx = *old; /* physical copy */
free(old);
@@ -3417,6 +3694,12 @@ static int parse_redirect(struct parse_context *ctx,
debug_printf_parse("duplicating redirect '%d>&%d'\n",
redir->rd_fd, redir->rd_dup);
} else {
+#if 0 /* Instead we emit error message at run time */
+ if (ctx->pending_redirect) {
+ /* For example, "cmd > <file" */
+ die_if_script("syntax error: %s", "invalid redirect");
+ }
+#endif
/* Set ctx->pending_redirect, so we know what to do at the
* end of the next parsed word. */
ctx->pending_redirect = redir;
@@ -3640,12 +3923,17 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
command->cmd_type = CMD_SUBSHELL;
} else {
/* bash does not allow "{echo...", requires whitespace */
- ch = i_getch(input);
- if (ch != ' ' && ch != '\t' && ch != '\n') {
+ ch = i_peek(input);
+ if (ch != ' ' && ch != '\t' && ch != '\n'
+ && ch != '(' /* but "{(..." is allowed (without whitespace) */
+ ) {
syntax_error_unexpected_ch(ch);
return 1;
}
- nommu_addchr(&ctx->as_string, ch);
+ if (ch != '(') {
+ ch = i_getch(input);
+ nommu_addchr(&ctx->as_string, ch);
+ }
}
{
@@ -3682,7 +3970,40 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
/* command remains "open", available for possible redirects */
}
-#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS
+static int i_getch_and_eat_bkslash_nl(struct in_str *input)
+{
+ for (;;) {
+ int ch, ch2;
+
+ ch = i_getch(input);
+ if (ch != '\\')
+ return ch;
+ ch2 = i_peek(input);
+ if (ch2 != '\n')
+ return ch;
+ /* backslash+newline, skip it */
+ i_getch(input);
+ }
+}
+
+static int i_peek_and_eat_bkslash_nl(struct in_str *input)
+{
+ for (;;) {
+ int ch, ch2;
+
+ ch = i_peek(input);
+ if (ch != '\\')
+ return ch;
+ ch2 = i_peek2(input);
+ if (ch2 != '\n')
+ return ch;
+ /* backslash+newline, skip it */
+ i_getch(input);
+ i_getch(input);
+ }
+}
+
+#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
/* Subroutines for copying $(...) and `...` things */
static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
/* '...' */
@@ -3799,7 +4120,7 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
if (!dbl)
break;
/* we look for closing )) of $((EXPR)) */
- if (i_peek(input) == end_ch) {
+ if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
i_getch(input); /* eat second ')' */
break;
}
@@ -3837,13 +4158,20 @@ static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsign
syntax_error_unterm_ch(')');
return 0;
}
+#if 0
+ if (ch == '\n') {
+ /* "backslash+newline", ignore both */
+ o_delchr(dest); /* undo insertion of '\' */
+ continue;
+ }
+#endif
o_addchr(dest, ch);
continue;
}
}
return ch;
}
-#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_DOLLAR_OPS */
+#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
/* Return code: 0 for OK, 1 for syntax error */
#if BB_MMU
@@ -3855,7 +4183,7 @@ static int parse_dollar(o_string *as_string,
o_string *dest,
struct in_str *input, unsigned char quote_mask)
{
- int ch = i_peek(input); /* first character after the $ */
+ int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
debug_printf_parse("parse_dollar entered: ch='%c'\n", ch);
if (isalpha(ch)) {
@@ -3867,9 +4195,11 @@ static int parse_dollar(o_string *as_string,
debug_printf_parse(": '%c'\n", ch);
o_addchr(dest, ch | quote_mask);
quote_mask = 0;
- ch = i_peek(input);
- if (!isalnum(ch) && ch != '_')
+ ch = i_peek_and_eat_bkslash_nl(input);
+ if (!isalnum(ch) && ch != '_') {
+ /* End of variable name reached */
break;
+ }
ch = i_getch(input);
nommu_addchr(as_string, ch);
}
@@ -3896,7 +4226,7 @@ static int parse_dollar(o_string *as_string,
ch = i_getch(input); /* eat '{' */
nommu_addchr(as_string, ch);
- ch = i_getch(input); /* first char after '{' */
+ ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
/* It should be ${?}, or ${#var},
* or even ${?+subst} - operator acting on a special variable,
* or the beginning of variable name.
@@ -3995,14 +4325,14 @@ static int parse_dollar(o_string *as_string,
o_addchr(dest, SPECIAL_VAR_SYMBOL);
break;
}
-#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
+#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
case '(': {
unsigned pos;
ch = i_getch(input);
nommu_addchr(as_string, ch);
-# if ENABLE_SH_MATH_SUPPORT
- if (i_peek(input) == '(') {
+# if ENABLE_FEATURE_SH_MATH
+ if (i_peek_and_eat_bkslash_nl(input) == '(') {
ch = i_getch(input);
nommu_addchr(as_string, ch);
o_addchr(dest, SPECIAL_VAR_SYMBOL);
@@ -4039,7 +4369,7 @@ static int parse_dollar(o_string *as_string,
case '_':
ch = i_getch(input);
nommu_addchr(as_string, ch);
- ch = i_peek(input);
+ ch = i_peek_and_eat_bkslash_nl(input);
if (isalnum(ch)) { /* it's $_name or $_123 */
ch = '_';
goto make_var;
@@ -4211,12 +4541,14 @@ static struct pipe *parse_stream(char **pstring,
syntax_error_unterm_str("here document");
goto parse_error;
}
- /* end_trigger == '}' case errors out earlier,
- * checking only ')' */
if (end_trigger == ')') {
syntax_error_unterm_ch('(');
goto parse_error;
}
+ if (end_trigger == '}') {
+ syntax_error_unterm_ch('{');
+ goto parse_error;
+ }
if (done_word(&dest, &ctx)) {
goto parse_error;
@@ -4234,7 +4566,7 @@ static struct pipe *parse_stream(char **pstring,
pi = NULL;
}
#if !BB_MMU
- debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
+ debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
if (pstring)
*pstring = ctx.as_string.data;
else
@@ -4258,6 +4590,7 @@ static struct pipe *parse_stream(char **pstring,
|| dest.has_quoted_part /* ""{... - non-special */
|| (next != ';' /* }; - special */
&& next != ')' /* }) - special */
+ && next != '(' /* {( - special */
&& next != '&' /* }& and }&& ... - special */
&& next != '|' /* }|| ... - special */
&& !strchr(defifs, next) /* {word - non-special */
@@ -4340,17 +4673,31 @@ static struct pipe *parse_stream(char **pstring,
* Pathological example: { ""}; } should exec "}" cmd
*/
if (ch == '}') {
- if (!IS_NULL_CMD(ctx.command) /* cmd } */
- || dest.length != 0 /* word} */
+ if (dest.length != 0 /* word} */
|| dest.has_quoted_part /* ""} */
) {
goto ordinary_char;
}
+ if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
+ /* Generally, there should be semicolon: "cmd; }"
+ * However, bash allows to omit it if "cmd" is
+ * a group. Examples:
+ * { { echo 1; } }
+ * {(echo 1)}
+ * { echo 0 >&2 | { echo 1; } }
+ * { while false; do :; done }
+ * { case a in b) ;; esac }
+ */
+ if (ctx.command->group)
+ goto term_group;
+ goto ordinary_char;
+ }
if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
+ /* Can't be an end of {cmd}, skip the check */
goto skip_end_trigger;
/* else: } does terminate a group */
}
-
+ term_group:
if (end_trigger && end_trigger == ch
&& (ch != ';' || heredoc_cnt == 0)
#if ENABLE_HUSH_CASE
@@ -4385,7 +4732,7 @@ static struct pipe *parse_stream(char **pstring,
) {
o_free(&dest);
#if !BB_MMU
- debug_printf_parse("as_string '%s'\n", ctx.as_string.data);
+ debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
if (pstring)
*pstring = ctx.as_string.data;
else
@@ -4625,9 +4972,6 @@ static struct pipe *parse_stream(char **pstring,
* with redirect_opt_num(), but bash doesn't do it.
* "echo foo 2| cat" yields "foo 2". */
done_command(&ctx);
-#if !BB_MMU
- o_reset_to_empty_unquoted(&ctx.as_string);
-#endif
}
goto new_cmd;
case '(':
@@ -4656,7 +5000,8 @@ static struct pipe *parse_stream(char **pstring,
* if we see {, we call parse_group(..., end_trigger='}')
* and it will match } earlier (not here). */
syntax_error_unexpected_ch(ch);
- goto parse_error;
+ G.last_exitcode = 2;
+ goto parse_error1;
default:
if (HUSH_DEBUG)
bb_error_msg_and_die("BUG: unexpected %c\n", ch);
@@ -4664,6 +5009,8 @@ static struct pipe *parse_stream(char **pstring,
} /* while (1) */
parse_error:
+ G.last_exitcode = 1;
+ parse_error1:
{
struct parse_context *pctx;
IF_HAS_KEYWORDS(struct parse_context *p2;)
@@ -4673,8 +5020,8 @@ static struct pipe *parse_stream(char **pstring,
* Run it from interactive shell, watch pmap `pidof hush`.
* while if false; then false; fi; do break; fi
* Samples to catch leaks at execution:
- * while if (true | {true;}); then echo ok; fi; do break; done
- * while if (true | {true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
+ * while if (true | { true;}); then echo ok; fi; do break; done
+ * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
*/
pctx = &ctx;
do {
@@ -4697,7 +5044,6 @@ static struct pipe *parse_stream(char **pstring,
} while (HAS_KEYWORDS && pctx);
o_free(&dest);
- G.last_exitcode = 1;
#if !BB_MMU
if (pstring)
*pstring = NULL;
@@ -4865,7 +5211,7 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int
return exp_str;
}
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
{
arith_state_t math_state;
@@ -5026,8 +5372,9 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
/* Handle any expansions */
if (exp_op == 'L') {
+ reinit_unicode_for_hush();
debug_printf_expand("expand: length(%s)=", val);
- val = utoa(val ? strlen(val) : 0);
+ val = utoa(val ? unicode_strlen(val) : 0);
debug_printf_expand("%s\n", val);
} else if (exp_op) {
if (exp_op == '%' || exp_op == '#') {
@@ -5116,7 +5463,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha
}
#endif
else if (exp_op == ':') {
-#if ENABLE_HUSH_BASH_COMPAT && ENABLE_SH_MATH_SUPPORT
+#if ENABLE_HUSH_BASH_COMPAT && ENABLE_FEATURE_SH_MATH
/* It's ${var:N[:M]} bashism.
* Note that in encoded form it has TWO parts:
* var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
@@ -5251,7 +5598,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
#if ENABLE_HUSH_TICK
o_string subst_result = NULL_O_STRING;
#endif
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
char arith_buf[sizeof(arith_t)*3 + 2];
#endif
@@ -5345,7 +5692,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
val = subst_result.data;
goto store_val;
#endif
-#if ENABLE_SH_MATH_SUPPORT
+#if ENABLE_FEATURE_SH_MATH
case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
arith_t res;
@@ -5375,7 +5722,6 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
!!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
}
break;
-
} /* switch (char after <SPECIAL_VAR_SYMBOL>) */
if (val && val[0]) {
@@ -5509,7 +5855,7 @@ static char* expand_strvec_to_string(char **argv)
n++;
}
}
- overlapping_strcpy((char*)list, list[0]);
+ overlapping_strcpy((char*)list, list[0] ? list[0] : "");
debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
return (char*)list;
}
@@ -5786,10 +6132,8 @@ static void parse_and_run_stream(struct in_str *inp, int end_trigger)
debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
run_and_free_list(pipe_list);
empty = 0;
-#if ENABLE_HUSH_FUNCTIONS
- if (G.flag_return_in_progress == 1)
+ if (G_flag_return_in_progress == 1)
break;
-#endif
}
}
@@ -5869,12 +6213,13 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
* Our solution: ONLY bare $(trap) or `trap` is special.
*/
s = skip_whitespace(s);
- if (strncmp(s, "trap", 4) == 0
+ if (is_prefixed_with(s, "trap")
&& skip_whitespace(s + 4)[0] == '\0'
) {
static const char *const argv[] = { NULL, NULL };
builtin_trap((char**)argv);
- exit(0); /* not _exit() - we need to fflush */
+ fflush_all(); /* important */
+ _exit(0);
}
# if BB_MMU
reset_traps_to_defaults();
@@ -5907,8 +6252,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
free(to_free);
# endif
close(channel[1]);
- close_on_exec_on(channel[0]);
- return xfdopen_for_read(channel[0]);
+ return remember_FILE(xfdopen_for_read(channel[0]));
}
/* Return code is exit status of the process that is run. */
@@ -5937,7 +6281,7 @@ static int process_command_subs(o_string *dest, const char *s)
}
debug_printf("done reading from `cmd` pipe, closing it\n");
- fclose(fp);
+ fclose_and_forget(fp);
/* We need to extract exitcode. Test case
* "true; echo `sleep 1; false` $?"
* should print 1 */
@@ -6029,6 +6373,74 @@ static void setup_heredoc(struct redir_struct *redir)
wait(NULL); /* wait till child has died */
}
+/* fd: redirect wants this fd to be used (e.g. 3>file).
+ * Move all conflicting internally used fds,
+ * and remember them so that we can restore them later.
+ */
+static int save_fds_on_redirect(int fd, int squirrel[3])
+{
+ if (squirrel) {
+ /* Handle redirects of fds 0,1,2 */
+
+ /* If we collide with an already moved stdio fd... */
+ if (fd == squirrel[0]) {
+ squirrel[0] = xdup_and_close(squirrel[0], F_DUPFD);
+ return 1;
+ }
+ if (fd == squirrel[1]) {
+ squirrel[1] = xdup_and_close(squirrel[1], F_DUPFD);
+ return 1;
+ }
+ if (fd == squirrel[2]) {
+ squirrel[2] = xdup_and_close(squirrel[2], F_DUPFD);
+ return 1;
+ }
+ /* If we are about to redirect stdio fd, and did not yet move it... */
+ if (fd <= 2 && squirrel[fd] < 0) {
+ /* We avoid taking stdio fds */
+ squirrel[fd] = fcntl(fd, F_DUPFD, 10);
+ if (squirrel[fd] < 0 && errno != EBADF)
+ xfunc_die();
+ return 0; /* "we did not close fd" */
+ }
+ }
+
+#if ENABLE_HUSH_INTERACTIVE
+ if (fd != 0 && fd == G.interactive_fd) {
+ G.interactive_fd = xdup_and_close(G.interactive_fd, F_DUPFD_CLOEXEC);
+ return 1;
+ }
+#endif
+
+ /* Are we called from setup_redirects(squirrel==NULL)? Two cases:
+ * (1) Redirect in a forked child. No need to save FILEs' fds,
+ * we aren't going to use them anymore, ok to trash.
+ * (2) "exec 3>FILE". Bummer. We can save FILEs' fds,
+ * but how are we doing to use them?
+ * "fileno(fd) = new_fd" can't be done.
+ */
+ if (!squirrel)
+ return 0;
+
+ return save_FILEs_on_redirect(fd);
+}
+
+static void restore_redirects(int squirrel[3])
+{
+ int i, fd;
+ for (i = 0; i <= 2; i++) {
+ fd = squirrel[i];
+ if (fd != -1) {
+ /* We simply die on error */
+ xmove_fd(fd, i);
+ }
+ }
+
+ /* Moved G.interactive_fd stays on new fd, not doing anything for it */
+
+ restore_redirected_FILEs();
+}
+
/* squirrel != NULL means we squirrel away copies of stdin, stdout,
* and stderr if they are redirected. */
static int setup_redirects(struct command *prog, int squirrel[])
@@ -6038,12 +6450,8 @@ static int setup_redirects(struct command *prog, int squirrel[])
for (redir = prog->redirects; redir; redir = redir->next) {
if (redir->rd_type == REDIRECT_HEREDOC2) {
- /* rd_fd<<HERE case */
- if (squirrel && redir->rd_fd < 3
- && squirrel[redir->rd_fd] < 0
- ) {
- squirrel[redir->rd_fd] = dup(redir->rd_fd);
- }
+ /* "rd_fd<<HERE" case */
+ save_fds_on_redirect(redir->rd_fd, squirrel);
/* for REDIRECT_HEREDOC2, rd_filename holds _contents_
* of the heredoc */
debug_printf_parse("set heredoc '%s'\n",
@@ -6053,12 +6461,15 @@ static int setup_redirects(struct command *prog, int squirrel[])
}
if (redir->rd_dup == REDIRFD_TO_FILE) {
- /* rd_fd<*>file case (<*> is <,>,>>,<>) */
+ /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
char *p;
if (redir->rd_filename == NULL) {
- /* Something went wrong in the parse.
- * Pretend it didn't happen */
- bb_error_msg("bug in redirect parse");
+ /*
+ * Examples:
+ * "cmd >" (no filename)
+ * "cmd > <file" (2nd redirect starts too early)
+ */
+ die_if_script("syntax error: %s", "invalid redirect");
continue;
}
mode = redir_table[redir->rd_type].mode;
@@ -6066,47 +6477,39 @@ static int setup_redirects(struct command *prog, int squirrel[])
openfd = open_or_warn(p, mode);
free(p);
if (openfd < 0) {
- /* this could get lost if stderr has been redirected, but
- * bash and ash both lose it as well (though zsh doesn't!) */
-//what the above comment tries to say?
+ /* Error message from open_or_warn can be lost
+ * if stderr has been redirected, but bash
+ * and ash both lose it as well
+ * (though zsh doesn't!)
+ */
return 1;
}
} else {
- /* rd_fd<*>rd_dup or rd_fd<*>- cases */
+ /* "rd_fd<*>rd_dup" or "rd_fd<*>-" cases */
openfd = redir->rd_dup;
}
if (openfd != redir->rd_fd) {
- if (squirrel && redir->rd_fd < 3
- && squirrel[redir->rd_fd] < 0
- ) {
- squirrel[redir->rd_fd] = dup(redir->rd_fd);
- }
+ int closed = save_fds_on_redirect(redir->rd_fd, squirrel);
if (openfd == REDIRFD_CLOSE) {
- /* "n>-" means "close me" */
- close(redir->rd_fd);
+ /* "rd_fd >&-" means "close me" */
+ if (!closed) {
+ /* ^^^ optimization: saving may already
+ * have closed it. If not... */
+ close(redir->rd_fd);
+ }
} else {
xdup2(openfd, redir->rd_fd);
if (redir->rd_dup == REDIRFD_TO_FILE)
+ /* "rd_fd > FILE" */
close(openfd);
+ /* else: "rd_fd > rd_dup" */
}
}
}
return 0;
}
-static void restore_redirects(int squirrel[])
-{
- int i, fd;
- for (i = 0; i < 3; i++) {
- fd = squirrel[i];
- if (fd != -1) {
- /* We simply die on error */
- xmove_fd(fd, i);
- }
- }
-}
-
static char *find_in_path(const char *arg)
{
char *ret = NULL;
@@ -6290,8 +6693,8 @@ static int run_function(const struct function *funcp, char **argv)
save_and_replace_G_args(&sv, argv);
/* "we are in function, ok to use return" */
- sv_flg = G.flag_return_in_progress;
- G.flag_return_in_progress = -1;
+ sv_flg = G_flag_return_in_progress;
+ G_flag_return_in_progress = -1;
# if ENABLE_HUSH_LOCAL
G.func_nest_level++;
# endif
@@ -6332,7 +6735,7 @@ static int run_function(const struct function *funcp, char **argv)
G.func_nest_level--;
}
# endif
- G.flag_return_in_progress = sv_flg;
+ G_flag_return_in_progress = sv_flg;
restore_G_args(&sv, argv);
@@ -6378,13 +6781,17 @@ static void exec_builtin(char ***to_free,
static void execvp_or_die(char **argv) NORETURN;
static void execvp_or_die(char **argv)
{
+ int e;
debug_printf_exec("execing '%s'\n", argv[0]);
/* Don't propagate SIG_IGN to the child */
if (SPECIAL_JOBSTOP_SIGS != 0)
switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
execvp(argv[0], argv);
+ e = 2;
+ if (errno == EACCES) e = 126;
+ if (errno == ENOENT) e = 127;
bb_perror_msg("can't execute '%s'", argv[0]);
- _exit(127); /* bash compat */
+ _exit(e);
}
#if ENABLE_HUSH_MODE_X
@@ -6427,7 +6834,8 @@ static void dump_cmd_in_x_mode(char **argv)
* Never returns.
* Don't exit() here. If you don't exec, use _exit instead.
* The at_exit handlers apparently confuse the calling process,
- * in particular stdin handling. Not sure why? -- because of vfork! (vda) */
+ * in particular stdin handling. Not sure why? -- because of vfork! (vda)
+ */
static void pseudo_exec_argv(nommu_save_t *nommu_save,
char **argv, int assignment_cnt,
char **argv_expanded) NORETURN;
@@ -6507,6 +6915,8 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
if (a >= 0) {
# if BB_MMU /* see above why on NOMMU it is not allowed */
if (APPLET_IS_NOEXEC(a)) {
+ /* Do not leak open fds from opened script files etc */
+ close_all_FILE_list();
debug_printf_exec("running applet '%s'\n", argv[0]);
run_applet_no_and_exit(a, argv);
}
@@ -6584,12 +6994,12 @@ static const char *get_cmdtext(struct pipe *pi)
* On subsequent bg argv is trashed, but we won't use it */
if (pi->cmdtext)
return pi->cmdtext;
+
argv = pi->cmds[0].argv;
- if (!argv || !argv[0]) {
+ if (!argv) {
pi->cmdtext = xzalloc(1);
return pi->cmdtext;
}
-
len = 0;
do {
len += strlen(*argv) + 1;
@@ -6598,9 +7008,7 @@ static const char *get_cmdtext(struct pipe *pi)
pi->cmdtext = p;
argv = pi->cmds[0].argv;
do {
- len = strlen(*argv);
- memcpy(p, *argv, len);
- p += len;
+ p = stpcpy(p, *argv);
*p++ = ' ';
} while (*++argv);
p[-1] = '\0';
@@ -6665,16 +7073,157 @@ static void delete_finished_bg_job(struct pipe *pi)
}
#endif /* JOB */
-/* Check to see if any processes have exited -- if they
- * have, figure out why and see if a job has completed */
-static int checkjobs(struct pipe *fg_pipe)
+static int job_exited_or_stopped(struct pipe *pi)
+{
+ int rcode, i;
+
+ if (pi->alive_cmds != pi->stopped_cmds)
+ return -1;
+
+ /* All processes in fg pipe have exited or stopped */
+ rcode = 0;
+ i = pi->num_cmds;
+ while (--i >= 0) {
+ rcode = pi->cmds[i].cmd_exitcode;
+ /* usually last process gives overall exitstatus,
+ * but with "set -o pipefail", last *failed* process does */
+ if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
+ break;
+ }
+ IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
+ return rcode;
+}
+
+static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
{
- int attributes;
- int status;
#if ENABLE_HUSH_JOB
struct pipe *pi;
#endif
- pid_t childpid;
+ int i, dead;
+
+ dead = WIFEXITED(status) || WIFSIGNALED(status);
+
+#if DEBUG_JOBS
+ if (WIFSTOPPED(status))
+ debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
+ childpid, WSTOPSIG(status), WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
+ childpid, WTERMSIG(status), WEXITSTATUS(status));
+ if (WIFEXITED(status))
+ debug_printf_jobs("pid %d exited, exitcode %d\n",
+ childpid, WEXITSTATUS(status));
+#endif
+ /* Were we asked to wait for a fg pipe? */
+ if (fg_pipe) {
+ i = fg_pipe->num_cmds;
+
+ while (--i >= 0) {
+ int rcode;
+
+ debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
+ if (fg_pipe->cmds[i].pid != childpid)
+ continue;
+ if (dead) {
+ int ex;
+ fg_pipe->cmds[i].pid = 0;
+ fg_pipe->alive_cmds--;
+ ex = WEXITSTATUS(status);
+ /* bash prints killer signal's name for *last*
+ * process in pipe (prints just newline for SIGINT/SIGPIPE).
+ * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
+ */
+ if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+ if (i == fg_pipe->num_cmds-1)
+ /* TODO: use strsignal() instead for bash compat? but that's bloat... */
+ puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
+ /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
+ /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
+ * Maybe we need to use sig | 128? */
+ ex = sig + 128;
+ }
+ fg_pipe->cmds[i].cmd_exitcode = ex;
+ } else {
+ fg_pipe->stopped_cmds++;
+ }
+ debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
+ fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
+ rcode = job_exited_or_stopped(fg_pipe);
+ if (rcode >= 0) {
+/* Note: *non-interactive* bash does not continue if all processes in fg pipe
+ * are stopped. Testcase: "cat | cat" in a script (not on command line!)
+ * and "killall -STOP cat" */
+ if (G_interactive_fd) {
+#if ENABLE_HUSH_JOB
+ if (fg_pipe->alive_cmds != 0)
+ insert_bg_job(fg_pipe);
+#endif
+ return rcode;
+ }
+ if (fg_pipe->alive_cmds == 0)
+ return rcode;
+ }
+ /* There are still running processes in the fg_pipe */
+ return -1;
+ }
+ /* It wasnt in fg_pipe, look for process in bg pipes */
+ }
+
+#if ENABLE_HUSH_JOB
+ /* We were asked to wait for bg or orphaned children */
+ /* No need to remember exitcode in this case */
+ for (pi = G.job_list; pi; pi = pi->next) {
+ for (i = 0; i < pi->num_cmds; i++) {
+ if (pi->cmds[i].pid == childpid)
+ goto found_pi_and_prognum;
+ }
+ }
+ /* Happens when shell is used as init process (init=/bin/sh) */
+ debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
+ return -1; /* this wasn't a process from fg_pipe */
+
+ found_pi_and_prognum:
+ if (dead) {
+ /* child exited */
+ pi->cmds[i].pid = 0;
+ pi->cmds[i].cmd_exitcode = WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status);
+ pi->alive_cmds--;
+ if (!pi->alive_cmds) {
+ if (G_interactive_fd)
+ printf(JOB_STATUS_FORMAT, pi->jobid,
+ "Done", pi->cmdtext);
+ delete_finished_bg_job(pi);
+ }
+ } else {
+ /* child stopped */
+ pi->stopped_cmds++;
+ }
+#endif
+ return -1; /* this wasn't a process from fg_pipe */
+}
+
+/* Check to see if any processes have exited -- if they have,
+ * figure out why and see if a job has completed.
+ *
+ * If non-NULL fg_pipe: wait for its completion or stop.
+ * Return its exitcode or zero if stopped.
+ *
+ * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
+ * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
+ * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
+ * or 0 if no children changed status.
+ *
+ * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
+ * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
+ * or 0 if no children changed status.
+ */
+static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
+{
+ int attributes;
+ int status;
int rcode = 0;
debug_printf_jobs("checkjobs %p\n", fg_pipe);
@@ -6711,12 +7260,10 @@ static int checkjobs(struct pipe *fg_pipe)
* 1 <========== bg pipe is not fully done, but exitcode is already known!
* [hush 1.14.0: yes we do it right]
*/
- wait_more:
while (1) {
- int i;
- int dead;
-
+ pid_t childpid;
#if ENABLE_HUSH_FAST
+ int i;
i = G.count_SIGCHLD;
#endif
childpid = waitpid(-1, &status, attributes);
@@ -6730,112 +7277,28 @@ static int checkjobs(struct pipe *fg_pipe)
//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
}
#endif
+ /* ECHILD (no children), or 0 (no change in children status) */
+ rcode = childpid;
break;
}
- dead = WIFEXITED(status) || WIFSIGNALED(status);
-
-#if DEBUG_JOBS
- if (WIFSTOPPED(status))
- debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
- childpid, WSTOPSIG(status), WEXITSTATUS(status));
- if (WIFSIGNALED(status))
- debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
- childpid, WTERMSIG(status), WEXITSTATUS(status));
- if (WIFEXITED(status))
- debug_printf_jobs("pid %d exited, exitcode %d\n",
- childpid, WEXITSTATUS(status));
-#endif
- /* Were we asked to wait for fg pipe? */
- if (fg_pipe) {
- i = fg_pipe->num_cmds;
- while (--i >= 0) {
- debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
- if (fg_pipe->cmds[i].pid != childpid)
- continue;
- if (dead) {
- int ex;
- fg_pipe->cmds[i].pid = 0;
- fg_pipe->alive_cmds--;
- ex = WEXITSTATUS(status);
- /* bash prints killer signal's name for *last*
- * process in pipe (prints just newline for SIGINT/SIGPIPE).
- * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
- */
- if (WIFSIGNALED(status)) {
- int sig = WTERMSIG(status);
- if (i == fg_pipe->num_cmds-1)
- /* TODO: use strsignal() instead for bash compat? but that's bloat... */
- printf("%s\n", sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
- /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
- /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
- * Maybe we need to use sig | 128? */
- ex = sig + 128;
- }
- fg_pipe->cmds[i].cmd_exitcode = ex;
- } else {
- fg_pipe->stopped_cmds++;
- }
- debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
- fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
- if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
- /* All processes in fg pipe have exited or stopped */
- i = fg_pipe->num_cmds;
- while (--i >= 0) {
- rcode = fg_pipe->cmds[i].cmd_exitcode;
- /* usually last process gives overall exitstatus,
- * but with "set -o pipefail", last *failed* process does */
- if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
- break;
- }
- IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
-/* Note: *non-interactive* bash does not continue if all processes in fg pipe
- * are stopped. Testcase: "cat | cat" in a script (not on command line!)
- * and "killall -STOP cat" */
- if (G_interactive_fd) {
-#if ENABLE_HUSH_JOB
- if (fg_pipe->alive_cmds != 0)
- insert_bg_job(fg_pipe);
-#endif
- return rcode;
- }
- if (fg_pipe->alive_cmds == 0)
- return rcode;
- }
- /* There are still running processes in the fg pipe */
- goto wait_more; /* do waitpid again */
- }
- /* it wasnt fg_pipe, look for process in bg pipes */
- }
-
-#if ENABLE_HUSH_JOB
- /* We asked to wait for bg or orphaned children */
- /* No need to remember exitcode in this case */
- for (pi = G.job_list; pi; pi = pi->next) {
- for (i = 0; i < pi->num_cmds; i++) {
- if (pi->cmds[i].pid == childpid)
- goto found_pi_and_prognum;
- }
- }
- /* Happens when shell is used as init process (init=/bin/sh) */
- debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
- continue; /* do waitpid again */
-
- found_pi_and_prognum:
- if (dead) {
- /* child exited */
- pi->cmds[i].pid = 0;
- pi->alive_cmds--;
- if (!pi->alive_cmds) {
- if (G_interactive_fd)
- printf(JOB_STATUS_FORMAT, pi->jobid,
- "Done", pi->cmdtext);
- delete_finished_bg_job(pi);
- }
- } else {
- /* child stopped */
- pi->stopped_cmds++;
+ rcode = process_wait_result(fg_pipe, childpid, status);
+ if (rcode >= 0) {
+ /* fg_pipe exited or stopped */
+ break;
}
-#endif
+ if (childpid == waitfor_pid) {
+ debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
+ rcode = WEXITSTATUS(status);
+ if (WIFSIGNALED(status))
+ rcode = 128 + WTERMSIG(status);
+ if (WIFSTOPPED(status))
+ /* bash: "cmd & wait $!" and cmd stops: $? = 128 + stopsig */
+ rcode = 128 + WSTOPSIG(status);
+ rcode++;
+ break; /* "wait PID" called us, give it exitcode+1 */
+ }
+ /* This wasn't one of our processes, or */
+ /* fg_pipe still has running processes, do waitpid again */
} /* while (waitpid succeeds)... */
return rcode;
@@ -6845,7 +7308,7 @@ static int checkjobs(struct pipe *fg_pipe)
static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
{
pid_t p;
- int rcode = checkjobs(fg_pipe);
+ int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
if (G_saved_tty_pgrp) {
/* Job finished, move the shell to the foreground */
p = getpgrp(); /* our process group id */
@@ -7082,6 +7545,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
if (x->b_function == builtin_exec && argv_expanded[1] == NULL) {
debug_printf("exec with redirects only\n");
rcode = setup_redirects(command, NULL);
+ /* rcode=1 can be if redir file can't be opened */
goto clean_up_and_ret1;
}
}
@@ -7208,9 +7672,20 @@ static NOINLINE int run_pipe(struct pipe *pi)
if (pipefds.rd > 1)
close(pipefds.rd);
/* Like bash, explicit redirects override pipes,
- * and the pipe fd is available for dup'ing. */
- if (setup_redirects(command, NULL))
+ * and the pipe fd (fd#1) is available for dup'ing:
+ * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
+ * of cmd1 goes into pipe.
+ */
+ if (setup_redirects(command, NULL)) {
+ /* Happens when redir file can't be opened:
+ * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
+ * FOO
+ * hush: can't open '/qwe/rty': No such file or directory
+ * BAZ
+ * (echo BAR is not executed, it hits _exit(1) below)
+ */
_exit(1);
+ }
/* Stores to nommu_save list of env vars putenv'ed
* (NOMMU, on MMU we don't need that) */
@@ -7339,8 +7814,12 @@ static int run_list(struct pipe *pi)
/* Go through list of pipes, (maybe) executing them. */
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
+ int r;
+
if (G.flag_SIGINT)
break;
+ if (G_flag_return_in_progress == 1)
+ break;
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
@@ -7434,12 +7913,14 @@ static int run_list(struct pipe *pi)
#endif
#if ENABLE_HUSH_CASE
if (rword == RES_CASE) {
+ debug_printf_exec("CASE cond_code:%d\n", cond_code);
case_word = expand_strvec_to_string(pi->cmds->argv);
continue;
}
if (rword == RES_MATCH) {
char **argv;
+ debug_printf_exec("MATCH cond_code:%d\n", cond_code);
if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
break;
/* all prev words didn't match, does this one match? */
@@ -7450,8 +7931,8 @@ static int run_list(struct pipe *pi)
cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
free(pattern);
if (cond_code == 0) { /* match! we will execute this branch */
- free(case_word); /* make future "word)" stop */
- case_word = NULL;
+ free(case_word);
+ case_word = NULL; /* make future "word)" stop */
break;
}
argv++;
@@ -7459,9 +7940,17 @@ static int run_list(struct pipe *pi)
continue;
}
if (rword == RES_CASE_BODY) { /* inside of a case branch */
+ debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
if (cond_code != 0)
continue; /* not matched yet, skip this pipe */
}
+ if (rword == RES_ESAC) {
+ debug_printf_exec("ESAC cond_code:%d\n", cond_code);
+ if (case_word) {
+ /* "case" did not match anything: still set $? (to 0) */
+ G.last_exitcode = rcode = EXIT_SUCCESS;
+ }
+ }
#endif
/* Just pressing <enter> in shell should check for jobs.
* OTOH, in non-interactive shell this is useless
@@ -7477,76 +7966,72 @@ static int run_list(struct pipe *pi)
* after run_pipe to collect any background children,
* even if list execution is to be stopped. */
debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
- {
- int r;
#if ENABLE_HUSH_LOOPS
- G.flag_break_continue = 0;
-#endif
- rcode = r = run_pipe(pi); /* NB: rcode is a smallint */
- if (r != -1) {
- /* We ran a builtin, function, or group.
- * rcode is already known
- * and we don't need to wait for anything. */
- G.last_exitcode = rcode;
- debug_printf_exec(": builtin/func exitcode %d\n", rcode);
- check_and_run_traps();
+ G.flag_break_continue = 0;
+#endif
+ rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
+ if (r != -1) {
+ /* We ran a builtin, function, or group.
+ * rcode is already known
+ * and we don't need to wait for anything. */
+ debug_printf_exec(": builtin/func exitcode %d\n", rcode);
+ G.last_exitcode = rcode;
+ check_and_run_traps();
#if ENABLE_HUSH_LOOPS
- /* Was it "break" or "continue"? */
- if (G.flag_break_continue) {
- smallint fbc = G.flag_break_continue;
- /* We might fall into outer *loop*,
- * don't want to break it too */
- if (loop_top) {
- G.depth_break_continue--;
- if (G.depth_break_continue == 0)
- G.flag_break_continue = 0;
- /* else: e.g. "continue 2" should *break* once, *then* continue */
- } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
- if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
- checkjobs(NULL);
- break;
- }
- /* "continue": simulate end of loop */
- rword = RES_DONE;
- continue;
- }
-#endif
-#if ENABLE_HUSH_FUNCTIONS
- if (G.flag_return_in_progress == 1) {
- checkjobs(NULL);
+ /* Was it "break" or "continue"? */
+ if (G.flag_break_continue) {
+ smallint fbc = G.flag_break_continue;
+ /* We might fall into outer *loop*,
+ * don't want to break it too */
+ if (loop_top) {
+ G.depth_break_continue--;
+ if (G.depth_break_continue == 0)
+ G.flag_break_continue = 0;
+ /* else: e.g. "continue 2" should *break* once, *then* continue */
+ } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
+ if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
+ checkjobs(NULL, 0 /*(no pid to wait for)*/);
break;
}
+ /* "continue": simulate end of loop */
+ rword = RES_DONE;
+ continue;
+ }
#endif
- } else if (pi->followup == PIPE_BG) {
- /* What does bash do with attempts to background builtins? */
- /* even bash 3.2 doesn't do that well with nested bg:
- * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
- * I'm NOT treating inner &'s as jobs */
- check_and_run_traps();
+ if (G_flag_return_in_progress == 1) {
+ checkjobs(NULL, 0 /*(no pid to wait for)*/);
+ break;
+ }
+ } else if (pi->followup == PIPE_BG) {
+ /* What does bash do with attempts to background builtins? */
+ /* even bash 3.2 doesn't do that well with nested bg:
+ * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
+ * I'm NOT treating inner &'s as jobs */
#if ENABLE_HUSH_JOB
- if (G.run_list_level == 1)
- insert_bg_job(pi);
-#endif
- /* Last command's pid goes to $! */
- G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
- G.last_exitcode = rcode = EXIT_SUCCESS;
- debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
- } else {
+ if (G.run_list_level == 1)
+ insert_bg_job(pi);
+#endif
+ /* Last command's pid goes to $! */
+ G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
+ debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
+/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */
+ rcode = EXIT_SUCCESS;
+ goto check_traps;
+ } else {
#if ENABLE_HUSH_JOB
- if (G.run_list_level == 1 && G_interactive_fd) {
- /* Waits for completion, then fg's main shell */
- rcode = checkjobs_and_fg_shell(pi);
- debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
- check_and_run_traps();
- } else
-#endif
- { /* This one just waits for completion */
- rcode = checkjobs(pi);
- debug_printf_exec(": checkjobs exitcode %d\n", rcode);
- check_and_run_traps();
- }
- G.last_exitcode = rcode;
+ if (G.run_list_level == 1 && G_interactive_fd) {
+ /* Waits for completion, then fg's main shell */
+ rcode = checkjobs_and_fg_shell(pi);
+ debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
+ goto check_traps;
}
+#endif
+ /* This one just waits for completion */
+ rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
+ debug_printf_exec(": checkjobs exitcode %d\n", rcode);
+ check_traps:
+ G.last_exitcode = rcode;
+ check_and_run_traps();
}
/* Analyze how result affects subsequent commands */
@@ -7555,7 +8040,7 @@ static int run_list(struct pipe *pi)
cond_code = rcode;
#endif
check_jobs_and_continue:
- checkjobs(NULL);
+ checkjobs(NULL, 0 /*(no pid to wait for)*/);
dont_check_jobs_but_continue: ;
#if ENABLE_HUSH_LOOPS
/* Beware of "while false; true; do ..."! */
@@ -7670,11 +8155,11 @@ static void install_fatal_sighandlers(void)
/* We will restore tty pgrp on these signals */
mask = 0
- + (1 << SIGILL ) * HUSH_DEBUG
- + (1 << SIGFPE ) * HUSH_DEBUG
+ /*+ (1 << SIGILL ) * HUSH_DEBUG*/
+ /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
+ (1 << SIGBUS ) * HUSH_DEBUG
+ (1 << SIGSEGV) * HUSH_DEBUG
- + (1 << SIGTRAP) * HUSH_DEBUG
+ /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
+ (1 << SIGABRT)
/* bash 3.2 seems to handle these just like 'fatal' ones */
+ (1 << SIGPIPE)
@@ -7748,6 +8233,7 @@ int hush_main(int argc, char **argv)
INIT_G();
if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
G.last_exitcode = EXIT_SUCCESS;
+
#if ENABLE_HUSH_FAST
G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
#endif
@@ -7785,6 +8271,14 @@ int hush_main(int argc, char **argv)
/* Export PWD */
set_pwd_var(/*exp:*/ 1);
+
+#if ENABLE_HUSH_BASH_COMPAT
+ /* Set (but not export) HOSTNAME unless already set */
+ if (!get_local_var_value("HOSTNAME")) {
+ struct utsname uts;
+ uname(&uts);
+ set_local_var_from_halves("HOSTNAME", uts.nodename);
+ }
/* bash also exports SHLVL and _,
* and sets (but doesn't export) the following variables:
* BASH=/bin/bash
@@ -7793,7 +8287,6 @@ int hush_main(int argc, char **argv)
* HOSTTYPE=i386
* MACHTYPE=i386-pc-linux-gnu
* OSTYPE=linux-gnu
- * HOSTNAME=<xxxxxxxxxx>
* PPID=<NNNNN> - we also do it elsewhere
* EUID=<NNNNN>
* UID=<NNNNN>
@@ -7821,6 +8314,7 @@ int hush_main(int argc, char **argv)
* PS2='> '
* PS4='+ '
*/
+#endif
#if ENABLE_FEATURE_EDITING
G.line_input_state = new_line_input_t(FOR_SHELL);
@@ -7829,12 +8323,7 @@ int hush_main(int argc, char **argv)
/* Initialize some more globals to non-zero values */
cmdedit_update_prompt();
- if (setjmp(die_jmp)) {
- /* xfunc has failed! die die die */
- /* no EXIT traps, this is an escape hatch! */
- G.exiting = 1;
- hush_exit(xfunc_error_retval);
- }
+ die_func = restore_ttypgrp_and__exit;
/* Shell is non-interactive at first. We need to call
* install_special_sighandlers() if we are going to execute "sh <script>",
@@ -7993,10 +8482,10 @@ int hush_main(int argc, char **argv)
debug_printf("sourcing /etc/profile\n");
input = fopen_for_read("/etc/profile");
if (input != NULL) {
- close_on_exec_on(fileno(input));
+ remember_FILE(input);
install_special_sighandlers();
parse_and_run_file(input);
- fclose(input);
+ fclose_and_forget(input);
}
/* bash: after sourcing /etc/profile,
* tries to source (in the given order):
@@ -8013,16 +8502,19 @@ int hush_main(int argc, char **argv)
* "bash <script>" (which is never interactive (unless -i?))
* sources $BASH_ENV here (without scanning $PATH).
* If called as sh, does the same but with $ENV.
+ * Also NB, per POSIX, $ENV should undergo parameter expansion.
*/
G.global_argc--;
G.global_argv++;
debug_printf("running script '%s'\n", G.global_argv[0]);
+ xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
input = xfopen_for_read(G.global_argv[0]);
- close_on_exec_on(fileno(input));
+ xfunc_error_retval = 1;
+ remember_FILE(input);
install_special_sighandlers();
parse_and_run_file(input);
#if ENABLE_FEATURE_CLEAN_UP
- fclose(input);
+ fclose_and_forget(input);
#endif
goto final_return;
}
@@ -8092,9 +8584,7 @@ int hush_main(int argc, char **argv)
/* Grab control of the terminal */
tcsetpgrp(G_interactive_fd, getpid());
}
- /* -1 is special - makes xfuncs longjmp, not exit
- * (we reset die_sleep = 0 whereever we [v]fork) */
- enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
+ enable_restore_tty_pgrp_on_exit();
# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
{
@@ -8165,7 +8655,7 @@ int hush_main(int argc, char **argv)
int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int msh_main(int argc, char **argv)
{
- //bb_error_msg("msh is deprecated, please use hush instead");
+ bb_error_msg("msh is deprecated, please use hush instead");
return hush_main(argc, argv);
}
#endif
@@ -8359,6 +8849,14 @@ static void helper_export_local(char **argv, int exp, int lvl)
continue;
}
}
+#if ENABLE_HUSH_LOCAL
+ if (exp == 0 /* local? */
+ && var && var->func_nest_level == lvl
+ ) {
+ /* "local x=abc; ...; local x" - ignore second local decl */
+ continue;
+ }
+#endif
/* Exporting non-existing variable.
* bash does not put it in environment,
* but remembers that it is exported,
@@ -8433,6 +8931,212 @@ static int FAST_FUNC builtin_local(char **argv)
}
#endif
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
+static int FAST_FUNC builtin_unset(char **argv)
+{
+ int ret;
+ unsigned opts;
+
+ /* "!": do not abort on errors */
+ /* "+": stop at 1st non-option */
+ opts = getopt32(argv, "!+vf");
+ if (opts == (unsigned)-1)
+ return EXIT_FAILURE;
+ if (opts == 3) {
+ bb_error_msg("unset: -v and -f are exclusive");
+ return EXIT_FAILURE;
+ }
+ argv += optind;
+
+ ret = EXIT_SUCCESS;
+ while (*argv) {
+ if (!(opts & 2)) { /* not -f */
+ if (unset_local_var(*argv)) {
+ /* unset <nonexistent_var> doesn't fail.
+ * Error is when one tries to unset RO var.
+ * Message was printed by unset_local_var. */
+ ret = EXIT_FAILURE;
+ }
+ }
+#if ENABLE_HUSH_FUNCTIONS
+ else {
+ unset_func(*argv);
+ }
+#endif
+ argv++;
+ }
+ return ret;
+}
+
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
+ * built-in 'set' handler
+ * SUSv3 says:
+ * set [-abCefhmnuvx] [-o option] [argument...]
+ * set [+abCefhmnuvx] [+o option] [argument...]
+ * set -- [argument...]
+ * set -o
+ * set +o
+ * Implementations shall support the options in both their hyphen and
+ * plus-sign forms. These options can also be specified as options to sh.
+ * Examples:
+ * Write out all variables and their values: set
+ * Set $1, $2, and $3 and set "$#" to 3: set c a b
+ * Turn on the -x and -v options: set -xv
+ * Unset all positional parameters: set --
+ * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
+ * Set the positional parameters to the expansion of x, even if x expands
+ * with a leading '-' or '+': set -- $x
+ *
+ * So far, we only support "set -- [argument...]" and some of the short names.
+ */
+static int FAST_FUNC builtin_set(char **argv)
+{
+ int n;
+ char **pp, **g_argv;
+ char *arg = *++argv;
+
+ if (arg == NULL) {
+ struct variable *e;
+ for (e = G.top_var; e; e = e->next)
+ puts(e->varstr);
+ return EXIT_SUCCESS;
+ }
+
+ do {
+ if (strcmp(arg, "--") == 0) {
+ ++argv;
+ goto set_argv;
+ }
+ if (arg[0] != '+' && arg[0] != '-')
+ break;
+ for (n = 1; arg[n]; ++n) {
+ if (set_mode((arg[0] == '-'), arg[n], argv[1]))
+ goto error;
+ if (arg[n] == 'o' && argv[1])
+ argv++;
+ }
+ } while ((arg = *++argv) != NULL);
+ /* Now argv[0] is 1st argument */
+
+ if (arg == NULL)
+ return EXIT_SUCCESS;
+ set_argv:
+
+ /* NB: G.global_argv[0] ($0) is never freed/changed */
+ g_argv = G.global_argv;
+ if (G.global_args_malloced) {
+ pp = g_argv;
+ while (*++pp)
+ free(*pp);
+ g_argv[1] = NULL;
+ } else {
+ G.global_args_malloced = 1;
+ pp = xzalloc(sizeof(pp[0]) * 2);
+ pp[0] = g_argv[0]; /* retain $0 */
+ g_argv = pp;
+ }
+ /* This realloc's G.global_argv */
+ G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
+
+ n = 1;
+ while (*++pp)
+ n++;
+ G.global_argc = n;
+
+ return EXIT_SUCCESS;
+
+ /* Nothing known, so abort */
+ error:
+ bb_error_msg("set: %s: invalid option", arg);
+ return EXIT_FAILURE;
+}
+
+static int FAST_FUNC builtin_shift(char **argv)
+{
+ int n = 1;
+ argv = skip_dash_dash(argv);
+ if (argv[0]) {
+ n = atoi(argv[0]);
+ }
+ if (n >= 0 && n < G.global_argc) {
+ if (G.global_args_malloced) {
+ int m = 1;
+ while (m <= n)
+ free(G.global_argv[m++]);
+ }
+ G.global_argc -= n;
+ memmove(&G.global_argv[1], &G.global_argv[n+1],
+ G.global_argc * sizeof(G.global_argv[0]));
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/* Interruptibility of read builtin in bash
+ * (tested on bash-4.2.8 by sending signals (not by ^C)):
+ *
+ * Empty trap makes read ignore corresponding signal, for any signal.
+ *
+ * SIGINT:
+ * - terminates non-interactive shell;
+ * - interrupts read in interactive shell;
+ * if it has non-empty trap:
+ * - executes trap and returns to command prompt in interactive shell;
+ * - executes trap and returns to read in non-interactive shell;
+ * SIGTERM:
+ * - is ignored (does not interrupt) read in interactive shell;
+ * - terminates non-interactive shell;
+ * if it has non-empty trap:
+ * - executes trap and returns to read;
+ * SIGHUP:
+ * - terminates shell (regardless of interactivity);
+ * if it has non-empty trap:
+ * - executes trap and returns to read;
+ */
+static int FAST_FUNC builtin_read(char **argv)
+{
+ const char *r;
+ char *opt_n = NULL;
+ char *opt_p = NULL;
+ char *opt_t = NULL;
+ char *opt_u = NULL;
+ const char *ifs;
+ int read_flags;
+
+ /* "!": do not abort on errors.
+ * Option string must start with "sr" to match BUILTIN_READ_xxx
+ */
+ read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
+ if (read_flags == (uint32_t)-1)
+ return EXIT_FAILURE;
+ argv += optind;
+ ifs = get_local_var_value("IFS"); /* can be NULL */
+
+ again:
+ r = shell_builtin_read(set_local_var_from_halves,
+ argv,
+ ifs,
+ read_flags,
+ opt_n,
+ opt_p,
+ opt_t,
+ opt_u
+ );
+
+ if ((uintptr_t)r == 1 && errno == EINTR) {
+ unsigned sig = check_and_run_traps();
+ if (sig && sig != SIGINT)
+ goto again;
+ }
+
+ if ((uintptr_t)r > 1) {
+ bb_error_msg("%s", r);
+ r = (char*)(uintptr_t)1;
+ }
+
+ return (uintptr_t)r;
+}
+
static int FAST_FUNC builtin_trap(char **argv)
{
int sig;
@@ -8557,10 +9261,28 @@ static int FAST_FUNC builtin_type(char **argv)
}
#if ENABLE_HUSH_JOB
+static struct pipe *parse_jobspec(const char *str)
+{
+ struct pipe *pi;
+ int jobnum;
+
+ if (sscanf(str, "%%%d", &jobnum) != 1) {
+ bb_error_msg("bad argument '%s'", str);
+ return NULL;
+ }
+ for (pi = G.job_list; pi; pi = pi->next) {
+ if (pi->jobid == jobnum) {
+ return pi;
+ }
+ }
+ bb_error_msg("%d: no such job", jobnum);
+ return NULL;
+}
+
/* built-in 'fg' and 'bg' handler */
static int FAST_FUNC builtin_fg_bg(char **argv)
{
- int i, jobnum;
+ int i;
struct pipe *pi;
if (!G_interactive_fd)
@@ -8576,17 +9298,10 @@ static int FAST_FUNC builtin_fg_bg(char **argv)
bb_error_msg("%s: no current job", argv[0]);
return EXIT_FAILURE;
}
- if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
- bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
+
+ pi = parse_jobspec(argv[1]);
+ if (!pi)
return EXIT_FAILURE;
- }
- for (pi = G.job_list; pi; pi = pi->next) {
- if (pi->jobid == jobnum) {
- goto found;
- }
- }
- bb_error_msg("%s: %d: no such job", argv[0], jobnum);
- return EXIT_FAILURE;
found:
/* TODO: bash prints a string representation
* of job being foregrounded (like "sleep 1 | cat") */
@@ -8631,7 +9346,6 @@ static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
if (x->b_descr)
printf("%-10s%s\n", x->b_cmd, x->b_descr);
}
- bb_putchar('\n');
return EXIT_SUCCESS;
}
#endif
@@ -8650,6 +9364,7 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
struct pipe *job;
const char *status_string;
+ checkjobs(NULL, 0 /*(no pid to wait for)*/);
for (job = G.job_list; job; job = job->next) {
if (job->alive_cmds == job->stopped_cmds)
status_string = "Stopped";
@@ -8681,6 +9396,15 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
if (l < (unsigned long)p) l = (unsigned long)p;
free(p);
+
+# if 0 /* debug */
+ {
+ struct mallinfo mi = mallinfo();
+ printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
+ mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
+ }
+# endif
+
if (!G.memleak_value)
G.memleak_value = l;
@@ -8702,175 +9426,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
return EXIT_SUCCESS;
}
-/* Interruptibility of read builtin in bash
- * (tested on bash-4.2.8 by sending signals (not by ^C)):
- *
- * Empty trap makes read ignore corresponding signal, for any signal.
- *
- * SIGINT:
- * - terminates non-interactive shell;
- * - interrupts read in interactive shell;
- * if it has non-empty trap:
- * - executes trap and returns to command prompt in interactive shell;
- * - executes trap and returns to read in non-interactive shell;
- * SIGTERM:
- * - is ignored (does not interrupt) read in interactive shell;
- * - terminates non-interactive shell;
- * if it has non-empty trap:
- * - executes trap and returns to read;
- * SIGHUP:
- * - terminates shell (regardless of interactivity);
- * if it has non-empty trap:
- * - executes trap and returns to read;
- */
-static int FAST_FUNC builtin_read(char **argv)
-{
- const char *r;
- char *opt_n = NULL;
- char *opt_p = NULL;
- char *opt_t = NULL;
- char *opt_u = NULL;
- const char *ifs;
- int read_flags;
-
- /* "!": do not abort on errors.
- * Option string must start with "sr" to match BUILTIN_READ_xxx
- */
- read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
- if (read_flags == (uint32_t)-1)
- return EXIT_FAILURE;
- argv += optind;
- ifs = get_local_var_value("IFS"); /* can be NULL */
-
- again:
- r = shell_builtin_read(set_local_var_from_halves,
- argv,
- ifs,
- read_flags,
- opt_n,
- opt_p,
- opt_t,
- opt_u
- );
-
- if ((uintptr_t)r == 1 && errno == EINTR) {
- unsigned sig = check_and_run_traps();
- if (sig && sig != SIGINT)
- goto again;
- }
-
- if ((uintptr_t)r > 1) {
- bb_error_msg("%s", r);
- r = (char*)(uintptr_t)1;
- }
-
- return (uintptr_t)r;
-}
-
-/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
- * built-in 'set' handler
- * SUSv3 says:
- * set [-abCefhmnuvx] [-o option] [argument...]
- * set [+abCefhmnuvx] [+o option] [argument...]
- * set -- [argument...]
- * set -o
- * set +o
- * Implementations shall support the options in both their hyphen and
- * plus-sign forms. These options can also be specified as options to sh.
- * Examples:
- * Write out all variables and their values: set
- * Set $1, $2, and $3 and set "$#" to 3: set c a b
- * Turn on the -x and -v options: set -xv
- * Unset all positional parameters: set --
- * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
- * Set the positional parameters to the expansion of x, even if x expands
- * with a leading '-' or '+': set -- $x
- *
- * So far, we only support "set -- [argument...]" and some of the short names.
- */
-static int FAST_FUNC builtin_set(char **argv)
-{
- int n;
- char **pp, **g_argv;
- char *arg = *++argv;
-
- if (arg == NULL) {
- struct variable *e;
- for (e = G.top_var; e; e = e->next)
- puts(e->varstr);
- return EXIT_SUCCESS;
- }
-
- do {
- if (strcmp(arg, "--") == 0) {
- ++argv;
- goto set_argv;
- }
- if (arg[0] != '+' && arg[0] != '-')
- break;
- for (n = 1; arg[n]; ++n) {
- if (set_mode((arg[0] == '-'), arg[n], argv[1]))
- goto error;
- if (arg[n] == 'o' && argv[1])
- argv++;
- }
- } while ((arg = *++argv) != NULL);
- /* Now argv[0] is 1st argument */
-
- if (arg == NULL)
- return EXIT_SUCCESS;
- set_argv:
-
- /* NB: G.global_argv[0] ($0) is never freed/changed */
- g_argv = G.global_argv;
- if (G.global_args_malloced) {
- pp = g_argv;
- while (*++pp)
- free(*pp);
- g_argv[1] = NULL;
- } else {
- G.global_args_malloced = 1;
- pp = xzalloc(sizeof(pp[0]) * 2);
- pp[0] = g_argv[0]; /* retain $0 */
- g_argv = pp;
- }
- /* This realloc's G.global_argv */
- G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
-
- n = 1;
- while (*++pp)
- n++;
- G.global_argc = n;
-
- return EXIT_SUCCESS;
-
- /* Nothing known, so abort */
- error:
- bb_error_msg("set: %s: invalid option", arg);
- return EXIT_FAILURE;
-}
-
-static int FAST_FUNC builtin_shift(char **argv)
-{
- int n = 1;
- argv = skip_dash_dash(argv);
- if (argv[0]) {
- n = atoi(argv[0]);
- }
- if (n >= 0 && n < G.global_argc) {
- if (G.global_args_malloced) {
- int m = 1;
- while (m <= n)
- free(G.global_argv[m++]);
- }
- G.global_argc -= n;
- memmove(&G.global_argv[1], &G.global_argv[n+1],
- G.global_argc * sizeof(G.global_argv[0]));
- return EXIT_SUCCESS;
- }
- return EXIT_FAILURE;
-}
-
static int FAST_FUNC builtin_source(char **argv)
{
char *arg_path, *filename;
@@ -8892,7 +9447,7 @@ static int FAST_FUNC builtin_source(char **argv)
if (arg_path)
filename = arg_path;
}
- input = fopen_or_warn(filename, "r");
+ input = remember_FILE(fopen_or_warn(filename, "r"));
free(arg_path);
if (!input) {
/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
@@ -8901,23 +9456,24 @@ static int FAST_FUNC builtin_source(char **argv)
*/
return EXIT_FAILURE;
}
- close_on_exec_on(fileno(input));
#if ENABLE_HUSH_FUNCTIONS
- sv_flg = G.flag_return_in_progress;
+ sv_flg = G_flag_return_in_progress;
/* "we are inside sourced file, ok to use return" */
- G.flag_return_in_progress = -1;
+ G_flag_return_in_progress = -1;
#endif
if (argv[1])
save_and_replace_G_args(&sv, argv);
+ /* "false; . ./empty_line; echo Zero:$?" should print 0 */
+ G.last_exitcode = 0;
parse_and_run_file(input);
- fclose(input);
+ fclose_and_forget(input);
if (argv[1])
restore_G_args(&sv, argv);
#if ENABLE_HUSH_FUNCTIONS
- G.flag_return_in_progress = sv_flg;
+ G_flag_return_in_progress = sv_flg;
#endif
return G.last_exitcode;
@@ -8928,24 +9484,29 @@ static int FAST_FUNC builtin_umask(char **argv)
int rc;
mode_t mask;
+ rc = 1;
mask = umask(0);
argv = skip_dash_dash(argv);
if (argv[0]) {
mode_t old_mask = mask;
- mask ^= 0777;
- rc = bb_parse_mode(argv[0], &mask);
- mask ^= 0777;
- if (rc == 0) {
+ /* numeric umasks are taken as-is */
+ /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
+ if (!isdigit(argv[0][0]))
+ mask ^= 0777;
+ mask = bb_parse_mode(argv[0], mask);
+ if (!isdigit(argv[0][0]))
+ mask ^= 0777;
+ if ((unsigned)mask > 0777) {
mask = old_mask;
/* bash messages:
* bash: umask: 'q': invalid symbolic mode operator
* bash: umask: 999: octal number out of range
*/
bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
+ rc = 0;
}
} else {
- rc = 1;
/* Mimic bash */
printf("%04o\n", (unsigned) mask);
/* fall through and restore mask which we set to 0 */
@@ -8955,47 +9516,83 @@ static int FAST_FUNC builtin_umask(char **argv)
return !rc; /* rc != 0 - success */
}
-/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
-static int FAST_FUNC builtin_unset(char **argv)
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
+#if !ENABLE_HUSH_JOB
+# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
+#endif
+static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
{
- int ret;
- unsigned opts;
+ int ret = 0;
+ for (;;) {
+ int sig;
+ sigset_t oldset;
- /* "!": do not abort on errors */
- /* "+": stop at 1st non-option */
- opts = getopt32(argv, "!+vf");
- if (opts == (unsigned)-1)
- return EXIT_FAILURE;
- if (opts == 3) {
- bb_error_msg("unset: -v and -f are exclusive");
- return EXIT_FAILURE;
- }
- argv += optind;
+ if (!sigisemptyset(&G.pending_set))
+ goto check_sig;
- ret = EXIT_SUCCESS;
- while (*argv) {
- if (!(opts & 2)) { /* not -f */
- if (unset_local_var(*argv)) {
- /* unset <nonexistent_var> doesn't fail.
- * Error is when one tries to unset RO var.
- * Message was printed by unset_local_var. */
- ret = EXIT_FAILURE;
- }
+ /* waitpid is not interruptible by SA_RESTARTed
+ * signals which we use. Thus, this ugly dance:
+ */
+
+ /* Make sure possible SIGCHLD is stored in kernel's
+ * pending signal mask before we call waitpid.
+ * Or else we may race with SIGCHLD, lose it,
+ * and get stuck in sigsuspend...
+ */
+ sigfillset(&oldset); /* block all signals, remember old set */
+ sigprocmask(SIG_SETMASK, &oldset, &oldset);
+
+ if (!sigisemptyset(&G.pending_set)) {
+ /* Crap! we raced with some signal! */
+ goto restore;
}
-#if ENABLE_HUSH_FUNCTIONS
- else {
- unset_func(*argv);
+
+ /*errno = 0; - checkjobs does this */
+/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
+ ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
+ debug_printf_exec("checkjobs:%d\n", ret);
+#if ENABLE_HUSH_JOB
+ if (waitfor_pipe) {
+ int rcode = job_exited_or_stopped(waitfor_pipe);
+ debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
+ if (rcode >= 0) {
+ ret = rcode;
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ break;
+ }
}
#endif
- argv++;
+ /* if ECHILD, there are no children (ret is -1 or 0) */
+ /* if ret == 0, no children changed state */
+ /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
+ if (errno == ECHILD || ret) {
+ ret--;
+ if (ret < 0) /* if ECHILD, may need to fix "ret" */
+ ret = 0;
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ break;
+ }
+ /* Wait for SIGCHLD or any other signal */
+ /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
+ /* Note: sigsuspend invokes signal handler */
+ sigsuspend(&oldset);
+ restore:
+ sigprocmask(SIG_SETMASK, &oldset, NULL);
+ check_sig:
+ /* So, did we get a signal? */
+ sig = check_and_run_traps();
+ if (sig /*&& sig != SIGCHLD - always true */) {
+ ret = 128 + sig;
+ break;
+ }
+ /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
}
return ret;
}
-/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
static int FAST_FUNC builtin_wait(char **argv)
{
- int ret = EXIT_SUCCESS;
+ int ret;
int status;
argv = skip_dash_dash(argv);
@@ -9016,77 +9613,64 @@ static int FAST_FUNC builtin_wait(char **argv)
* ^C <-- after ~4 sec from keyboard
* $
*/
- while (1) {
- int sig;
- sigset_t oldset, allsigs;
-
- /* waitpid is not interruptible by SA_RESTARTed
- * signals which we use. Thus, this ugly dance:
- */
-
- /* Make sure possible SIGCHLD is stored in kernel's
- * pending signal mask before we call waitpid.
- * Or else we may race with SIGCHLD, lose it,
- * and get stuck in sigwaitinfo...
- */
- sigfillset(&allsigs);
- sigprocmask(SIG_SETMASK, &allsigs, &oldset);
-
- if (!sigisemptyset(&G.pending_set)) {
- /* Crap! we raced with some signal! */
- // sig = 0;
- goto restore;
- }
-
- checkjobs(NULL); /* waitpid(WNOHANG) inside */
- if (errno == ECHILD) {
- sigprocmask(SIG_SETMASK, &oldset, NULL);
- break;
- }
-
- /* Wait for SIGCHLD or any other signal */
- //sig = sigwaitinfo(&allsigs, NULL);
- /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
- /* Note: sigsuspend invokes signal handler */
- sigsuspend(&oldset);
- restore:
- sigprocmask(SIG_SETMASK, &oldset, NULL);
-
- /* So, did we get a signal? */
- //if (sig > 0)
- // raise(sig); /* run handler */
- sig = check_and_run_traps();
- if (sig /*&& sig != SIGCHLD - always true */) {
- /* see note 2 */
- ret = 128 + sig;
- break;
- }
- /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
- }
- return ret;
+ return wait_for_child_or_signal(NULL, 0 /*(no job and no pid to wait for)*/);
}
- /* This is probably buggy wrt interruptible-ness */
- while (*argv) {
+ do {
pid_t pid = bb_strtou(*argv, NULL, 10);
- if (errno) {
+ if (errno || pid <= 0) {
+#if ENABLE_HUSH_JOB
+ if (argv[0][0] == '%') {
+ struct pipe *wait_pipe;
+ wait_pipe = parse_jobspec(*argv);
+ if (wait_pipe) {
+ ret = job_exited_or_stopped(wait_pipe);
+ if (ret < 0)
+ ret = wait_for_child_or_signal(wait_pipe, 0);
+ continue;
+ }
+ }
+#endif
/* mimic bash message */
bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
- return EXIT_FAILURE;
+ ret = EXIT_FAILURE;
+ continue; /* bash checks all argv[] */
}
- if (waitpid(pid, &status, 0) == pid) {
+
+ /* Do we have such child? */
+ ret = waitpid(pid, &status, WNOHANG);
+ if (ret < 0) {
+ /* No */
+ if (errno == ECHILD) {
+ if (G.last_bg_pid > 0 && pid == G.last_bg_pid) {
+ /* "wait $!" but last bg task has already exited. Try:
+ * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
+ * In bash it prints exitcode 0, then 3.
+ * In dash, it is 127.
+ */
+ /* ret = G.last_bg_pid_exitstatus - FIXME */
+ } else {
+ /* Example: "wait 1". mimic bash message */
+ bb_error_msg("wait: pid %d is not a child of this shell", (int)pid);
+ }
+ } else {
+ /* ??? */
+ bb_perror_msg("wait %s", *argv);
+ }
+ ret = 127;
+ continue; /* bash checks all argv[] */
+ }
+ if (ret == 0) {
+ /* Yes, and it still runs */
+ ret = wait_for_child_or_signal(NULL, pid);
+ } else {
+ /* Yes, and it just exited */
+ process_wait_result(NULL, pid, status);
+ ret = WEXITSTATUS(status);
if (WIFSIGNALED(status))
ret = 128 + WTERMSIG(status);
- else if (WIFEXITED(status))
- ret = WEXITSTATUS(status);
- else /* wtf? */
- ret = EXIT_FAILURE;
- } else {
- bb_perror_msg("wait %s", *argv);
- ret = 127;
}
- argv++;
- }
+ } while (*++argv);
return ret;
}
@@ -9111,9 +9695,11 @@ static int FAST_FUNC builtin_break(char **argv)
unsigned depth;
if (G.depth_of_loop == 0) {
bb_error_msg("%s: only meaningful in a loop", argv[0]);
+ /* if we came from builtin_continue(), need to undo "= 1" */
+ G.flag_break_continue = 0;
return EXIT_SUCCESS; /* bash compat */
}
- G.flag_break_continue++; /* BC_BREAK = 1 */
+ G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
if (depth == UINT_MAX)
@@ -9136,12 +9722,12 @@ static int FAST_FUNC builtin_return(char **argv)
{
int rc;
- if (G.flag_return_in_progress != -1) {
+ if (G_flag_return_in_progress != -1) {
bb_error_msg("%s: not in a function or sourced script", argv[0]);
return EXIT_FAILURE; /* bash compat */
}
- G.flag_return_in_progress = 1;
+ G_flag_return_in_progress = 1;
/* bash:
* out of range: wraps around at 256, does not error out
diff --git a/shell/hush_test/hush-glob/bash_brace1.tests b/shell/hush_test/hush-glob/bash_brace1.tests
index eb2f0e9..eae2e2a 100755
--- a/shell/hush_test/hush-glob/bash_brace1.tests
+++ b/shell/hush_test/hush-glob/bash_brace1.tests
@@ -4,7 +4,7 @@ v='*brace1.t*'; echo $v
# ...but not brace expanded:
v='*{b,b}race1.t*'; echo $v
-# whereas direct brces are expanded:
+# whereas direct braces are expanded:
echo *{b,b}race1.t*
echo Done: $?
diff --git a/shell/hush_test/hush-glob/glob3.right b/shell/hush_test/hush-glob/glob3.right
new file mode 100644
index 0000000..161b589
--- a/dev/null
+++ b/shell/hush_test/hush-glob/glob3.right
@@ -0,0 +1,2 @@
+glob3.tests
+./glob3.tests
diff --git a/shell/hush_test/hush-glob/glob3.tests b/shell/hush_test/hush-glob/glob3.tests
new file mode 100755
index 0000000..bdf5400
--- a/dev/null
+++ b/shell/hush_test/hush-glob/glob3.tests
@@ -0,0 +1,2 @@
+echo "glob3.test"*
+echo "./glob3.test"*
diff --git a/shell/hush_test/hush-glob/glob_dir.right b/shell/hush_test/hush-glob/glob_dir.right
new file mode 100644
index 0000000..aa90514
--- a/dev/null
+++ b/shell/hush_test/hush-glob/glob_dir.right
@@ -0,0 +1,19 @@
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+dirtest/z.tmp
+
+*/z.tmp
+*/z.*
+*/?.*
+*/z*p
+d*r*e*t/z*p
+*\/z.tmp
+*/z.*
+*/z*p
+d*r*e*t/z*p
diff --git a/shell/hush_test/hush-glob/glob_dir.tests b/shell/hush_test/hush-glob/glob_dir.tests
new file mode 100755
index 0000000..dc4c4fd
--- a/dev/null
+++ b/shell/hush_test/hush-glob/glob_dir.tests
@@ -0,0 +1,25 @@
+mkdir dirtest
+ >dirtest/z.tmp
+
+echo */z.tmp
+echo */z.*
+echo */?.*
+echo */z*p
+echo d*r*e*t/z*p
+echo *"/z.t"mp
+echo */z"."*
+echo *"/z"*"p"
+echo "d"*r*e*t"/"z*p
+echo
+echo \*/z.tmp
+echo "*"/z.*
+echo */"?".*
+echo */z"*p"
+echo d*r*e\*t/z*p
+echo *"\\/z.t"mp
+echo */z".*"
+echo *"/z"\*"p"
+echo "d*"r*e*t"/"z*p
+
+rm dirtest/z.tmp
+rmdir dirtest
diff --git a/shell/hush_test/hush-misc/heredoc1.right b/shell/hush_test/hush-heredoc/heredoc1.right
index 7fc68f3..7fc68f3 100644
--- a/shell/hush_test/hush-misc/heredoc1.right
+++ b/shell/hush_test/hush-heredoc/heredoc1.right
diff --git a/shell/hush_test/hush-misc/heredoc1.tests b/shell/hush_test/hush-heredoc/heredoc1.tests
index 2eeb472..2eeb472 100755
--- a/shell/hush_test/hush-misc/heredoc1.tests
+++ b/shell/hush_test/hush-heredoc/heredoc1.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc2.right b/shell/hush_test/hush-heredoc/heredoc2.right
new file mode 100644
index 0000000..a486f1a
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc2.right
@@ -0,0 +1,2 @@
+bar
+bar
diff --git a/shell/hush_test/hush-heredoc/heredoc2.tests b/shell/hush_test/hush-heredoc/heredoc2.tests
new file mode 100755
index 0000000..6d9ccb6
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc2.tests
@@ -0,0 +1,7 @@
+foo () {
+cat <<EOF && { echo "$1" ; }
+$1
+EOF
+}
+
+foo "bar"
diff --git a/shell/hush_test/hush-misc/heredoc3.right b/shell/hush_test/hush-heredoc/heredoc3.right
index 6ed517f..6ed517f 100644
--- a/shell/hush_test/hush-misc/heredoc3.right
+++ b/shell/hush_test/hush-heredoc/heredoc3.right
diff --git a/shell/hush_test/hush-misc/heredoc3.tests b/shell/hush_test/hush-heredoc/heredoc3.tests
index 938577a..938577a 100755
--- a/shell/hush_test/hush-misc/heredoc3.tests
+++ b/shell/hush_test/hush-heredoc/heredoc3.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc4.right b/shell/hush_test/hush-heredoc/heredoc4.right
new file mode 100644
index 0000000..371b092
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc4.right
@@ -0,0 +1 @@
+'$'
diff --git a/shell/hush_test/hush-heredoc/heredoc4.tests b/shell/hush_test/hush-heredoc/heredoc4.tests
new file mode 100755
index 0000000..642ddb3
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc4.tests
@@ -0,0 +1,3 @@
+cat <<EOF
+'$'
+EOF
diff --git a/shell/hush_test/hush-misc/heredoc2.right b/shell/hush_test/hush-heredoc/heredoc5.right
index 74110e3..74110e3 100644
--- a/shell/hush_test/hush-misc/heredoc2.right
+++ b/shell/hush_test/hush-heredoc/heredoc5.right
diff --git a/shell/hush_test/hush-misc/heredoc2.tests b/shell/hush_test/hush-heredoc/heredoc5.tests
index e619bde..e619bde 100755
--- a/shell/hush_test/hush-misc/heredoc2.tests
+++ b/shell/hush_test/hush-heredoc/heredoc5.tests
diff --git a/shell/hush_test/hush-heredoc/heredoc6.right b/shell/hush_test/hush-heredoc/heredoc6.right
new file mode 100644
index 0000000..5d0f077
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc6.right
@@ -0,0 +1,2 @@
+test
+OK:0
diff --git a/shell/hush_test/hush-heredoc/heredoc6.tests b/shell/hush_test/hush-heredoc/heredoc6.tests
new file mode 100755
index 0000000..346f594
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc6.tests
@@ -0,0 +1,4 @@
+eval 'cat <<- NOT
+test
+NOT'
+echo OK:$?
diff --git a/shell/hush_test/hush-heredoc/heredoc7.right b/shell/hush_test/hush-heredoc/heredoc7.right
new file mode 100644
index 0000000..5d9c6c6
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc7.right
@@ -0,0 +1 @@
+_ASBOX
diff --git a/shell/hush_test/hush-heredoc/heredoc7.tests b/shell/hush_test/hush-heredoc/heredoc7.tests
new file mode 100755
index 0000000..abd5941
--- a/dev/null
+++ b/shell/hush_test/hush-heredoc/heredoc7.tests
@@ -0,0 +1,3 @@
+cat << _ACEOF
+_ASBOX
+_ACEOF
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.right b/shell/hush_test/hush-heredoc/heredoc_backslash1.right
index 6a61148..6a61148 100644
--- a/shell/hush_test/hush-misc/heredoc_backslash1.right
+++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.right
diff --git a/shell/hush_test/hush-misc/heredoc_backslash1.tests b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests
index 501af54..501af54 100755
--- a/shell/hush_test/hush-misc/heredoc_backslash1.tests
+++ b/shell/hush_test/hush-heredoc/heredoc_backslash1.tests
diff --git a/shell/hush_test/hush-misc/heredoc_huge.right b/shell/hush_test/hush-heredoc/heredoc_huge.right
index 11740f6..11740f6 100644
--- a/shell/hush_test/hush-misc/heredoc_huge.right
+++ b/shell/hush_test/hush-heredoc/heredoc_huge.right
diff --git a/shell/hush_test/hush-misc/heredoc_huge.tests b/shell/hush_test/hush-heredoc/heredoc_huge.tests
index c2ec281..c2ec281 100755
--- a/shell/hush_test/hush-misc/heredoc_huge.tests
+++ b/shell/hush_test/hush-heredoc/heredoc_huge.tests
diff --git a/shell/hush_test/hush-invert/invert.right b/shell/hush_test/hush-invert/invert.right
new file mode 100644
index 0000000..5a9239a
--- a/dev/null
+++ b/shell/hush_test/hush-invert/invert.right
@@ -0,0 +1,10 @@
+1
+1
+1
+0
+0
+1
+0
+1
+0
+1
diff --git a/shell/hush_test/hush-invert/invert.tests b/shell/hush_test/hush-invert/invert.tests
new file mode 100755
index 0000000..8393d95
--- a/dev/null
+++ b/shell/hush_test/hush-invert/invert.tests
@@ -0,0 +1,19 @@
+# tests of return value inversion
+# placeholder for future expansion
+
+# user subshells (...) did this wrong in bash versions before 2.04
+
+! ( echo hello | grep h >/dev/null 2>&1 ); echo $?
+! echo hello | grep h >/dev/null 2>&1 ; echo $?
+
+! true ; echo $?
+! false; echo $?
+
+! (false) ; echo $?
+! (true); echo $?
+
+! true | false ; echo $?
+! false | true ; echo $?
+
+! (true | false) ; echo $?
+! (false | true) ; echo $?
diff --git a/shell/hush_test/hush-misc/assignment2.right b/shell/hush_test/hush-misc/assignment2.right
new file mode 100644
index 0000000..9d1860b
--- a/dev/null
+++ b/shell/hush_test/hush-misc/assignment2.right
@@ -0,0 +1,2 @@
+hush: can't execute 'a=b': No such file or directory
+127
diff --git a/shell/hush_test/hush-misc/assignment2.rigth b/shell/hush_test/hush-misc/assignment2.rigth
deleted file mode 100644
index 591552c..0000000
--- a/shell/hush_test/hush-misc/assignment2.rigth
+++ b/dev/null
@@ -1,2 +0,0 @@
-hush: can't exec 'a=b': No such file or directory
-1
diff --git a/shell/hush_test/hush-misc/assignment2.tests b/shell/hush_test/hush-misc/assignment2.tests
index 540e01e..f693843 100755
--- a/shell/hush_test/hush-misc/assignment2.tests
+++ b/shell/hush_test/hush-misc/assignment2.tests
@@ -1,4 +1,3 @@
# This must not be interpreted as an assignment
a''=b true
echo $?
-# (buglet: $? should be 127. it is currently 1)
diff --git a/shell/hush_test/hush-misc/eval1.right b/shell/hush_test/hush-misc/eval1.right
new file mode 100644
index 0000000..7b24a35
--- a/dev/null
+++ b/shell/hush_test/hush-misc/eval1.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/shell/hush_test/hush-misc/eval1.tests b/shell/hush_test/hush-misc/eval1.tests
new file mode 100755
index 0000000..b78c6cc
--- a/dev/null
+++ b/shell/hush_test/hush-misc/eval1.tests
@@ -0,0 +1,4 @@
+# empty eval nevertheless sets $? = 0
+false
+eval
+echo Ok:$?
diff --git a/shell/hush_test/hush-misc/eval2.right b/shell/hush_test/hush-misc/eval2.right
new file mode 100644
index 0000000..a7ce6cc
--- a/dev/null
+++ b/shell/hush_test/hush-misc/eval2.right
@@ -0,0 +1,3 @@
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/hush_test/hush-misc/eval2.tests b/shell/hush_test/hush-misc/eval2.tests
new file mode 100755
index 0000000..6bfb87a
--- a/dev/null
+++ b/shell/hush_test/hush-misc/eval2.tests
@@ -0,0 +1,4 @@
+false; eval; echo Zero:$?
+false; eval ""; echo Zero:$?
+false; eval "
+"; echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode1.right b/shell/hush_test/hush-misc/exitcode1.right
new file mode 100644
index 0000000..e5fefef
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.right
@@ -0,0 +1,2 @@
+One:1
+Zero:0
diff --git a/shell/hush_test/hush-misc/exitcode1.tests b/shell/hush_test/hush-misc/exitcode1.tests
new file mode 100755
index 0000000..dc8619d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode1.tests
@@ -0,0 +1,2 @@
+false || case a in a) echo One:$?;; esac
+echo Zero:$?
diff --git a/shell/hush_test/hush-misc/exitcode2.right b/shell/hush_test/hush-misc/exitcode2.right
new file mode 100644
index 0000000..0a57b9b
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode2.right
@@ -0,0 +1,4 @@
+hush: syntax error: unexpected )
+Done:2
+hush: can't open 'does_not_exist': No such file or directory
+Done:1
diff --git a/shell/hush_test/hush-misc/exitcode2.tests b/shell/hush_test/hush-misc/exitcode2.tests
new file mode 100755
index 0000000..79a6ebd
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode2.tests
@@ -0,0 +1,12 @@
+# syntax error should return status 2
+cat >test.sh <<EOF
+)
+EOF
+chmod +x test.sh
+$THIS_SH ./test.sh
+echo Done:$?
+rm -f test.sh
+
+# redirection error with special builtin should return status 1
+(eval cat <does_not_exist)
+echo Done:$?
diff --git a/shell/hush_test/hush-misc/exitcode_EACCES.right b/shell/hush_test/hush-misc/exitcode_EACCES.right
new file mode 100644
index 0000000..a80d551
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode_EACCES.right
@@ -0,0 +1,2 @@
+hush: can't execute './': Permission denied
+126
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.tests b/shell/hush_test/hush-misc/exitcode_EACCES.tests
index 26b5c61..26b5c61 100755
--- a/shell/msh_test/msh-execution/exitcode_EACCES.tests
+++ b/shell/hush_test/hush-misc/exitcode_EACCES.tests
diff --git a/shell/hush_test/hush-misc/exitcode_ENOENT.right b/shell/hush_test/hush-misc/exitcode_ENOENT.right
new file mode 100644
index 0000000..d831550
--- a/dev/null
+++ b/shell/hush_test/hush-misc/exitcode_ENOENT.right
@@ -0,0 +1,2 @@
+hush: can't execute './does_not_exist_for_sure': No such file or directory
+127
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.tests b/shell/hush_test/hush-misc/exitcode_ENOENT.tests
index 7f1b88a..7f1b88a 100755
--- a/shell/msh_test/msh-execution/exitcode_ENOENT.tests
+++ b/shell/hush_test/hush-misc/exitcode_ENOENT.tests
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/hush_test/hush-misc/for.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/hush_test/hush-misc/for.right
diff --git a/shell/hush_test/hush-misc/for.tests b/shell/hush_test/hush-misc/for.tests
new file mode 100755
index 0000000..4889a9f
--- a/dev/null
+++ b/shell/hush_test/hush-misc/for.tests
@@ -0,0 +1,5 @@
+for i
+in OK
+do
+ echo $i
+done
diff --git a/shell/hush_test/hush-misc/func6.right b/shell/hush_test/hush-misc/func6.right
new file mode 100644
index 0000000..0ebd8e5
--- a/dev/null
+++ b/shell/hush_test/hush-misc/func6.right
@@ -0,0 +1,2 @@
+Two:2
+Two:2
diff --git a/shell/hush_test/hush-misc/func6.tests b/shell/hush_test/hush-misc/func6.tests
new file mode 100755
index 0000000..029c3e8
--- a/dev/null
+++ b/shell/hush_test/hush-misc/func6.tests
@@ -0,0 +1,11 @@
+f1() {
+ while return 2; do :; done
+}
+f1
+echo Two:$?
+
+f2() {
+ while :; do return 2; done
+}
+f2
+echo Two:$?
diff --git a/shell/hush_test/hush-misc/func_args1.tests b/shell/hush_test/hush-misc/func_args1.tests
index 157921f..d394c63 100755
--- a/shell/hush_test/hush-misc/func_args1.tests
+++ b/shell/hush_test/hush-misc/func_args1.tests
@@ -1,5 +1,3 @@
-# UNFIXED BUG
-
f() { echo "'f $1 $2 $3' called"; }
set -- a b c
diff --git a/shell/hush_test/hush-misc/group_in_braces.right b/shell/hush_test/hush-misc/group_in_braces.right
new file mode 100644
index 0000000..a706449
--- a/dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.right
@@ -0,0 +1,5 @@
+Zero:0
+Zero:0
+Zero:0
+Zero:0
+Zero:0
diff --git a/shell/hush_test/hush-misc/group_in_braces.tests b/shell/hush_test/hush-misc/group_in_braces.tests
new file mode 100755
index 0000000..f6571c3
--- a/dev/null
+++ b/shell/hush_test/hush-misc/group_in_braces.tests
@@ -0,0 +1,11 @@
+# Test cases where { cmd } does not require semicolon after "cmd"
+(exit 2); { { true; } }
+echo Zero:$?
+(exit 2); {(true)}
+echo Zero:$?
+(exit 2); { true | { true; } }
+echo Zero:$?
+(exit 2); { while false; do :; done }
+echo Zero:$?
+(exit 2); { case a in b) ;; esac }
+echo Zero:$?
diff --git a/shell/hush_test/hush-misc/last_amp.right b/shell/hush_test/hush-misc/last_amp.right
new file mode 100644
index 0000000..3da21ae
--- a/dev/null
+++ b/shell/hush_test/hush-misc/last_amp.right
@@ -0,0 +1,2 @@
+3
+End
diff --git a/shell/hush_test/hush-misc/last_amp.tests b/shell/hush_test/hush-misc/last_amp.tests
new file mode 100755
index 0000000..1609376
--- a/dev/null
+++ b/shell/hush_test/hush-misc/last_amp.tests
@@ -0,0 +1,8 @@
+$THIS_SH -c 'echo 3&'
+d=`date`
+while test "`date`" = "$d"; do true; done
+d1=`date`
+$THIS_SH -c 'sleep 1&'
+d2=`date`
+test "$d1" = "$d2" || echo BAD
+echo End
diff --git a/shell/hush_test/hush-misc/local1.right b/shell/hush_test/hush-misc/local1.right
new file mode 100644
index 0000000..a2d121d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/local1.right
@@ -0,0 +1,4 @@
+A1:'A'
+A2:''
+A3:''
+A4:'A'
diff --git a/shell/hush_test/hush-misc/local1.tests b/shell/hush_test/hush-misc/local1.tests
new file mode 100755
index 0000000..b1e6750
--- a/dev/null
+++ b/shell/hush_test/hush-misc/local1.tests
@@ -0,0 +1,11 @@
+a=A
+f() {
+ local a
+ # the above line unsets $a
+ echo "A2:'$a'"
+ unset a
+ echo "A3:'$a'"
+}
+echo "A1:'$a'"
+f
+echo "A4:'$a'"
diff --git a/shell/hush_test/hush-misc/nommu3.right b/shell/hush_test/hush-misc/nommu3.right
new file mode 100644
index 0000000..da1534b
--- a/dev/null
+++ b/shell/hush_test/hush-misc/nommu3.right
@@ -0,0 +1,2 @@
+Ok
+0
diff --git a/shell/hush_test/hush-misc/nommu3.tests b/shell/hush_test/hush-misc/nommu3.tests
new file mode 100755
index 0000000..ac82a6a
--- a/dev/null
+++ b/shell/hush_test/hush-misc/nommu3.tests
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+func()
+{
+ while read p; do echo "$p"; done
+}
+
+pipe_to_func()
+{
+ # We had a NOMMU bug which caused "echo Ok |" part to be lost
+ echo Ok | func
+}
+
+pipe_to_func | cat
+echo $?
diff --git a/shell/hush_test/hush-misc/nulltick1.right b/shell/hush_test/hush-misc/nulltick1.right
new file mode 100644
index 0000000..f90b820
--- a/dev/null
+++ b/shell/hush_test/hush-misc/nulltick1.right
@@ -0,0 +1,3 @@
+Test 1
+Test 2
+Done
diff --git a/shell/hush_test/hush-misc/nulltick1.tests b/shell/hush_test/hush-misc/nulltick1.tests
new file mode 100755
index 0000000..f81923d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/nulltick1.tests
@@ -0,0 +1,3 @@
+echo Test ` ` 1
+echo Test `</dev/null` 2
+echo Done
diff --git a/shell/hush_test/hush-misc/source1.right b/shell/hush_test/hush-misc/source1.right
index d425603..0ab7c54 100644
--- a/shell/hush_test/hush-misc/source1.right
+++ b/shell/hush_test/hush-misc/source1.right
@@ -1,5 +1,2 @@
-hush: syntax error: unterminated ${name}
-line2
-Ok1:0
-hush: syntax error: unterminated '
-Ok2:1
+Sourced ok
+Done
diff --git a/shell/hush_test/hush-misc/source1.tests b/shell/hush_test/hush-misc/source1.tests
index c138883..e2e75b2 100755
--- a/shell/hush_test/hush-misc/source1.tests
+++ b/shell/hush_test/hush-misc/source1.tests
@@ -1,10 +1,5 @@
-echo 'echo ${^}
-echo line2' >sourced1
-. ./sourced1
-echo Ok1:$?
-
-echo "echo '" >sourced1
-. ./sourced1
-echo Ok2:$?
-
-rm sourced1
+echo "echo Sourced ok" >../sourced.sh
+PATH="..:$PATH"
+. sourced.sh
+rm ../sourced.sh
+echo Done
diff --git a/shell/hush_test/hush-misc/source2.right b/shell/hush_test/hush-misc/source2.right
index 0587bad..ce7171c 100644
--- a/shell/hush_test/hush-misc/source2.right
+++ b/shell/hush_test/hush-misc/source2.right
@@ -1,4 +1 @@
-0:arg0 1:arg1 2:arg2
-Ok1:0
-0:arg0 1:q 2:w
-Ok2:0
+Done: 0
diff --git a/shell/hush_test/hush-misc/source2.tests b/shell/hush_test/hush-misc/source2.tests
index 40b6b83..1870cdf 100755
--- a/shell/hush_test/hush-misc/source2.tests
+++ b/shell/hush_test/hush-misc/source2.tests
@@ -1,8 +1,3 @@
-echo 'echo "0:$0 1:$1 2:$2"' >sourced1
-set -- 1 2 3
-"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
-echo Ok1:$?
-"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
-echo Ok2:$?
-
-rm sourced1
+false
+. /dev/null
+echo Done: $?
diff --git a/shell/hush_test/hush-misc/source3.right b/shell/hush_test/hush-misc/source3.right
new file mode 100644
index 0000000..bdf9001
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source3.right
@@ -0,0 +1,2 @@
+Zero:0
+Zero:0
diff --git a/shell/hush_test/hush-misc/source3.tests b/shell/hush_test/hush-misc/source3.tests
new file mode 100755
index 0000000..1abf156
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source3.tests
@@ -0,0 +1,6 @@
+# Test both empty file, and one-empty-line file
+echo >sourced1
+true >sourced2
+false; . ./sourced1; echo Zero:$?
+false; . ./sourced2; echo Zero:$?
+rm sourced1 sourced2
diff --git a/shell/hush_test/hush-misc/source4.right b/shell/hush_test/hush-misc/source4.right
new file mode 100644
index 0000000..d425603
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source4.right
@@ -0,0 +1,5 @@
+hush: syntax error: unterminated ${name}
+line2
+Ok1:0
+hush: syntax error: unterminated '
+Ok2:1
diff --git a/shell/hush_test/hush-misc/source4.tests b/shell/hush_test/hush-misc/source4.tests
new file mode 100755
index 0000000..c138883
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source4.tests
@@ -0,0 +1,10 @@
+echo 'echo ${^}
+echo line2' >sourced1
+. ./sourced1
+echo Ok1:$?
+
+echo "echo '" >sourced1
+. ./sourced1
+echo Ok2:$?
+
+rm sourced1
diff --git a/shell/hush_test/hush-misc/source5.right b/shell/hush_test/hush-misc/source5.right
new file mode 100644
index 0000000..0587bad
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source5.right
@@ -0,0 +1,4 @@
+0:arg0 1:arg1 2:arg2
+Ok1:0
+0:arg0 1:q 2:w
+Ok2:0
diff --git a/shell/hush_test/hush-misc/source5.tests b/shell/hush_test/hush-misc/source5.tests
new file mode 100755
index 0000000..40b6b83
--- a/dev/null
+++ b/shell/hush_test/hush-misc/source5.tests
@@ -0,0 +1,8 @@
+echo 'echo "0:$0 1:$1 2:$2"' >sourced1
+set -- 1 2 3
+"$THIS_SH" -c '. ./sourced1' arg0 arg1 arg2
+echo Ok1:$?
+"$THIS_SH" -c '. ./sourced1 q w e' arg0 arg1 arg2
+echo Ok2:$?
+
+rm sourced1
diff --git a/shell/hush_test/hush-misc/unicode1.right b/shell/hush_test/hush-misc/unicode1.right
new file mode 100644
index 0000000..d3bbbf6
--- a/dev/null
+++ b/shell/hush_test/hush-misc/unicode1.right
@@ -0,0 +1,3 @@
+1
+1
+Ok
diff --git a/shell/hush_test/hush-misc/unicode1.tests b/shell/hush_test/hush-misc/unicode1.tests
new file mode 100755
index 0000000..8788ba9
--- a/dev/null
+++ b/shell/hush_test/hush-misc/unicode1.tests
@@ -0,0 +1,13 @@
+LANG=en_US.UTF-8
+
+# A combining character U+300
+a=`printf "\xcc\x80"`
+# Should print 1
+echo ${#a}
+
+# A Japanese katakana charachter U+30a3
+a=`printf "\xe3\x82\xa3"`
+# Should print 1
+echo ${#a}
+
+echo Ok
diff --git a/shell/hush_test/hush-misc/wait1.right b/shell/hush_test/hush-misc/wait1.right
new file mode 100644
index 0000000..20f514d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait1.right
@@ -0,0 +1,2 @@
+0
+[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
new file mode 100755
index 0000000..f9cf6d4
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -0,0 +1,3 @@
+sleep 2 & sleep 1 & wait $!
+echo $?
+jobs
diff --git a/shell/hush_test/hush-misc/wait2.right b/shell/hush_test/hush-misc/wait2.right
new file mode 100644
index 0000000..f018c2c
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait2.right
@@ -0,0 +1,2 @@
+0
+[1] Running sleep 3
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
new file mode 100755
index 0000000..be20f95
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -0,0 +1,4 @@
+sleep 3 & sleep 2 & sleep 1
+wait $!
+echo $?
+jobs
diff --git a/shell/hush_test/hush-misc/wait3.right b/shell/hush_test/hush-misc/wait3.right
new file mode 100644
index 0000000..5437b1d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait3.right
@@ -0,0 +1,2 @@
+3
+[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
new file mode 100755
index 0000000..ac541c3
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -0,0 +1,3 @@
+sleep 2 & (sleep 1;exit 3) & wait $!
+echo $?
+jobs
diff --git a/shell/hush_test/hush-misc/wait4.right b/shell/hush_test/hush-misc/wait4.right
new file mode 100644
index 0000000..f7987db
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait4.right
@@ -0,0 +1 @@
+Three:3
diff --git a/shell/hush_test/hush-misc/wait4.tests b/shell/hush_test/hush-misc/wait4.tests
new file mode 100755
index 0000000..cc34059
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait4.tests
@@ -0,0 +1,2 @@
+sleep 1 | (sleep 1;exit 3) & wait %1
+echo Three:$?
diff --git a/shell/hush_test/hush-misc/wait5.right b/shell/hush_test/hush-misc/wait5.right
new file mode 100644
index 0000000..82c9d56
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait5.right
@@ -0,0 +1,2 @@
+Zero:0
+Three:3
diff --git a/shell/hush_test/hush-misc/wait5.tests b/shell/hush_test/hush-misc/wait5.tests
new file mode 100755
index 0000000..1b4762d
--- a/dev/null
+++ b/shell/hush_test/hush-misc/wait5.tests
@@ -0,0 +1,5 @@
+sleep 0 | (sleep 0;exit 3) &
+sleep 1
+echo Zero:$?
+wait %1
+echo Three:$?
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right
new file mode 100644
index 0000000..b212c24
--- a/dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.right
@@ -0,0 +1,10 @@
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
diff --git a/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests
new file mode 100755
index 0000000..74dca1c
--- a/dev/null
+++ b/shell/hush_test/hush-quoting/dollar_repl_slash_bash1.tests
@@ -0,0 +1,21 @@
+# The bug here was triggered by:
+# * performing pathname expansion because we see [
+# * replace operator did not escape \ in replace string
+
+IP=192.168.0.1
+
+rm -f '192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}[" # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+echo >'192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}[" # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+rm -f '192.168.0.1['
diff --git a/shell/hush_test/hush-read/read_r.tests b/shell/hush_test/hush-read/read_r.tests
index 2c4cc61..1f0a18a 100755
--- a/shell/hush_test/hush-read/read_r.tests
+++ b/shell/hush_test/hush-read/read_r.tests
@@ -1,2 +1,4 @@
-echo -e 'test\\\nbest' | (read reply; echo "$reply")
-echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
+echo 'test\
+best' | (read reply; echo "$reply")
+echo 'test\
+best' | (read -r reply; echo "$reply")
diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/hush_test/hush-redir/redir1.right
index 15515d1..15515d1 100644
--- a/shell/hush_test/hush-misc/redir1.right
+++ b/shell/hush_test/hush-redir/redir1.right
diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/hush_test/hush-redir/redir1.tests
index ef2fbfb..ef2fbfb 100755
--- a/shell/hush_test/hush-misc/redir1.tests
+++ b/shell/hush_test/hush-redir/redir1.tests
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/hush_test/hush-redir/redir2.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/hush_test/hush-redir/redir2.right
diff --git a/shell/hush_test/hush-redir/redir2.tests b/shell/hush_test/hush-redir/redir2.tests
new file mode 100755
index 0000000..61ccea3
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir2.tests
@@ -0,0 +1,5 @@
+# ash once couldn't redirect above fd#9
+exec 1>/dev/null
+(echo LOST1 >&22) 22>&1
+(echo LOST2 >&22) 22>&1
+(echo OK >&22) 22>&2
diff --git a/shell/hush_test/hush-misc/redir3.right b/shell/hush_test/hush-redir/redir3.right
index 3d20bbf..3d20bbf 100644
--- a/shell/hush_test/hush-misc/redir3.right
+++ b/shell/hush_test/hush-redir/redir3.right
diff --git a/shell/hush_test/hush-misc/redir3.tests b/shell/hush_test/hush-redir/redir3.tests
index 7c28e43..7c28e43 100755
--- a/shell/hush_test/hush-misc/redir3.tests
+++ b/shell/hush_test/hush-redir/redir3.tests
diff --git a/shell/msh_test/msh-execution/many_continues.right b/shell/hush_test/hush-redir/redir4.right
index d86bac9..d86bac9 100644
--- a/shell/msh_test/msh-execution/many_continues.right
+++ b/shell/hush_test/hush-redir/redir4.right
diff --git a/shell/hush_test/hush-redir/redir4.tests b/shell/hush_test/hush-redir/redir4.tests
new file mode 100755
index 0000000..4bdf5ae
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir4.tests
@@ -0,0 +1,72 @@
+# ash uses fd 10 (usually) for reading the script
+exec 13>&-
+exec 12>&-
+exec 11>&-
+exec 10>&-
+# some amount of input is prefetched.
+# make sure final echo is far enough to not be prefetched.
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+echo "OK"
diff --git a/shell/hush_test/hush-misc/redir5.right b/shell/hush_test/hush-redir/redir5.right
index 52cce4f..52cce4f 100644
--- a/shell/hush_test/hush-misc/redir5.right
+++ b/shell/hush_test/hush-redir/redir5.right
diff --git a/shell/hush_test/hush-misc/redir5.tests b/shell/hush_test/hush-redir/redir5.tests
index 957f9c8..957f9c8 100755
--- a/shell/hush_test/hush-misc/redir5.tests
+++ b/shell/hush_test/hush-redir/redir5.tests
diff --git a/shell/hush_test/hush-redir/redir6.right b/shell/hush_test/hush-redir/redir6.right
new file mode 100644
index 0000000..ed754df
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir6.right
@@ -0,0 +1,2 @@
+Hello
+OK
diff --git a/shell/hush_test/hush-redir/redir6.tests b/shell/hush_test/hush-redir/redir6.tests
new file mode 100755
index 0000000..33b6d4c
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir6.tests
@@ -0,0 +1,3 @@
+# we had a bug where this would hang
+(head -n 1 <redir6.right)
+echo OK
diff --git a/shell/hush_test/hush-redir/redir7.right b/shell/hush_test/hush-redir/redir7.right
new file mode 100644
index 0000000..6430b02
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir7.right
@@ -0,0 +1,3 @@
+Ok
+Ok
+Done
diff --git a/shell/hush_test/hush-redir/redir7.tests b/shell/hush_test/hush-redir/redir7.tests
new file mode 100755
index 0000000..e873a46
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir7.tests
@@ -0,0 +1,12 @@
+# Chars above 0x7f are used as special codes.
+# 0x81 is CTLESC (see ash.c).
+# The bug was that quoting and unquoting of them
+# was out of sync for redirect filenames.
+
+>unicode.sh
+printf 'echo Ok >uni\x81code\n' >>unicode.sh
+printf 'cat uni\x81code\n' >>unicode.sh
+printf 'cat uni?code\n' >>unicode.sh
+. ./unicode.sh
+rm uni*code*
+echo Done
diff --git a/shell/hush_test/hush-redir/redir8.right b/shell/hush_test/hush-redir/redir8.right
new file mode 100644
index 0000000..6430b02
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir8.right
@@ -0,0 +1,3 @@
+Ok
+Ok
+Done
diff --git a/shell/hush_test/hush-redir/redir8.tests b/shell/hush_test/hush-redir/redir8.tests
new file mode 100755
index 0000000..2bd3867
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir8.tests
@@ -0,0 +1,15 @@
+# Chars above 0x7f are used as special codes.
+# 0x81 is CTLESC (see ash.c).
+# The bug was that quoting and unquoting of them
+# was out of sync for redirect filenames.
+
+# Subcase when redirect filename is specified in a variable.
+
+>unicode.sh
+printf 'v=uni\x81code\n' >>unicode.sh
+printf 'echo Ok >"$v"\n' >>unicode.sh
+printf 'cat uni\x81code\n' >>unicode.sh
+printf 'cat uni?code\n' >>unicode.sh
+. ./unicode.sh
+rm uni*code*
+echo Done
diff --git a/shell/hush_test/hush-redir/redir9.right b/shell/hush_test/hush-redir/redir9.right
new file mode 100644
index 0000000..34c2512
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir9.right
@@ -0,0 +1,2 @@
+Ok
+Done:0
diff --git a/shell/hush_test/hush-redir/redir9.tests b/shell/hush_test/hush-redir/redir9.tests
new file mode 100755
index 0000000..8befa61
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir9.tests
@@ -0,0 +1,4 @@
+echo Ok >file.tmp
+cat 0<>file.tmp
+echo Done:$?
+rm file.tmp
diff --git a/shell/hush_test/hush-redir/redirA.right b/shell/hush_test/hush-redir/redirA.right
new file mode 100644
index 0000000..31406e3
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redirA.right
@@ -0,0 +1,2 @@
+tmp11
+tmp11
diff --git a/shell/hush_test/hush-redir/redirA.tests b/shell/hush_test/hush-redir/redirA.tests
new file mode 100755
index 0000000..56833f9
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redirA.tests
@@ -0,0 +1,11 @@
+x="tmp11:tmp22"
+
+# Bug was incorrectly expanding variables in >redir
+echo "${x%:*}" >"${x%:*}"
+echo tmp1*
+rm tmp1*
+
+# Also try unquoted
+echo "${x%:*}" >${x%:*}
+echo tmp1*
+rm tmp1*
diff --git a/shell/hush_test/hush-misc/redir2.right b/shell/hush_test/hush-redir/redir_escapednum.right
index 7326d96..7326d96 100644
--- a/shell/hush_test/hush-misc/redir2.right
+++ b/shell/hush_test/hush-redir/redir_escapednum.right
diff --git a/shell/hush_test/hush-misc/redir2.tests b/shell/hush_test/hush-redir/redir_escapednum.tests
index 81983ca..81983ca 100755
--- a/shell/hush_test/hush-misc/redir2.tests
+++ b/shell/hush_test/hush-redir/redir_escapednum.tests
diff --git a/shell/hush_test/hush-misc/redir4.right b/shell/hush_test/hush-redir/redir_expand.right
index ead25f6..ead25f6 100644
--- a/shell/hush_test/hush-misc/redir4.right
+++ b/shell/hush_test/hush-redir/redir_expand.right
diff --git a/shell/hush_test/hush-misc/redir4.tests b/shell/hush_test/hush-redir/redir_expand.tests
index c50b8ce..c50b8ce 100755
--- a/shell/hush_test/hush-misc/redir4.tests
+++ b/shell/hush_test/hush-redir/redir_expand.tests
diff --git a/shell/hush_test/hush-redir/redir_leak.right b/shell/hush_test/hush-redir/redir_leak.right
new file mode 100644
index 0000000..b1c4829
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir_leak.right
@@ -0,0 +1,6 @@
+4
+4
+4
+4
+4
+4
diff --git a/shell/hush_test/hush-redir/redir_leak.tests b/shell/hush_test/hush-redir/redir_leak.tests
new file mode 100755
index 0000000..c8a9c63
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir_leak.tests
@@ -0,0 +1,10 @@
+# Each of these should show only four lines:
+# fds 0,1,2 are stdio; fd 3 is open by opendir() in ls.
+# This test detects bugs where redirects leave stray open fds.
+
+ls -1 /proc/self/fd | wc -l
+ls -1 /proc/self/fd >/proc/self/fd/1 | wc -l
+ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 | wc -l
+echo "`ls -1 /proc/self/fd `" | wc -l
+echo "`ls -1 /proc/self/fd >/proc/self/fd/1 `" | wc -l
+echo "`ls -1 /proc/self/fd >/proc/self/fd/1 2>&1 `" | wc -l
diff --git a/shell/hush_test/hush-misc/redir6.right b/shell/hush_test/hush-redir/redir_multi.right
index a97c4bd..a97c4bd 100644
--- a/shell/hush_test/hush-misc/redir6.right
+++ b/shell/hush_test/hush-redir/redir_multi.right
diff --git a/shell/hush_test/hush-misc/redir6.tests b/shell/hush_test/hush-redir/redir_multi.tests
index c639ebb..c639ebb 100755
--- a/shell/hush_test/hush-misc/redir6.tests
+++ b/shell/hush_test/hush-redir/redir_multi.tests
diff --git a/shell/hush_test/hush-redir/redir_script.right b/shell/hush_test/hush-redir/redir_script.right
new file mode 100644
index 0000000..6694ed3
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir_script.right
@@ -0,0 +1 @@
+Ok: script fd is not closed
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests
new file mode 100755
index 0000000..ccc497d
--- a/dev/null
+++ b/shell/hush_test/hush-redir/redir_script.tests
@@ -0,0 +1,29 @@
+# Builds a " 3>&- 4>&-" string.
+# Note: one of these fds is a directory opened to /proc/self/fd
+# for globbing. It is unwanted, but I don't know how to filter it out.
+find_fds() {
+ fds=""
+ for f in /proc/self/fd/*; do
+ test "$f" = "/proc/self/fd/0" && continue
+ test "$f" = "/proc/self/fd/1" && continue
+ test "$f" = "/proc/self/fd/2" && continue
+ fds="$fds ${f##*/}>&-"
+ done
+}
+
+find_fds
+fds1="$fds"
+
+# One of the fds is open to the script body
+# Close it while executing something.
+eval "find_fds $fds"
+
+# Shell should not lose that fd. Did it?
+find_fds
+test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; }
+
+echo "Bug: script fd is closed"
+echo "fds1:$fds1"
+echo "fds2:$fds"
+exit 1
+
diff --git a/shell/hush_test/hush-parsing/redir_space.right b/shell/hush_test/hush-redir/redir_space.right
index 0842952..0842952 100644
--- a/shell/hush_test/hush-parsing/redir_space.right
+++ b/shell/hush_test/hush-redir/redir_space.right
diff --git a/shell/hush_test/hush-parsing/redir_space.tests b/shell/hush_test/hush-redir/redir_space.tests
index c0b5430..c0b5430 100755
--- a/shell/hush_test/hush-parsing/redir_space.tests
+++ b/shell/hush_test/hush-redir/redir_space.tests
diff --git a/shell/hush_test/hush-trap/catch.right b/shell/hush_test/hush-signals/catch.right
index 80a062c..80a062c 100644
--- a/shell/hush_test/hush-trap/catch.right
+++ b/shell/hush_test/hush-signals/catch.right
diff --git a/shell/hush_test/hush-trap/catch.tests b/shell/hush_test/hush-signals/catch.tests
index d2a21d1..d2a21d1 100755
--- a/shell/hush_test/hush-trap/catch.tests
+++ b/shell/hush_test/hush-signals/catch.tests
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.right b/shell/hush_test/hush-signals/continue_and_trap1.right
new file mode 100644
index 0000000..d2dd0af
--- a/dev/null
+++ b/shell/hush_test/hush-signals/continue_and_trap1.right
@@ -0,0 +1 @@
+Exiting
diff --git a/shell/hush_test/hush-signals/continue_and_trap1.tests b/shell/hush_test/hush-signals/continue_and_trap1.tests
new file mode 100755
index 0000000..2a5c147
--- a/dev/null
+++ b/shell/hush_test/hush-signals/continue_and_trap1.tests
@@ -0,0 +1,7 @@
+trap "echo Exiting; exit" INT
+
+(sleep 1; kill -s INT $$) &
+
+while continue; do
+ continue;
+done
diff --git a/shell/hush_test/hush-trap/exit.right b/shell/hush_test/hush-signals/exit.right
index 3d00725..3d00725 100644
--- a/shell/hush_test/hush-trap/exit.right
+++ b/shell/hush_test/hush-signals/exit.right
diff --git a/shell/hush_test/hush-trap/exit.tests b/shell/hush_test/hush-signals/exit.tests
index 2061105..2061105 100755
--- a/shell/hush_test/hush-trap/exit.tests
+++ b/shell/hush_test/hush-signals/exit.tests
diff --git a/shell/hush_test/hush-misc/redir2.right b/shell/hush_test/hush-signals/reap1.right
index 7326d96..7326d96 100644
--- a/shell/hush_test/hush-misc/redir2.right
+++ b/shell/hush_test/hush-signals/reap1.right
diff --git a/shell/hush_test/hush-signals/reap1.tests b/shell/hush_test/hush-signals/reap1.tests
new file mode 100755
index 0000000..bf1a1f9
--- a/dev/null
+++ b/shell/hush_test/hush-signals/reap1.tests
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Must not find us alive
+{ sleep 2; kill -9 $$; } 2>/dev/null &
+
+sleep 1 &
+PID=$!
+
+# We must exit the loop in one second.
+# We had bug 5304: builtins never waited for exited children
+while kill -0 $PID >/dev/null 2>&1; do
+ true
+done
+echo Ok
diff --git a/shell/hush_test/hush-signals/return_in_trap1.right b/shell/hush_test/hush-signals/return_in_trap1.right
new file mode 100644
index 0000000..a6e6378
--- a/dev/null
+++ b/shell/hush_test/hush-signals/return_in_trap1.right
@@ -0,0 +1,4 @@
+a:2
+b:0
+Trap
+d:3
diff --git a/shell/hush_test/hush-signals/return_in_trap1.tests b/shell/hush_test/hush-signals/return_in_trap1.tests
new file mode 100755
index 0000000..4c0d53b
--- a/dev/null
+++ b/shell/hush_test/hush-signals/return_in_trap1.tests
@@ -0,0 +1,18 @@
+a() {
+ (exit 2)
+ echo a:$?
+ (kill -s USR1 $$; echo b:$?; exit 3)
+ echo c:$? # does not execute
+ (exit 4)
+}
+
+trap "echo Trap; return" USR1
+a
+
+echo d:$?
+# It's debatable what is the correct value above.
+# Does 'return' in trap see $? == 2 or $? == 3?
+# IOW: after (kill..), does shell first wait for its completion
+# and sets $?, then checks pending signals and runs a trap handler,
+# or does it first check pending signals and runs handler?
+# hush does the former, and prints 3.
diff --git a/shell/hush_test/hush-trap/save-ret.right b/shell/hush_test/hush-signals/save-ret.right
index a3e12ce..a3e12ce 100644
--- a/shell/hush_test/hush-trap/save-ret.right
+++ b/shell/hush_test/hush-signals/save-ret.right
diff --git a/shell/hush_test/hush-trap/save-ret.tests b/shell/hush_test/hush-signals/save-ret.tests
index 0786b6d..0786b6d 100755
--- a/shell/hush_test/hush-trap/save-ret.tests
+++ b/shell/hush_test/hush-signals/save-ret.tests
diff --git a/shell/hush_test/hush-trap/savetrap.right b/shell/hush_test/hush-signals/savetrap.right
index a59225b..a59225b 100644
--- a/shell/hush_test/hush-trap/savetrap.right
+++ b/shell/hush_test/hush-signals/savetrap.right
diff --git a/shell/hush_test/hush-trap/savetrap.tests b/shell/hush_test/hush-signals/savetrap.tests
index c2b312f..c2b312f 100755
--- a/shell/hush_test/hush-trap/savetrap.tests
+++ b/shell/hush_test/hush-signals/savetrap.tests
diff --git a/shell/hush_test/hush-signals/sigint1.right b/shell/hush_test/hush-signals/sigint1.right
new file mode 100644
index 0000000..a9094b0
--- a/dev/null
+++ b/shell/hush_test/hush-signals/sigint1.right
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/shell/hush_test/hush-signals/sigint1.tests b/shell/hush_test/hush-signals/sigint1.tests
new file mode 100755
index 0000000..3d483d3
--- a/dev/null
+++ b/shell/hush_test/hush-signals/sigint1.tests
@@ -0,0 +1,41 @@
+# What should happen if non-interactive shell gets SIGINT?
+
+(sleep 1; echo Sending SIGINT to main shell PID; exec kill -INT $$) &
+
+# We create a child which exits with 0 even on SIGINT
+# (The complex command is necessary only if SIGINT is generated by ^C,
+# in this testcase even bare "sleep 2" would do because
+# in the testcase we don't send SIGINT *to the child*...)
+$THIS_SH -c 'trap "exit 0" SIGINT; sleep 2'
+
+# In one second, we (main shell) get SIGINT here.
+# The question is whether we should, or should not, exit.
+
+# bash will not stop here. It will execute next command(s).
+
+# The rationale for this is described here:
+# http://www.cons.org/cracauer/sigint.html
+#
+# Basically, bash will not exit on SIGINT immediately if it waits
+# for a child. It will wait for the child to exit.
+# If child exits NOT by dying on SIGINT, then bash will not exit.
+#
+# The idea is that the following script:
+# | emacs file.txt
+# | more cmds
+# User may use ^C to interrupt editor's ops like search. But then
+# emacs exits normally. User expects that script doesn't stop.
+#
+# This is a nice idea, but detecting "did process really exit
+# with SIGINT?" is racy. Consider:
+# | bash -c 'while true; do /bin/true; done'
+# When ^C is pressed while bash waits for /bin/true to exit,
+# it may happen that /bin/true exits with exitcode 0 before
+# ^C is delivered to it as SIGINT. bash will see SIGINT, then
+# it will see that child exited with 0, and bash will NOT EXIT.
+
+# Therefore we do not implement bash behavior.
+# I'd say that emacs need to put itself into a separate pgrp
+# to isolate shell from getting stray SIGINTs from ^C.
+
+echo Next command after SIGINT was executed
diff --git a/shell/hush_test/hush-signals/signal2.right b/shell/hush_test/hush-signals/signal2.right
new file mode 100644
index 0000000..a2af919
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal2.right
@@ -0,0 +1,3 @@
+child sleeps
+child exits as expected
+parent exits
diff --git a/shell/hush_test/hush-signals/signal2.tests b/shell/hush_test/hush-signals/signal2.tests
new file mode 100755
index 0000000..df639ca
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal2.tests
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+$THIS_SH -c '
+cleanup() {
+ echo "child exits as expected"
+ exit
+}
+trap cleanup HUP
+echo "child sleeps"
+sleep 1
+echo "BAD exit from child!"
+' &
+
+child=$!
+sleep 0.1 # let child install handler first
+kill -HUP $child
+wait
+echo "parent exits"
diff --git a/shell/hush_test/hush-signals/signal3.right b/shell/hush_test/hush-signals/signal3.right
new file mode 100644
index 0000000..3113ba5
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal3.right
@@ -0,0 +1,4 @@
+child sleeps
+child got HUP
+child exits
+parent exits
diff --git a/shell/hush_test/hush-signals/signal3.tests b/shell/hush_test/hush-signals/signal3.tests
new file mode 100755
index 0000000..b56c2d9
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal3.tests
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+$THIS_SH -c '
+hup() {
+ echo "child got HUP"
+}
+trap hup HUP
+echo "child sleeps"
+sleep 1
+echo "child exits"
+' &
+
+child=$!
+sleep 0.1 # let child install handler first
+kill -HUP $child
+wait
+echo "parent exits"
diff --git a/shell/hush_test/hush-signals/signal5.right b/shell/hush_test/hush-signals/signal5.right
new file mode 100644
index 0000000..7cfd411
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal5.right
@@ -0,0 +1,12 @@
+Sleeping
+Sleeping
+Waiting
+2 sec passed, sending USR1 to parent
+USR1 received
+Wait exit code: 138
+Waiting
+3 sec passed, sending USR1 to parent
+USR1 received
+Wait exit code: 138
+Waiting
+Wait returned 0
diff --git a/shell/hush_test/hush-signals/signal5.tests b/shell/hush_test/hush-signals/signal5.tests
new file mode 100755
index 0000000..179bcdd
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal5.tests
@@ -0,0 +1,14 @@
+trap "echo USR1 received" USR1
+stub() {
+ echo "Sleeping"
+ sleep $1
+ echo "$1 sec passed, sending USR1 to parent"
+ kill -USR1 $$
+}
+stub 3 &
+stub 2 &
+sleep 1
+until { echo "Waiting"; wait; } do
+ echo "Wait exit code: $?"
+done
+echo "Wait returned 0"
diff --git a/shell/hush_test/hush-signals/signal6.right b/shell/hush_test/hush-signals/signal6.right
new file mode 100644
index 0000000..df4d930
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal6.right
@@ -0,0 +1,2 @@
+got TERM
+Done: 0
diff --git a/shell/hush_test/hush-signals/signal6.tests b/shell/hush_test/hush-signals/signal6.tests
new file mode 100755
index 0000000..3ce1510
--- a/dev/null
+++ b/shell/hush_test/hush-signals/signal6.tests
@@ -0,0 +1,2 @@
+{ trap "echo got TERM" TERM; sleep 3; }& sleep 1; kill $!; wait
+echo Done: $?
diff --git a/shell/hush_test/hush-trap/signal7.right b/shell/hush_test/hush-signals/signal7.right
index ba7453e..ba7453e 100644
--- a/shell/hush_test/hush-trap/signal7.right
+++ b/shell/hush_test/hush-signals/signal7.right
diff --git a/shell/hush_test/hush-trap/signal7.tests b/shell/hush_test/hush-signals/signal7.tests
index c2b1381..c2b1381 100755
--- a/shell/hush_test/hush-trap/signal7.tests
+++ b/shell/hush_test/hush-signals/signal7.tests
diff --git a/shell/hush_test/hush-trap/signal_read1.right b/shell/hush_test/hush-signals/signal_read1.right
index 2870a8e..2870a8e 100644
--- a/shell/hush_test/hush-trap/signal_read1.right
+++ b/shell/hush_test/hush-signals/signal_read1.right
diff --git a/shell/hush_test/hush-trap/signal_read1.tests b/shell/hush_test/hush-signals/signal_read1.tests
index 1105479..1105479 100755
--- a/shell/hush_test/hush-trap/signal_read1.tests
+++ b/shell/hush_test/hush-signals/signal_read1.tests
diff --git a/shell/hush_test/hush-trap/signal_read2.right b/shell/hush_test/hush-signals/signal_read2.right
index 71a6bc1..71a6bc1 100644
--- a/shell/hush_test/hush-trap/signal_read2.right
+++ b/shell/hush_test/hush-signals/signal_read2.right
diff --git a/shell/hush_test/hush-trap/signal_read2.tests b/shell/hush_test/hush-signals/signal_read2.tests
index eab5b9b..eab5b9b 100755
--- a/shell/hush_test/hush-trap/signal_read2.tests
+++ b/shell/hush_test/hush-signals/signal_read2.tests
diff --git a/shell/hush_test/hush-signals/sigquit_exec.right b/shell/hush_test/hush-signals/sigquit_exec.right
new file mode 100644
index 0000000..a804192
--- a/dev/null
+++ b/shell/hush_test/hush-signals/sigquit_exec.right
@@ -0,0 +1,2 @@
+SigIgn: 0000000000000000
+SigIgn: 0000000000000000
diff --git a/shell/hush_test/hush-signals/sigquit_exec.tests b/shell/hush_test/hush-signals/sigquit_exec.tests
new file mode 100755
index 0000000..24bda69
--- a/dev/null
+++ b/shell/hush_test/hush-signals/sigquit_exec.tests
@@ -0,0 +1,4 @@
+# Should show no masked signals in both cases.
+# We had a bug where SIGQUIT was masked on exec.
+grep SigIgn: /proc/self/status
+exec grep SigIgn: /proc/self/status
diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-signals/subshell.right
index f865b93..f865b93 100644
--- a/shell/hush_test/hush-trap/subshell.right
+++ b/shell/hush_test/hush-signals/subshell.right
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-signals/subshell.tests
index d877f2b..d877f2b 100755
--- a/shell/hush_test/hush-trap/subshell.tests
+++ b/shell/hush_test/hush-signals/subshell.tests
diff --git a/shell/hush_test/hush-trap/usage.right b/shell/hush_test/hush-signals/usage.right
index c0dbd6c..c0dbd6c 100644
--- a/shell/hush_test/hush-trap/usage.right
+++ b/shell/hush_test/hush-signals/usage.right
diff --git a/shell/hush_test/hush-trap/usage.tests b/shell/hush_test/hush-signals/usage.tests
index d29c6e7..d29c6e7 100755
--- a/shell/hush_test/hush-trap/usage.tests
+++ b/shell/hush_test/hush-signals/usage.tests
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.right b/shell/hush_test/hush-standalone/noexec_gets_no_env.right
new file mode 100644
index 0000000..8522dff
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.right
@@ -0,0 +1,4 @@
+VAR7=VAL
+0
+VAR8=VAL
+0
diff --git a/shell/hush_test/hush-standalone/noexec_gets_no_env.tests b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests
new file mode 100755
index 0000000..0d347bd
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/noexec_gets_no_env.tests
@@ -0,0 +1,5 @@
+export VAR7=VAL
+env | grep ^VAR7=
+echo $?
+VAR8=VAL env | grep ^VAR8=
+echo $?
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.right b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right
new file mode 100644
index 0000000..573541a
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.right
@@ -0,0 +1 @@
+0
diff --git a/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests
new file mode 100755
index 0000000..f42c507
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/nofork_trashes_getopt.tests
@@ -0,0 +1,6 @@
+# In this test, rm is NOFORK and it modifies getopt internal state
+rm -f non_existent_file
+# Subsequent hexdump is run as NOEXEC, and thus still uses this state
+hexdump </dev/null
+# Did hexdump segfault etc?
+echo $?
diff --git a/shell/hush_test/hush-standalone/var_standalone1.right b/shell/hush_test/hush-standalone/var_standalone1.right
new file mode 100644
index 0000000..37457fd
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/var_standalone1.right
@@ -0,0 +1 @@
+Done: 1
diff --git a/shell/hush_test/hush-standalone/var_standalone1.tests b/shell/hush_test/hush-standalone/var_standalone1.tests
new file mode 100755
index 0000000..1e905ba
--- a/dev/null
+++ b/shell/hush_test/hush-standalone/var_standalone1.tests
@@ -0,0 +1,2 @@
+VAR=42 $THIS_SH -c 'unset VAR; env | grep ^VAR'
+echo Done: $?
diff --git a/shell/hush_test/hush-vars/param_expand_len.right b/shell/hush_test/hush-vars/param_expand_len.right
index 96e8cb5..48d01d2 100644
--- a/shell/hush_test/hush-vars/param_expand_len.right
+++ b/shell/hush_test/hush-vars/param_expand_len.right
@@ -7,3 +7,6 @@ Make sure len parsing doesnt break arg count
Testing len op
4 3 2 1 0 0
0 3 0
+Nothing:
+Nothing:
+One:1
diff --git a/shell/hush_test/hush-vars/param_expand_len.tests b/shell/hush_test/hush-vars/param_expand_len.tests
index fe20a45..369c8d4 100755
--- a/shell/hush_test/hush-vars/param_expand_len.tests
+++ b/shell/hush_test/hush-vars/param_expand_len.tests
@@ -15,3 +15,10 @@ unset e
f=abc
g=
echo ${#e} ${#f} ${#g}
+
+set -- a
+# This must be interpreted as: $# ("1"), then remove trailing "1".
+# IOW: empty result.
+echo Nothing:${##1}
+echo Nothing:${#%1}
+echo One:${##x}
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
new file mode 100644
index 0000000..81a1585
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.right
@@ -0,0 +1,2 @@
+12
+9
diff --git a/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
new file mode 100755
index 0000000..e97a08a
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-collapse-arithmetic-expansion-at-parse-time.tests
@@ -0,0 +1,3 @@
+unset a
+echo $((3 + ${a:=$((4 + 5))}))
+echo $a
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
new file mode 100644
index 0000000..4b9b4f0
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.right
@@ -0,0 +1 @@
+~root
diff --git a/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
new file mode 100755
index 0000000..d8eb8fc
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-expand-tilde-in-parameter-expansion-in-quotes.tests
@@ -0,0 +1,2 @@
+unset a
+echo "${a:-~root}"
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
new file mode 100644
index 0000000..030ebde
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.right
@@ -0,0 +1 @@
+/b/c/
diff --git a/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
new file mode 100755
index 0000000..fb93714
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-do-not-quote-backslashes-in-parameter-expansions-outside-quotes.tests
@@ -0,0 +1,3 @@
+a=/b/c/*
+b=\\
+echo ${a%$b*}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right
new file mode 100644
index 0000000..2da3272
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.right
@@ -0,0 +1 @@
+a_\_z_c
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests
new file mode 100755
index 0000000..e4529c6
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-1.tests
@@ -0,0 +1,2 @@
+v="a\bc"
+echo ${v/\\b/_\\_\z_}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right
new file mode 100644
index 0000000..7447c0a
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.right
@@ -0,0 +1 @@
+ax/yc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests
new file mode 100755
index 0000000..2db1db8
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-2.tests
@@ -0,0 +1,2 @@
+v="abc"
+echo ${v/b/x/y}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right
new file mode 100644
index 0000000..5ea5ff8
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.right
@@ -0,0 +1 @@
+axcabc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests
new file mode 100755
index 0000000..0935e45
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-3.tests
@@ -0,0 +1,2 @@
+v="abcabc"
+echo ${v/b/x}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right
new file mode 100644
index 0000000..46dd750
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.right
@@ -0,0 +1 @@
+axcaxc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests
new file mode 100755
index 0000000..d8de843
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-4.tests
@@ -0,0 +1,2 @@
+v="abcabc"
+echo ${v//b/x}
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right
new file mode 100644
index 0000000..699b27b
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.right
@@ -0,0 +1 @@
+axc
diff --git a/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests
new file mode 100755
index 0000000..5523888
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-pattern-replacement-in-parameter-expansion-5.tests
@@ -0,0 +1,2 @@
+v="ab/c"
+echo ${v/b\//x}
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.right b/shell/hush_test/hush-vars/var-runtime-quote-detection.right
new file mode 100644
index 0000000..b554d9e
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.right
@@ -0,0 +1 @@
+<>
diff --git a/shell/hush_test/hush-vars/var-runtime-quote-detection.tests b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests
new file mode 100755
index 0000000..e570631
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var-runtime-quote-detection.tests
@@ -0,0 +1 @@
+foo=\\ echo "<${foo#[\\]}>"
diff --git a/shell/hush_test/hush-vars/var3.right b/shell/hush_test/hush-vars/var3.right
index 40e67fd..8eb0e33 100644
--- a/shell/hush_test/hush-vars/var3.right
+++ b/shell/hush_test/hush-vars/var3.right
@@ -1,2 +1,5 @@
-hush: invalid number '1q'
-hush: syntax error: unterminated ${name}
+1
+1
+
+
+0
diff --git a/shell/hush_test/hush-vars/var3.tests b/shell/hush_test/hush-vars/var3.tests
index aea36d6..97b102c 100755
--- a/shell/hush_test/hush-vars/var3.tests
+++ b/shell/hush_test/hush-vars/var3.tests
@@ -1,4 +1 @@
-# reject invalid vars
-"$THIS_SH" -c 'echo ${1q}'
-"$THIS_SH" -c 'echo ${&}'
-#"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$
+x=0; f() { local x=1; echo $x; local x; echo $x; unset x; echo $x; local x; echo $x; }; f; echo $x
diff --git a/shell/hush_test/hush-vars/var4.right b/shell/hush_test/hush-vars/var4.right
new file mode 100644
index 0000000..8fed138
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var4.right
@@ -0,0 +1 @@
+bus/usb/1/2
diff --git a/shell/hush_test/hush-vars/var4.tests b/shell/hush_test/hush-vars/var4.tests
new file mode 100755
index 0000000..07feaeb
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var4.tests
@@ -0,0 +1 @@
+X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
diff --git a/shell/hush_test/hush-vars/var5.right b/shell/hush_test/hush-vars/var5.right
new file mode 100644
index 0000000..2a01291
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var5.right
@@ -0,0 +1,6 @@
+a=a A=a
+a=a A=a
+a= A=
+a= A=
+a=a A=a
+a=a A=a
diff --git a/shell/hush_test/hush-vars/var5.tests b/shell/hush_test/hush-vars/var5.tests
new file mode 100755
index 0000000..802e489
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var5.tests
@@ -0,0 +1,14 @@
+# check that first assignment has proper effect on second one
+
+(
+a=a A=$a
+echo a=$a A=$A
+)
+(a=a A=$a; echo a=$a A=$A)
+(a=a A=$a echo a=$a A=$A)
+(a=a A=$a /bin/echo a=$a A=$A)
+
+f() { echo a=$a A=$A; }
+
+(a=a A=$a f)
+(a=a A=$a; f)
diff --git a/shell/hush_test/hush-vars/var6.right b/shell/hush_test/hush-vars/var6.right
new file mode 100644
index 0000000..40e67fd
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var6.right
@@ -0,0 +1,2 @@
+hush: invalid number '1q'
+hush: syntax error: unterminated ${name}
diff --git a/shell/hush_test/hush-vars/var6.tests b/shell/hush_test/hush-vars/var6.tests
new file mode 100755
index 0000000..aea36d6
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var6.tests
@@ -0,0 +1,4 @@
+# reject invalid vars
+"$THIS_SH" -c 'echo ${1q}'
+"$THIS_SH" -c 'echo ${&}'
+#"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.right b/shell/hush_test/hush-vars/var_unbackslash1.right
new file mode 100644
index 0000000..3e0c0e2
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash1.right
@@ -0,0 +1,7 @@
+Ok
+Ba d
+Ok
+Ok
+Ok
+Forty two:42
+Forty two:42
diff --git a/shell/hush_test/hush-vars/var_unbackslash1.tests b/shell/hush_test/hush-vars/var_unbackslash1.tests
new file mode 100755
index 0000000..a4665e4
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_unbackslash1.tests
@@ -0,0 +1,35 @@
+ad="Ok"
+a="Ba"
+
+# "Ok"
+echo $a\
+d
+
+# This variable contains backslash+newline!
+e='echo $a\
+d'
+
+# "Ba d"
+eval $e
+# "Ok"
+eval "$e"
+
+echo $\
+(echo Ok\
+)
+echo "$\
+(echo Ok\
+)"
+
+echo Forty two:$\
+(\
+(\
+42\
+)\
+)
+echo "Forty two:$\
+(\
+(\
+42\
+)\
+)"
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.right b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right
new file mode 100644
index 0000000..cf583d0
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.right
@@ -0,0 +1,41 @@
+Testing: !IFS $*
+.abc.
+.d.
+.e.
+Testing: !IFS $@
+.abc.
+.d.
+.e.
+Testing: !IFS "$*"
+.abc d e.
+Testing: !IFS "$@"
+.abc.
+.d e.
+Testing: IFS="" $*
+.abc.
+.d e.
+Testing: IFS="" $@
+.abc.
+.d e.
+Testing: IFS="" "$*"
+.abcd e.
+Testing: IFS="" "$@"
+.abc.
+.d e.
+Testing: !IFS v=$*
+v='abc d e'
+Testing: !IFS v=$@
+v='abc d e'
+Testing: !IFS v="$*"
+v='abc d e'
+Testing: !IFS v="$@"
+v='abc d e'
+Testing: IFS="" v=$*
+v='abcd e'
+Testing: IFS="" v=$@
+v='abcd e'
+Testing: IFS="" v="$*"
+v='abcd e'
+Testing: IFS="" v="$@"
+v='abcd e'
+Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests
new file mode 100755
index 0000000..a62afc6
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs1.tests
@@ -0,0 +1,42 @@
+set -- abc "d e"
+
+echo 'Testing: !IFS $*'
+unset IFS; for a in $*; do echo ".$a."; done
+echo 'Testing: !IFS $@'
+unset IFS; for a in $@; do echo ".$a."; done
+echo 'Testing: !IFS "$*"'
+unset IFS; for a in "$*"; do echo ".$a."; done
+echo 'Testing: !IFS "$@"'
+unset IFS; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: IFS="" $*'
+IFS=""; for a in $*; do echo ".$a."; done
+echo 'Testing: IFS="" $@'
+IFS=""; for a in $@; do echo ".$a."; done
+echo 'Testing: IFS="" "$*"'
+IFS=""; for a in "$*"; do echo ".$a."; done
+echo 'Testing: IFS="" "$@"'
+IFS=""; for a in "$@"; do echo ".$a."; done
+
+echo 'Testing: !IFS v=$*'
+unset IFS; v=$*; echo "v='$v'"
+echo 'Testing: !IFS v=$@'
+unset IFS; v=$@; echo "v='$v'"
+echo 'Testing: !IFS v="$*"'
+unset IFS; v="$*"; echo "v='$v'"
+echo 'Testing: !IFS v="$@"'
+unset IFS; v="$@"; echo "v='$v'"
+
+echo 'Testing: IFS="" v=$*'
+IFS=""; v=$*; echo "v='$v'"
+echo 'Testing: IFS="" v=$@'
+IFS=""; v=$@; echo "v='$v'"
+echo 'Testing: IFS="" v="$*"'
+IFS=""; v="$*"; echo "v='$v'"
+echo 'Testing: IFS="" v="$@"'
+IFS=""; v="$@"; echo "v='$v'"
+
+# Note: in IFS="" v=$@ and IFS="" v="$@" cases, bash produces "abc d e"
+# We produce "abcd e"
+
+echo Finished
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.right b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right
new file mode 100644
index 0000000..c234193
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.right
@@ -0,0 +1,3 @@
+Unquoted:<1>
+Unquoted:<3>
+Quoted:<123>
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests
new file mode 100755
index 0000000..4752354
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs2.tests
@@ -0,0 +1,13 @@
+# 123 chars long
+a="\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+01234567890123456789\
+0123456789\
+0123456789\
+012"
+
+IFS=2; for v in ${#a}; do echo Unquoted:"<$v>"; done
+IFS=2; for v in "${#a}"; do echo Quoted:"<$v>"; done
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.right b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right
new file mode 100644
index 0000000..5ab72e1
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.right
@@ -0,0 +1,12 @@
+Unquoted%:<q>
+Unquoted%:<w>
+Unquoted%:<e>
+Unquoted%:<r>
+Unquoted%:<t>
+Unquoted#:<w>
+Unquoted#:<e>
+Unquoted#:<r>
+Unquoted#:<t>
+Unquoted#:<y>
+Quoted%:<q w e r t >
+Quoted#:< w e r t y>
diff --git a/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests
new file mode 100755
index 0000000..4aa6557
--- a/dev/null
+++ b/shell/hush_test/hush-vars/var_wordsplit_ifs3.tests
@@ -0,0 +1,5 @@
+a="q w e r t y"
+for v in ${a%y}; do echo Unquoted%:"<$v>"; done
+for v in ${a#q}; do echo Unquoted#:"<$v>"; done
+for v in "${a%y}"; do echo Quoted%:"<$v>"; done
+for v in "${a#q}"; do echo Quoted#:"<$v>"; done
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all
index 64a7abc..837b3f7 100755
--- a/shell/hush_test/run-all
+++ b/shell/hush_test/run-all
@@ -64,11 +64,12 @@ do_test()
echo -n "$1/$x:"
(
"$THIS_SH" "./$x" >"$name.xx" 2>&1
+ r=$?
# filter C library differences
sed -i \
-e "/: invalid option /s:'::g" \
"$name.xx"
- test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77
+ test $r -eq 77 && rm -f "../$1-$x.fail" && exit 77
diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
)
case $? in
diff --git a/shell/math.c b/shell/math.c
index 3da1511..006221b 100644
--- a/shell/math.c
+++ b/shell/math.c
@@ -415,10 +415,29 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_
}
else if (right_side_val == 0)
return "divide by zero";
- else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
- rez /= right_side_val;
- else if (op == TOK_REM || op == TOK_REM_ASSIGN)
- rez %= right_side_val;
+ else if (op == TOK_DIV || op == TOK_DIV_ASSIGN
+ || op == TOK_REM || op == TOK_REM_ASSIGN) {
+ /*
+ * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))'
+ *
+ * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1
+ * and thus is not representable.
+ * Some CPUs segfault trying such op.
+ * Others overflow MAX_POSITIVE_INT+1 to
+ * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000).
+ * Make sure to at least not SEGV here:
+ */
+ if (right_side_val == -1
+ && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */
+ ) {
+ right_side_val = 1;
+ }
+ if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
+ rez /= right_side_val;
+ else {
+ rez %= right_side_val;
+ }
+ }
}
if (is_assign_op(op)) {
diff --git a/shell/math.h b/shell/math.h
index 864bee6..32e1ffe 100644
--- a/shell/math.h
+++ b/shell/math.h
@@ -63,7 +63,7 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
-#if ENABLE_SH_MATH_SUPPORT_64
+#if ENABLE_FEATURE_SH_MATH_64
typedef long long arith_t;
#define ARITH_FMT "%lld"
#define strto_arith_t strtoull
diff --git a/shell/msh_test/msh-bugs/noeol3.right b/shell/msh_test/msh-bugs/noeol3.right
deleted file mode 100644
index 56f8515..0000000
--- a/shell/msh_test/msh-bugs/noeol3.right
+++ b/dev/null
@@ -1 +0,0 @@
-hush: syntax error: unterminated "
diff --git a/shell/msh_test/msh-bugs/noeol3.tests b/shell/msh_test/msh-bugs/noeol3.tests
deleted file mode 100755
index ec958ed..0000000
--- a/shell/msh_test/msh-bugs/noeol3.tests
+++ b/dev/null
@@ -1,2 +0,0 @@
-# last line has no EOL!
-echo "unterminated \ No newline at end of file
diff --git a/shell/msh_test/msh-bugs/process_subst.right b/shell/msh_test/msh-bugs/process_subst.right
deleted file mode 100644
index 397bc80..0000000
--- a/shell/msh_test/msh-bugs/process_subst.right
+++ b/dev/null
@@ -1,3 +0,0 @@
-TESTzzBEST
-TEST$(echo zz)BEST
-TEST'BEST
diff --git a/shell/msh_test/msh-bugs/process_subst.tests b/shell/msh_test/msh-bugs/process_subst.tests
deleted file mode 100755
index 21996bc..0000000
--- a/shell/msh_test/msh-bugs/process_subst.tests
+++ b/dev/null
@@ -1,3 +0,0 @@
-echo "TEST`echo zz;echo;echo`BEST"
-echo "TEST`echo '$(echo zz)'`BEST"
-echo "TEST`echo "'"`BEST"
diff --git a/shell/msh_test/msh-bugs/starquoted.right b/shell/msh_test/msh-bugs/starquoted.right
deleted file mode 100644
index b56323f..0000000
--- a/shell/msh_test/msh-bugs/starquoted.right
+++ b/dev/null
@@ -1,8 +0,0 @@
-.1 abc d e f.
-.1.
-.abc.
-.d e f.
-.-1 abc d e f-.
-.-1.
-.abc.
-.d e f-.
diff --git a/shell/msh_test/msh-bugs/starquoted.tests b/shell/msh_test/msh-bugs/starquoted.tests
deleted file mode 100755
index 2fe49b1..0000000
--- a/shell/msh_test/msh-bugs/starquoted.tests
+++ b/dev/null
@@ -1,8 +0,0 @@
-if test $# = 0; then
- exec "$THIS_SH" "$0" 1 abc 'd e f'
-fi
-
-for a in "$*"; do echo ".$a."; done
-for a in "$@"; do echo ".$a."; done
-for a in "-$*-"; do echo ".$a."; done
-for a in "-$@-"; do echo ".$a."; done
diff --git a/shell/msh_test/msh-bugs/syntax_err.right b/shell/msh_test/msh-bugs/syntax_err.right
deleted file mode 100644
index 08a270c..0000000
--- a/shell/msh_test/msh-bugs/syntax_err.right
+++ b/dev/null
@@ -1,2 +0,0 @@
-shown
-hush: syntax error: unterminated '
diff --git a/shell/msh_test/msh-bugs/syntax_err.tests b/shell/msh_test/msh-bugs/syntax_err.tests
deleted file mode 100755
index d10ed42..0000000
--- a/shell/msh_test/msh-bugs/syntax_err.tests
+++ b/dev/null
@@ -1,3 +0,0 @@
-echo shown
-echo test `echo 'aa`
-echo not shown
diff --git a/shell/msh_test/msh-execution/exitcode_EACCES.right b/shell/msh_test/msh-execution/exitcode_EACCES.right
deleted file mode 100644
index 6e5480b..0000000
--- a/shell/msh_test/msh-execution/exitcode_EACCES.right
+++ b/dev/null
@@ -1,2 +0,0 @@
-./: can't execute
-126
diff --git a/shell/msh_test/msh-execution/exitcode_ENOENT.right b/shell/msh_test/msh-execution/exitcode_ENOENT.right
deleted file mode 100644
index dd49d2c..0000000
--- a/shell/msh_test/msh-execution/exitcode_ENOENT.right
+++ b/dev/null
@@ -1,2 +0,0 @@
-./does_not_exist_for_sure: not found
-127
diff --git a/shell/msh_test/msh-execution/many_continues.tests b/shell/msh_test/msh-execution/many_continues.tests
deleted file mode 100755
index 86c729a..0000000
--- a/shell/msh_test/msh-execution/many_continues.tests
+++ b/dev/null
@@ -1,15 +0,0 @@
-if test $# = 0; then
- # Child will kill us in 1 second
- "$THIS_SH" "$0" $$ &
-
- # Loop many, many times
- trap 'echo OK; exit 0' 15
- while true; do
- continue
- done
- echo BAD
- exit 1
-fi
-
-sleep 1
-kill $1
diff --git a/shell/msh_test/msh-execution/nested_break.right b/shell/msh_test/msh-execution/nested_break.right
deleted file mode 100644
index 4e8b6b0..0000000
--- a/shell/msh_test/msh-execution/nested_break.right
+++ b/dev/null
@@ -1,8 +0,0 @@
-A
-B
-iteration
-C
-A
-B
-iteration
-D
diff --git a/shell/msh_test/msh-execution/nested_break.tests b/shell/msh_test/msh-execution/nested_break.tests
deleted file mode 100755
index 1a954d2..0000000
--- a/shell/msh_test/msh-execution/nested_break.tests
+++ b/dev/null
@@ -1,17 +0,0 @@
-# Testcase for http://bugs.busybox.net/view.php?id=846
-
-n=0
-while :
-do
- echo A
- while :
- do
- echo B
- break
- done
- echo iteration
- [ $n = 1 ] && break
- echo C
- n=`expr $n + 1`
-done
-echo D
diff --git a/shell/msh_test/msh-misc/tick.right b/shell/msh_test/msh-misc/tick.right
deleted file mode 100644
index 6ed281c..0000000
--- a/shell/msh_test/msh-misc/tick.right
+++ b/dev/null
@@ -1,2 +0,0 @@
-1
-1
diff --git a/shell/msh_test/msh-misc/tick.tests b/shell/msh_test/msh-misc/tick.tests
deleted file mode 100755
index 1f749a9..0000000
--- a/shell/msh_test/msh-misc/tick.tests
+++ b/dev/null
@@ -1,4 +0,0 @@
-true
-false; echo `echo $?`
-true
-{ false; echo `echo $?`; }
diff --git a/shell/msh_test/msh-parsing/argv0.tests b/shell/msh_test/msh-parsing/argv0.tests
deleted file mode 100755
index f5c40f6..0000000
--- a/shell/msh_test/msh-parsing/argv0.tests
+++ b/dev/null
@@ -1,4 +0,0 @@
-if test $# = 0; then
- exec "$THIS_SH" "$0" arg
-fi
-echo OK
diff --git a/shell/msh_test/msh-parsing/noeol.right b/shell/msh_test/msh-parsing/noeol.right
deleted file mode 100644
index e427984..0000000
--- a/shell/msh_test/msh-parsing/noeol.right
+++ b/dev/null
@@ -1 +0,0 @@
-HELLO
diff --git a/shell/msh_test/msh-parsing/noeol.tests b/shell/msh_test/msh-parsing/noeol.tests
deleted file mode 100755
index a93113a..0000000
--- a/shell/msh_test/msh-parsing/noeol.tests
+++ b/dev/null
@@ -1,2 +0,0 @@
-# next line has no EOL!
-echo HELLO \ No newline at end of file
diff --git a/shell/msh_test/msh-parsing/noeol2.right b/shell/msh_test/msh-parsing/noeol2.right
deleted file mode 100644
index d00491f..0000000
--- a/shell/msh_test/msh-parsing/noeol2.right
+++ b/dev/null
@@ -1 +0,0 @@
-1
diff --git a/shell/msh_test/msh-parsing/noeol2.tests b/shell/msh_test/msh-parsing/noeol2.tests
deleted file mode 100755
index 1220f05..0000000
--- a/shell/msh_test/msh-parsing/noeol2.tests
+++ b/dev/null
@@ -1,7 +0,0 @@
-# last line has no EOL!
-if true
-then
- echo 1
-else
- echo 2
-fi \ No newline at end of file
diff --git a/shell/msh_test/msh-parsing/quote1.right b/shell/msh_test/msh-parsing/quote1.right
deleted file mode 100644
index cb38205..0000000
--- a/shell/msh_test/msh-parsing/quote1.right
+++ b/dev/null
@@ -1 +0,0 @@
-'1'
diff --git a/shell/msh_test/msh-parsing/quote1.tests b/shell/msh_test/msh-parsing/quote1.tests
deleted file mode 100755
index f558954..0000000
--- a/shell/msh_test/msh-parsing/quote1.tests
+++ b/dev/null
@@ -1,2 +0,0 @@
-a=1
-echo "'$a'"
diff --git a/shell/msh_test/msh-parsing/quote2.right b/shell/msh_test/msh-parsing/quote2.right
deleted file mode 100644
index 3bc9edc..0000000
--- a/shell/msh_test/msh-parsing/quote2.right
+++ b/dev/null
@@ -1 +0,0 @@
->1
diff --git a/shell/msh_test/msh-parsing/quote2.tests b/shell/msh_test/msh-parsing/quote2.tests
deleted file mode 100755
index bd966f3..0000000
--- a/shell/msh_test/msh-parsing/quote2.tests
+++ b/dev/null
@@ -1,2 +0,0 @@
-a=1
-echo ">$a"
diff --git a/shell/msh_test/msh-parsing/quote3.right b/shell/msh_test/msh-parsing/quote3.right
deleted file mode 100644
index 069a46e..0000000
--- a/shell/msh_test/msh-parsing/quote3.right
+++ b/dev/null
@@ -1,3 +0,0 @@
-Testing: in $empty""
-..
-Finished
diff --git a/shell/msh_test/msh-parsing/quote3.tests b/shell/msh_test/msh-parsing/quote3.tests
deleted file mode 100755
index 075e785..0000000
--- a/shell/msh_test/msh-parsing/quote3.tests
+++ b/dev/null
@@ -1,8 +0,0 @@
-if test $# = 0; then
- exec "$THIS_SH" quote3.tests abc "d e"
-fi
-
-echo 'Testing: in $empty""'
-empty=''
-for a in $empty""; do echo ".$a."; done
-echo Finished
diff --git a/shell/msh_test/msh-parsing/quote4.right b/shell/msh_test/msh-parsing/quote4.right
deleted file mode 100644
index b2901ea..0000000
--- a/shell/msh_test/msh-parsing/quote4.right
+++ b/dev/null
@@ -1 +0,0 @@
-a b
diff --git a/shell/msh_test/msh-parsing/quote4.tests b/shell/msh_test/msh-parsing/quote4.tests
deleted file mode 100755
index f1dabfa..0000000
--- a/shell/msh_test/msh-parsing/quote4.tests
+++ b/dev/null
@@ -1,2 +0,0 @@
-a_b='a b'
-echo "$a_b"
diff --git a/shell/msh_test/msh-vars/var.right b/shell/msh_test/msh-vars/var.right
deleted file mode 100644
index 14b2314..0000000
--- a/shell/msh_test/msh-vars/var.right
+++ b/dev/null
@@ -1,4 +0,0 @@
-http://busybox.net
-http://busybox.net_abc
-1
-1
diff --git a/shell/msh_test/msh-vars/var.tests b/shell/msh_test/msh-vars/var.tests
deleted file mode 100755
index 0a63696..0000000
--- a/shell/msh_test/msh-vars/var.tests
+++ b/dev/null
@@ -1,9 +0,0 @@
-URL=http://busybox.net
-
-echo $URL
-echo ${URL}_abc
-
-true
-false; echo $?
-true
-{ false; echo $?; }
diff --git a/shell/msh_test/msh-vars/var_subst_in_for.tests b/shell/msh_test/msh-vars/var_subst_in_for.tests
deleted file mode 100755
index 4d1c112..0000000
--- a/shell/msh_test/msh-vars/var_subst_in_for.tests
+++ b/dev/null
@@ -1,40 +0,0 @@
-if test $# = 0; then
- exec "$THIS_SH" var_subst_in_for.tests abc "d e"
-fi
-
-echo 'Testing: in x y z'
-for a in x y z; do echo ".$a."; done
-
-echo 'Testing: in u $empty v'
-empty=''
-for a in u $empty v; do echo ".$a."; done
-
-echo 'Testing: in u " $empty" v'
-empty=''
-for a in u " $empty" v; do echo ".$a."; done
-
-echo 'Testing: in u $empty $empty$a v'
-a='a'
-for a in u $empty $empty$a v; do echo ".$a."; done
-
-echo 'Testing: in $a_b'
-a_b='a b'
-for a in $a_b; do echo ".$a."; done
-
-echo 'Testing: in $*'
-for a in $*; do echo ".$a."; done
-
-echo 'Testing: in $@'
-for a in $@; do echo ".$a."; done
-
-echo 'Testing: in -$*-'
-for a in -$*-; do echo ".$a."; done
-
-echo 'Testing: in -$@-'
-for a in -$@-; do echo ".$a."; done
-
-echo 'Testing: in $a_b -$a_b-'
-a_b='a b'
-for a in $a_b -$a_b-; do echo ".$a."; done
-
-echo Finished
diff --git a/shell/msh_test/run-all b/shell/msh_test/run-all
deleted file mode 100755
index 29f62a5..0000000
--- a/shell/msh_test/run-all
+++ b/dev/null
@@ -1,64 +0,0 @@
-#!/bin/sh
-
-test -x msh || {
- echo "No ./msh - creating a link to ../../busybox"
- ln -s ../../busybox msh
-}
-
-PATH="$PWD:$PATH" # for msh
-export PATH
-
-THIS_SH="$PWD/msh"
-export THIS_SH
-
-do_test()
-{
- test -d "$1" || return 0
-# echo Running tests in directory "$1"
- (
- cd "$1" || { echo "cannot cd $1!"; exit 1; }
- for x in run-*; do
- test -f "$x" || continue
- case "$x" in
- "$0"|run-minimal|run-gprof) ;;
- *.orig|*~) ;;
- #*) echo $x ; sh $x ;;
- *)
- sh "$x" >"../$1-$x.fail" 2>&1 && \
- { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
- ;;
- esac
- done
- # Many bash run-XXX scripts just do this,
- # no point in duplication it all over the place
- for x in *.tests; do
- test -x "$x" || continue
- name="${x%%.tests}"
- test -f "$name.right" || continue
-# echo Running test: "$name.right"
- {
- "$THIS_SH" "./$x" >"$name.xx" 2>&1
- diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
- } && echo "$1/$x: ok" || echo "$1/$x: fail"
- done
- )
-}
-
-# Main part of this script
-# Usage: run-all [directories]
-
-if [ $# -lt 1 ]; then
- # All sub directories
- modules=`ls -d msh-*`
-
- for module in $modules; do
- do_test $module
- done
-else
- while [ $# -ge 1 ]; do
- if [ -d $1 ]; then
- do_test $1
- fi
- shift
- done
-fi
diff --git a/shell/random.c b/shell/random.c
index 853ab08..5d36205 100644
--- a/shell/random.c
+++ b/shell/random.c
@@ -6,17 +6,51 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-#include "libbb.h"
-#include "random.h"
+
+/* For testing against dieharder, you need only random.{c,h}
+ * Howto:
+ * gcc -O2 -Wall -DRANDTEST random.c -o random
+ * ./random | dieharder -g 200 -a
+ */
+
+#if !defined RANDTEST
+
+# include "libbb.h"
+# include "random.h"
+# define RAND_BASH_MASK 0x7fff
+
+#else
+# include <stdint.h>
+# include <unistd.h>
+# include <stdio.h>
+# include <time.h>
+# define FAST_FUNC /* nothing */
+# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN /* nothing */
+# define POP_SAVED_FUNCTION_VISIBILITY /* nothing */
+# define monotonic_us() time(NULL)
+# include "random.h"
+# define RAND_BASH_MASK 0xffffffff /* off */
+#endif
uint32_t FAST_FUNC
next_random(random_t *rnd)
{
- /* Galois LFSR parameter */
- /* Taps at 32 31 29 1: */
+ /* Galois LFSR parameter:
+ * Taps at 32 31 29 1:
+ */
enum { MASK = 0x8000000b };
/* Another example - taps at 32 31 30 10: */
- /* MASK = 0x00400007 */
+ /* enum { MASK = 0x00400007 }; */
+
+ /* Xorshift parameters:
+ * Choices for a,b,c: 10,13,10; 8,9,22; 2,7,3; 23,3,24
+ * (given by algorithm author)
+ */
+ enum {
+ a = 2,
+ b = 7,
+ c = 3,
+ };
uint32_t t;
@@ -27,18 +61,100 @@ next_random(random_t *rnd)
INIT_RANDOM_T(rnd, getpid(), monotonic_us());
}
- /* LCG has period of 2^32 and alternating lowest bit */
+ /* LCG: period of 2^32, but quite weak:
+ * bit 0 alternates beetween 0 and 1 (pattern of length 2)
+ * bit 1 has a repeating pattern of length 4
+ * bit 2 has a repeating pattern of length 8
+ * etc...
+ */
rnd->LCG = 1664525 * rnd->LCG + 1013904223;
- /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
+
+ /* Galois LFSR:
+ * period of 2^32-1 = 3 * 5 * 17 * 257 * 65537.
+ * Successive values are right-shifted one bit
+ * and possibly xored with a sparse constant.
+ */
t = (rnd->galois_LFSR << 1);
if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */
t ^= MASK;
rnd->galois_LFSR = t;
- /* Both are weak, combining them gives better randomness
- * and ~2^64 period. & 0x7fff is probably bash compat
- * for $RANDOM range. Combining with subtraction is
- * just for fun. + and ^ would work equally well. */
- t = (t - rnd->LCG) & 0x7fff;
- return t;
+ /* http://en.wikipedia.org/wiki/Xorshift
+ * Moderately good statistical properties:
+ * fails the following "dieharder -g 200 -a" tests:
+ * diehard_operm5| 0
+ * diehard_oqso| 0
+ * diehard_count_1s_byt| 0
+ * diehard_3dsphere| 3
+ * diehard_squeeze| 0
+ * diehard_runs| 0
+ * diehard_runs| 0
+ * diehard_craps| 0
+ * diehard_craps| 0
+ * rgb_minimum_distance| 3
+ * rgb_minimum_distance| 4
+ * rgb_minimum_distance| 5
+ * rgb_permutations| 3
+ * rgb_permutations| 4
+ * rgb_permutations| 5
+ * dab_filltree| 32
+ * dab_filltree| 32
+ * dab_monobit2| 12
+ */
+ again:
+ t = rnd->xs64_x ^ (rnd->xs64_x << a);
+ rnd->xs64_x = rnd->xs64_y;
+ rnd->xs64_y = rnd->xs64_y ^ (rnd->xs64_y >> c) ^ t ^ (t >> b);
+ /*
+ * Period 2^64-1 = 2^32+1 * 2^32-1 has a common divisor with Galois LFSR.
+ * By skipping two possible states (0x1 and 0x2) we reduce period to
+ * 2^64-3 = 13 * 3889 * 364870227143809 which has no common divisors:
+ */
+ if (rnd->xs64_y == 0 && rnd->xs64_x <= 2)
+ goto again;
+
+ /* Combined LCG + Galois LFSR rng has 2^32 * 2^32-1 period.
+ * Strength:
+ * individually, both are extremely weak cryptographycally;
+ * when combined, they fail the following "dieharder -g 200 -a" tests:
+ * diehard_rank_6x8| 0
+ * diehard_oqso| 0
+ * diehard_dna| 0
+ * diehard_count_1s_byt| 0
+ * rgb_bitdist| 2
+ * dab_monobit2| 12
+ *
+ * Combining them with xorshift-64 increases period to
+ * 2^32 * 2^32-1 * 2^64-3
+ * which is about 2^128, or in base 10 ~3.40*10^38.
+ * Strength of the combination:
+ * passes all "dieharder -g 200 -a" tests.
+ *
+ * Combining with subtraction and addition is just for fun.
+ * It does not add meaningful strength, could use xor operation instead.
+ */
+ t = rnd->galois_LFSR - rnd->LCG + rnd->xs64_y;
+
+ /* bash compat $RANDOM range: */
+ return t & RAND_BASH_MASK;
}
+
+#ifdef RANDTEST
+static random_t rnd;
+
+int main(int argc, char **argv)
+{
+ int i;
+ uint32_t buf[4096];
+
+ for (;;) {
+ for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++) {
+ buf[i] = next_random(&rnd);
+ }
+ write(1, buf, sizeof(buf));
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/shell/random.h b/shell/random.h
index 180c48a..c4eb44c 100644
--- a/shell/random.h
+++ b/shell/random.h
@@ -12,16 +12,24 @@
PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
typedef struct random_t {
- /* Random number generators */
- int32_t galois_LFSR; /* Galois LFSR (fast but weak). signed! */
- uint32_t LCG; /* LCG (fast but weak) */
+ /* State of random number generators: */
+
+ /* Galois LFSR (fast but weak) */
+ int32_t galois_LFSR; /* must be signed! */
+
+ /* LCG (fast but weak) */
+ uint32_t LCG;
+
+ /* 64-bit xorshift (fast, moderate strength) */
+ uint32_t xs64_x;
+ uint32_t xs64_y;
} random_t;
#define UNINITED_RANDOM_T(rnd) \
((rnd)->galois_LFSR == 0)
#define INIT_RANDOM_T(rnd, nonzero, v) \
- ((rnd)->galois_LFSR = (nonzero), (rnd)->LCG = (v))
+ ((rnd)->galois_LFSR = (rnd)->xs64_x = (nonzero), (rnd)->LCG = (rnd)->xs64_y = (v))
#define CLEAR_RANDOM_T(rnd) \
((rnd)->galois_LFSR = 0)
diff --git a/shell/shell_common.c b/shell/shell_common.c
index 5729715..98d8627 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -21,6 +21,7 @@
#include <sys/resource.h> /* getrlimit */
const char defifsvar[] ALIGN1 = "IFS= \t\n";
+const char defoptindvar[] ALIGN1 = "OPTIND=1";
int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
@@ -328,7 +329,7 @@ enum {
};
/* "-": treat args as parameters of option with ASCII code 1 */
-static const char ulimit_opt_string[] = "-HSa"
+static const char ulimit_opt_string[] ALIGN1 = "-HSa"
#ifdef RLIMIT_FSIZE
"f::"
#endif
@@ -380,7 +381,7 @@ static void printlim(unsigned opts, const struct rlimit *limit,
val = limit->rlim_cur;
if (val == RLIM_INFINITY)
- printf("unlimited\n");
+ puts("unlimited");
else {
val >>= l->factor_shift;
printf("%llu\n", (long long) val);
@@ -493,7 +494,6 @@ shell_builtin_ulimit(char **argv)
/* bad option. getopt already complained. */
break;
}
-
} /* while (there are options) */
return 0;
diff --git a/shell/shell_common.h b/shell/shell_common.h
index 993ed59..a82535c 100644
--- a/shell/shell_common.h
+++ b/shell/shell_common.h
@@ -24,6 +24,8 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
extern const char defifsvar[] ALIGN1; /* "IFS= \t\n" */
#define defifs (defifsvar + 4)
+extern const char defoptindvar[] ALIGN1; /* "OPTIND=1" */
+
int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
/* Builtins */
diff --git a/sysklogd/Config.src b/sysklogd/Config.src
index fcf9930..684e7d4 100644
--- a/sysklogd/Config.src
+++ b/sysklogd/Config.src
@@ -7,163 +7,4 @@ menu "System Logging Utilities"
INSERT
-config SYSLOGD
- bool "syslogd"
- default y
- help
- The syslogd utility is used to record logs of all the
- significant events that occur on a system. Every
- message that is logged records the date and time of the
- event, and will generally also record the name of the
- application that generated the message. When used in
- conjunction with klogd, messages from the Linux kernel
- can also be recorded. This is terribly useful,
- especially for finding what happened when something goes
- wrong. And something almost always will go wrong if
- you wait long enough....
-
-config FEATURE_ROTATE_LOGFILE
- bool "Rotate message files"
- default y
- depends on SYSLOGD
- help
- This enables syslogd to rotate the message files
- on his own. No need to use an external rotatescript.
-
-config FEATURE_REMOTE_LOG
- bool "Remote Log support"
- default y
- depends on SYSLOGD
- help
- When you enable this feature, the syslogd utility can
- be used to send system log messages to another system
- connected via a network. This allows the remote
- machine to log all the system messages, which can be
- terribly useful for reducing the number of serial
- cables you use. It can also be a very good security
- measure to prevent system logs from being tampered with
- by an intruder.
-
-config FEATURE_SYSLOGD_DUP
- bool "Support -D (drop dups) option"
- default y
- depends on SYSLOGD
- help
- Option -D instructs syslogd to drop consecutive messages
- which are totally the same.
-
-config FEATURE_SYSLOGD_CFG
- bool "Support syslog.conf"
- default y
- depends on SYSLOGD
- help
- Supports restricted syslogd config. See docs/syslog.conf.txt
-
-config FEATURE_SYSLOGD_READ_BUFFER_SIZE
- int "Read buffer size in bytes"
- default 256
- range 256 20000
- depends on SYSLOGD
- help
- This option sets the size of the syslog read buffer.
- Actual memory usage increases around five times the
- change done here.
-
-config FEATURE_IPC_SYSLOG
- bool "Circular Buffer support"
- default y
- depends on SYSLOGD
- help
- When you enable this feature, the syslogd utility will
- use a circular buffer to record system log messages.
- When the buffer is filled it will continue to overwrite
- the oldest messages. This can be very useful for
- systems with little or no permanent storage, since
- otherwise system logs can eventually fill up your
- entire filesystem, which may cause your system to
- break badly.
-
-config FEATURE_IPC_SYSLOG_BUFFER_SIZE
- int "Circular buffer size in Kbytes (minimum 4KB)"
- default 16
- range 4 2147483647
- depends on FEATURE_IPC_SYSLOG
- help
- This option sets the size of the circular buffer
- used to record system log messages.
-
-config LOGREAD
- bool "logread"
- default y
- depends on FEATURE_IPC_SYSLOG
- help
- If you enabled Circular Buffer support, you almost
- certainly want to enable this feature as well. This
- utility will allow you to read the messages that are
- stored in the syslogd circular buffer.
-
-config FEATURE_LOGREAD_REDUCED_LOCKING
- bool "Double buffering"
- default y
- depends on LOGREAD
- help
- 'logread' ouput to slow serial terminals can have
- side effects on syslog because of the semaphore.
- This option make logread to double buffer copy
- from circular buffer, minimizing semaphore
- contention at some minor memory expense.
-
-config FEATURE_KMSG_SYSLOG
- bool "Linux kernel printk buffer support"
- default y
- depends on SYSLOGD
- select PLATFORM_LINUX
- help
- When you enable this feature, the syslogd utility will
- write system log message to the Linux kernel's printk buffer.
- This can be used as a smaller alternative to the syslogd IPC
- support, as klogd and logread aren't needed.
-
- NOTICE: Syslog facilities in log entries needs kernel 3.5+.
-
-config KLOGD
- bool "klogd"
- default y
- help
- klogd is a utility which intercepts and logs all
- messages from the Linux kernel and sends the messages
- out to the 'syslogd' utility so they can be logged. If
- you wish to record the messages produced by the kernel,
- you should enable this option.
-
-comment "klogd should not be used together with syslog to kernel printk buffer"
- depends on KLOGD && FEATURE_KMSG_SYSLOG
-
-config FEATURE_KLOGD_KLOGCTL
- bool "Use the klogctl() interface"
- default y
- depends on KLOGD
- select PLATFORM_LINUX
- help
- The klogd applet supports two interfaces for reading
- kernel messages. Linux provides the klogctl() interface
- which allows reading messages from the kernel ring buffer
- independently from the file system.
-
- If you answer 'N' here, klogd will use the more portable
- approach of reading them from /proc or a device node.
- However, this method requires the file to be available.
-
- If in doubt, say 'Y'.
-
-config LOGGER
- bool "logger"
- default y
- select FEATURE_SYSLOG
- help
- The logger utility allows you to send arbitrary text
- messages to the system log (i.e. the 'syslogd' utility) so
- they can be logged. This is generally used to help locate
- problems that occur within programs and scripts.
-
endmenu
diff --git a/sysklogd/Kbuild.src b/sysklogd/Kbuild.src
index d386cc2..6b4fb74 100644
--- a/sysklogd/Kbuild.src
+++ b/sysklogd/Kbuild.src
@@ -7,7 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_KLOGD) += klogd.o
-lib-$(CONFIG_LOGGER) += syslogd_and_logger.o
-lib-$(CONFIG_LOGREAD) += logread.o
-lib-$(CONFIG_SYSLOGD) += syslogd_and_logger.o
diff --git a/sysklogd/klogd.c b/sysklogd/klogd.c
index 432ded1..4db7211 100644
--- a/sysklogd/klogd.c
+++ b/sysklogd/klogd.c
@@ -16,6 +16,39 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config KLOGD
+//config: bool "klogd"
+//config: default y
+//config: help
+//config: klogd is a utility which intercepts and logs all
+//config: messages from the Linux kernel and sends the messages
+//config: out to the 'syslogd' utility so they can be logged. If
+//config: you wish to record the messages produced by the kernel,
+//config: you should enable this option.
+//config:
+//config:comment "klogd should not be used together with syslog to kernel printk buffer"
+//config: depends on KLOGD && FEATURE_KMSG_SYSLOG
+//config:
+//config:config FEATURE_KLOGD_KLOGCTL
+//config: bool "Use the klogctl() interface"
+//config: default y
+//config: depends on KLOGD
+//config: select PLATFORM_LINUX
+//config: help
+//config: The klogd applet supports two interfaces for reading
+//config: kernel messages. Linux provides the klogctl() interface
+//config: which allows reading messages from the kernel ring buffer
+//config: independently from the file system.
+//config:
+//config: If you answer 'N' here, klogd will use the more portable
+//config: approach of reading them from /proc or a device node.
+//config: However, this method requires the file to be available.
+//config:
+//config: If in doubt, say 'Y'.
+
+//applet:IF_KLOGD(APPLET(klogd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_KLOGD) += klogd.o
//usage:#define klogd_trivial_usage
//usage: "[-c N] [-n]"
@@ -25,6 +58,7 @@
//usage: "\n -n Run in foreground"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <syslog.h>
@@ -65,7 +99,6 @@ static void klogd_close(void)
#else
-# include <paths.h>
# ifndef _PATH_KLOG
# ifdef __GNU__
# define _PATH_KLOG "/dev/klog"
@@ -115,7 +148,7 @@ static void klogd_close(void)
#define log_buffer bb_common_bufsiz1
enum {
- KLOGD_LOGBUF_SIZE = sizeof(log_buffer),
+ KLOGD_LOGBUF_SIZE = COMMON_BUFSIZE,
OPT_LEVEL = (1 << 0),
OPT_FOREGROUND = (1 << 1),
};
@@ -141,6 +174,8 @@ int klogd_main(int argc UNUSED_PARAM, char **argv)
int opt;
int used;
+ setup_common_bufsiz();
+
opt = getopt32(argv, "c:n", &opt_c);
if (opt & OPT_LEVEL) {
/* Valid levels are between 1 and 8 */
diff --git a/sysklogd/logger.c b/sysklogd/logger.c
index 5a70277..f9eafeb 100644
--- a/sysklogd/logger.c
+++ b/sysklogd/logger.c
@@ -6,6 +6,19 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LOGGER
+//config: bool "logger"
+//config: default y
+//config: select FEATURE_SYSLOG
+//config: help
+//config: The logger utility allows you to send arbitrary text
+//config: messages to the system log (i.e. the 'syslogd' utility) so
+//config: they can be logged. This is generally used to help locate
+//config: problems that occur within programs and scripts.
+
+//applet:IF_LOGGER(APPLET(logger, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LOGGER) += syslogd_and_logger.o
//usage:#define logger_trivial_usage
//usage: "[OPTIONS] [MESSAGE]"
@@ -86,6 +99,8 @@ int logger_main(int argc UNUSED_PARAM, char **argv)
int opt;
int i = 0;
+ setup_common_bufsiz();
+
/* Fill out the name string early (may be overwritten later) */
str_t = uid2uname_utoa(geteuid());
diff --git a/sysklogd/logread.c b/sysklogd/logread.c
index bea73d9..1f0c625 100644
--- a/sysklogd/logread.c
+++ b/sysklogd/logread.c
@@ -8,14 +8,42 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LOGREAD
+//config: bool "logread"
+//config: default y
+//WRONG: it should be compilable without SYSLOG=y:
+//WRONG: depends on FEATURE_IPC_SYSLOG
+//config: help
+//config: If you enabled Circular Buffer support, you almost
+//config: certainly want to enable this feature as well. This
+//config: utility will allow you to read the messages that are
+//config: stored in the syslogd circular buffer.
+//config:
+//config:config FEATURE_LOGREAD_REDUCED_LOCKING
+//config: bool "Double buffering"
+//config: default y
+//config: depends on LOGREAD
+//config: help
+//config: 'logread' ouput to slow serial terminals can have
+//config: side effects on syslog because of the semaphore.
+//config: This option make logread to double buffer copy
+//config: from circular buffer, minimizing semaphore
+//config: contention at some minor memory expense.
+//config:
+
+//applet:IF_LOGREAD(APPLET(logread, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LOGREAD) += logread.o
//usage:#define logread_trivial_usage
-//usage: "[-f]"
+//usage: "[-fF]"
//usage:#define logread_full_usage "\n\n"
//usage: "Show messages in syslogd's circular buffer\n"
//usage: "\n -f Output data as log grows"
+//usage: "\n -F Same as -f, but dump buffer first"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
@@ -41,11 +69,12 @@ struct globals {
struct sembuf SMrdn[2]; // {1, 0}, {0, +1, SEM_UNDO}
struct shbuf_ds *shbuf;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define SMrup (G.SMrup)
#define SMrdn (G.SMrdn)
#define shbuf (G.shbuf)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
memcpy(SMrup, init_sem, sizeof(init_sem)); \
} while (0)
@@ -83,7 +112,7 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
unsigned cur;
int log_semid; /* ipc semaphore id */
int log_shmid; /* ipc shared memory id */
- smallint follow = getopt32(argv, "f");
+ int follow = getopt32(argv, "fF");
INIT_G();
@@ -106,7 +135,7 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
/* Max possible value for tail is shbuf->size - 1 */
cur = shbuf->tail;
- /* Loop for logread -f, one pass if there was no -f */
+ /* Loop for -f or -F, one pass otherwise */
do {
unsigned shbuf_size;
unsigned shbuf_tail;
@@ -129,7 +158,12 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
printf("cur:%u tail:%u size:%u\n",
cur, shbuf_tail, shbuf_size);
- if (!follow) {
+ if (!(follow & 1)) { /* not -f */
+ /* if -F, "convert" it to -f, so that we dont
+ * dump the entire buffer on each iteration
+ */
+ follow >>= 1;
+
/* advance to oldest complete message */
/* find NUL */
cur += strlen(shbuf_data + cur);
@@ -142,7 +176,7 @@ int logread_main(int argc UNUSED_PARAM, char **argv)
cur++;
if (cur >= shbuf_size) /* last byte in buffer? */
cur = 0;
- } else { /* logread -f */
+ } else { /* -f */
if (cur == shbuf_tail) {
sem_up(log_semid);
fflush_all();
diff --git a/sysklogd/syslogd.c b/sysklogd/syslogd.c
index a6a4ff2..ae0840b 100644
--- a/sysklogd/syslogd.c
+++ b/sysklogd/syslogd.c
@@ -12,6 +12,107 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SYSLOGD
+//config: bool "syslogd"
+//config: default y
+//config: help
+//config: The syslogd utility is used to record logs of all the
+//config: significant events that occur on a system. Every
+//config: message that is logged records the date and time of the
+//config: event, and will generally also record the name of the
+//config: application that generated the message. When used in
+//config: conjunction with klogd, messages from the Linux kernel
+//config: can also be recorded. This is terribly useful,
+//config: especially for finding what happened when something goes
+//config: wrong. And something almost always will go wrong if
+//config: you wait long enough....
+//config:
+//config:config FEATURE_ROTATE_LOGFILE
+//config: bool "Rotate message files"
+//config: default y
+//config: depends on SYSLOGD
+//config: help
+//config: This enables syslogd to rotate the message files
+//config: on his own. No need to use an external rotate script.
+//config:
+//config:config FEATURE_REMOTE_LOG
+//config: bool "Remote Log support"
+//config: default y
+//config: depends on SYSLOGD
+//config: help
+//config: When you enable this feature, the syslogd utility can
+//config: be used to send system log messages to another system
+//config: connected via a network. This allows the remote
+//config: machine to log all the system messages, which can be
+//config: terribly useful for reducing the number of serial
+//config: cables you use. It can also be a very good security
+//config: measure to prevent system logs from being tampered with
+//config: by an intruder.
+//config:
+//config:config FEATURE_SYSLOGD_DUP
+//config: bool "Support -D (drop dups) option"
+//config: default y
+//config: depends on SYSLOGD
+//config: help
+//config: Option -D instructs syslogd to drop consecutive messages
+//config: which are totally the same.
+//config:
+//config:config FEATURE_SYSLOGD_CFG
+//config: bool "Support syslog.conf"
+//config: default y
+//config: depends on SYSLOGD
+//config: help
+//config: Supports restricted syslogd config. See docs/syslog.conf.txt
+//config:
+//config:config FEATURE_SYSLOGD_READ_BUFFER_SIZE
+//config: int "Read buffer size in bytes"
+//config: default 256
+//config: range 256 20000
+//config: depends on SYSLOGD
+//config: help
+//config: This option sets the size of the syslog read buffer.
+//config: Actual memory usage increases around five times the
+//config: change done here.
+//config:
+//config:config FEATURE_IPC_SYSLOG
+//config: bool "Circular Buffer support"
+//config: default y
+//config: depends on SYSLOGD
+//config: help
+//config: When you enable this feature, the syslogd utility will
+//config: use a circular buffer to record system log messages.
+//config: When the buffer is filled it will continue to overwrite
+//config: the oldest messages. This can be very useful for
+//config: systems with little or no permanent storage, since
+//config: otherwise system logs can eventually fill up your
+//config: entire filesystem, which may cause your system to
+//config: break badly.
+//config:
+//config:config FEATURE_IPC_SYSLOG_BUFFER_SIZE
+//config: int "Circular buffer size in Kbytes (minimum 4KB)"
+//config: default 16
+//config: range 4 2147483647
+//config: depends on FEATURE_IPC_SYSLOG
+//config: help
+//config: This option sets the size of the circular buffer
+//config: used to record system log messages.
+//config:
+//config:config FEATURE_KMSG_SYSLOG
+//config: bool "Linux kernel printk buffer support"
+//config: default y
+//config: depends on SYSLOGD
+//config: select PLATFORM_LINUX
+//config: help
+//config: When you enable this feature, the syslogd utility will
+//config: write system log message to the Linux kernel's printk buffer.
+//config: This can be used as a smaller alternative to the syslogd IPC
+//config: support, as klogd and logread aren't needed.
+//config:
+//config: NOTICE: Syslog facilities in log entries needs kernel 3.5+.
+
+//applet:IF_SYSLOGD(APPLET(syslogd, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SYSLOGD) += syslogd_and_logger.o
//usage:#define syslogd_trivial_usage
//usage: "[OPTIONS]"
@@ -21,31 +122,31 @@
//usage: "(this version of syslogd ignores /etc/syslog.conf)\n"
//usage: )
//usage: "\n -n Run in foreground"
-//usage: "\n -O FILE Log to FILE (default:/var/log/messages)"
-//usage: "\n -l N Log only messages more urgent than prio N (1-8)"
-//usage: "\n -S Smaller output"
-//usage: IF_FEATURE_ROTATE_LOGFILE(
-//usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)"
-//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)"
-//usage: )
//usage: IF_FEATURE_REMOTE_LOG(
//usage: "\n -R HOST[:PORT] Log to HOST:PORT (default PORT:514)"
//usage: "\n -L Log locally and via network (default is network only if -R)"
//usage: )
-//usage: IF_FEATURE_SYSLOGD_DUP(
-//usage: "\n -D Drop duplicates"
-//usage: )
//usage: IF_FEATURE_IPC_SYSLOG(
/* NB: -Csize shouldn't have space (because size is optional) */
//usage: "\n -C[size_kb] Log to shared mem buffer (use logread to read it)"
//usage: )
+//usage: IF_FEATURE_KMSG_SYSLOG(
+//usage: "\n -K Log to kernel printk buffer (use dmesg to read it)"
+//usage: )
+//usage: "\n -O FILE Log to FILE (default: /var/log/messages, stdout if -)"
+//usage: IF_FEATURE_ROTATE_LOGFILE(
+//usage: "\n -s SIZE Max size (KB) before rotation (default:200KB, 0=off)"
+//usage: "\n -b N N rotated logs to keep (default:1, max=99, 0=purge)"
+//usage: )
+//usage: "\n -l N Log only messages more urgent than prio N (1-8)"
+//usage: "\n -S Smaller output"
+//usage: IF_FEATURE_SYSLOGD_DUP(
+//usage: "\n -D Drop duplicates"
+//usage: )
//usage: IF_FEATURE_SYSLOGD_CFG(
//usage: "\n -f FILE Use FILE as config (default:/etc/syslog.conf)"
//usage: )
/* //usage: "\n -m MIN Minutes between MARK lines (default:20, 0=off)" */
-//usage: IF_FEATURE_KMSG_SYSLOG(
-//usage: "\n -K Log to kernel printk buffer (use dmesg to read it)"
-//usage: )
//usage:
//usage:#define syslogd_example_usage
//usage: "$ syslogd -R masterlog:514\n"
@@ -110,6 +211,7 @@ typedef struct {
typedef struct logFile_t {
const char *path;
int fd;
+ time_t last_log_time;
#if ENABLE_FEATURE_ROTATE_LOGFILE
unsigned size;
uint8_t isRegular;
@@ -165,7 +267,6 @@ struct globals {
#if ENABLE_FEATURE_IPC_SYSLOG
struct shbuf_ds *shbuf;
#endif
- time_t last_log_time;
/* localhost's name. We print only first 64 chars */
char *hostname;
@@ -237,12 +338,11 @@ enum {
OPT_dup = IF_FEATURE_SYSLOGD_DUP( (1 << OPTBIT_dup )) + 0,
OPT_cfg = IF_FEATURE_SYSLOGD_CFG( (1 << OPTBIT_cfg )) + 0,
OPT_kmsg = IF_FEATURE_KMSG_SYSLOG( (1 << OPTBIT_kmsg )) + 0,
-
};
#define OPTION_STR "m:nO:l:S" \
IF_FEATURE_ROTATE_LOGFILE("s:" ) \
IF_FEATURE_ROTATE_LOGFILE("b:" ) \
- IF_FEATURE_REMOTE_LOG( "R:" ) \
+ IF_FEATURE_REMOTE_LOG( "R:*") \
IF_FEATURE_REMOTE_LOG( "L" ) \
IF_FEATURE_IPC_SYSLOG( "C::") \
IF_FEATURE_SYSLOGD_DUP( "D" ) \
@@ -569,7 +669,7 @@ static void log_to_kmsg(int pri, const char *msg)
*/
pri &= G.primask;
- write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));
+ full_write(G.kmsgfd, G.printbuf, sprintf(G.printbuf, "<%d>%s\n", pri, msg));
}
#else
static void kmsg_init(void) {}
@@ -585,42 +685,54 @@ static void log_locally(time_t now, char *msg, logFile_t *log_file)
#endif
int len = strlen(msg);
- if (log_file->fd >= 0) {
- /* Reopen log file every second. This allows admin
- * to delete the file and not worry about restarting us.
+ /* fd can't be 0 (we connect fd 0 to /dev/log socket) */
+ /* fd is 1 if "-O -" is in use */
+ if (log_file->fd > 1) {
+ /* Reopen log files every second. This allows admin
+ * to delete the files and not worry about restarting us.
* This costs almost nothing since it happens
- * _at most_ once a second.
+ * _at most_ once a second for each file, and happens
+ * only when each file is actually written.
*/
if (!now)
now = time(NULL);
- if (G.last_log_time != now) {
- G.last_log_time = now;
+ if (log_file->last_log_time != now) {
+ log_file->last_log_time = now;
close(log_file->fd);
goto reopen;
}
- } else {
+ }
+ else if (log_file->fd == 1) {
+ /* We are logging to stdout: do nothing */
+ }
+ else {
+ if (LONE_DASH(log_file->path)) {
+ log_file->fd = 1;
+ /* log_file->isRegular = 0; - already is */
+ } else {
reopen:
- log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
+ log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
| O_NOCTTY | O_APPEND | O_NONBLOCK,
0666);
- if (log_file->fd < 0) {
- /* cannot open logfile? - print to /dev/console then */
- int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
- if (fd < 0)
- fd = 2; /* then stderr, dammit */
- full_write(fd, msg, len);
- if (fd != 2)
- close(fd);
- return;
- }
+ if (log_file->fd < 0) {
+ /* cannot open logfile? - print to /dev/console then */
+ int fd = device_open(DEV_CONSOLE, O_WRONLY | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ fd = 2; /* then stderr, dammit */
+ full_write(fd, msg, len);
+ if (fd != 2)
+ close(fd);
+ return;
+ }
#if ENABLE_FEATURE_ROTATE_LOGFILE
- {
- struct stat statf;
- log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
- /* bug (mostly harmless): can wrap around if file > 4gb */
- log_file->size = statf.st_size;
- }
+ {
+ struct stat statf;
+ log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
+ /* bug (mostly harmless): can wrap around if file > 4gb */
+ log_file->size = statf.st_size;
+ }
#endif
+ }
}
#ifdef SYSLOGD_WRLOCK
@@ -648,26 +760,33 @@ static void log_locally(time_t now, char *msg, logFile_t *log_file)
}
/* newFile == "f.0" now */
rename(log_file->path, newFile);
- /* Incredibly, if F and F.0 are hardlinks, POSIX
- * _demands_ that rename returns 0 but does not
- * remove F!!!
- * (hardlinked F/F.0 pair was observed after
- * power failure during rename()).
- * Ensure old file is gone:
- */
- unlink(log_file->path);
+ }
+
+ /* We may or may not have just renamed the file away;
+ * if we didn't rename because we aren't keeping any backlog,
+ * then it's time to clobber the file. If we did rename it...,
+ * incredibly, if F and F.0 are hardlinks, POSIX _demands_
+ * that rename returns 0 but does not remove F!!!
+ * (hardlinked F/F.0 pair was observed after
+ * power failure during rename()).
+ * So ensure old file is gone in any case:
+ */
+ unlink(log_file->path);
#ifdef SYSLOGD_WRLOCK
- fl.l_type = F_UNLCK;
- fcntl(log_file->fd, F_SETLKW, &fl);
+ fl.l_type = F_UNLCK;
+ fcntl(log_file->fd, F_SETLKW, &fl);
#endif
- close(log_file->fd);
- goto reopen;
- }
- ftruncate(log_file->fd, 0);
+ close(log_file->fd);
+ goto reopen;
}
- log_file->size +=
+/* TODO: what to do on write errors ("disk full")? */
+ len = full_write(log_file->fd, msg, len);
+ if (len > 0)
+ log_file->size += len;
+#else
+ full_write(log_file->fd, msg, len);
#endif
- full_write(log_file->fd, msg, len);
+
#ifdef SYSLOGD_WRLOCK
fl.l_type = F_UNLCK;
fcntl(log_file->fd, F_SETLKW, &fl);
@@ -817,11 +936,6 @@ static NOINLINE int create_socket(void)
int sock_fd;
char *dev_log_name;
-#if ENABLE_FEATURE_SYSTEMD
- if (sd_listen_fds() == 1)
- return SD_LISTEN_FDS_START;
-#endif
-
memset(&sunx, 0, sizeof(sunx));
sunx.sun_family = AF_UNIX;
@@ -863,7 +977,6 @@ static int try_to_resolve_remote(remoteHost_t *rh)
static void do_syslogd(void) NORETURN;
static void do_syslogd(void)
{
- int sock_fd;
#if ENABLE_FEATURE_REMOTE_LOG
llist_t *item;
#endif
@@ -884,7 +997,7 @@ static void do_syslogd(void)
signal(SIGALRM, do_mark);
alarm(G.markInterval);
#endif
- sock_fd = create_socket();
+ xmove_fd(create_socket(), STDIN_FILENO);
if (option_mask32 & OPT_circularlog)
ipcsyslog_init();
@@ -905,7 +1018,7 @@ static void do_syslogd(void)
recvbuf = G.recvbuf;
#endif
read_again:
- sz = read(sock_fd, recvbuf, MAX_READ - 1);
+ sz = read(STDIN_FILENO, recvbuf, MAX_READ - 1);
if (sz < 0) {
if (!bb_got_signal)
bb_perror_msg("read from %s", _PATH_LOG);
@@ -976,7 +1089,6 @@ static void do_syslogd(void)
} /* while (!bb_got_signal) */
timestamp_and_log_internal("syslogd exiting");
- puts("syslogd exiting");
remove_pidfile(CONFIG_PID_FILE_PATH "/syslogd.pid");
ipcsyslog_cleanup();
if (option_mask32 & OPT_kmsg)
@@ -996,8 +1108,8 @@ int syslogd_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
- /* No non-option params, -R can occur multiple times */
- opt_complementary = "=0" IF_FEATURE_REMOTE_LOG(":R::");
+ /* No non-option params */
+ opt_complementary = "=0";
opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
#if ENABLE_FEATURE_REMOTE_LOG
while (remoteAddrList) {
diff --git a/sysklogd/syslogd_and_logger.c b/sysklogd/syslogd_and_logger.c
index 0964f23..6458a93 100644
--- a/sysklogd/syslogd_and_logger.c
+++ b/sysklogd/syslogd_and_logger.c
@@ -8,6 +8,7 @@
*/
#include "libbb.h"
+#include "common_bufsiz.h"
#define SYSLOG_NAMES
#define SYSLOG_NAMES_CONST
#include <syslog.h>
diff --git a/testsuite/ar.tests b/testsuite/ar.tests
index 0a8eb9b..ad7b8fe 100755
--- a/testsuite/ar.tests
+++ b/testsuite/ar.tests
@@ -15,7 +15,7 @@ testing "ar creates archives" \
"$(md5sum <README)\n" \
"" \
""
-rm test.a
+rm test.a 2>/dev/null
testing "ar replaces things in archives" \
"echo 'blah!' >file1 && echo 'blast!' >file2 && ar cr test.a README file1 file2 && mv file2 file1 && ar cr test.a file1 && ar p test.a file1" \
diff --git a/testsuite/awk.tests b/testsuite/awk.tests
index 132afc6..82937bc 100755
--- a/testsuite/awk.tests
+++ b/testsuite/awk.tests
@@ -281,6 +281,11 @@ testing "awk length(array)" \
"2\n" \
"" ""
+testing "awk length()" \
+ "awk '{print length; print length(); print length(\"qwe\"); print length(99+9)}'" \
+ "3\n3\n3\n3\n" \
+ "" "qwe"
+
testing "awk -f and ARGC" \
"awk -f - input" \
"re\n2\n" \
@@ -295,6 +300,25 @@ testing "awk -e and ARGC" \
""
SKIP=
+# The examples are in fact not valid awk programs (break/continue
+# can only be used inside loops).
+# But we do accept them outside of loops.
+# We had a bug with misparsing "break ; else" sequence.
+# Test that *that* bug is fixed, using simplest possible scripts:
+testing "awk break" \
+ "awk -f - 2>&1; echo \$?" \
+ "0\n" \
+ "" \
+ 'BEGIN { if (1) break; else a = 1 }'
+testing "awk continue" \
+ "awk -f - 2>&1; echo \$?" \
+ "0\n" \
+ "" \
+ 'BEGIN { if (1) continue; else a = 1 }'
+
+testing "awk handles invalid for loop" \
+ "awk '{ for() }' 2>&1" "awk: cmd. line:1: Unexpected token\n" "" ""
+
# testing "description" "command" "result" "infile" "stdin"
exit $FAILCOUNT
diff --git a/testsuite/busybox.tests b/testsuite/busybox.tests
index 04fea3e..545cad5 100755
--- a/testsuite/busybox.tests
+++ b/testsuite/busybox.tests
@@ -6,6 +6,16 @@
. ./testing.sh
+ln -s `which busybox` unknown
+
+testing "busybox as unknown name" "./unknown 2>&1" \
+ "unknown: applet not found\n" "" ""
+rm unknown
+
+# We need busybox --help to be enabled for the rest of tests
+test x"$CONFIG_BUSYBOX" = x"y" \
+|| { echo "SKIPPED: busybox --help"; exit 0; }
+
HELPDUMP=`true | busybox 2>&1 | cat`
# We need to test under calling the binary under other names.
@@ -38,10 +48,4 @@ do
done
rm busybox-suffix
-ln -s `which busybox` unknown
-
-testing "busybox as unknown name" "./unknown 2>&1" \
- "unknown: applet not found\n" "" ""
-rm unknown
-
exit $FAILCOUNT
diff --git a/testsuite/bzcat.tests b/testsuite/bzcat.tests
index 1c1fd65..0ae530d 100755
--- a/testsuite/bzcat.tests
+++ b/testsuite/bzcat.tests
@@ -2,8 +2,6 @@
FAILCOUNT=0
-ext=bz2
-
bb="busybox "
unset LC_ALL
@@ -11,6 +9,11 @@ unset LC_MESSAGES
unset LANG
unset LANGUAGE
+hello_Z() {
+ # Compressed "HELLO\n"
+ $ECHO -ne "\x1f\x9d\x90\x48\x8a\x30\x61\xf2\x44\x01"
+}
+
hello_gz() {
# Gzipped "HELLO\n"
#_________________________ vvv vvv vvv vvv - mtime
@@ -25,32 +28,38 @@ hello_bz2() {
$ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3"
}
-prep() {
- rm -f t*
- hello_$ext >t1.$ext
- hello_$ext >t2.$ext
-}
-
-check() {
- eval $2 >t_actual 2>&1
- if $ECHO -ne "$expected" | cmp - t_actual; then
- echo "PASS: $1"
- else
- echo "FAIL: $1"
- FAILCOUNT=$((FAILCOUNT + 1))
- fi
-}
-
-mkdir testdir 2>/dev/null
-(
-cd testdir || { echo "cannot cd testdir!"; exit 1; }
-
-expected="HELLO\nok\n"
-prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo ok"
-
-)
-rm -rf testdir
-
+for ext in \
+ `test x"$CONFIG_GUNZIP" = x"y" && echo gz` \
+ `test x"$CONFIG_BUNZIP2" = x"y" && echo bz2` \
+ `test x"$CONFIG_UNCOMPRESS" = x"y" && echo Z`
+do
+ prep() {
+ rm -f t1.$ext t2.$ext t_actual
+ hello_$ext >t1.$ext
+ hello_$ext >t2.$ext
+ }
+
+ check() {
+ eval $2 >t_actual 2>&1
+ if $ECHO -ne "$expected" | cmp - t_actual; then
+ echo "PASS: $1"
+ else
+ echo "FAIL: $1"
+ FAILCOUNT=$((FAILCOUNT + 1))
+ fi
+ }
+
+ mkdir testdir 2>/dev/null
+ (
+ cd testdir || { echo "cannot cd testdir!"; exit 1; }
+ expected="HELLO\nok\n"
+ prep
+ check "zcat: dont delete $ext src" "${bb}zcat t2.$ext; test -f t2.$ext && echo ok"
+ exit $FAILCOUNT
+ )
+ FAILCOUNT=$?
+ rm -rf testdir
+done
# Copyright 2011 by Denys Vlasenko
@@ -60,6 +69,8 @@ rm -rf testdir
# testing "test name" "command" "expected result" "file input" "stdin"
+## bzip algorithm
+
# "input" file is bzipped file with "a\n" data
testing "bzcat can print many files" \
"$ECHO -ne '$hexdump' | bzcat input input; echo \$?" \
@@ -79,6 +90,27 @@ testing "bzcat can handle compressed zero-length bzip2 files" \
"0\n" \
"\x42\x5a\x68\x39\x17\x72\x45\x38\x50\x90\x00\x00\x00\x00" ""
+## compress algorithm
+
+# "input" file is compressed (.Z) file with "a\n" data
+test x"$CONFIG_UNCOMPRESS" = x"y" && \
+testing "zcat can print many files" \
+"$ECHO -ne '$hexdump' | zcat input input; echo \$?" \
+"\
+a
+a
+0
+" "\
+\x1f\x9d\x90\x61\x14\x00\
+" ""
+
+# "input" file is compressed (.Z) zero byte file
+test x"$CONFIG_UNCOMPRESS" = x"y" && \
+testing "zcat can handle compressed zero-length (.Z) files" \
+"$ECHO -ne '$hexdump' | zcat input input; echo \$?" \
+"0\n" \
+"\x1f\x9d\x90\x00" ""
+
exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
diff --git a/testsuite/bunzip2/bzcat-does-not-remove-compressed-file b/testsuite/bzcat/bzcat-does-not-remove-compressed-file
index 7d4016e..7d4016e 100644
--- a/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
+++ b/testsuite/bzcat/bzcat-does-not-remove-compressed-file
diff --git a/testsuite/cpio.tests b/testsuite/cpio.tests
index 4cd441a..88ec086 100755
--- a/testsuite/cpio.tests
+++ b/testsuite/cpio.tests
@@ -127,6 +127,33 @@ testing "cpio extracts in existing directory" \
" "" ""
SKIP=
+optional FEATURE_CPIO_O
+testing "cpio uses by default uid/gid" \
+"echo $0 | cpio -o -H newc | cpio -tv 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \
+"\
+$user/$group
+0
+" "" ""
+SKIP=
+
+optional FEATURE_CPIO_O
+testing "cpio -R with create" \
+"echo $0 | cpio -o -H newc -R 1234:5678 | cpio -tv 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \
+"\
+1234/5678
+0
+" "" ""
+SKIP=
+
+optional FEATURE_CPIO_O
+testing "cpio -R with extract" \
+"echo $0 | cpio -o -H newc | cpio -tv -R 8765:4321 2>&1 | tail -n +2 | awk ' { print \$2 } '; echo \$?" \
+"\
+8765/4321
+0
+" "" ""
+SKIP=
+
# Clean up
rm -rf cpio.testdir cpio.testdir2 2>/dev/null
diff --git a/testsuite/dc.tests b/testsuite/dc.tests
new file mode 100755
index 0000000..a5da537
--- a/dev/null
+++ b/testsuite/dc.tests
@@ -0,0 +1,56 @@
+#!/bin/sh
+# Copyright 2015 by Bernhard Reutner-Fischer
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "dc basic syntax (stdin, multiple args)" \
+ "dc" \
+ "30\n" \
+ "" "10 20+p"
+
+testing "dc basic syntax (argv, single arg)" \
+ "dc '10 20+p'" \
+ "30\n" \
+ "" ""
+
+testing "dc basic syntax (argv, multiple args)" \
+ "dc 10 20+p" \
+ "30\n" \
+ "" ""
+
+testing "dc complex with spaces (single arg)" \
+ "dc '8 8 * 2 2 + / p'" \
+ "16\n" \
+ "" ""
+
+testing "dc complex without spaces (single arg)" \
+ "dc '8 8*2 2+/p'" \
+ "16\n" \
+ "" ""
+
+testing "dc complex with spaces (multiple args)" \
+ "dc 8 8 \* 2 2 + / p" \
+ "16\n" \
+ "" ""
+
+testing "dc complex without spaces (multiple args)" \
+ "dc 8 8\*2 2+/p" \
+ "16\n" \
+ "" ""
+
+exit $FAILCOUNT
+
+# we do not support arguments
+testing "dc -e <exprs>" \
+ "dc -e '10 2+f'" \
+ "12\n" \
+ "" ""
+
+testing "dc -f <exprs-from-given-file>" \
+ "dc -f input" \
+ "12\n" \
+ "10 2+f" ""
+
diff --git a/testsuite/diff.tests b/testsuite/diff.tests
index 6de4648..0ced0f2 100755
--- a/testsuite/diff.tests
+++ b/testsuite/diff.tests
@@ -44,6 +44,17 @@ testing "diff of stdin, twice" \
"" \
"stdin"
+testing "diff of empty file against stdin" \
+ "diff -u - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -1 +0,0 @@
+-a
+" \
+ "" \
+ "a\n"
+
testing "diff of empty file against nonempty one" \
"diff -u - input | $TRIM_TAB" \
"\
@@ -87,6 +98,18 @@ testing "diff -B does not ignore changes whose lines are not all blank" \
"a\n" \
"\nb\n\n"
+testing "diff -B ignores blank single line change" \
+ 'diff -qB - input; echo $?' \
+ "0\n" \
+ "\n1\n" \
+ "1\n"
+
+testing "diff -B does not ignore non-blank single line change" \
+ 'diff -qB - input; echo $?' \
+ "Files - and input differ\n1\n" \
+ "0\n" \
+ "1\n"
+
testing "diff always takes context from old file" \
"diff -ub - input | $TRIM_TAB" \
"\
diff --git a/testsuite/du/du-m-works b/testsuite/du/du-m-works
index 9fa7437..d9693c7 100644
--- a/testsuite/du/du-m-works
+++ b/testsuite/du/du-m-works
@@ -1,4 +1,4 @@
# FEATURE: CONFIG_FEATURE_HUMAN_READABLE
dd if=/dev/zero of=file bs=1M count=1 2>/dev/null
-test x"`busybox du -m .`" = x"1 ."
+test x"`busybox du -m file`" = x"1 file"
diff --git a/testsuite/find.tests b/testsuite/find.tests
new file mode 100755
index 0000000..138236c
--- a/dev/null
+++ b/testsuite/find.tests
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+# Copyright 2014 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "command" "result" "infile" "stdin"
+
+mkdir -p find.tempdir
+touch find.tempdir/testfile
+
+optional FEATURE_FIND_TYPE
+testing "find -type f" \
+ "cd find.tempdir && find -type f 2>&1" \
+ "./testfile\n" \
+ "" ""
+SKIP=
+optional FEATURE_FIND_EXEC
+testing "find -exec exitcode 1" \
+ "cd find.tempdir && find testfile -exec true {} \; 2>&1; echo \$?" \
+ "0\n" \
+ "" ""
+SKIP=
+optional FEATURE_FIND_EXEC_PLUS
+testing "find -exec exitcode 2" \
+ "cd find.tempdir && find testfile -exec true {} + 2>&1; echo \$?" \
+ "0\n" \
+ "" ""
+SKIP=
+# Surprisingly, "-exec false ;" results in exitcode 0! "-exec false +" is different!!!
+optional FEATURE_FIND_EXEC
+testing "find -exec exitcode 3" \
+ "cd find.tempdir && find testfile -exec false {} \; 2>&1; echo \$?" \
+ "0\n" \
+ "" ""
+SKIP=
+optional FEATURE_FIND_EXEC_PLUS
+testing "find -exec exitcode 4" \
+ "cd find.tempdir && find testfile -exec false {} + 2>&1; echo \$?" \
+ "1\n" \
+ "" ""
+SKIP=
+optional FEATURE_FIND_MAXDEPTH
+testing "find / -maxdepth 0 -name /" \
+ "find / -maxdepth 0 -name /" \
+ "/\n" \
+ "" ""
+testing "find // -maxdepth 0 -name /" \
+ "find // -maxdepth 0 -name /" \
+ "//\n" \
+ "" ""
+testing "find / -maxdepth 0 -name //" \
+ "find / -maxdepth 0 -name //" \
+ "" \
+ "" ""
+testing "find // -maxdepth 0 -name //" \
+ "find // -maxdepth 0 -name //" \
+ "" \
+ "" ""
+SKIP=
+
+testing "find ./// -name ." \
+ "find ./// -name ." \
+ ".///\n" \
+ "" ""
+testing "find ./// -name .///" \
+ "find ./// -name .///" \
+ "" \
+ "" ""
+
+# testing "description" "command" "result" "infile" "stdin"
+
+rm -rf find.tempdir
+
+exit $FAILCOUNT
diff --git a/testsuite/grep.tests b/testsuite/grep.tests
index 412efff..ed4ba45 100755
--- a/testsuite/grep.tests
+++ b/testsuite/grep.tests
@@ -96,7 +96,7 @@ testing "grep -x -F (partial match 1)" "grep -x -F foo input ; echo \$?" \
testing "grep -x -F (partial match 2)" "grep -x -F foo input ; echo \$?" \
"1\n" "bar foo\n" ""
-optional FEATURE_GREP_EGREP_ALIAS
+optional EGREP
testing "grep -E supports extended regexps" "grep -E fo+" "foo\n" "" \
"b\ar\nfoo\nbaz"
testing "grep is also egrep" "egrep foo" "foo\n" "" "foo\nbar\n"
@@ -159,6 +159,38 @@ testing "grep -w ^ doesn't hang" \
"anything\n" \
""
+testing "grep -w word doesn't match wordword" \
+ "grep -w word input" \
+ "" \
+ "wordword\n" \
+ ""
+
+testing "grep -w word match second word" \
+ "grep -w word input" \
+ "bword,word\n""wordb,word\n""bwordb,word\n" \
+ "bword,word\n""wordb,word\n""bwordb,word\n" \
+ ""
+
+# -r on symlink to dir should recurse into dir
+mkdir -p grep.testdir/foo
+echo bar > grep.testdir/foo/file
+ln -s foo grep.testdir/symfoo
+testing "grep -r on symlink to dir" \
+ "grep -r . grep.testdir/symfoo" \
+ "grep.testdir/symfoo/file:bar\n" \
+ "" ""
+rm -Rf grep.testdir
+
+# But -r on dir/symlink_to_dir should not recurse into symlink_to_dir
+mkdir -p grep.testdir/foo
+echo bar > grep.testdir/foo/file
+ln -s foo grep.testdir/symfoo
+testing "grep -r on dir/symlink to dir" \
+ "grep -r . grep.testdir" \
+ "grep.testdir/foo/file:bar\n" \
+ "" ""
+rm -Rf grep.testdir
+
# testing "test name" "commands" "expected result" "file input" "stdin"
# file input will be file called "input"
# test can create a file "actual" instead of writing to stdout
diff --git a/testsuite/gzip/gzip-compression-levels b/testsuite/gzip/gzip-compression-levels
new file mode 100644
index 0000000..6d9a13d
--- a/dev/null
+++ b/testsuite/gzip/gzip-compression-levels
@@ -0,0 +1,5 @@
+# FEATURE: CONFIG_FEATURE_GZIP_LEVELS
+
+level1=$(busybox gzip -c -1 $(which busybox) | wc -c)
+level9=$(busybox gzip -c -9 $(which busybox) | wc -c)
+test $level1 -gt $level9
diff --git a/testsuite/makedevs.tests b/testsuite/makedevs.tests
index fd12460..b51fe07 100755
--- a/testsuite/makedevs.tests
+++ b/testsuite/makedevs.tests
@@ -25,7 +25,7 @@ FILTER_LS2="sed -e 's/, */,/g' -e 's/ */ /g' | cut -d' ' -f 1-4,9-"
rm -rf makedevs.testdir
mkdir makedevs.testdir
-optional FEATURE_MAKEDEVS_TABLE FEATURE_FIND_NOT FEATURE_FIND_TYPE FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES
+optional FEATURE_MAKEDEVS_TABLE FEATURE_FIND_NOT FEATURE_FIND_TYPE FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES FEATURE_LS_TIMESTAMPS
testing "makedevs -d ../makedevs.device_table.txt ." \
"(cd makedevs.testdir && makedevs -d ../makedevs.device_table.txt . 2>&1);
find makedevs.testdir ! -type d | sort | xargs ls -lnR | $FILTER_LS" \
diff --git a/testsuite/md5sum.tests b/testsuite/md5sum.tests
index 6c75b6d..cca26dc 100755
--- a/testsuite/md5sum.tests
+++ b/testsuite/md5sum.tests
@@ -23,6 +23,8 @@ test -f "$bindir/.config" && . "$bindir/.config"
test x"$CONFIG_FEATURE_FANCY_HEAD" != x"y" \
&& { echo "SKIPPED: $sum"; exit 0; }
+FAILCOUNT=0
+
text="The quick brown fox jumps over the lazy dog"
text=`yes "$text" | head -c 9999`
@@ -33,11 +35,21 @@ while test $n -le 999; do
n=$(($n+1))
done | "$sum"
)`
-
-if test x"$result" = x"$expected -"; then
+if test x"$result" != x"$expected -"; then
+ echo "FAIL: $sum (r:$result exp:$expected)"
+ : $((FAILCOUNT++))
+else
echo "PASS: $sum"
- exit 0
fi
-echo "FAIL: $sum (r:$result exp:$expected)"
-exit 1
+# GNU compat: -c EMPTY must fail (exitcode 1)!
+>EMPTY
+if "$sum" -c EMPTY 2>/dev/null; then
+ echo "FAIL: $sum -c EMPTY"
+ : $((FAILCOUNT++))
+else
+ echo "PASS: $sum -c EMPTY"
+fi
+rm EMPTY
+
+exit $FAILCOUNT
diff --git a/testsuite/mdev.tests b/testsuite/mdev.tests
index 48d3dcc..5987301 100755
--- a/testsuite/mdev.tests
+++ b/testsuite/mdev.tests
@@ -168,7 +168,7 @@ SKIP=
# continuing to use directory structure from prev test
rm -rf mdev.testdir/dev/*
echo "sda 0:0 644 @echo @echo TEST" >mdev.testdir/etc/mdev.conf
-optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH
+optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH ASH_BUILTIN_ECHO
testing "mdev command" \
"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
ls -lnR mdev.testdir/dev | $FILTER_LS" \
diff --git a/testsuite/patch.tests b/testsuite/patch.tests
index 2759d2a..3920524 100755
--- a/testsuite/patch.tests
+++ b/testsuite/patch.tests
@@ -242,6 +242,51 @@ patch: can't open 'dir2///file': No such file or directory
zxc
"
+testing "patch internal buffering bug?" \
+ 'patch -p1 2>&1; echo $?; cat input' \
+"\
+patching file input
+0
+foo
+
+
+
+
+
+
+1
+2
+3
+
+bar
+" \
+"\
+foo
+
+
+
+
+
+
+
+bar
+" \
+"\
+--- a/input.orig
++++ b/input
+@@ -5,5 +5,8 @@ foo
+
+
+
++1
++2
++3
+
+ bar
+--
+2.9.2
+" \
+
rm input.orig 2>/dev/null
exit $FAILCOUNT
diff --git a/testsuite/pwd/pwd-prints-working-directory b/testsuite/pwd/pwd-prints-working-directory
index 8575347..971adb5 100644
--- a/testsuite/pwd/pwd-prints-working-directory
+++ b/testsuite/pwd/pwd-prints-working-directory
@@ -1 +1,4 @@
-test $(pwd) = $(busybox pwd)
+# shell's $PWD may leave symlinks unresolved.
+# "pwd" may be a built-in and have the same problem.
+# External pwd _can't_ have that problem (current dir on Unix is physical).
+test $(`which pwd`) = $(busybox pwd)
diff --git a/testsuite/readlink.tests b/testsuite/readlink.tests
index c7fc8ad..e9d8da0 100755
--- a/testsuite/readlink.tests
+++ b/testsuite/readlink.tests
@@ -21,10 +21,15 @@ testing "readlink on a link" "readlink ./$TESTLINK" "./$TESTFILE\n" "" ""
optional FEATURE_READLINK_FOLLOW
-testing "readlink -f on a file" "readlink -f ./$TESTFILE" "$PWD/$TESTFILE\n" "" ""
-testing "readlink -f on a link" "readlink -f ./$TESTLINK" "$PWD/$TESTFILE\n" "" ""
+# shell's $PWD may leave symlinks unresolved.
+# "pwd" may be a built-in and have the same problem.
+# External pwd _can't_ have that problem (current dir on Unix is physical).
+pwd=`which pwd`
+pwd=`$pwd`
+testing "readlink -f on a file" "readlink -f ./$TESTFILE" "$pwd/$TESTFILE\n" "" ""
+testing "readlink -f on a link" "readlink -f ./$TESTLINK" "$pwd/$TESTFILE\n" "" ""
testing "readlink -f on an invalid link" "readlink -f ./$FAILLINK" "" "" ""
-testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$PWD/$TESTFILE\n" "" ""
+testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$pwd/$TESTFILE\n" "" ""
# clean up
diff --git a/testsuite/sed.tests b/testsuite/sed.tests
index 9494ac2..05c00a9 100755
--- a/testsuite/sed.tests
+++ b/testsuite/sed.tests
@@ -73,13 +73,9 @@ testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \
testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \
"1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n"
-test x"$SKIP_KNOWN_BUGS" = x"" && {
-# Normal sed end-of-script doesn't print "c" because n flushed the pattern
-# space. If n hits EOF, pattern space is empty when script ends.
-# Query: how does this interact with no newline at EOF?
testing "sed n (flushes pattern space, terminates early)" "sed -e 'n;p'" \
"a\nb\nb\nc\n" "" "a\nb\nc\n"
-}
+
# non-GNU sed: N does _not_ flush pattern space, therefore c is eaten @ script end
# GNU sed: N flushes pattern space, therefore c is printed too @ script end
testing "sed N (flushes pattern space (GNU behavior))" "sed -e 'N;p'" \
@@ -135,10 +131,12 @@ testing "sed empty file plus cat" "sed -e 's/nohit//' input -" "one\ntwo" \
"" "one\ntwo"
testing "sed cat plus empty file" "sed -e 's/nohit//' input -" "one\ntwo" \
"one\ntwo" ""
-test x"$SKIP_KNOWN_BUGS" = x"" && {
testing "sed append autoinserts newline" "sed -e '/woot/a woo' -" \
"woot\nwoo\n" "" "woot"
-}
+testing "sed append autoinserts newline 2" "sed -e '/oot/a woo' - input" \
+ "woot\nwoo\nboot\nwoo\n" "boot" "woot"
+testing "sed append autoinserts newline 3" "sed -e '/oot/a woo' -i input && cat input" \
+ "boot\nwoo\n" "boot" ""
testing "sed insert doesn't autoinsert newline" "sed -e '/woot/i woo' -" \
"woo\nwoot" "" "woot"
testing "sed print autoinsert newlines" "sed -e 'p' -" "one\none" "" "one"
@@ -275,6 +273,24 @@ testing "sed a cmd ended by double backslash" \
| two \\
'
+testing "sed a cmd understands \\n,\\t,\\r" \
+ "sed '/1/a\\\\t\\rzero\\none\\\\ntwo\\\\\\nthree'" \
+"\
+line1
+\t\rzero
+one\\\\ntwo\\
+three
+" "" "line1\n"
+
+testing "sed i cmd understands \\n,\\t,\\r" \
+ "sed '/1/i\\\\t\\rzero\\none\\\\ntwo\\\\\\nthree'" \
+"\
+\t\rzero
+one\\\\ntwo\\
+three
+line1
+" "" "line1\n"
+
# first three lines are deleted; 4th line is matched and printed by "2,3" and by "4" ranges
testing "sed with N skipping lines past ranges on next cmds" \
"sed -n '1{N;N;d};1p;2,3p;3p;4p'" \
@@ -329,6 +345,48 @@ line with \\
continuation
"
+testing "sed s///NUM test" \
+ "sed -e 's/a/b/2; s/a/c/g'" \
+ "cb\n" "" "aa\n"
+
+testing "sed /regex/,N{...} addresses work" \
+ "sed /^2/,2{d}" \
+ "1\n3\n4\n5\n" \
+ "" \
+ "1\n2\n3\n4\n5\n"
+
+testing "sed /regex/,+N{...} addresses work" \
+ "sed /^2/,+2{d}" \
+ "1\n5\n" \
+ "" \
+ "1\n2\n3\n4\n5\n"
+
+testing "sed /regex/,+N{...} -i works" \
+ "cat - >input2; sed /^4/,+2{d} -i input input2; echo \$?; cat input input2; rm input2" \
+ "0\n""1\n2\n3\n7\n8\n""1\n2\n7\n8\n" \
+ "1\n2\n3\n4\n5\n6\n7\n8\n" \
+ "1\n2\n4\n5\n6\n7\n8\n" \
+
+# GNU sed 4.2.1 would also accept "/^4/,+{d}" with the same meaning, we don't
+testing "sed /regex/,+0{...} -i works" \
+ "cat - >input2; sed /^4/,+0{d} -i input input2; echo \$?; cat input input2; rm input2" \
+ "0\n""1\n2\n3\n5\n6\n7\n8\n""1\n2\n5\n6\n7\n8\n" \
+ "1\n2\n3\n4\n5\n6\n7\n8\n" \
+ "1\n2\n4\n5\n6\n7\n8\n" \
+
+# GNU sed 4.2.1 would also accept "/^4/,+d" with the same meaning, we don't
+testing "sed /regex/,+0<cmd> -i works" \
+ "cat - >input2; sed /^4/,+0d -i input input2; echo \$?; cat input input2; rm input2" \
+ "0\n""1\n2\n3\n5\n6\n7\n8\n""1\n2\n5\n6\n7\n8\n" \
+ "1\n2\n3\n4\n5\n6\n7\n8\n" \
+ "1\n2\n4\n5\n6\n7\n8\n" \
+
+testing "sed 's///w FILE'" \
+ "sed 's/qwe/ZZZ/wz'; cat z; rm z" \
+ "123\nZZZ\nasd\n""ZZZ\n" \
+ "" \
+ "123\nqwe\nasd\n"
+
# testing "description" "commands" "result" "infile" "stdin"
exit $FAILCOUNT
diff --git a/testsuite/sha3sum.tests b/testsuite/sha3sum.tests
index 82fada6..2cd8e3b 100755
--- a/testsuite/sha3sum.tests
+++ b/testsuite/sha3sum.tests
@@ -1,3 +1,3 @@
#!/bin/sh
-. ./md5sum.tests sha3sum c29d77bc548fa2b20a04c861400a5360879c52156e2a54a3415b99a9a3123e1d5f36714a24eca8c1f05a8e2d8ba859c930d41141f64a255c6794436fc99c486a
+. ./md5sum.tests sha3sum 11659f09370139f8ef384f4a6260947fafa6e4fcd87a1ef3f35410e9
diff --git a/testsuite/sort.tests b/testsuite/sort.tests
index 91b282e..c51a8e4 100755
--- a/testsuite/sort.tests
+++ b/testsuite/sort.tests
@@ -47,8 +47,6 @@ egg 1 2 papyrus
999 3 0 algebra
" "$data" ""
-test x"$SKIP_KNOWN_BUGS" = x"" && {
-# Busybox is definitely doing these wrong. FIXME
testing "sort key range with numeric option and global reverse" \
"sort -k2,3n -r input" \
"egg 1 2 papyrus
@@ -65,7 +63,6 @@ testing "sort key range with multiple options" "sort -k2,3rn input" \
42 1 3 woot
egg 1 2 papyrus
" "$data" ""
-}
testing "sort key range with two -k options" "sort -k 2,2n -k 1,1r input" "\
d 2
@@ -101,6 +98,58 @@ testing "sort with non-default leading delim 3" "sort -n -k3 -t/ input" "\
//b/1
" ""
+testing "sort with non-default leading delim 4" "sort -t: -k1,1 input" "\
+a:b
+a/a:a
+" "\
+a/a:a
+a:b
+" ""
+
+testing "sort with ENDCHAR" "sort -t. -k1,1.1 -k2 input" "\
+ab.1
+aa.2
+" "\
+aa.2
+ab.1
+" ""
+
+testing "glibc build sort" "sort -t. -k 1,1 -k 2n,2n -k 3 input" "\
+GLIBC_2.1
+GLIBC_2.1.1
+GLIBC_2.2
+GLIBC_2.2.1
+GLIBC_2.10
+GLIBC_2.20
+GLIBC_2.21
+" "\
+GLIBC_2.21
+GLIBC_2.1.1
+GLIBC_2.2.1
+GLIBC_2.2
+GLIBC_2.20
+GLIBC_2.10
+GLIBC_2.1
+" ""
+
+testing "glibc build sort unique" "sort -u -t. -k 1,1 -k 2n,2n -k 3 input" "\
+GLIBC_2.1
+GLIBC_2.1.1
+GLIBC_2.2
+GLIBC_2.2.1
+GLIBC_2.10
+GLIBC_2.20
+GLIBC_2.21
+" "\
+GLIBC_2.10
+GLIBC_2.2.1
+GLIBC_2.1.1
+GLIBC_2.20
+GLIBC_2.2
+GLIBC_2.1
+GLIBC_2.21
+" ""
+
testing "sort -u should consider field only when discarding" "sort -u -k2 input" "\
a c
" "\
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 4929f4e..9f7ce15 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -24,7 +24,7 @@ tar: short read
"" ""
SKIP=
-optional FEATURE_SEAMLESS_GZ
+optional FEATURE_SEAMLESS_GZ GUNZIP
# In NOMMU case, "invalid magic" message comes from gunzip child process.
# Otherwise, it comes from tar.
# Need to fix output up to avoid false positive.
@@ -53,6 +53,15 @@ dd if=/dev/zero bs=512 count=20 2>/dev/null | tar xvf - 2>&1; echo $?
"" ""
SKIP=
+# "tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input":
+# GNU tar 1.26 records as hardlinks:
+# input_hard2 -> input_hard1
+# input_hard1 -> input_hard1 (!!!)
+# input_dir/file -> input_dir/file
+# input -> input
+# As of 1.24.0, we don't record last two: for them, nlink==1
+# and we check for "hardlink"ness only files with nlink!=1
+# We also don't use "hrw-r--r--" notation for hardlinks in "tar tv" listing.
optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
testing "tar hardlinks and repeated files" '\
rm -rf input_* test.tar 2>/dev/null
@@ -64,6 +73,7 @@ chmod -R 644 *
chmod 755 input_dir
tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input
tar tvf test.tar | sed "s/.*[0-9] input/input/"
+rm -rf input_dir
tar xf test.tar 2>&1
echo Ok: $?
ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
@@ -194,7 +204,7 @@ SKIP=
# Had a bug where on extract autodetect first "switched off" -z
# and then failed to recognize .tgz extension
-optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ
+optional FEATURE_TAR_CREATE FEATURE_SEAMLESS_GZ GUNZIP
testing "tar extract tgz" "\
dd count=1 bs=1M if=/dev/zero of=F0 2>/dev/null
tar -czf F0.tgz F0
@@ -246,6 +256,69 @@ Ok
"" ""
SKIP=
+# attack.tar.bz2 has symlink pointing to a system file
+# followed by a regular file with the same name
+# containing "root::0:0::/root:/bin/sh":
+# lrwxrwxrwx root/root passwd -> /tmp/passwd
+# -rw-r--r-- root/root passwd
+# naive tar implementation may end up creating the symlink
+# and then writing into it.
+# The correct implementation unlinks target before
+# creating the second file.
+# We test that /tmp/passwd remains empty:
+optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
+testing "tar does not extract into symlinks" "\
+>>/tmp/passwd && uudecode -o input && tar xf input 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
+" "\
+0
+" \
+"" "\
+begin-base64 644 attack.tar.bz2
+QlpoOTFBWSZTWRVn/bIAAKt7hMqwAEBAAP2QAhB0Y96AAACACCAAlISgpqe0
+po0DIaDynqAkpDRP1ANAhiYNSPR8VchKhAz0AK59+DA6FcMKBggOARIJdVHL
+DGllrjs20ATUgR1HmccBX3EhoMnpMJaNyggmxgLDMz54lBnBTJO/1L1lbMS4
+l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI=
+====
+"
+SKIP=
+# And same with -k
+optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_BZ2
+testing "tar -k does not extract into symlinks" "\
+>>/tmp/passwd && uudecode -o input && tar xf input -k 2>&1 && rm passwd; cat /tmp/passwd; echo \$?
+" "\
+tar: can't open 'passwd': File exists
+0
+" \
+"" "\
+begin-base64 644 attack.tar.bz2
+QlpoOTFBWSZTWRVn/bIAAKt7hMqwAEBAAP2QAhB0Y96AAACACCAAlISgpqe0
+po0DIaDynqAkpDRP1ANAhiYNSPR8VchKhAz0AK59+DA6FcMKBggOARIJdVHL
+DGllrjs20ATUgR1HmccBX3EhoMnpMJaNyggmxgLDMz54lBnBTJO/1L1lbMS4
+l4/V8LDoe90yiWJhOJvIypgEfxdyRThQkBVn/bI=
+====
+"
+SKIP=
+
+optional UNICODE_SUPPORT FEATURE_TAR_GNU_EXTENSIONS FEATURE_SEAMLESS_BZ2 FEATURE_TAR_AUTODETECT
+testing "Pax-encoded UTF8 names and symlinks" '\
+tar xvf ../tar.utf8.tar.bz2 2>&1; echo $?
+export LANG=en_US.UTF-8
+ls -l etc/ssl/certs/* | sed "s:.*etc/:etc/:" | sort
+unset LANG
+rm -rf etc usr
+' "\
+etc/ssl/certs/3b2716e5.0
+etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
+etc/ssl/certs/f80cc7f6.0
+usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt
+0
+etc/ssl/certs/3b2716e5.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
+etc/ssl/certs/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem -> /usr/share/ca-certificates/mozilla/EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.crt
+etc/ssl/certs/f80cc7f6.0 -> EBG_Elektronik_Sertifika_Hizmet_Sağlayıcısı.pem
+" \
+"" ""
+SKIP=
+
cd .. && rm -rf tar.tempdir || exit 1
diff --git a/testsuite/tar.utf8.tar.bz2 b/testsuite/tar.utf8.tar.bz2
new file mode 100644
index 0000000..0398e1a
--- a/dev/null
+++ b/testsuite/tar.utf8.tar.bz2
@@ -0,0 +1,3 @@
+BZh91AY&SY¨Õž"
+šÐÖCBôÂq<A¶ DRšú,@ðÁÄ£„¢
+C£´¤ÉSLšA‰bÔ¶h™¶X”9#,¾KÅ&Ôg’“.¾.Ñþ£¦Á<i¦©;£Ö*IEѻѿ&#þ.äŠp¡!Q«<D \ No newline at end of file
diff --git a/testsuite/test.tests b/testsuite/test.tests
index 2c92e34..1c2edaf 100755
--- a/testsuite/test.tests
+++ b/testsuite/test.tests
@@ -76,4 +76,24 @@ testing "test ! a = b -a ! c = d: should be true (0)" \
"0\n" \
"" ""
+testing "test '!' = '!': should be true (0)" \
+ "busybox test '!' = '!'; echo \$?" \
+ "0\n" \
+ "" ""
+
+testing "test '(' = '(': should be true (0)" \
+ "busybox test '(' = '('; echo \$?" \
+ "0\n" \
+ "" ""
+
+testing "test '!' '!' = '!': should be false (1)" \
+ "busybox test '!' '!' = '!'; echo \$?" \
+ "1\n" \
+ "" ""
+
+testing "test '!' '(' = '(': should be false (1)" \
+ "busybox test '!' '(' = '('; echo \$?" \
+ "1\n" \
+ "" ""
+
exit $FAILCOUNT
diff --git a/testsuite/unzip.tests b/testsuite/unzip.tests
index 8677a03..d8738a3 100755
--- a/testsuite/unzip.tests
+++ b/testsuite/unzip.tests
@@ -7,7 +7,7 @@
. ./testing.sh
-# testing "test name" "options" "expected result" "file input" "stdin"
+# testing "test name" "commands" "expected result" "file input" "stdin"
# file input will be file called "input"
# test can create a file "actual" instead of writing to stdout
@@ -30,6 +30,28 @@ testing "unzip (subdir only)" "unzip -q foo.zip foo/ && test -d foo && test ! -f
rmdir foo
rm foo.zip
+# File containing some damaged encrypted stream
+testing "unzip (bad archive)" "uudecode; unzip bad.zip 2>&1; echo \$?" \
+"Archive: bad.zip
+ inflating: ]3j½r«IK-%Ix
+unzip: corrupted data
+unzip: inflate error
+1
+" \
+"" "\
+begin-base64 644 bad.zip
+UEsDBBQAAgkIAAAAIQA5AAAANwAAADwAAAAQAAcAXTNqwr1ywqtJGxJLLSVJ
+eCkBD0AdKBk8JzQsIj01JC0/ORJQSwMEFAECCAAAAAAhADoAAAAPAAAANgAA
+AAwAAQASw73Ct1DCokohPXQiNjoUNTUiHRwgLT4WHlBLAQIQABQAAggIAAAA
+oQA5AAAANwAAADwAAAAQQAcADAAAACwAMgCAAAAAAABdM2rCvXLCq0kbEkst
+JUl4KQEPQB0oGSY4Cz4QNgEnJSYIPVBLAQIAABQAAggAAAAAIQAqAAAADwAA
+BDYAAAAMAAEADQAAADIADQAAAEEAAAASw73Ct1DKokohPXQiNzA+FAI1HCcW
+NzITNFBLBQUKAC4JAA04Cw0EOhZQSwUGAQAABAIAAgCZAAAAeQAAAAIALhM=
+====
+"
+
+rm *
+
# Clean up scratch directory.
cd ..
diff --git a/testsuite/which/which-uses-default-path b/testsuite/which/which-uses-default-path
index 349583d..92b6018 100644
--- a/testsuite/which/which-uses-default-path
+++ b/testsuite/which/which-uses-default-path
@@ -1,4 +1,4 @@
-BUSYBOX=$(command -pv busybox)
+BUSYBOX=$(command -v busybox)
SAVED_PATH=$PATH
unset PATH
$BUSYBOX which ls
diff --git a/util-linux/Config.src b/util-linux/Config.src
index 9af923e..3c522f9 100644
--- a/util-linux/Config.src
+++ b/util-linux/Config.src
@@ -7,650 +7,6 @@ menu "Linux System Utilities"
INSERT
-config ACPID
- bool "acpid"
- default y
- select PLATFORM_LINUX
- help
- acpid listens to ACPI events coming either in textual form from
- /proc/acpi/event (though it is marked deprecated it is still widely
- used and _is_ a standard) or in binary form from specified evdevs
- (just use /dev/input/event*).
-
- It parses the event to retrieve ACTION and a possible PARAMETER.
- It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
- (if the resulting path is a directory) or directly as an executable.
-
- N.B. acpid relies on run-parts so have the latter installed.
-
-config FEATURE_ACPID_COMPAT
- bool "Accept and ignore redundant options"
- default y
- depends on ACPID
- help
- Accept and ignore compatibility options -g -m -s -S -v.
-
-config BLKID
- bool "blkid"
- default y
- select PLATFORM_LINUX
- select VOLUMEID
- help
- Lists labels and UUIDs of all filesystems.
- WARNING:
- With all submodules selected, it will add ~8k to busybox.
-
-config FEATURE_BLKID_TYPE
- bool "Print filesystem type"
- default n
- depends on BLKID
- help
- Show TYPE="filesystem type"
-
-config DMESG
- bool "dmesg"
- default y
- select PLATFORM_LINUX
- help
- dmesg is used to examine or control the kernel ring buffer. When the
- Linux kernel prints messages to the system log, they are stored in
- the kernel ring buffer. You can use dmesg to print the kernel's ring
- buffer, clear the kernel ring buffer, change the size of the kernel
- ring buffer, and change the priority level at which kernel messages
- are also logged to the system console. Enable this option if you
- wish to enable the 'dmesg' utility.
-
-config FEATURE_DMESG_PRETTY
- bool "Pretty dmesg output"
- default y
- depends on DMESG
- help
- If you wish to scrub the syslog level from the output, say 'Y' here.
- The syslog level is a string prefixed to every line with the form
- "<#>".
-
- With this option you will see:
- # dmesg
- Linux version 2.6.17.4 .....
- BIOS-provided physical RAM map:
- BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
-
- Without this option (or with -r) you will see:
- # dmesg
- <5>Linux version 2.6.17.4 .....
- <6>BIOS-provided physical RAM map:
- <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
-
-config FEATURE_DMESG_COLOR
- bool "Colored dmesg output"
- default y
- depends on DMESG
- help
- Allow to show errors and warnings in different colors
- dmesg -C
-
-config FBSET
- bool "fbset"
- default y
- select PLATFORM_LINUX
- help
- fbset is used to show or change the settings of a Linux frame buffer
- device. The frame buffer device provides a simple and unique
- interface to access a graphics display. Enable this option
- if you wish to enable the 'fbset' utility.
-
-config FEATURE_FBSET_FANCY
- bool "Turn on extra fbset options"
- default y
- depends on FBSET
- help
- This option enables extended fbset options, allowing one to set the
- framebuffer size, color depth, etc. interface to access a graphics
- display. Enable this option if you wish to enable extended fbset
- options.
-
-config FEATURE_FBSET_READMODE
- bool "Turn on fbset readmode support"
- default y
- depends on FBSET
- help
- This option allows fbset to read the video mode database stored by
- default as /etc/fb.modes, which can be used to set frame buffer
- device to pre-defined video modes.
-
-config FDFLUSH
- bool "fdflush"
- default y
- select PLATFORM_LINUX
- help
- fdflush is only needed when changing media on slightly-broken
- removable media drives. It is used to make Linux believe that a
- hardware disk-change switch has been actuated, which causes Linux to
- forget anything it has cached from the previous media. If you have
- such a slightly-broken drive, you will need to run fdflush every time
- you change a disk. Most people have working hardware and can safely
- leave this disabled.
-
-config FDFORMAT
- bool "fdformat"
- default y
- select PLATFORM_LINUX
- help
- fdformat is used to low-level format a floppy disk.
-
-config FDISK
- bool "fdisk"
- default y
- select PLATFORM_LINUX
- help
- The fdisk utility is used to divide hard disks into one or more
- logical disks, which are generally called partitions. This utility
- can be used to list and edit the set of partitions or BSD style
- 'disk slices' that are defined on a hard drive.
-
-config FDISK_SUPPORT_LARGE_DISKS
- bool "Support over 4GB disks"
- default y
- depends on FDISK
- depends on !LFS # with LFS no special code is needed
- help
- Enable this option to support large disks > 4GB.
-
-config FEATURE_FDISK_WRITABLE
- bool "Write support"
- default y
- depends on FDISK
- help
- Enabling this option allows you to create or change a partition table
- and write those changes out to disk. If you leave this option
- disabled, you will only be able to view the partition table.
-
-config FEATURE_AIX_LABEL
- bool "Support AIX disklabels"
- default n
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to create or change AIX disklabels.
- Most people can safely leave this option disabled.
-
-config FEATURE_SGI_LABEL
- bool "Support SGI disklabels"
- default n
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to create or change SGI disklabels.
- Most people can safely leave this option disabled.
-
-config FEATURE_SUN_LABEL
- bool "Support SUN disklabels"
- default n
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to create or change SUN disklabels.
- Most people can safely leave this option disabled.
-
-config FEATURE_OSF_LABEL
- bool "Support BSD disklabels"
- default n
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to create or change BSD disklabels
- and define and edit BSD disk slices.
-
-config FEATURE_GPT_LABEL
- bool "Support GPT disklabels"
- default n
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to view GUID Partition Table
- disklabels.
-
-config FEATURE_FDISK_ADVANCED
- bool "Support expert mode"
- default y
- depends on FDISK && FEATURE_FDISK_WRITABLE
- help
- Enabling this option allows you to do terribly unsafe things like
- define arbitrary drive geometry, move the beginning of data in a
- partition, and similarly evil things. Unless you have a very good
- reason you would be wise to leave this disabled.
-
-config FINDFS
- bool "findfs"
- default y
- select PLATFORM_LINUX
- select VOLUMEID
- help
- Prints the name of a filesystem with given label or UUID.
- WARNING:
- With all submodules selected, it will add ~8k to busybox.
-
-config FLOCK
- bool "flock"
- default y
- help
- Manage locks from shell scripts
-
-config FREERAMDISK
- bool "freeramdisk"
- default y
- select PLATFORM_LINUX
- help
- Linux allows you to create ramdisks. This utility allows you to
- delete them and completely free all memory that was used for the
- ramdisk. For example, if you boot Linux into a ramdisk and later
- pivot_root, you may want to free the memory that is allocated to the
- ramdisk. If you have no use for freeing memory from a ramdisk, leave
- this disabled.
-
-config FSCK_MINIX
- bool "fsck_minix"
- default y
- help
- The minix filesystem is a nice, small, compact, read-write filesystem
- with little overhead. It is not a journaling filesystem however and
- can experience corruption if it is not properly unmounted or if the
- power goes off in the middle of a write. This utility allows you to
- check for and attempt to repair any corruption that occurs to a minix
- filesystem.
-
-config MKFS_EXT2
- bool "mkfs_ext2"
- default y
- select PLATFORM_LINUX
- help
- Utility to create EXT2 filesystems.
-
-config MKFS_MINIX
- bool "mkfs_minix"
- default y
- select PLATFORM_LINUX
- help
- The minix filesystem is a nice, small, compact, read-write filesystem
- with little overhead. If you wish to be able to create minix
- filesystems this utility will do the job for you.
-
-config FEATURE_MINIX2
- bool "Support Minix fs v2 (fsck_minix/mkfs_minix)"
- default y
- depends on FSCK_MINIX || MKFS_MINIX
- help
- If you wish to be able to create version 2 minix filesystems, enable
- this. If you enabled 'mkfs_minix' then you almost certainly want to
- be using the version 2 filesystem support.
-
-config MKFS_REISER
- bool "mkfs_reiser"
- default n
- select PLATFORM_LINUX
- help
- Utility to create ReiserFS filesystems.
- Note: this applet needs a lot of testing and polishing.
-
-config MKFS_VFAT
- bool "mkfs_vfat"
- default y
- select PLATFORM_LINUX
- help
- Utility to create FAT32 filesystems.
-
-config GETOPT
- bool "getopt"
- default y
- help
- The getopt utility is used to break up (parse) options in command
- lines to make it easy to write complex shell scripts that also check
- for legal (and illegal) options. If you want to write horribly
- complex shell scripts, or use some horribly complex shell script
- written by others, this utility may be for you. Most people will
- wisely leave this disabled.
-
-config FEATURE_GETOPT_LONG
- bool "Support option -l"
- default y if LONG_OPTS
- depends on GETOPT
- help
- Enable support for long options (option -l).
-
-config HEXDUMP
- bool "hexdump"
- default y
- help
- The hexdump utility is used to display binary data in a readable
- way that is comparable to the output from most hex editors.
-
-config FEATURE_HEXDUMP_REVERSE
- bool "Support -R, reverse of 'hexdump -Cv'"
- default y
- depends on HEXDUMP
- help
- The hexdump utility is used to display binary data in an ascii
- readable way. This option creates binary data from an ascii input.
- NB: this option is non-standard. It's unwise to use it in scripts
- aimed to be portable.
-
-config HD
- bool "hd"
- default y
- depends on HEXDUMP
- help
- hd is an alias to hexdump -C.
-
-config HWCLOCK
- bool "hwclock"
- default y
- select PLATFORM_LINUX
- help
- The hwclock utility is used to read and set the hardware clock
- on a system. This is primarily used to set the current time on
- shutdown in the hardware clock, so the hardware will keep the
- correct time when Linux is _not_ running.
-
-config FEATURE_HWCLOCK_LONG_OPTIONS
- bool "Support long options (--hctosys,...)"
- default y
- depends on HWCLOCK && LONG_OPTS
- help
- By default, the hwclock utility only uses short options. If you
- are overly fond of its long options, such as --hctosys, --utc, etc)
- then enable this option.
-
-config FEATURE_HWCLOCK_ADJTIME_FHS
- bool "Use FHS /var/lib/hwclock/adjtime"
- default n # util-linux-ng in Fedora 13 still uses /etc/adjtime
- depends on HWCLOCK
- help
- Starting with FHS 2.3, the adjtime state file is supposed to exist
- at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish
- to use the FHS behavior, answer Y here, otherwise answer N for the
- classic /etc/adjtime path.
-
- pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
-
-config IPCRM
- bool "ipcrm"
- default y
- help
- The ipcrm utility allows the removal of System V interprocess
- communication (IPC) objects and the associated data structures
- from the system.
-
-config IPCS
- bool "ipcs"
- default y
- depends on PLATFORM_LINUX
- help
- The ipcs utility is used to provide information on the currently
- allocated System V interprocess (IPC) objects in the system.
-
-config LOSETUP
- bool "losetup"
- default y
- select PLATFORM_LINUX
- help
- losetup is used to associate or detach a loop device with a regular
- file or block device, and to query the status of a loop device. This
- version does not currently support enabling data encryption.
-
-config LSPCI
- bool "lspci"
- default y
- #select PLATFORM_LINUX
- help
- lspci is a utility for displaying information about PCI buses in the
- system and devices connected to them.
-
- This version uses sysfs (/sys/bus/pci/devices) only.
-
-config LSUSB
- bool "lsusb"
- default y
- #select PLATFORM_LINUX
- help
- lsusb is a utility for displaying information about USB buses in the
- system and devices connected to them.
-
- This version uses sysfs (/sys/bus/usb/devices) only.
-
-config MKSWAP
- bool "mkswap"
- default y
- help
- The mkswap utility is used to configure a file or disk partition as
- Linux swap space. This allows Linux to use the entire file or
- partition as if it were additional RAM, which can greatly increase
- the capability of low-memory machines. This additional memory is
- much slower than real RAM, but can be very helpful at preventing your
- applications being killed by the Linux out of memory (OOM) killer.
- Once you have created swap space using 'mkswap' you need to enable
- the swap space using the 'swapon' utility.
-
-config FEATURE_MKSWAP_UUID
- bool "UUID support"
- default y
- depends on MKSWAP
- help
- Generate swap spaces with universally unique identifiers.
-
-config MORE
- bool "more"
- default y
- help
- more is a simple utility which allows you to read text one screen
- sized page at a time. If you want to read text that is larger than
- the screen, and you are using anything faster than a 300 baud modem,
- you will probably find this utility very helpful. If you don't have
- any need to reading text files, you can leave this disabled.
-
-config MOUNT
- bool "mount"
- default y
- select PLATFORM_LINUX
- help
- All files and filesystems in Unix are arranged into one big directory
- tree. The 'mount' utility is used to graft a filesystem onto a
- particular part of the tree. A filesystem can either live on a block
- device, or it can be accessible over the network, as is the case with
- NFS filesystems. Most people using BusyBox will also want to enable
- the 'mount' utility.
-
-config FEATURE_MOUNT_FAKE
- bool "Support option -f"
- default y
- depends on MOUNT
- help
- Enable support for faking a file system mount.
-
-config FEATURE_MOUNT_VERBOSE
- bool "Support option -v"
- default y
- depends on MOUNT
- help
- Enable multi-level -v[vv...] verbose messages. Useful if you
- debug mount problems and want to see what is exactly passed
- to the kernel.
-
-config FEATURE_MOUNT_HELPERS
- bool "Support mount helpers"
- default n
- depends on MOUNT
- help
- Enable mounting of virtual file systems via external helpers.
- E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
- "obexfs -b00.11.22.33.44.55 /mnt"
- Also "mount -t sometype [-o opts] fs /mnt" will try
- "sometype [-o opts] fs /mnt" if simple mount syscall fails.
- The idea is to use such virtual filesystems in /etc/fstab.
-
-config FEATURE_MOUNT_LABEL
- bool "Support specifying devices by label or UUID"
- default y
- depends on MOUNT
- select VOLUMEID
- help
- This allows for specifying a device by label or uuid, rather than by
- name. This feature utilizes the same functionality as blkid/findfs.
- This also enables label or uuid support for swapon.
-
-config FEATURE_MOUNT_NFS
- bool "Support mounting NFS file systems on Linux < 2.6.23"
- default n
- depends on MOUNT
- select FEATURE_HAVE_RPC
- select FEATURE_SYSLOG
- help
- Enable mounting of NFS file systems on Linux kernels prior
- to version 2.6.23. Note that in this case mounting of NFS
- over IPv6 will not be possible.
-
- Note that this option links in RPC support from libc,
- which is rather large (~10 kbytes on uclibc).
-
-config FEATURE_MOUNT_CIFS
- bool "Support mounting CIFS/SMB file systems"
- default y
- depends on MOUNT
- help
- Enable support for samba mounts.
-
-config FEATURE_MOUNT_FLAGS
- depends on MOUNT
- bool "Support lots of -o flags in mount"
- default y
- help
- Without this, mount only supports ro/rw/remount. With this, it
- supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
- noatime, diratime, nodiratime, loud, bind, move, shared, slave,
- private, unbindable, rshared, rslave, rprivate, and runbindable.
-
-config FEATURE_MOUNT_FSTAB
- depends on MOUNT
- bool "Support /etc/fstab and -a"
- default y
- help
- Support mount all and looking for files in /etc/fstab.
-
-config PIVOT_ROOT
- bool "pivot_root"
- default y
- select PLATFORM_LINUX
- help
- The pivot_root utility swaps the mount points for the root filesystem
- with some other mounted filesystem. This allows you to do all sorts
- of wild and crazy things with your Linux system and is far more
- powerful than 'chroot'.
-
- Note: This is for initrd in linux 2.4. Under initramfs (introduced
- in linux 2.6) use switch_root instead.
-
-config RDATE
- bool "rdate"
- default y
- help
- The rdate utility allows you to synchronize the date and time of your
- system clock with the date and time of a remote networked system using
- the RFC868 protocol, which is built into the inetd daemon on most
- systems.
-
-config RDEV
- bool "rdev"
- default y
- help
- Print the device node associated with the filesystem mounted at '/'.
-
-config READPROFILE
- bool "readprofile"
- default y
- #select PLATFORM_LINUX
- help
- This allows you to parse /proc/profile for basic profiling.
-
-config RTCWAKE
- bool "rtcwake"
- default y
- select PLATFORM_LINUX
- help
- Enter a system sleep state until specified wakeup time.
-
-config SCRIPT
- bool "script"
- default y
- help
- The script makes typescript of terminal session.
-
-config SCRIPTREPLAY
- bool "scriptreplay"
- default y
- help
- This program replays a typescript, using timing information
- given by script -t.
-
-config SETARCH
- bool "setarch"
- default y
- select PLATFORM_LINUX
- help
- The linux32 utility is used to create a 32bit environment for the
- specified program (usually a shell). It only makes sense to have
- this util on a system that supports both 64bit and 32bit userland
- (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
-
-config SWAPONOFF
- bool "swaponoff"
- default y
- select PLATFORM_LINUX
- help
- This option enables both the 'swapon' and the 'swapoff' utilities.
- Once you have created some swap space using 'mkswap', you also need
- to enable your swap space with the 'swapon' utility. The 'swapoff'
- utility is used, typically at system shutdown, to disable any swap
- space. If you are not using any swap space, you can leave this
- option disabled.
-
-config FEATURE_SWAPON_PRI
- bool "Support priority option -p"
- default y
- depends on SWAPONOFF
- help
- Enable support for setting swap device priority in swapon.
-
-config SWITCH_ROOT
- bool "switch_root"
- default y
- select PLATFORM_LINUX
- help
- The switch_root utility is used from initramfs to select a new
- root device. Under initramfs, you have to use this instead of
- pivot_root. (Stop reading here if you don't care why.)
-
- Booting with initramfs extracts a gzipped cpio archive into rootfs
- (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
- or unmounted*, pivot_root will not work from initramfs. Instead,
- switch_root deletes everything out of rootfs (including itself),
- does a mount --move that overmounts rootfs with the new root, and
- then execs the specified init program.
-
- * Because the Linux kernel uses rootfs internally as the starting
- and ending point for searching through the kernel's doubly linked
- list of active mount points. That's why.
-
-config UMOUNT
- bool "umount"
- default y
- select PLATFORM_LINUX
- help
- When you want to remove a mounted filesystem from its current mount
- point, for example when you are shutting down the system, the
- 'umount' utility is the tool to use. If you enabled the 'mount'
- utility, you almost certainly also want to enable 'umount'.
-
-config FEATURE_UMOUNT_ALL
- bool "Support option -a"
- default y
- depends on UMOUNT
- help
- Support -a option to unmount all currently mounted filesystems.
-
comment "Common options for mount/umount"
depends on MOUNT || UMOUNT
diff --git a/util-linux/Kbuild.src b/util-linux/Kbuild.src
index 468fc6b..6b4fb74 100644
--- a/util-linux/Kbuild.src
+++ b/util-linux/Kbuild.src
@@ -7,40 +7,3 @@
lib-y:=
INSERT
-lib-$(CONFIG_ACPID) += acpid.o
-lib-$(CONFIG_BLKID) += blkid.o
-lib-$(CONFIG_DMESG) += dmesg.o
-lib-$(CONFIG_FBSET) += fbset.o
-lib-$(CONFIG_FDFLUSH) += freeramdisk.o
-lib-$(CONFIG_FDFORMAT) += fdformat.o
-lib-$(CONFIG_FDISK) += fdisk.o
-lib-$(CONFIG_FINDFS) += findfs.o
-lib-$(CONFIG_FLOCK) += flock.o
-lib-$(CONFIG_FREERAMDISK) += freeramdisk.o
-lib-$(CONFIG_FSCK_MINIX) += fsck_minix.o
-lib-$(CONFIG_GETOPT) += getopt.o
-lib-$(CONFIG_HEXDUMP) += hexdump.o
-lib-$(CONFIG_HWCLOCK) += hwclock.o
-lib-$(CONFIG_IPCRM) += ipcrm.o
-lib-$(CONFIG_IPCS) += ipcs.o
-lib-$(CONFIG_LOSETUP) += losetup.o
-lib-$(CONFIG_LSPCI) += lspci.o
-lib-$(CONFIG_LSUSB) += lsusb.o
-lib-$(CONFIG_MKFS_EXT2) += mkfs_ext2.o
-lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o
-lib-$(CONFIG_MKFS_REISER) += mkfs_reiser.o
-lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o
-lib-$(CONFIG_MKSWAP) += mkswap.o
-lib-$(CONFIG_MORE) += more.o
-lib-$(CONFIG_MOUNT) += mount.o
-lib-$(CONFIG_PIVOT_ROOT) += pivot_root.o
-lib-$(CONFIG_RDATE) += rdate.o
-lib-$(CONFIG_RDEV) += rdev.o
-lib-$(CONFIG_READPROFILE) += readprofile.o
-lib-$(CONFIG_RTCWAKE) += rtcwake.o
-lib-$(CONFIG_SCRIPT) += script.o
-lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o
-lib-$(CONFIG_SETARCH) += setarch.o
-lib-$(CONFIG_SWAPONOFF) += swaponoff.o
-lib-$(CONFIG_SWITCH_ROOT) += switch_root.o
-lib-$(CONFIG_UMOUNT) += umount.o
diff --git a/util-linux/acpid.c b/util-linux/acpid.c
index 38421c2..3e68b61 100644
--- a/util-linux/acpid.c
+++ b/util-linux/acpid.c
@@ -6,6 +6,32 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config ACPID
+//config: bool "acpid"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: acpid listens to ACPI events coming either in textual form from
+//config: /proc/acpi/event (though it is marked deprecated it is still widely
+//config: used and _is_ a standard) or in binary form from specified evdevs
+//config: (just use /dev/input/event*).
+//config:
+//config: It parses the event to retrieve ACTION and a possible PARAMETER.
+//config: It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+//config: (if the resulting path is a directory) or directly as an executable.
+//config:
+//config: N.B. acpid relies on run-parts so have the latter installed.
+//config:
+//config:config FEATURE_ACPID_COMPAT
+//config: bool "Accept and ignore redundant options"
+//config: default y
+//config: depends on ACPID
+//config: help
+//config: Accept and ignore compatibility options -g -m -s -S -v.
+
+//applet:IF_ACPID(APPLET(acpid, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_ACPID) += acpid.o
//usage:#define acpid_trivial_usage
//usage: "[-df] [-c CONFDIR] [-l LOGFILE] [-a ACTIONFILE] [-M MAPFILE] [-e PROC_EVENT_FILE] [-p PIDFILE]"
@@ -121,10 +147,8 @@ static void process_event(const char *event)
char *handler = xasprintf("./%s", event);
const char *args[] = { "run-parts", handler, NULL };
- // debug info
- if (option_mask32 & OPT_d) {
- bb_error_msg("%s", event);
- }
+ // log the event
+ bb_error_msg("%s", event);
// spawn handler
// N.B. run-parts would require scripts to have #!/bin/sh
@@ -153,7 +177,7 @@ static const char *find_action(struct input_event *ev, const char *buf)
}
if (buf) {
- if (strncmp(buf, evt_tab[i].desc, strlen(buf)) == 0) {
+ if (is_prefixed_with(evt_tab[i].desc, buf)) {
action = evt_tab[i].desc;
break;
}
@@ -256,7 +280,7 @@ int acpid_main(int argc UNUSED_PARAM, char **argv)
/* No -d "Debug", we log to log file.
* This includes any output from children.
*/
- xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+ xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_APPEND), STDOUT_FILENO);
xdup2(STDOUT_FILENO, STDERR_FILENO);
/* Also, acpid's messages (but not children) will go to syslog too */
openlog(applet_name, LOG_PID, LOG_DAEMON);
diff --git a/util-linux/blkdiscard.c b/util-linux/blkdiscard.c
new file mode 100644
index 0000000..af0bc94
--- a/dev/null
+++ b/util-linux/blkdiscard.c
@@ -0,0 +1,82 @@
+/*
+ * Mini blkdiscard implementation for busybox
+ *
+ * Copyright (C) 2015 by Ari Sundholm <ari@tuxera.com> and Tuxera Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+//config:config BLKDISCARD
+//config: bool "blkdiscard"
+//config: default y
+//config: help
+//config: blkdiscard discards sectors on a given device.
+
+//kbuild:lib-$(CONFIG_BLKDISCARD) += blkdiscard.o
+//applet:IF_BLKDISCARD(APPLET(blkdiscard, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//usage:#define blkdiscard_trivial_usage
+//usage: "[-o OFS] [-l LEN] [-s] DEVICE"
+//usage:#define blkdiscard_full_usage "\n\n"
+//usage: "Discard sectors on DEVICE\n"
+//usage: "\n -o OFS Byte offset into device"
+//usage: "\n -l LEN Number of bytes to discard"
+//usage: "\n -s Perform a secure discard"
+//usage:
+//usage:#define blkdiscard_example_usage
+//usage: "$ blkdiscard -o 0 -l 1G /dev/sdb"
+
+#include "libbb.h"
+#include <linux/fs.h>
+
+int blkdiscard_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int blkdiscard_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned opts;
+ const char *offset_str = "0";
+ const char *length_str;
+ uint64_t offset; /* Leaving these two variables out does not */
+ uint64_t length; /* shrink code size and hampers readability. */
+ uint64_t range[2];
+// struct stat st;
+ int fd;
+
+ enum {
+ OPT_OFFSET = (1 << 0),
+ OPT_LENGTH = (1 << 1),
+ OPT_SECURE = (1 << 2),
+ };
+
+ opt_complementary = "=1";
+ opts = getopt32(argv, "o:l:s", &offset_str, &length_str);
+ argv += optind;
+
+ fd = xopen(argv[0], O_RDWR|O_EXCL);
+//Why bother, BLK[SEC]DISCARD will fail on non-blockdevs anyway?
+// xfstat(fd, &st);
+// if (!S_ISBLK(st.st_mode))
+// bb_error_msg_and_die("%s: not a block device", argv[0]);
+
+ offset = xatoull_sfx(offset_str, kMG_suffixes);
+
+ if (opts & OPT_LENGTH)
+ length = xatoull_sfx(length_str, kMG_suffixes);
+ else {
+ xioctl(fd, BLKGETSIZE64, &length);
+ length -= offset;
+ }
+
+ range[0] = offset;
+ range[1] = length;
+ ioctl_or_perror_and_die(fd,
+ (opts & OPT_SECURE) ? BLKSECDISCARD : BLKDISCARD,
+ &range,
+ "%s: %s failed",
+ argv[0],
+ (opts & OPT_SECURE) ? "BLKSECDISCARD" : "BLKDISCARD"
+ );
+
+ if (ENABLE_FEATURE_CLEAN_UP)
+ close(fd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/util-linux/blkid.c b/util-linux/blkid.c
index 1bbc803..b6f33b5 100644
--- a/util-linux/blkid.c
+++ b/util-linux/blkid.c
@@ -6,6 +6,26 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config BLKID
+//config: bool "blkid"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: select VOLUMEID
+//config: help
+//config: Lists labels and UUIDs of all filesystems.
+//config: WARNING:
+//config: With all submodules selected, it will add ~8k to busybox.
+//config:
+//config:config FEATURE_BLKID_TYPE
+//config: bool "Print filesystem type"
+//config: default n
+//config: depends on BLKID
+//config: help
+//config: Show TYPE="filesystem type"
+
+//applet:IF_BLKID(APPLET(blkid, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BLKID) += blkid.o
//usage:#define blkid_trivial_usage
//usage: "[BLOCKDEV]..."
diff --git a/util-linux/blockdev.c b/util-linux/blockdev.c
index e25e529..bf4d497 100644
--- a/util-linux/blockdev.c
+++ b/util-linux/blockdev.c
@@ -5,17 +5,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-
-//applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
-
//config:config BLOCKDEV
//config: bool "blockdev"
//config: default y
//config: help
//config: Performs some ioctls with block devices.
+//applet:IF_BLOCKDEV(APPLET(blockdev, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_BLOCKDEV) += blockdev.o
+
//usage:#define blockdev_trivial_usage
//usage: "OPTION BLOCKDEV"
//usage:#define blockdev_full_usage "\n\n"
diff --git a/util-linux/dmesg.c b/util-linux/dmesg.c
index ba52036..b7b2c69 100644
--- a/util-linux/dmesg.c
+++ b/util-linux/dmesg.c
@@ -8,58 +8,70 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config DMESG
+//config: bool "dmesg"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: dmesg is used to examine or control the kernel ring buffer. When the
+//config: Linux kernel prints messages to the system log, they are stored in
+//config: the kernel ring buffer. You can use dmesg to print the kernel's ring
+//config: buffer, clear the kernel ring buffer, change the size of the kernel
+//config: ring buffer, and change the priority level at which kernel messages
+//config: are also logged to the system console. Enable this option if you
+//config: wish to enable the 'dmesg' utility.
+//config:
+//config:config FEATURE_DMESG_PRETTY
+//config: bool "Pretty dmesg output"
+//config: default y
+//config: depends on DMESG
+//config: help
+//config: If you wish to scrub the syslog level from the output, say 'Y' here.
+//config: The syslog level is a string prefixed to every line with the form
+//config: "<#>".
+//config:
+//config: With this option you will see:
+//config: # dmesg
+//config: Linux version 2.6.17.4 .....
+//config: BIOS-provided physical RAM map:
+//config: BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+//config:
+//config: Without this option you will see:
+//config: # dmesg
+//config: <5>Linux version 2.6.17.4 .....
+//config: <6>BIOS-provided physical RAM map:
+//config: <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+//applet:IF_DMESG(APPLET(dmesg, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DMESG) += dmesg.o
//usage:#define dmesg_trivial_usage
//usage: "[-c] [-n LEVEL] [-s SIZE]"
-//usage: IF_FEATURE_DMESG_PRETTY(" [-r]")
-//usage: IF_FEATURE_DMESG_COLOR(" [-C]")
//usage:#define dmesg_full_usage "\n\n"
//usage: "Print or control the kernel ring buffer\n"
//usage: "\n -c Clear ring buffer after printing"
//usage: "\n -n LEVEL Set console logging level"
//usage: "\n -s SIZE Buffer size"
-//usage: IF_FEATURE_DMESG_PRETTY(
-//usage: "\n -r Show level prefix")
-//usage: IF_FEATURE_DMESG_COLOR(
-//usage: "\n -C Colored output")
+//usage: "\n -r Print raw message buffer"
#include <sys/klog.h>
#include "libbb.h"
-#if ENABLE_FEATURE_DMESG_COLOR
-#define COLOR_DEFAULT 0
-#define COLOR_WHITE 97
-#define COLOR_YELLOW 93
-#define COLOR_ORANGE 33
-#define COLOR_RED 91
-
-static void set_color(int color)
-{
- printf("%c[%dm", 0x1B, color);
-}
-
-#else
-#define set_color(c) {}
-#endif
-
int dmesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int dmesg_main(int argc UNUSED_PARAM, char **argv)
{
int len, level;
char *buf;
unsigned opts;
- int color = 0;
enum {
OPT_c = 1 << 0,
OPT_s = 1 << 1,
OPT_n = 1 << 2,
- OPT_r = 1 << 3,
- OPT_C = 1 << 4,
- OPT_end
+ OPT_r = 1 << 3
};
- opt_complementary = "s+:n+"; /* numeric */
- opts = getopt32(argv, "cs:n:rC", &len, &level);
+ opts = getopt32(argv, "cs:+n:+r", &len, &level);
if (opts & OPT_n) {
if (klogctl(8, NULL, (long) level))
bb_perror_msg_and_die("klogctl");
@@ -81,34 +93,13 @@ int dmesg_main(int argc UNUSED_PARAM, char **argv)
return EXIT_SUCCESS;
- if ((ENABLE_FEATURE_DMESG_PRETTY || (opts & OPT_C)) && !(opts & OPT_r)) {
+ if (ENABLE_FEATURE_DMESG_PRETTY && !(opts & OPT_r)) {
int last = '\n';
int in = 0;
/* Skip <[0-9]+> at the start of lines */
while (1) {
if (last == '\n' && buf[in] == '<') {
-
-#if ENABLE_FEATURE_DMESG_COLOR
- if (opts & OPT_C) {
- char *lvl = buf + in + 1;
- sscanf(lvl, "%d", &level);
-
- switch (level) {
- case 1:
- case 2:
- case 3: color = COLOR_RED; break;
- case 4: color = COLOR_ORANGE; break;
- case 5: color = COLOR_YELLOW; break;
- case 7: color = COLOR_WHITE; break;
- case 6: /* common dmesg info */
- default:
- color = COLOR_DEFAULT;
- }
-
- set_color(color);
- }
-#endif
while (buf[in++] != '>' && in < len)
;
} else {
@@ -129,9 +120,5 @@ int dmesg_main(int argc UNUSED_PARAM, char **argv)
if (ENABLE_FEATURE_CLEAN_UP) free(buf);
- /* Reset default terminal color */
- if (color)
- set_color(0);
-
return EXIT_SUCCESS;
}
diff --git a/util-linux/fatattr.c b/util-linux/fatattr.c
new file mode 100644
index 0000000..030978f
--- a/dev/null
+++ b/util-linux/fatattr.c
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Display or change file attributes on a fat file system
+ *
+ * Copyright 2005 H. Peter Anvin
+ * Busybox'ed (2014) by Pascal Bellard <pascal.bellard@ads-lu.com>
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+//config:config FATATTR
+//config: bool "fatattr"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: fatattr lists or changes the file attributes on a fat file system.
+
+//applet:IF_FATATTR(APPLET(fatattr, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FATATTR) += fatattr.o
+
+//usage:#define fatattr_trivial_usage
+//usage: "[-+rhsvda] FILE..."
+//usage:#define fatattr_full_usage "\n\n"
+//usage: "Change file attributes on FAT filesystem\n"
+//usage: "\n - Clear attributes"
+//usage: "\n + Set attributes"
+//usage: "\n r Read only"
+//usage: "\n h Hidden"
+//usage: "\n s System"
+//usage: "\n v Volume label"
+//usage: "\n d Directory"
+//usage: "\n a Archive"
+
+#include "libbb.h"
+/* linux/msdos_fs.h says: */
+#ifndef FAT_IOCTL_GET_ATTRIBUTES
+# define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, uint32_t)
+# define FAT_IOCTL_SET_ATTRIBUTES _IOW('r', 0x11, uint32_t)
+#endif
+
+/* Currently supports only the FAT flags, not the NTFS ones.
+ * Extra space at the end is a hack to print space separator in file listing.
+ * Let's hope no one ever passes space as an option char :)
+ */
+static const char bit_to_char[] ALIGN1 = "rhsvda67 ";
+
+static inline unsigned long get_flag(char c)
+{
+ const char *fp = strchr(bit_to_char, c);
+ if (!fp)
+ bb_error_msg_and_die("invalid character '%c'", c);
+ return 1 << (fp - bit_to_char);
+}
+
+static unsigned decode_arg(const char *arg)
+{
+ unsigned fl = 0;
+ while (*++arg)
+ fl |= get_flag(*arg);
+ return fl;
+}
+
+int fatattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fatattr_main(int argc UNUSED_PARAM, char **argv)
+{
+ unsigned set_mask = 0;
+ unsigned clear_mask = 0;
+
+ for (;;) {
+ unsigned fl;
+ char *arg = *++argv;
+
+ if (!arg)
+ bb_show_usage();
+ if (arg[0] != '-' && arg[0] != '+')
+ break;
+ fl = decode_arg(arg);
+ if (arg[0] == '+')
+ set_mask |= fl;
+ else
+ clear_mask |= fl;
+ }
+
+ do {
+ int fd, i;
+ uint32_t attr;
+
+ fd = xopen(*argv, O_RDONLY);
+ xioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &attr);
+ attr = (attr | set_mask) & ~clear_mask;
+ if (set_mask | clear_mask)
+ xioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
+ else {
+ for (i = 0; bit_to_char[i]; i++) {
+ bb_putchar((attr & 1) ? bit_to_char[i] : ' ');
+ attr >>= 1;
+ }
+ puts(*argv);
+ }
+ close(fd);
+ } while (*++argv);
+
+ return EXIT_SUCCESS;
+}
diff --git a/util-linux/fbset.c b/util-linux/fbset.c
index e9aacce..8a78c1e 100644
--- a/util-linux/fbset.c
+++ b/util-linux/fbset.c
@@ -11,6 +11,38 @@
* the GPL, and is (c) 1995-1999 by:
* Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
*/
+//config:config FBSET
+//config: bool "fbset"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: fbset is used to show or change the settings of a Linux frame buffer
+//config: device. The frame buffer device provides a simple and unique
+//config: interface to access a graphics display. Enable this option
+//config: if you wish to enable the 'fbset' utility.
+//config:
+//config:config FEATURE_FBSET_FANCY
+//config: bool "Turn on extra fbset options"
+//config: default y
+//config: depends on FBSET
+//config: help
+//config: This option enables extended fbset options, allowing one to set the
+//config: framebuffer size, color depth, etc. interface to access a graphics
+//config: display. Enable this option if you wish to enable extended fbset
+//config: options.
+//config:
+//config:config FEATURE_FBSET_READMODE
+//config: bool "Turn on fbset readmode support"
+//config: default y
+//config: depends on FBSET
+//config: help
+//config: This option allows fbset to read the video mode database stored by
+//config: default as /etc/fb.modes, which can be used to set frame buffer
+//config: device to pre-defined video modes.
+
+//applet:IF_FBSET(APPLET(fbset, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FBSET) += fbset.o
//usage:#define fbset_trivial_usage
//usage: "[OPTIONS] [MODE]"
@@ -164,6 +196,7 @@ static const struct cmdoptions_t {
const unsigned char code;
} g_cmdoptions[] = {
/*"12345678" + NUL */
+//TODO: convert to index_in_strings()
{ "fb" , 1, CMD_FB },
{ "db" , 1, CMD_DB },
{ "a" , 0, CMD_ALL },
@@ -248,12 +281,12 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
if (!p)
continue;
s = p + strlen(mode);
- //bb_info_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
+ //bb_error_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
/* exact match? */
if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */
|| ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */
) {
- //bb_info_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
+ //bb_error_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
break;
}
}
@@ -264,9 +297,9 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) {
int i;
-//bb_info_msg("???[%s][%s]", token[0], token[1]);
+//bb_error_msg("???[%s][%s]", token[0], token[1]);
if (strcmp(token[0], "endmode") == 0) {
-//bb_info_msg("OK[%s]", mode);
+//bb_error_msg("OK[%s]", mode);
return 1;
}
p = token[1];
@@ -294,7 +327,7 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
base->yres_virtual = base_yres_virtual;
base->bits_per_pixel = base_bits_per_pixel;
}
-//bb_info_msg("GEO[%s]", p);
+//bb_error_msg("GEO[%s]", p);
break;
case 1:
if (sizeof(int) == sizeof(base->xres)) {
@@ -321,13 +354,13 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
base->hsync_len = base_hsync_len;
base->vsync_len = base_vsync_len;
}
-//bb_info_msg("TIM[%s]", p);
+//bb_error_msg("TIM[%s]", p);
break;
case 2:
case 3: {
static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE};
ss(&base->vmode, syncs[i-2], p, "false");
-//bb_info_msg("VMODE[%s]", p);
+//bb_error_msg("VMODE[%s]", p);
break;
}
case 4:
@@ -335,12 +368,12 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
case 6: {
static const uint32_t syncs[] = {FB_SYNC_VERT_HIGH_ACT, FB_SYNC_HOR_HIGH_ACT, FB_SYNC_COMP_HIGH_ACT};
ss(&base->sync, syncs[i-4], p, "low");
-//bb_info_msg("SYNC[%s]", p);
+//bb_error_msg("SYNC[%s]", p);
break;
}
case 7:
ss(&base->sync, FB_SYNC_EXT, p, "false");
-//bb_info_msg("EXTSYNC[%s]", p);
+//bb_error_msg("EXTSYNC[%s]", p);
break;
case 8: {
int red_offset, red_length;
@@ -349,10 +382,10 @@ static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
int transp_offset, transp_length;
sscanf(p, "%d/%d,%d/%d,%d/%d,%d/%d",
- &red_offset, &red_length,
- &green_offset, &green_length,
- &blue_offset, &blue_length,
- &transp_offset, &transp_length);
+ &red_length, &red_offset,
+ &green_length, &green_offset,
+ &blue_length, &blue_offset,
+ &transp_length, &transp_offset);
base->red.offset = red_offset;
base->red.length = red_length;
base->red.msb_right = 0;
@@ -416,7 +449,7 @@ int fbset_main(int argc, char **argv)
unsigned options = 0;
const char *fbdev = DEFAULTFBDEV;
- const char *modefile = DEFAULTFBMODE;
+ IF_FEATURE_FBSET_READMODE(const char *modefile = DEFAULTFBMODE;)
char *thisarg;
char *mode = mode; /* for compiler */
@@ -444,7 +477,7 @@ int fbset_main(int argc, char **argv)
fbdev = argv[1];
break;
case CMD_DB:
- modefile = argv[1];
+ IF_FEATURE_FBSET_READMODE(modefile = argv[1];)
break;
case CMD_ALL:
options |= OPT_ALL;
diff --git a/util-linux/fdformat.c b/util-linux/fdformat.c
index 6f49cec..67c6e15 100644
--- a/util-linux/fdformat.c
+++ b/util-linux/fdformat.c
@@ -4,6 +4,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FDFORMAT
+//config: bool "fdformat"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: fdformat is used to low-level format a floppy disk.
+
+//applet:IF_FDFORMAT(APPLET(fdformat, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FDFORMAT) += fdformat.o
//usage:#define fdformat_trivial_usage
//usage: "[-n] DEVICE"
@@ -93,7 +103,7 @@ int fdformat_main(int argc UNUSED_PARAM, char **argv)
}
xioctl(fd, FDFMTEND, NULL);
- printf("done\n");
+ puts("Done");
/* VERIFY */
if (verify) {
@@ -126,7 +136,7 @@ int fdformat_main(int argc UNUSED_PARAM, char **argv)
if (ENABLE_FEATURE_CLEAN_UP) free(data);
- printf("done\n");
+ puts("Done");
}
if (ENABLE_FEATURE_CLEAN_UP) close(fd);
diff --git a/util-linux/fdisk.c b/util-linux/fdisk.c
index ce4c24f..6593137 100644
--- a/util-linux/fdisk.c
+++ b/util-linux/fdisk.c
@@ -6,6 +6,86 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FDISK
+//config: bool "fdisk"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The fdisk utility is used to divide hard disks into one or more
+//config: logical disks, which are generally called partitions. This utility
+//config: can be used to list and edit the set of partitions or BSD style
+//config: 'disk slices' that are defined on a hard drive.
+//config:
+//config:config FDISK_SUPPORT_LARGE_DISKS
+//config: bool "Support over 4GB disks"
+//config: default y
+//config: depends on FDISK
+//config: depends on !LFS # with LFS no special code is needed
+//config: help
+//config: Enable this option to support large disks > 4GB.
+//config:
+//config:config FEATURE_FDISK_WRITABLE
+//config: bool "Write support"
+//config: default y
+//config: depends on FDISK
+//config: help
+//config: Enabling this option allows you to create or change a partition table
+//config: and write those changes out to disk. If you leave this option
+//config: disabled, you will only be able to view the partition table.
+//config:
+//config:config FEATURE_AIX_LABEL
+//config: bool "Support AIX disklabels"
+//config: default n
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to create or change AIX disklabels.
+//config: Most people can safely leave this option disabled.
+//config:
+//config:config FEATURE_SGI_LABEL
+//config: bool "Support SGI disklabels"
+//config: default n
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to create or change SGI disklabels.
+//config: Most people can safely leave this option disabled.
+//config:
+//config:config FEATURE_SUN_LABEL
+//config: bool "Support SUN disklabels"
+//config: default n
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to create or change SUN disklabels.
+//config: Most people can safely leave this option disabled.
+//config:
+//config:config FEATURE_OSF_LABEL
+//config: bool "Support BSD disklabels"
+//config: default n
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to create or change BSD disklabels
+//config: and define and edit BSD disk slices.
+//config:
+//config:config FEATURE_GPT_LABEL
+//config: bool "Support GPT disklabels"
+//config: default n
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to view GUID Partition Table
+//config: disklabels.
+//config:
+//config:config FEATURE_FDISK_ADVANCED
+//config: bool "Support expert mode"
+//config: default y
+//config: depends on FDISK && FEATURE_FDISK_WRITABLE
+//config: help
+//config: Enabling this option allows you to do terribly unsafe things like
+//config: define arbitrary drive geometry, move the beginning of data in a
+//config: partition, and similarly evil things. Unless you have a very good
+//config: reason you would be wise to leave this disabled.
+
+//applet:IF_FDISK(APPLET(fdisk, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FDISK) += fdisk.o
/* Looks like someone forgot to add this to config system */
//usage:#ifndef ENABLE_FEATURE_FDISK_BLKSIZE
@@ -41,6 +121,7 @@
# define BLKGETSIZE64 _IOR(0x12,114,size_t)
#endif
#include "libbb.h"
+#include "unicode.h"
#if BB_LITTLE_ENDIAN
# define inline_if_little_endian ALWAYS_INLINE
@@ -363,7 +444,6 @@ struct globals {
jmp_buf listingbuf;
char line_buffer[80];
- char partname_buffer[80];
/* Raw disk label. For DOS-type partition tables the MBR,
* with descriptions of the primary partitions. */
char MBRbuffer[MAX_SECTOR_SIZE];
@@ -399,7 +479,6 @@ struct globals {
#define total_number_of_sectors (G.total_number_of_sectors)
#define listingbuf (G.listingbuf )
#define line_buffer (G.line_buffer )
-#define partname_buffer (G.partname_buffer)
#define MBRbuffer (G.MBRbuffer )
#define ptes (G.ptes )
#define INIT_G() do { \
@@ -468,9 +547,6 @@ static sector_t bb_BLKGETSIZE_sectors(int fd)
#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
-#define hsc2sector(h,s,c) \
- (sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
-
static void
close_dev_fd(void)
{
@@ -478,9 +554,7 @@ close_dev_fd(void)
xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd);
}
-/*
- * Return partition name - uses static storage
- */
+/* Return partition name */
static const char *
partname(const char *dev, int pno, int lth)
{
@@ -489,8 +563,8 @@ partname(const char *dev, int pno, int lth)
int bufsiz;
char *bufp;
- bufp = partname_buffer;
- bufsiz = sizeof(partname_buffer);
+ bufp = auto_string(xzalloc(80));
+ bufsiz = 80;
w = strlen(dev);
p = "";
@@ -1102,11 +1176,11 @@ warn_geometry(void)
printf(" sectors");
if (!g_cylinders)
printf(" cylinders");
- printf(
#if ENABLE_FEATURE_FDISK_WRITABLE
- " (settable in the extra functions menu)"
+ puts(" (settable in the extra functions menu)");
+#else
+ bb_putchar('\n');
#endif
- "\n");
return 1;
}
@@ -1150,7 +1224,7 @@ read_extended(int ext)
p = pex->part_table;
if (!get_start_sect(p)) {
- printf("Bad offset in primary extended partition\n");
+ puts("Bad offset in primary extended partition");
return;
}
@@ -1450,8 +1524,8 @@ static int get_boot(void)
current_label_type = LABEL_OSF;
return 0;
}
- printf("This disk has both DOS and BSD magic.\n"
- "Give the 'b' command to go to BSD mode.\n");
+ puts("This disk has both DOS and BSD magic.\n"
+ "Give the 'b' command to go to BSD mode.");
}
#endif
@@ -1461,9 +1535,9 @@ static int get_boot(void)
#else
if (!valid_part_table_flag(MBRbuffer)) {
if (what == OPEN_MAIN) {
- printf("Device contains neither a valid DOS "
- "partition table, nor Sun, SGI, OSF or GPT "
- "disklabel\n");
+ puts("Device contains neither a valid DOS "
+ "partition table, nor Sun, SGI, OSF or GPT "
+ "disklabel");
#ifdef __sparc__
IF_FEATURE_SUN_LABEL(create_sunlabel();)
#else
@@ -1596,7 +1670,7 @@ read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *
}
if (value >= low && value <= high)
break;
- printf("Value is out of range\n");
+ puts("Value is out of range");
}
return value;
}
@@ -1641,7 +1715,7 @@ get_existing_partition(int warn, unsigned max)
printf("Selected partition %u\n", pno+1);
return pno;
}
- printf("No partition is defined yet!\n");
+ puts("No partition is defined yet!");
return -1;
not_unique:
@@ -1668,7 +1742,7 @@ get_nonexisting_partition(int warn, unsigned max)
printf("Selected partition %u\n", pno+1);
return pno;
}
- printf("All primary partitions have been defined already!\n");
+ puts("All primary partitions have been defined already!");
return -1;
not_unique:
@@ -1703,10 +1777,10 @@ toggle_dos_compatibility_flag(void)
dos_compatible_flag = 1 - dos_compatible_flag;
if (dos_compatible_flag) {
sector_offset = g_sectors;
- printf("DOS Compatibility flag is set\n");
+ printf("DOS Compatibility flag is %sset\n", "");
} else {
sector_offset = 1;
- printf("DOS Compatibility flag is not set\n");
+ printf("DOS Compatibility flag is %sset\n", "not ");
}
}
@@ -1813,16 +1887,16 @@ change_sysid(void)
sys = read_hex(get_sys_types());
if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
- printf("Type 0 means free space to many systems\n"
- "(but not to Linux). Having partitions of\n"
- "type 0 is probably unwise.\n");
+ puts("Type 0 means free space to many systems\n"
+ "(but not to Linux). Having partitions of\n"
+ "type 0 is probably unwise.");
/* break; */
}
if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
- printf("You cannot change a partition into"
- " an extended one or vice versa\n");
+ puts("You cannot change a partition into"
+ " an extended one or vice versa");
break;
}
}
@@ -1830,10 +1904,10 @@ change_sysid(void)
if (sys < 256) {
#if ENABLE_FEATURE_SUN_LABEL
if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
- printf("Consider leaving partition 3 "
- "as Whole disk (5),\n"
- "as SunOS/Solaris expects it and "
- "even Linux likes it\n\n");
+ puts("Consider leaving partition 3 "
+ "as Whole disk (5),\n"
+ "as SunOS/Solaris expects it and "
+ "even Linux likes it\n");
#endif
#if ENABLE_FEATURE_SGI_LABEL
if (LABEL_IS_SGI &&
@@ -1842,10 +1916,10 @@ change_sysid(void)
(i == 8 && sys != 0)
)
) {
- printf("Consider leaving partition 9 "
- "as volume header (0),\nand "
- "partition 11 as entire volume (6)"
- "as IRIX expects it\n\n");
+ puts("Consider leaving partition 9 "
+ "as volume header (0),\nand "
+ "partition 11 as entire volume (6)"
+ "as IRIX expects it\n");
}
#endif
if (sys == origsys)
@@ -1898,14 +1972,14 @@ check_consistency(const struct partition *p, int partition)
return; /* do not check extended partitions */
/* physical beginning c, h, s */
- pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+ pbc = cylinder(p->sector, p->cyl);
pbh = p->head;
- pbs = p->sector & 0x3f;
+ pbs = sector(p->sector);
/* physical ending c, h, s */
- pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+ pec = cylinder(p->end_sector, p->end_cyl);
peh = p->end_head;
- pes = p->end_sector & 0x3f;
+ pes = sector(p->end_sector);
/* compute logical beginning (c, h, s) */
linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
@@ -1916,17 +1990,17 @@ check_consistency(const struct partition *p, int partition)
/* Same physical / logical beginning? */
if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
printf("Partition %u has different physical/logical "
- "beginnings (non-Linux?):\n", partition + 1);
- printf(" phys=(%u, %u, %u) ", pbc, pbh, pbs);
- printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs);
+ "start (non-Linux?):\n", partition + 1);
+ printf(" phys=(%u,%u,%u) ", pbc, pbh, pbs);
+ printf("logical=(%u,%u,%u)\n", lbc, lbh, lbs);
}
/* Same physical / logical ending? */
if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
printf("Partition %u has different physical/logical "
- "endings:\n", partition + 1);
- printf(" phys=(%u, %u, %u) ", pec, peh, pes);
- printf("logical=(%u, %u, %u)\n", lec, leh, les);
+ "end:\n", partition + 1);
+ printf(" phys=(%u,%u,%u) ", pec, peh, pes);
+ printf("logical=(%u,%u,%u)\n", lec, leh, les);
}
/* Ending on cylinder boundary? */
@@ -1940,22 +2014,23 @@ static void
list_disk_geometry(void)
{
ullong bytes = ((ullong)total_number_of_sectors << 9);
- long megabytes = bytes / 1000000;
-
- if (megabytes < 10000)
- printf("\nDisk %s: %lu MB, %llu bytes\n",
- disk_device, megabytes, bytes);
- else
- printf("\nDisk %s: %lu.%lu GB, %llu bytes\n",
- disk_device, megabytes/1000, (megabytes/100)%10, bytes);
- printf("%u heads, %u sectors/track, %u cylinders",
- g_heads, g_sectors, g_cylinders);
- if (units_per_sector == 1)
- printf(", total %"SECT_FMT"u sectors",
- total_number_of_sectors / (sector_size/512));
- printf("\nUnits = %s of %u * %u = %u bytes\n\n",
+ ullong xbytes = bytes / (1024*1024);
+ char x = 'M';
+
+ if (xbytes >= 10000) {
+ xbytes += 512; /* fdisk util-linux 2.28 does this */
+ xbytes /= 1024;
+ x = 'G';
+ }
+ printf("Disk %s: %llu %cB, %llu bytes, %"SECT_FMT"u sectors\n"
+ "%u cylinders, %u heads, %u sectors/track\n"
+ "Units: %s of %u * %u = %u bytes\n\n",
+ disk_device, xbytes, x,
+ bytes, total_number_of_sectors,
+ g_cylinders, g_heads, g_sectors,
str_units(PLURAL),
- units_per_sector, sector_size, units_per_sector * sector_size);
+ units_per_sector, sector_size, units_per_sector * sector_size
+ );
}
/*
@@ -2067,7 +2142,7 @@ fix_partition_table_order(void)
int i,k;
if (!wrong_p_order(NULL)) {
- printf("Ordering is already correct\n\n");
+ puts("Ordering is already correct\n");
return;
}
@@ -2095,14 +2170,21 @@ fix_partition_table_order(void)
if (i)
fix_chain_of_logicals();
- printf("Done.\n");
+ puts("Done");
}
#endif
+static const char *
+chs_string11(unsigned cyl, unsigned head, unsigned sect)
+{
+ char *buf = auto_string(xzalloc(sizeof(int)*3 * 3));
+ sprintf(buf, "%u,%u,%u", cylinder(sect,cyl), head, sector(sect));
+ return buf;
+}
+
static void
list_table(int xtra)
{
- const struct partition *p;
int i, w;
if (LABEL_IS_SUN) {
@@ -2126,50 +2208,62 @@ list_table(int xtra)
}
/* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
- but if the device name ends in a digit, say /dev/foo1,
- then the partition is called /dev/foo1p3. */
+ * but if the device name ends in a digit, say /dev/foo1,
+ * then the partition is called /dev/foo1p3.
+ */
w = strlen(disk_device);
if (w && isdigit(disk_device[w-1]))
w++;
- if (w < 5)
- w = 5;
+ if (w < 7)
+ w = 7;
- // 1 12345678901 12345678901 12345678901 12
- printf("%*s Boot Start End Blocks Id System\n",
- w+1, "Device");
+ printf("%-*s Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type\n",
+ w-1, "Device");
for (i = 0; i < g_partitions; i++) {
+ const struct partition *p;
const struct pte *pe = &ptes[i];
- sector_t psects;
- sector_t pblocks;
- unsigned podd;
+ char boot4[4];
+ char numstr6[6];
+ sector_t start_sect;
+ sector_t end_sect;
+ sector_t nr_sects;
p = pe->part_table;
if (!p || is_cleared_partition(p))
continue;
- psects = get_nr_sects(p);
- pblocks = psects;
- podd = 0;
-
- if (sector_size < 1024) {
- pblocks /= (1024 / sector_size);
- podd = psects % (1024 / sector_size);
+ sprintf(boot4, "%02x", p->boot_ind);
+ if ((p->boot_ind & 0x7f) == 0) {
+ /* 0x80 shown as '*', 0x00 is ' ' */
+ boot4[0] = p->boot_ind ? '*' : ' ';
+ boot4[1] = ' ';
}
- if (sector_size > 1024)
- pblocks *= (sector_size / 1024);
- printf("%s %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n",
- partname(disk_device, i+1, w+2),
- !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
- ? '*' : '?',
- cround(get_partition_start_from_dev_start(pe)), /* start */
- cround(get_partition_start_from_dev_start(pe) + psects /* end */
- - (psects ? 1 : 0)),
- pblocks, podd ? '+' : ' ', /* odd flag on end */
- p->sys_ind, /* type id */
- partition_type(p->sys_ind)); /* type name */
+ start_sect = get_partition_start_from_dev_start(pe);
+ end_sect = start_sect;
+ nr_sects = get_nr_sects(p);
+ if (nr_sects != 0)
+ end_sect += nr_sects - 1;
+ smart_ulltoa5((ullong)nr_sects * sector_size,
+ numstr6, " KMGTPEZY")[0] = '\0';
+
+#define SFMT SECT_FMT
+ // Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
+ printf("%s%s %-11s"/**/" %-11s"/**/" %10"SFMT"u %10"SFMT"u %10"SFMT"u %s %2x %s\n",
+ partname(disk_device, i+1, w+2),
+ boot4,
+ chs_string11(p->cyl, p->head, p->sector),
+ chs_string11(p->end_cyl, p->end_head, p->end_sector),
+ start_sect,
+ end_sect,
+ nr_sects,
+ numstr6,
+ p->sys_ind,
+ partition_type(p->sys_ind)
+ );
+#undef SFMT
check_consistency(p, i);
}
@@ -2178,7 +2272,7 @@ list_table(int xtra)
* if this is a sgi, sun or aix labeled disk... */
if (LABEL_IS_DOS && wrong_p_order(NULL)) {
/* FIXME */
- printf("\nPartition table entries are not in disk order\n");
+ puts("\nPartition table entries are not in disk order");
}
}
@@ -2192,19 +2286,23 @@ x_list_table(int extend)
printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n",
disk_device, g_heads, g_sectors, g_cylinders);
- printf("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n");
+ puts("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID");
for (i = 0; i < g_partitions; i++) {
pe = &ptes[i];
p = (extend ? pe->ext_pointer : pe->part_table);
if (p != NULL) {
printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n",
- i + 1, p->boot_ind, p->head,
+ i + 1, p->boot_ind,
+ p->head,
sector(p->sector),
- cylinder(p->sector, p->cyl), p->end_head,
+ cylinder(p->sector, p->cyl),
+ p->end_head,
sector(p->end_sector),
cylinder(p->end_sector, p->end_cyl),
- get_start_sect(p), get_nr_sects(p),
- p->sys_ind);
+ get_start_sect(p),
+ get_nr_sects(p),
+ p->sys_ind
+ );
if (p->sys_ind)
check_consistency(p, i);
}
@@ -2261,6 +2359,7 @@ verify(void)
{
int i, j;
sector_t total = 1;
+ sector_t chs_size;
sector_t first[g_partitions], last[g_partitions];
struct partition *p;
@@ -2322,11 +2421,14 @@ verify(void)
}
}
- if (total > g_heads * g_sectors * g_cylinders)
- printf("Total allocated sectors %u greater than the maximum "
- "%u\n", total, g_heads * g_sectors * g_cylinders);
+ chs_size = (sector_t)g_heads * g_sectors * g_cylinders;
+ if (total > chs_size)
+ printf("Total allocated sectors %u"
+ " greater than CHS size %"SECT_FMT"u\n",
+ total, chs_size
+ );
else {
- total = g_heads * g_sectors * g_cylinders - total;
+ total = chs_size - total;
if (total != 0)
printf("%"SECT_FMT"u unallocated sectors\n", total);
}
@@ -2419,7 +2521,7 @@ add_partition(int n, int sys)
limit = first[i] - 1;
}
if (start > limit) {
- printf("No free sectors available\n");
+ puts("No free sectors available");
if (n > 4)
g_partitions--;
return;
@@ -2490,9 +2592,9 @@ new_partition(void)
return;
}
if (LABEL_IS_AIX) {
- printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
+ puts("Sorry - this fdisk cannot handle AIX disk labels.\n"
"If you want to add DOS-type partitions, create a new empty DOS partition\n"
-"table first (use 'o'). This will destroy the present disk contents.\n");
+"table first (use 'o'). This will destroy the present disk contents.");
return;
}
@@ -2500,7 +2602,7 @@ new_partition(void)
free_primary += !ptes[i].part_table->sys_ind;
if (!free_primary && g_partitions >= MAXIMUM_PARTS) {
- printf("The maximum number of partitions has been created\n");
+ puts("The maximum number of partitions has been created");
return;
}
@@ -2508,8 +2610,8 @@ new_partition(void)
if (extended_offset)
add_logical();
else
- printf("You must delete some partition and add "
- "an extended partition first\n");
+ puts("You must delete some partition and add "
+ "an extended partition first");
} else {
char c, line[80];
snprintf(line, sizeof(line),
@@ -2547,7 +2649,7 @@ reread_partition_table(int leave)
{
int i;
- printf("Calling ioctl() to re-read partition table\n");
+ puts("Calling ioctl() to re-read partition table");
sync();
/* Users with slow external USB disks on a 320MHz ARM system (year 2011)
* report that sleep is needed, otherwise BLKRRPART may fail with -EIO:
@@ -2558,10 +2660,10 @@ reread_partition_table(int leave)
"failed, kernel still uses old table");
#if 0
if (dos_changed)
- printf(
+ puts(
"\nWARNING: If you have created or modified any DOS 6.x\n"
"partitions, please see the fdisk manual page for additional\n"
- "information\n");
+ "information");
#endif
if (leave) {
@@ -2589,7 +2691,7 @@ write_table(void)
}
}
else if (LABEL_IS_SGI) {
- /* no test on change? the printf below might be mistaken */
+ /* no test on change? the "altered" msg below might be mistaken */
sgi_write_table();
}
else if (LABEL_IS_SUN) {
@@ -2601,7 +2703,7 @@ write_table(void)
}
}
- printf("The partition table has been altered.\n");
+ puts("The partition table has been altered.");
reread_partition_table(1);
}
#endif /* FEATURE_FDISK_WRITABLE */
@@ -2745,8 +2847,8 @@ xselect(void)
user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors");
if (dos_compatible_flag) {
sector_offset = g_sectors;
- printf("Warning: setting sector offset for DOS "
- "compatiblity\n");
+ puts("Warning: setting sector offset for DOS "
+ "compatiblity");
}
update_units();
break;
@@ -2782,14 +2884,14 @@ is_ide_cdrom_or_tape(const char *device)
the process hangs on the attempt to read a music CD.
So try to be careful. This only works since 2.1.73. */
- if (strncmp("/dev/hd", device, 7))
+ if (!is_prefixed_with(device, "/dev/hd"))
return 0;
snprintf(buf, sizeof(buf), "/proc/ide/%s/media", device+5);
procf = fopen_for_read(buf);
if (procf != NULL && fgets(buf, sizeof(buf), procf))
- is_ide = (!strncmp(buf, "cdrom", 5) ||
- !strncmp(buf, "tape", 4));
+ is_ide = (is_prefixed_with(buf, "cdrom") ||
+ is_prefixed_with(buf, "tape"));
else
/* Now when this proc file does not exist, skip the
device when it is read-only. */
@@ -2920,8 +3022,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
close_dev_fd(); /* needed: fd 3 must not stay closed */
- opt_complementary = "b+:C+:H+:S+"; /* numeric params */
- opt = getopt32(argv, "b:C:H:lS:u" IF_FEATURE_FDISK_BLKSIZE("s"),
+ opt = getopt32(argv, "b:+C:+H:+lS:+u" IF_FEATURE_FDISK_BLKSIZE("s"),
&sector_size, &user_cylinders, &user_heads, &user_sectors);
argv += optind;
if (opt & OPT_b) {
@@ -3025,7 +3126,7 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
sgi_get_bootfile());
if (read_maybe_empty("Please enter the name of the "
"new boot file: ") == '\n')
- printf("Boot file unchanged\n");
+ puts("Boot file unchanged");
else
sgi_set_bootfile(line_ptr);
}
@@ -3107,8 +3208,8 @@ int fdisk_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_FDISK_ADVANCED
case 'x':
if (LABEL_IS_SGI) {
- printf("\n\tSorry, no experts menu for SGI "
- "partition tables available\n\n");
+ puts("\n\tSorry, no experts menu for SGI "
+ "partition tables available\n");
} else
xselect();
break;
diff --git a/util-linux/fdisk_gpt.c b/util-linux/fdisk_gpt.c
index 4ec3ee4..8337bfe 100644
--- a/util-linux/fdisk_gpt.c
+++ b/util-linux/fdisk_gpt.c
@@ -36,14 +36,13 @@ typedef struct {
uint64_t lba_start;
uint64_t lba_end;
uint64_t flags;
- uint16_t name[36];
+ uint16_t name36[36];
} gpt_partition;
static gpt_header *gpt_hdr;
static char *part_array;
static unsigned int n_parts;
-static unsigned int part_array_len;
static unsigned int part_entry_len;
static inline gpt_partition *
@@ -73,18 +72,34 @@ gpt_print_guid(uint8_t *buf)
buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
}
-/* TODO: real unicode support */
static void
-gpt_print_wide(uint16_t *s, int max_len)
+gpt_print_wide36(uint16_t *s)
{
+#if ENABLE_UNICODE_SUPPORT
+ char buf[37 * 4];
+ wchar_t wc[37];
int i = 0;
-
- while (i < max_len) {
- if (*s == 0)
- return;
- fputc(*s, stdout);
- s++;
+ while (i < ARRAY_SIZE(wc)-1) {
+ if (s[i] == 0)
+ break;
+ wc[i] = s[i];
+ i++;
+ }
+ wc[i] = 0;
+ if (wcstombs(buf, wc, sizeof(buf)) <= sizeof(buf)-1)
+ fputs(printable_string(NULL, buf), stdout);
+#else
+ char buf[37];
+ int i = 0;
+ while (i < ARRAY_SIZE(buf)-1) {
+ if (s[i] == 0)
+ break;
+ buf[i] = (0x20 <= s[i] && s[i] < 0x7f) ? s[i] : '?';
+ i++;
}
+ buf[i] = '\0';
+ fputs(buf, stdout);
+#endif
}
static void
@@ -106,20 +121,29 @@ gpt_list_table(int xtra UNUSED_PARAM)
(unsigned long long)SWAP_LE64(gpt_hdr->first_usable_lba),
(unsigned long long)SWAP_LE64(gpt_hdr->last_usable_lba));
- printf("Number Start (sector) End (sector) Size Code Name\n");
+/* "GPT fdisk" has a concept of 16-bit extension of the original MBR 8-bit type codes,
+ * which it displays here: its output columns are ... Size Code Name
+ * They are their own invention and are not stored on disk.
+ * Looks like they use them to support "hybrid" GPT: for example, they have
+ * AddType(0x8307, "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3", "Linux ARM32 root (/)");
+ * and then (code>>8) matches what you need to put into MBR's type field for such a partition.
+ * To print those codes, we'd need a GUID lookup table. Lets just drop the "Code" column instead:
+ */
+ puts("Number Start (sector) End (sector) Size Name");
+ // 123456 123456789012345 123456789012345 12345 abc
for (i = 0; i < n_parts; i++) {
gpt_partition *p = gpt_part(i);
if (p->lba_start) {
smart_ulltoa5((1 + SWAP_LE64(p->lba_end) - SWAP_LE64(p->lba_start)) * sector_size,
numstr6, " KMGTPEZY")[0] = '\0';
- printf("%4u %15llu %15llu %11s %04x ",
+ printf("%6u %15llu %15llu %s ",
i + 1,
(unsigned long long)SWAP_LE64(p->lba_start),
(unsigned long long)SWAP_LE64(p->lba_end),
- numstr6,
- 0x0700 /* FIXME */);
- gpt_print_wide(p->name, 18);
- printf("\n");
+ numstr6
+ );
+ gpt_print_wide36(p->name36);
+ bb_putchar('\n');
}
}
}
@@ -127,6 +151,7 @@ gpt_list_table(int xtra UNUSED_PARAM)
static int
check_gpt_label(void)
{
+ unsigned part_array_len;
struct partition *first = pt_offset(MBRbuffer, 0);
struct pte pe;
uint32_t crc;
@@ -150,6 +175,7 @@ check_gpt_label(void)
return 0;
}
+ init_unicode();
if (!global_crc32_table) {
global_crc32_table = crc32_filltable(NULL, 0);
}
diff --git a/util-linux/fdisk_osf.c b/util-linux/fdisk_osf.c
index ff16389..89f1f32 100644
--- a/util-linux/fdisk_osf.c
+++ b/util-linux/fdisk_osf.c
@@ -366,10 +366,11 @@ bsd_select(void)
}
printf("Reading disklabel of %s at sector %u\n",
partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
- if (xbsd_readlabel(xbsd_part) == 0)
+ if (xbsd_readlabel(xbsd_part) == 0) {
if (xbsd_create_disklabel() == 0)
return;
break;
+ }
}
}
@@ -854,7 +855,7 @@ xbsd_initlabel(struct partition *p)
d->d_magic = BSD_DISKMAGIC;
- if (strncmp(disk_device, "/dev/sd", 7) == 0)
+ if (is_prefixed_with(disk_device, "/dev/sd"))
d->d_type = BSD_DTYPE_SCSI;
else
d->d_type = BSD_DTYPE_ST506;
diff --git a/util-linux/fdisk_sgi.c b/util-linux/fdisk_sgi.c
index 785fc66..30def09 100644
--- a/util-linux/fdisk_sgi.c
+++ b/util-linux/fdisk_sgi.c
@@ -440,7 +440,7 @@ sgi_write_table(void)
(unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
write_sector(0, sgilabel);
- if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+ if (is_prefixed_with((char*)sgilabel->directory[0].vol_file_name, "sgilabel")) {
/*
* keep this habit of first writing the "sgilabel".
* I never tested whether it works without (AN 981002).
@@ -504,17 +504,19 @@ verify_sgi(int verbose)
if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) {
if ((Index[0] != 10) && verbose)
printf("IRIX likes when Partition 11 covers the entire disk\n");
- if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
+ if ((sgi_get_start_sector(Index[0]) != 0) && verbose) {
printf("The entire disk partition should start "
"at block 0,\n"
"not at diskblock %u\n",
sgi_get_start_sector(Index[0]));
- if (SGI_DEBUG) /* I do not understand how some disks fulfil it */
+ }
+ if (SGI_DEBUG) { /* I do not understand how some disks fulfil it */
if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
printf("The entire disk partition is only %u diskblock large,\n"
"but the disk is %u diskblocks long\n",
sgi_get_num_sectors(Index[0]), lastblock);
lastblock = sgi_get_num_sectors(Index[0]);
+ }
} else {
if (verbose)
printf("One Partition (#11) should cover the entire disk\n");
@@ -669,16 +671,17 @@ sgi_set_volhdr(void)
int n;
for (n = 8; n < g_partitions; n++) {
- if (!sgi_get_num_sectors(n)) {
- /*
- * 5 cylinders is an arbitrary value I like
- * IRIX 5.3 stored files in the volume header
- * (like sash, symmon, fx, ide) with ca. 3200
- * sectors.
- */
- if (g_heads * g_sectors * 5 < sgi_get_lastblock())
- sgi_set_partition(n, 0, g_heads * g_sectors * 5, SGI_VOLHDR);
- break;
+ if (!sgi_get_num_sectors(n)) {
+ /*
+ * 5 cylinders is an arbitrary value I like
+ * IRIX 5.3 stored files in the volume header
+ * (like sash, symmon, fx, ide) with ca. 3200
+ * sectors.
+ */
+ if (g_heads * g_sectors * 5 < sgi_get_lastblock()) {
+ sgi_set_partition(n, 0, g_heads * g_sectors * 5, SGI_VOLHDR);
+ break;
+ }
}
}
}
diff --git a/util-linux/findfs.c b/util-linux/findfs.c
index 49e8979..daa46b0 100644
--- a/util-linux/findfs.c
+++ b/util-linux/findfs.c
@@ -7,6 +7,20 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config FINDFS
+//config: bool "findfs"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: select VOLUMEID
+//config: help
+//config: Prints the name of a filesystem with given label or UUID.
+//config: WARNING:
+//config: With all submodules selected, it will add ~8k to busybox.
+
+/* Benefits from suid root: better access to /dev/BLOCKDEVs: */
+//applet:IF_FINDFS(APPLET(findfs, BB_DIR_SBIN, BB_SUID_MAYBE))
+
+//kbuild:lib-$(CONFIG_FINDFS) += findfs.o
//usage:#define findfs_trivial_usage
//usage: "LABEL=label or UUID=uuid"
@@ -27,7 +41,7 @@ int findfs_main(int argc UNUSED_PARAM, char **argv)
if (!dev)
bb_show_usage();
- if (strncmp(dev, "/dev/", 5) == 0) {
+ if (is_prefixed_with(dev, "/dev/")) {
/* Just pass any /dev/xxx name right through.
* This might aid in some scripts being able
* to call this unconditionally */
diff --git a/util-linux/flock.c b/util-linux/flock.c
index 05a747f..f34c0ad 100644
--- a/util-linux/flock.c
+++ b/util-linux/flock.c
@@ -3,6 +3,15 @@
*
* This is free software, licensed under the GNU General Public License v2.
*/
+//config:config FLOCK
+//config: bool "flock"
+//config: default y
+//config: help
+//config: Manage locks from shell scripts
+
+//applet:IF_FLOCK(APPLET(flock, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FLOCK) += flock.o
//usage:#define flock_trivial_usage
//usage: "[-sxun] FD|{FILE [-c] PROG ARGS}"
@@ -57,7 +66,6 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
/* If it is "flock FILE -c PROG", then -c isn't caught by getopt32:
* we use "+" in order to support "flock -opt FILE PROG -with-opts",
* we need to remove -c by hand.
- * TODO: in upstream, -c 'PROG ARGS' means "run sh -c 'PROG ARGS'"
*/
if (argv[0]
&& argv[0][0] == '-'
@@ -66,6 +74,9 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
)
) {
argv++;
+ if (argv[1])
+ bb_error_msg_and_die("-c takes only one argument");
+ opt |= OPT_c;
}
if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) {
@@ -90,8 +101,21 @@ int flock_main(int argc UNUSED_PARAM, char **argv)
bb_perror_nomsg_and_die();
}
- if (argv[0])
- return spawn_and_wait(argv);
+ if (argv[0]) {
+ int rc;
+ if (opt & OPT_c) {
+ /* -c 'PROG ARGS' means "run sh -c 'PROG ARGS'" */
+ argv -= 2;
+ argv[0] = (char*)get_shell_name();
+ argv[1] = (char*)"-c";
+ /* argv[2] = "PROG ARGS"; */
+ /* argv[3] = NULL; */
+ }
+ rc = spawn_and_wait(argv);
+ if (rc < 0)
+ bb_simple_perror_msg(argv[0]);
+ return rc;
+ }
return EXIT_SUCCESS;
}
diff --git a/util-linux/freeramdisk.c b/util-linux/freeramdisk.c
index a89ae1a..8bc2c44 100644
--- a/util-linux/freeramdisk.c
+++ b/util-linux/freeramdisk.c
@@ -8,6 +8,36 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config FDFLUSH
+//config: bool "fdflush"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: fdflush is only needed when changing media on slightly-broken
+//config: removable media drives. It is used to make Linux believe that a
+//config: hardware disk-change switch has been actuated, which causes Linux to
+//config: forget anything it has cached from the previous media. If you have
+//config: such a slightly-broken drive, you will need to run fdflush every time
+//config: you change a disk. Most people have working hardware and can safely
+//config: leave this disabled.
+//config:
+//config:config FREERAMDISK
+//config: bool "freeramdisk"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Linux allows you to create ramdisks. This utility allows you to
+//config: delete them and completely free all memory that was used for the
+//config: ramdisk. For example, if you boot Linux into a ramdisk and later
+//config: pivot_root, you may want to free the memory that is allocated to the
+//config: ramdisk. If you have no use for freeing memory from a ramdisk, leave
+//config: this disabled.
+
+//applet:IF_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, BB_DIR_BIN, BB_SUID_DROP, fdflush))
+//applet:IF_FREERAMDISK(APPLET(freeramdisk, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_FDFLUSH) += freeramdisk.o
+//kbuild:lib-$(CONFIG_FREERAMDISK) += freeramdisk.o
//usage:#define freeramdisk_trivial_usage
//usage: "DEVICE"
diff --git a/util-linux/fsck_minix.c b/util-linux/fsck_minix.c
index 33767a1..0eaac17 100644
--- a/util-linux/fsck_minix.c
+++ b/util-linux/fsck_minix.c
@@ -85,6 +85,20 @@
* The device may be a block device or a image of one, but this isn't
* enforced (but it's not much fun on a character device :-).
*/
+//config:config FSCK_MINIX
+//config: bool "fsck_minix"
+//config: default y
+//config: help
+//config: The minix filesystem is a nice, small, compact, read-write filesystem
+//config: with little overhead. It is not a journaling filesystem however and
+//config: can experience corruption if it is not properly unmounted or if the
+//config: power goes off in the middle of a write. This utility allows you to
+//config: check for and attempt to repair any corruption that occurs to a minix
+//config: filesystem.
+
+//applet:IF_FSCK_MINIX(APPLET_ODDNAME(fsck.minix, fsck_minix, BB_DIR_SBIN, BB_SUID_DROP, fsck_minix))
+
+//kbuild:lib-$(CONFIG_FSCK_MINIX) += fsck_minix.o
//usage:#define fsck_minix_trivial_usage
//usage: "[-larvsmf] BLOCKDEV"
@@ -371,9 +385,9 @@ static int ask(const char *string, int def)
}
}
if (def)
- printf("y\n");
+ puts("y");
else {
- printf("n\n");
+ puts("n");
errors_uncorrected = 1;
}
return def;
@@ -405,7 +419,7 @@ static void check_mount(void)
if (isatty(0) && isatty(1))
cont = ask("Do you really want to continue", 0);
if (!cont) {
- printf("Check aborted\n");
+ puts("Check aborted");
exit(EXIT_SUCCESS);
}
}
@@ -470,8 +484,8 @@ static void write_block(unsigned nr, void *addr)
if (!nr)
return;
if (nr < FIRSTZONE || nr >= ZONES) {
- printf("Internal error: trying to write bad block\n"
- "Write request ignored\n");
+ puts("Internal error: trying to write bad block\n"
+ "Write request ignored");
errors_uncorrected = 1;
return;
}
@@ -659,7 +673,7 @@ static void read_tables(void)
if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
die("can't read inodes");
if (NORM_FIRSTZONE != FIRSTZONE) {
- printf("warning: firstzone!=norm_firstzone\n");
+ puts("warning: firstzone!=norm_firstzone");
errors_uncorrected = 1;
}
get_dirsize();
@@ -713,7 +727,7 @@ static void get_inode_common(unsigned nr, uint16_t i_mode)
} else
links++;
if (!++inode_count[nr]) {
- printf("Warning: inode count too big\n");
+ puts("Warning: inode count too big");
inode_count[nr]--;
errors_uncorrected = 1;
}
@@ -1299,7 +1313,7 @@ int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
}
if (changed) {
write_tables();
- printf("FILE SYSTEM HAS BEEN CHANGED\n");
+ puts("FILE SYSTEM HAS BEEN CHANGED");
sync();
} else if (OPT_repair)
write_superblock();
diff --git a/util-linux/fstrim.c b/util-linux/fstrim.c
index 675a021..fc51878 100644
--- a/util-linux/fstrim.c
+++ b/util-linux/fstrim.c
@@ -32,7 +32,7 @@
//usage: " -o OFFSET Offset in bytes to discard from"
//usage: "\n -l LEN Bytes to discard"
//usage: "\n -m MIN Minimum extent length"
-//usage: "\n -v, Print number of discarded bytes"
+//usage: "\n -v Print number of discarded bytes"
//usage: )
#include "libbb.h"
@@ -47,25 +47,6 @@ struct fstrim_range {
#define FITRIM _IOWR('X', 121, struct fstrim_range)
#endif
-static const struct suffix_mult fstrim_sfx[] = {
- { "KiB", 1024 },
- { "kiB", 1024 },
- { "K", 1024 },
- { "k", 1024 },
- { "MiB", 1048576 },
- { "miB", 1048576 },
- { "M", 1048576 },
- { "m", 1048576 },
- { "GiB", 1073741824 },
- { "giB", 1073741824 },
- { "G", 1073741824 },
- { "g", 1073741824 },
- { "KB", 1000 },
- { "MB", 1000000 },
- { "GB", 1000000000 },
- { "", 0 }
-};
-
int fstrim_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int fstrim_main(int argc UNUSED_PARAM, char **argv)
{
@@ -98,11 +79,11 @@ int fstrim_main(int argc UNUSED_PARAM, char **argv)
range.len = ULLONG_MAX;
if (opts & OPT_o)
- range.start = xatoull_sfx(arg_o, fstrim_sfx);
+ range.start = xatoull_sfx(arg_o, kmg_i_suffixes);
if (opts & OPT_l)
- range.len = xatoull_sfx(arg_l, fstrim_sfx);
+ range.len = xatoull_sfx(arg_l, kmg_i_suffixes);
if (opts & OPT_m)
- range.minlen = xatoull_sfx(arg_m, fstrim_sfx);
+ range.minlen = xatoull_sfx(arg_m, kmg_i_suffixes);
mp = argv[optind];
if (find_block_device(mp)) {
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
index 58df1c8..f6ecc3d 100644
--- a/util-linux/getopt.c
+++ b/util-linux/getopt.c
@@ -28,8 +28,28 @@
* Removed --version/-V and --help/-h
* Removed parse_error(), using bb_error_msg() from Busybox instead
* Replaced our_malloc with xmalloc and our_realloc with xrealloc
- *
*/
+//config:config GETOPT
+//config: bool "getopt"
+//config: default y
+//config: help
+//config: The getopt utility is used to break up (parse) options in command
+//config: lines to make it easy to write complex shell scripts that also check
+//config: for legal (and illegal) options. If you want to write horribly
+//config: complex shell scripts, or use some horribly complex shell script
+//config: written by others, this utility may be for you. Most people will
+//config: wisely leave this disabled.
+//config:
+//config:config FEATURE_GETOPT_LONG
+//config: bool "Support option -l"
+//config: default y if LONG_OPTS
+//config: depends on GETOPT
+//config: help
+//config: Enable support for long options (option -l).
+
+//applet:IF_GETOPT(APPLET(getopt, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_GETOPT) += getopt.o
//usage:#define getopt_trivial_usage
//usage: "[OPTIONS] [--] OPTSTRING PARAMS"
@@ -327,9 +347,9 @@ static struct option *add_long_options(struct option *long_options, char *option
static void set_shell(const char *new_shell)
{
- if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh"))
+ if (strcmp(new_shell, "bash") == 0 || strcmp(new_shell, "sh") == 0)
return;
- if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh"))
+ if (strcmp(new_shell, "tcsh") == 0 || strcmp(new_shell, "csh") == 0)
option_mask32 |= SHELL_IS_TCSH;
else
bb_error_msg("unknown shell '%s', assuming bash", new_shell);
@@ -378,7 +398,7 @@ int getopt_main(int argc, char **argv)
if (compatible) {
/* For some reason, the original getopt gave no error
* when there were no arguments. */
- printf(" --\n");
+ puts(" --");
return 0;
}
bb_error_msg_and_die("missing optstring argument");
@@ -397,8 +417,7 @@ int getopt_main(int argc, char **argv)
opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
#else
applet_long_options = getopt_longopts;
- opt_complementary = "l::";
- opt = getopt32(argv, "+o:n:qQs:Tual:",
+ opt = getopt32(argv, "+o:n:qQs:Tual:*",
&optstr, &name, &s_arg, &l_arg);
/* Effectuate the read options for the applet itself */
while (l_arg) {
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c
index ac7e24f..4a7f641 100644
--- a/util-linux/hexdump.c
+++ b/util-linux/hexdump.c
@@ -8,6 +8,34 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config HEXDUMP
+//config: bool "hexdump"
+//config: default y
+//config: help
+//config: The hexdump utility is used to display binary data in a readable
+//config: way that is comparable to the output from most hex editors.
+//config:
+//config:config FEATURE_HEXDUMP_REVERSE
+//config: bool "Support -R, reverse of 'hexdump -Cv'"
+//config: default y
+//config: depends on HEXDUMP
+//config: help
+//config: The hexdump utility is used to display binary data in an ascii
+//config: readable way. This option creates binary data from an ascii input.
+//config: NB: this option is non-standard. It's unwise to use it in scripts
+//config: aimed to be portable.
+//config:
+//config:config HD
+//config: bool "hd"
+//config: default y
+//config: help
+//config: hd is an alias to hexdump -C.
+
+//applet:IF_HEXDUMP(APPLET_NOEXEC(hexdump, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hexdump))
+//applet:IF_HD(APPLET_NOEXEC(hd, hexdump, BB_DIR_USR_BIN, BB_SUID_DROP, hd))
+
+//kbuild:lib-$(CONFIG_HEXDUMP) += hexdump.o
+//kbuild:lib-$(CONFIG_HD) += hexdump.o
//usage:#define hexdump_trivial_usage
//usage: "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..."
@@ -77,7 +105,9 @@ int hexdump_main(int argc, char **argv)
smallint rdump = 0;
#endif
- if (ENABLE_HD && !applet_name[2]) { /* we are "hd" */
+ if (ENABLE_HD
+ && (!ENABLE_HEXDUMP || !applet_name[2])
+ ) { /* we are "hd" */
ch = 'C';
goto hd_applet;
}
diff --git a/util-linux/hwclock.c b/util-linux/hwclock.c
index 379eeb2..084a7f1 100644
--- a/util-linux/hwclock.c
+++ b/util-linux/hwclock.c
@@ -5,7 +5,41 @@
* Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
-*/
+ */
+//config:config HWCLOCK
+//config: bool "hwclock"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The hwclock utility is used to read and set the hardware clock
+//config: on a system. This is primarily used to set the current time on
+//config: shutdown in the hardware clock, so the hardware will keep the
+//config: correct time when Linux is _not_ running.
+//config:
+//config:config FEATURE_HWCLOCK_LONG_OPTIONS
+//config: bool "Support long options (--hctosys,...)"
+//config: default y
+//config: depends on HWCLOCK && LONG_OPTS
+//config: help
+//config: By default, the hwclock utility only uses short options. If you
+//config: are overly fond of its long options, such as --hctosys, --utc, etc)
+//config: then enable this option.
+//config:
+//config:config FEATURE_HWCLOCK_ADJTIME_FHS
+//config: bool "Use FHS /var/lib/hwclock/adjtime"
+//config: default n # util-linux-ng in Fedora 13 still uses /etc/adjtime
+//config: depends on HWCLOCK
+//config: help
+//config: Starting with FHS 2.3, the adjtime state file is supposed to exist
+//config: at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish
+//config: to use the FHS behavior, answer Y here, otherwise answer N for the
+//config: classic /etc/adjtime path.
+//config:
+//config: pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
+
+//applet:IF_HWCLOCK(APPLET(hwclock, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_HWCLOCK) += hwclock.o
#include "libbb.h"
/* After libbb.h, since it needs sys/types.h on some systems */
@@ -69,7 +103,7 @@ static void show_clock(const char **pp_rtcname, int utc)
strftime(cp, sizeof(cp), "%c", ptm);
#else
char *cp = ctime(&t);
- strchrnul(cp, '\n')[0] = '\0';
+ chomp(cp);
#endif
#if !SHOW_HWCLOCK_DIFF
@@ -97,7 +131,11 @@ static void to_sys_clock(const char **pp_rtcname, int utc)
struct timeval tv;
struct timezone tz;
- tz.tz_minuteswest = timezone/60 - 60*daylight;
+ tz.tz_minuteswest = timezone/60;
+ /* ^^^ used to also subtract 60*daylight, but it's wrong:
+ * daylight!=0 means "this timezone has some DST
+ * during the year", not "DST is in effect now".
+ */
tz.tz_dsttime = 0;
tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
@@ -248,7 +286,7 @@ static void set_system_clock_timezone(int utc)
gettimeofday(&tv, NULL);
broken = localtime(&tv.tv_sec);
tz.tz_minuteswest = timezone / 60;
- if (broken->tm_isdst)
+ if (broken->tm_isdst > 0)
tz.tz_minuteswest -= 60;
tz.tz_dsttime = 0;
gettimeofday(&tv, NULL);
@@ -305,6 +343,10 @@ int hwclock_main(int argc UNUSED_PARAM, char **argv)
;
applet_long_options = hwclock_longopts;
#endif
+
+ /* Initialize "timezone" (libc global variable) */
+ tzset();
+
opt_complementary = "r--wst:w--rst:s--wrt:t--rsw:l--u:u--l";
opt = getopt32(argv, "lurswtf:", &rtcname);
diff --git a/util-linux/ipcrm.c b/util-linux/ipcrm.c
index 888f70e..76ea3ca 100644
--- a/util-linux/ipcrm.c
+++ b/util-linux/ipcrm.c
@@ -7,6 +7,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IPCRM
+//config: bool "ipcrm"
+//config: default y
+//config: help
+//config: The ipcrm utility allows the removal of System V interprocess
+//config: communication (IPC) objects and the associated data structures
+//config: from the system.
+
+//applet:IF_IPCRM(APPLET(ipcrm, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IPCRM) += ipcrm.o
//usage:#define ipcrm_trivial_usage
//usage: "[-MQS key] [-mqs id]"
@@ -119,7 +130,7 @@ int ipcrm_main(int argc, char **argv)
if (remove_ids(what, &argv[2]))
fflush_stdout_and_exit(EXIT_FAILURE);
- printf("resource(s) deleted\n");
+ puts("resource(s) deleted");
return 0;
}
}
diff --git a/util-linux/ipcs.c b/util-linux/ipcs.c
index 67a25a8..88ae922 100644
--- a/util-linux/ipcs.c
+++ b/util-linux/ipcs.c
@@ -7,6 +7,17 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config IPCS
+//config: bool "ipcs"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The ipcs utility is used to provide information on the currently
+//config: allocated System V interprocess (IPC) objects in the system.
+
+//applet:IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IPCS) += ipcs.o
//usage:#define ipcs_trivial_usage
//usage: "[[-smq] -i shmid] | [[-asmq] [-tcplu]]"
diff --git a/util-linux/losetup.c b/util-linux/losetup.c
index d450b5a..4424d9c 100644
--- a/util-linux/losetup.c
+++ b/util-linux/losetup.c
@@ -6,6 +6,18 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LOSETUP
+//config: bool "losetup"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: losetup is used to associate or detach a loop device with a regular
+//config: file or block device, and to query the status of a loop device. This
+//config: version does not currently support enabling data encryption.
+
+//kbuild:lib-$(CONFIG_LOSETUP) += losetup.o
+
+//applet:IF_LOSETUP(APPLET(losetup, BB_DIR_SBIN, BB_SUID_DROP))
//usage:#define losetup_trivial_usage
//usage: "[-r] [-o OFS] {-f|LOOPDEV} FILE - associate loop devices\n"
diff --git a/util-linux/lspci.c b/util-linux/lspci.c
index 514678a..8b38a23 100644
--- a/util-linux/lspci.c
+++ b/util-linux/lspci.c
@@ -6,6 +6,19 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LSPCI
+//config: bool "lspci"
+//config: default y
+//config: #select PLATFORM_LINUX
+//config: help
+//config: lspci is a utility for displaying information about PCI buses in the
+//config: system and devices connected to them.
+//config:
+//config: This version uses sysfs (/sys/bus/pci/devices) only.
+
+//applet:IF_LSPCI(APPLET(lspci, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LSPCI) += lspci.o
//usage:#define lspci_trivial_usage
//usage: "[-mk]"
diff --git a/util-linux/lsusb.c b/util-linux/lsusb.c
index 540f21e..7d36d6f 100644
--- a/util-linux/lsusb.c
+++ b/util-linux/lsusb.c
@@ -6,6 +6,19 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config LSUSB
+//config: bool "lsusb"
+//config: default y
+//config: #select PLATFORM_LINUX
+//config: help
+//config: lsusb is a utility for displaying information about USB buses in the
+//config: system and devices connected to them.
+//config:
+//config: This version uses sysfs (/sys/bus/usb/devices) only.
+
+//applet:IF_LSUSB(APPLET(lsusb, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_LSUSB) += lsusb.o
//usage:#define lsusb_trivial_usage NOUSAGE_STR
//usage:#define lsusb_full_usage ""
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index e80b58f..a59115d 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -97,6 +97,7 @@
//usage: "If /dev/mdev.log file exists, debug log will be appended to it."
#include "libbb.h"
+#include "common_bufsiz.h"
#include "xregex.h"
/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
@@ -283,10 +284,11 @@ struct globals {
unsigned rule_idx;
#endif
struct rule cur_rule;
- char timestr[sizeof("60.123456")];
+ char timestr[sizeof("HH:MM:SS.123456")];
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { \
+ setup_common_bufsiz(); \
IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.maj = -1;) \
IF_NOT_FEATURE_MDEV_CONF(G.cur_rule.mode = 0660;) \
} while (0)
@@ -400,13 +402,13 @@ static void parse_next_rule(void)
}
/* 2nd field: uid:gid - device ownership */
- if (get_uidgid(&G.cur_rule.ugid, tokens[1], /*allow_numeric:*/ 1) == 0) {
+ if (get_uidgid(&G.cur_rule.ugid, tokens[1]) == 0) {
bb_error_msg("unknown user/group '%s' on line %d", tokens[1], G.parser->lineno);
goto next_rule;
}
/* 3rd field: mode - device permissions */
- bb_parse_mode(tokens[2], &G.cur_rule.mode);
+ G.cur_rule.mode = bb_parse_mode(tokens[2], G.cur_rule.mode);
/* 4th field (opt): ">|=alias" or "!" to not create the node */
val = tokens[3];
@@ -471,7 +473,7 @@ static const struct rule *next_rule(void)
if (G.parser) {
parse_next_rule();
if (G.rule_vec) { /* mdev -s */
- rule = memcpy(xmalloc(sizeof(G.cur_rule)), &G.cur_rule, sizeof(G.cur_rule));
+ rule = xmemdup(&G.cur_rule, sizeof(G.cur_rule));
G.rule_vec = xrealloc_vector(G.rule_vec, 4, G.rule_idx);
G.rule_vec[G.rule_idx++] = rule;
dbg3("> G.rule_vec[G.rule_idx:%d]=%p", G.rule_idx, G.rule_vec[G.rule_idx]);
@@ -541,8 +543,7 @@ static char *build_alias(char *alias, const char *device_name)
/* mknod in /dev based on a path like "/sys/block/hda/hda1"
* NB1: path parameter needs to have SCRATCH_SIZE scratch bytes
- * after NUL, but we promise to not mangle (IOW: to restore NUL if needed)
- * path string.
+ * after NUL, but we promise to not mangle it (IOW: to restore NUL if needed).
* NB2: "mdev -s" may call us many times, do not leak memory/fds!
*
* device_name = $DEVNAME (may be NULL)
@@ -610,7 +611,7 @@ static void make_device(char *device_name, char *path, int operation)
* We use strstr("/block/") to forestall future surprises.
*/
type = S_IFCHR;
- if (strstr(path, "/block/") || (G.subsystem && strncmp(G.subsystem, "block", 5) == 0))
+ if (strstr(path, "/block/") || (G.subsystem && is_prefixed_with(G.subsystem, "block")))
type = S_IFBLK;
#if ENABLE_FEATURE_MDEV_CONF
@@ -808,41 +809,39 @@ static void make_device(char *device_name, char *path, int operation)
} /* for (;;) */
}
-/* File callback for /sys/ traversal */
+/* File callback for /sys/ traversal.
+ * We act only on "/sys/.../dev" (pseudo)file
+ */
static int FAST_FUNC fileAction(const char *fileName,
struct stat *statbuf UNUSED_PARAM,
void *userData,
int depth UNUSED_PARAM)
{
size_t len = strlen(fileName) - 4; /* can't underflow */
- char *scratch = userData;
-
- /* len check is for paranoid reasons */
- if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
- return FALSE;
-
- strcpy(scratch, fileName);
- scratch[len] = '\0';
- make_device(/*DEVNAME:*/ NULL, scratch, OP_add);
-
- return TRUE;
-}
-
-/* Directory callback for /sys/ traversal */
-static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
- struct stat *statbuf UNUSED_PARAM,
- void *userData UNUSED_PARAM,
- int depth)
-{
- /* Extract device subsystem -- the name of the directory
- * under /sys/class/ */
- if (1 == depth) {
+ char *path = userData; /* char array[PATH_MAX + SCRATCH_SIZE] */
+ char subsys[PATH_MAX];
+ int res;
+
+ /* Is it a ".../dev" file? (len check is for paranoid reasons) */
+ if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX - 32)
+ return FALSE; /* not .../dev */
+
+ strcpy(path, fileName);
+ path[len] = '\0';
+
+ /* Read ".../subsystem" symlink in the same directory where ".../dev" is */
+ strcpy(subsys, path);
+ strcpy(subsys + len, "/subsystem");
+ res = readlink(subsys, subsys, sizeof(subsys)-1);
+ if (res > 0) {
+ subsys[res] = '\0';
free(G.subsystem);
if (G.subsys_env) {
bb_unsetenv_and_free(G.subsys_env);
G.subsys_env = NULL;
}
- G.subsystem = strrchr(fileName, '/');
+ /* Set G.subsystem and $SUBSYSTEM from symlink's last component */
+ G.subsystem = strrchr(subsys, '/');
if (G.subsystem) {
G.subsystem = xstrdup(G.subsystem + 1);
G.subsys_env = xasprintf("%s=%s", "SUBSYSTEM", G.subsystem);
@@ -850,6 +849,17 @@ static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
}
}
+ make_device(/*DEVNAME:*/ NULL, path, OP_add);
+
+ return TRUE;
+}
+
+/* Directory callback for /sys/ traversal */
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+ struct stat *statbuf UNUSED_PARAM,
+ void *userData UNUSED_PARAM,
+ int depth)
+{
return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
}
@@ -870,8 +880,9 @@ static void load_firmware(const char *firmware, const char *sysfs_path)
int firmware_fd, loading_fd;
/* check for /lib/firmware/$FIRMWARE */
- xchdir("/lib/firmware");
- firmware_fd = open(firmware, O_RDONLY); /* can fail */
+ firmware_fd = -1;
+ if (chdir("/lib/firmware") == 0)
+ firmware_fd = open(firmware, O_RDONLY); /* can fail */
/* check for /sys/$DEVPATH/loading ... give 30 seconds to appear */
xchdir(sysfs_path);
@@ -923,7 +934,11 @@ static char *curtime(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
- sprintf(G.timestr, "%u.%06u", (unsigned)tv.tv_sec % 60, (unsigned)tv.tv_usec);
+ sprintf(
+ strftime_HHMMSS(G.timestr, sizeof(G.timestr), &tv.tv_sec),
+ ".%06u",
+ (unsigned)tv.tv_usec
+ );
return G.timestr;
}
@@ -943,7 +958,7 @@ static void open_mdev_log(const char *seq, unsigned my_pid)
* Active mdev pokes us with SIGCHLD to check the new file.
*/
static int
-wait_for_seqfile(const char *seq)
+wait_for_seqfile(unsigned expected_seq)
{
/* We time out after 2 sec */
static const struct timespec ts = { 0, 32*1000*1000 };
@@ -958,12 +973,14 @@ wait_for_seqfile(const char *seq)
for (;;) {
int seqlen;
- char seqbuf[sizeof(int)*3 + 2];
+ char seqbuf[sizeof(long)*3 + 2];
+ unsigned seqbufnum;
if (seq_fd < 0) {
seq_fd = open("mdev.seq", O_RDWR);
if (seq_fd < 0)
break;
+ close_on_exec_on(seq_fd);
}
seqlen = pread(seq_fd, seqbuf, sizeof(seqbuf) - 1, 0);
if (seqlen < 0) {
@@ -972,19 +989,27 @@ wait_for_seqfile(const char *seq)
break;
}
seqbuf[seqlen] = '\0';
- if (seqbuf[0] == '\n') {
+ if (seqbuf[0] == '\n' || seqbuf[0] == '\0') {
/* seed file: write out seq ASAP */
- xwrite_str(seq_fd, seq);
+ xwrite_str(seq_fd, utoa(expected_seq));
xlseek(seq_fd, 0, SEEK_SET);
dbg2("first seq written");
break;
}
- if (strcmp(seq, seqbuf) == 0) {
+ seqbufnum = atoll(seqbuf);
+ if (seqbufnum == expected_seq) {
/* correct idx */
break;
}
+ if (seqbufnum > expected_seq) {
+ /* a later mdev runs already (this was seen by users to happen) */
+ /* do not overwrite seqfile on exit */
+ close(seq_fd);
+ seq_fd = -1;
+ break;
+ }
if (do_once) {
- dbg2("%s waiting for '%s'", curtime(), seqbuf);
+ dbg2("%s mdev.seq='%s', need '%u'", curtime(), seqbuf, expected_seq);
do_once = 0;
}
if (sigtimedwait(&set_CHLD, NULL, &ts) >= 0) {
@@ -992,7 +1017,7 @@ wait_for_seqfile(const char *seq)
continue; /* don't decrement timeout! */
}
if (--timeout == 0) {
- dbg1("%s waiting for '%s'", "timed out", seqbuf);
+ dbg1("%s mdev.seq='%s'", "timed out", seqbuf);
break;
}
}
@@ -1049,25 +1074,10 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
putenv((char*)"ACTION=add");
- /* ACTION_FOLLOWLINKS is needed since in newer kernels
- * /sys/block/loop* (for example) are symlinks to dirs,
- * not real directories.
- * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
- * but we can't enforce that on users)
- */
- if (access("/sys/class/block", F_OK) != 0) {
- /* Scan obsolete /sys/block only if /sys/class/block
- * doesn't exist. Otherwise we'll have dupes.
- * Also, do not complain if it doesn't exist.
- * Some people configure kernel to have no blockdevs.
- */
- recursive_action("/sys/block",
- ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
- fileAction, dirAction, temp, 0);
- }
- recursive_action("/sys/class",
- ACTION_RECURSE | ACTION_FOLLOWLINKS,
- fileAction, dirAction, temp, 0);
+ /* Create all devices from /sys/dev hierarchy */
+ recursive_action("/sys/dev",
+ ACTION_RECURSE | ACTION_FOLLOWLINKS,
+ fileAction, dirAction, temp, 0);
} else {
char *fw;
char *seq;
@@ -1075,6 +1085,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
char *env_devname;
char *env_devpath;
unsigned my_pid;
+ unsigned seqnum = seqnum; /* for compiler */
int seq_fd;
smalluint op;
@@ -1096,7 +1107,11 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
my_pid = getpid();
open_mdev_log(seq, my_pid);
- seq_fd = seq ? wait_for_seqfile(seq) : -1;
+ seq_fd = -1;
+ if (seq) {
+ seqnum = atoll(seq);
+ seq_fd = wait_for_seqfile(seqnum);
+ }
dbg1("%s "
"ACTION:%s SUBSYSTEM:%s DEVNAME:%s DEVPATH:%s"
@@ -1124,7 +1139,7 @@ int mdev_main(int argc UNUSED_PARAM, char **argv)
dbg1("%s exiting", curtime());
if (seq_fd >= 0) {
- xwrite_str(seq_fd, utoa(xatou(seq) + 1));
+ xwrite_str(seq_fd, utoa(seqnum + 1));
signal_mdevs(my_pid);
}
}
diff --git a/util-linux/minix.h b/util-linux/minix.h
index e0fbcf7..83ffe6d 100644
--- a/util-linux/minix.h
+++ b/util-linux/minix.h
@@ -61,9 +61,14 @@ enum {
MINIX_ROOT_INO = 1,
MINIX_BAD_INO = 2,
+#undef MINIX1_SUPER_MAGIC
MINIX1_SUPER_MAGIC = 0x137F, /* original minix fs */
+#undef MINIX1_SUPER_MAGIC2
MINIX1_SUPER_MAGIC2 = 0x138F, /* minix fs, 30 char names */
+/* bionic has this define */
+#undef MINIX2_SUPER_MAGIC
MINIX2_SUPER_MAGIC = 0x2468, /* minix V2 fs */
+#undef MINIX2_SUPER_MAGIC2
MINIX2_SUPER_MAGIC2 = 0x2478, /* minix V2 fs, 30 char names */
MINIX_VALID_FS = 0x0001, /* clean fs */
MINIX_ERROR_FS = 0x0002, /* fs has errors */
diff --git a/util-linux/mkfs_ext2.c b/util-linux/mkfs_ext2.c
index c437307..68dce5f 100644
--- a/util-linux/mkfs_ext2.c
+++ b/util-linux/mkfs_ext2.c
@@ -7,6 +7,26 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MKE2FS
+//config: bool "mke2fs"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Utility to create EXT2 filesystems.
+//config:
+//config:config MKFS_EXT2
+//config: bool "mkfs.ext2"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Alias to "mke2fs".
+
+//applet:IF_MKE2FS(APPLET_ODDNAME(mke2fs, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
+//applet:IF_MKFS_EXT2(APPLET_ODDNAME(mkfs.ext2, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
+////////:IF_MKFS_EXT3(APPLET_ODDNAME(mkfs.ext3, mkfs_ext2, BB_DIR_SBIN, BB_SUID_DROP, mkfs_ext2))
+
+//kbuild:lib-$(CONFIG_MKE2FS) += mkfs_ext2.o
+//kbuild:lib-$(CONFIG_MKFS_EXT2) += mkfs_ext2.o
//usage:#define mkfs_ext2_trivial_usage
//usage: "[-Fn] "
@@ -116,7 +136,7 @@ static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32
{
uint32_t i;
-//bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
+//bb_error_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
memset(bitmap, 0, blocksize);
i = start / 8;
memset(bitmap, 0xFF, i);
@@ -151,7 +171,7 @@ static uint32_t has_super(uint32_t x)
static void PUT(uint64_t off, void *buf, uint32_t size)
{
-// bb_info_msg("PUT[%llu]:[%u]", off, size);
+ //bb_error_msg("PUT[%llu]:[%u]", off, size);
xlseek(fd, off, SEEK_SET);
xwrite(fd, buf, size);
}
@@ -244,8 +264,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
// using global "option_mask32" instead of local "opts":
// we are register starved here
- opt_complementary = "-1:b+:i+:I+:m+";
- /*opts =*/ getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
+ /*opts =*/ getopt32(argv, "cl:b:+f:i:+I:+J:G:N:m:+o:g:L:M:O:r:E:T:U:jnqvFS",
/*lbfi:*/ NULL, &bs, NULL, &bpi,
/*IJGN:*/ &user_inodesize, NULL, NULL, NULL,
/*mogL:*/ &reserved_percent, NULL, NULL, &label,
@@ -412,7 +431,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
// (a bit after 8M image size), but it works for two->three groups
// transition (at 16M).
if (remainder && (remainder < overhead + 50)) {
-//bb_info_msg("CHOP[%u]", remainder);
+//bb_error_msg("CHOP[%u]", remainder);
nblocks -= remainder;
goto retry;
}
@@ -568,7 +587,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
// mark preallocated blocks as allocated
-//bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
+//bb_error_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
allocate(buf, blocksize,
// reserve "overhead" blocks
overhead,
@@ -647,7 +666,7 @@ int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
n = FETCH_LE32(inode->i_block[0]) + 1;
for (i = 0; i < lost_and_found_blocks; ++i)
STORE_LE(inode->i_block[i], i + n); // use next block
-//bb_info_msg("LAST BLOCK USED[%u]", i + n);
+//bb_error_msg("LAST BLOCK USED[%u]", i + n);
PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
buf, inodesize);
diff --git a/util-linux/mkfs_minix.c b/util-linux/mkfs_minix.c
index d65a516..912246b 100644
--- a/util-linux/mkfs_minix.c
+++ b/util-linux/mkfs_minix.c
@@ -62,6 +62,27 @@
* Modified for BusyBox by Erik Andersen <andersen@debian.org> --
* removed getopt based parser and added a hand rolled one.
*/
+//config:config MKFS_MINIX
+//config: bool "mkfs_minix"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The minix filesystem is a nice, small, compact, read-write filesystem
+//config: with little overhead. If you wish to be able to create minix
+//config: filesystems this utility will do the job for you.
+//config:
+//config:config FEATURE_MINIX2
+//config: bool "Support Minix fs v2 (fsck_minix/mkfs_minix)"
+//config: default y
+//config: depends on FSCK_MINIX || MKFS_MINIX
+//config: help
+//config: If you wish to be able to create version 2 minix filesystems, enable
+//config: this. If you enabled 'mkfs_minix' then you almost certainly want to
+//config: be using the version 2 filesystem support.
+
+//applet:IF_MKFS_MINIX(APPLET_ODDNAME(mkfs.minix, mkfs_minix, BB_DIR_SBIN, BB_SUID_DROP, mkfs_minix))
+
+//kbuild:lib-$(CONFIG_MKFS_MINIX) += mkfs_minix.o
//usage:#define mkfs_minix_trivial_usage
//usage: "[-c | -l FILE] [-nXX] [-iXX] BLOCKDEV [KBYTES]"
@@ -576,11 +597,11 @@ static void setup_tables(void)
for (i = MINIX_ROOT_INO; i <= SB_INODES; i++)
unmark_inode(i);
G.inode_buffer = xzalloc(INODE_BUFFER_SIZE);
- printf("%ld inodes\n", (long)SB_INODES);
- printf("%ld blocks\n", (long)SB_ZONES);
- printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone);
- printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE);
- printf("Maxsize=%ld\n", (long)SB_MAXSIZE);
+ printf("%lu inodes\n", (unsigned long)SB_INODES);
+ printf("%lu blocks\n", (unsigned long)SB_ZONES);
+ printf("Firstdatazone=%lu (%lu)\n", (unsigned long)SB_FIRSTZONE, (unsigned long)norm_firstzone);
+ printf("Zonesize=%u\n", BLOCK_SIZE << SB_ZONE_SIZE);
+ printf("Maxsize=%lu\n", (unsigned long)SB_MAXSIZE);
}
int mkfs_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -604,8 +625,7 @@ int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("bad inode size");
#endif
- opt_complementary = "n+"; /* -n N */
- opt = getopt32(argv, "ci:l:n:v", &str_i, &listfile, &G.namelen);
+ opt = getopt32(argv, "ci:l:n:+v", &str_i, &listfile, &G.namelen);
argv += optind;
//if (opt & 1) -c
if (opt & 2) G.req_nr_inodes = xatoul(str_i); // -i
diff --git a/util-linux/mkfs_reiser.c b/util-linux/mkfs_reiser.c
index b4efb9e..d01119f 100644
--- a/util-linux/mkfs_reiser.c
+++ b/util-linux/mkfs_reiser.c
@@ -6,6 +6,17 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MKFS_REISER
+//config: bool "mkfs_reiser"
+//config: default n
+//config: select PLATFORM_LINUX
+//config: help
+//config: Utility to create ReiserFS filesystems.
+//config: Note: this applet needs a lot of testing and polishing.
+
+//applet:IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, BB_DIR_SBIN, BB_SUID_DROP, mkfs_reiser))
+
+//kbuild:lib-$(CONFIG_MKFS_REISER) += mkfs_reiser.o
//usage:#define mkfs_reiser_trivial_usage
//usage: "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]"
@@ -66,7 +77,7 @@ struct reiserfs_super_block {
char s_magic[10]; /* 52 "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */
uint16_t sb_fs_state; /* 62 it is set to used by fsck to mark which phase of rebuilding is done (used for fsck debugging) */
- uint32_t sb_hash_function_code; /* 64 code of fuction which was/is/will be used to sort names in a directory. See codes in above */
+ uint32_t sb_hash_function_code; /* 64 code of function which was/is/will be used to sort names in a directory. See codes in above */
uint16_t sb_tree_height; /* 68 height of filesytem tree. Tree consisting of only one root block has 2 here */
uint16_t sb_bmap_nr; /* 70 amount of bitmap blocks needed to address each block of file system */
uint16_t sb_version; /* 72 this field is only reliable on filesystem with non-standard journal */
@@ -169,8 +180,8 @@ int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
// using global "option_mask32" instead of local "opts":
// we are register starved here
- opt_complementary = "-1:b+";
- /*opts =*/ getopt32(argv, "b:j:s:o:t:B:h:u:l:fqd",
+ opt_complementary = "-1";
+ /*opts =*/ getopt32(argv, "b:+j:s:o:t:B:h:u:l:fqd",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label);
argv += optind; // argv[0] -- device
@@ -224,8 +235,8 @@ int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
jp = &sb->sb_journal;
STORE_LE(jp->jp_journal_1st_block, REISERFS_DISK_OFFSET_IN_BYTES / blocksize + 1/*sb*/ + 1/*bmp#0*/);
timestamp = time(NULL);
- srandom(timestamp);
- STORE_LE(jp->jp_journal_magic, random());
+ srand(timestamp);
+ STORE_LE(jp->jp_journal_magic, rand());
STORE_LE(jp->jp_journal_size, journal_blocks);
STORE_LE(jp->jp_journal_trans_max, JOURNAL_TRANS_MAX);
STORE_LE(jp->jp_journal_max_batch, JOURNAL_MAX_BATCH);
diff --git a/util-linux/mkfs_vfat.c b/util-linux/mkfs_vfat.c
index 7d81ed0..ab70853 100644
--- a/util-linux/mkfs_vfat.c
+++ b/util-linux/mkfs_vfat.c
@@ -7,6 +7,25 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MKDOSFS
+//config: bool "mkdosfs"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Utility to create FAT32 filesystems.
+//config:
+//config:config MKFS_VFAT
+//config: bool "mkfs.vfat"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Alias to "mkdosfs".
+
+//applet:IF_MKDOSFS(APPLET_ODDNAME(mkdosfs, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
+//applet:IF_MKFS_VFAT(APPLET_ODDNAME(mkfs.vfat, mkfs_vfat, BB_DIR_SBIN, BB_SUID_DROP, mkfs_vfat))
+
+//kbuild:lib-$(CONFIG_MKDOSFS) += mkfs_vfat.o
+//kbuild:lib-$(CONFIG_MKFS_VFAT) += mkfs_vfat.o
//usage:#define mkfs_vfat_trivial_usage
//usage: "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
@@ -578,7 +597,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
- bb_info_msg("searching for bad blocks ");
+ bb_error_msg("searching for bad blocks");
currently_testing = 0;
try = TEST_BUFFER_BLOCKS;
while (currently_testing < volume_size_blocks) {
@@ -616,7 +635,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
}
free(blkbuf);
if (badblocks)
- bb_info_msg("%d bad block(s)", badblocks);
+ bb_error_msg("%d bad block(s)", badblocks);
}
#endif
diff --git a/util-linux/mkswap.c b/util-linux/mkswap.c
index b5d2c49..954a194 100644
--- a/util-linux/mkswap.c
+++ b/util-linux/mkswap.c
@@ -5,6 +5,29 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config MKSWAP
+//config: bool "mkswap"
+//config: default y
+//config: help
+//config: The mkswap utility is used to configure a file or disk partition as
+//config: Linux swap space. This allows Linux to use the entire file or
+//config: partition as if it were additional RAM, which can greatly increase
+//config: the capability of low-memory machines. This additional memory is
+//config: much slower than real RAM, but can be very helpful at preventing your
+//config: applications being killed by the Linux out of memory (OOM) killer.
+//config: Once you have created swap space using 'mkswap' you need to enable
+//config: the swap space using the 'swapon' utility.
+//config:
+//config:config FEATURE_MKSWAP_UUID
+//config: bool "UUID support"
+//config: default y
+//config: depends on MKSWAP
+//config: help
+//config: Generate swap spaces with universally unique identifiers.
+
+//applet:IF_MKSWAP(APPLET(mkswap, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MKSWAP) += mkswap.o
//usage:#define mkswap_trivial_usage
//usage: "[-L LBL] BLOCKDEV [KBYTES]"
@@ -13,6 +36,7 @@
//usage: "\n -L LBL Label"
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_SELINUX
static void mkswap_selinux_setcontext(int fd, const char *path)
@@ -75,6 +99,7 @@ struct swap_header_v1 {
#define NWORDS 129
#define hdr ((struct swap_header_v1*)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
struct BUG_sizes {
char swap_header_v1_wrong[sizeof(*hdr) != (NWORDS * 4) ? -1 : 1];
@@ -92,6 +117,8 @@ int mkswap_main(int argc UNUSED_PARAM, char **argv)
off_t len;
const char *label = "";
+ INIT_G();
+
opt_complementary = "-1"; /* at least one param */
/* TODO: -p PAGESZ, -U UUID */
getopt32(argv, "L:", &label);
diff --git a/util-linux/more.c b/util-linux/more.c
index 04ad2d1..31e27ab 100644
--- a/util-linux/more.c
+++ b/util-linux/more.c
@@ -13,6 +13,19 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config MORE
+//config: bool "more"
+//config: default y
+//config: help
+//config: more is a simple utility which allows you to read text one screen
+//config: sized page at a time. If you want to read text that is larger than
+//config: the screen, and you are using anything faster than a 300 baud modem,
+//config: you will probably find this utility very helpful. If you don't have
+//config: any need to reading text files, you can leave this disabled.
+
+//applet:IF_MORE(APPLET(more, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MORE) += more.o
//usage:#define more_trivial_usage
//usage: "[FILE]..."
@@ -23,6 +36,7 @@
//usage: "$ dmesg | more\n"
#include "libbb.h"
+#include "common_bufsiz.h"
/* Support for FEATURE_USE_TERMIOS */
@@ -32,10 +46,10 @@ struct globals {
struct termios new_settings;
} FIX_ALIASING;
#define G (*(struct globals*)bb_common_bufsiz1)
-#define INIT_G() ((void)0)
#define initial_settings (G.initial_settings)
#define new_settings (G.new_settings )
#define cin_fileno (G.cin_fileno )
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#define setTermSettings(fd, argp) \
do { \
@@ -71,7 +85,16 @@ int more_main(int argc UNUSED_PARAM, char **argv)
INIT_G();
- argv++;
+ /* Parse options */
+ /* Accepted but ignored: */
+ /* -d Display help instead of ringing bell is pressed */
+ /* -f Count logical lines (IOW: long lines are not folded) */
+ /* -l Do not pause after any line containing a ^L (form feed) */
+ /* -s Squeeze blank lines into one */
+ /* -u Suppress underlining */
+ getopt32(argv, "dflsu");
+ argv += optind;
+
/* Another popular pager, most, detects when stdout
* is not a tty and turns into cat. This makes sense. */
if (!isatty(STDOUT_FILENO))
diff --git a/util-linux/mount.c b/util-linux/mount.c
index 16b58a0..4e4e154 100644
--- a/util-linux/mount.c
+++ b/util-linux/mount.c
@@ -17,8 +17,111 @@
// mount_it_now() does the actual mount.
//
+//config:config MOUNT
+//config: bool "mount"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: All files and filesystems in Unix are arranged into one big directory
+//config: tree. The 'mount' utility is used to graft a filesystem onto a
+//config: particular part of the tree. A filesystem can either live on a block
+//config: device, or it can be accessible over the network, as is the case with
+//config: NFS filesystems. Most people using BusyBox will also want to enable
+//config: the 'mount' utility.
+//config:
+//config:config FEATURE_MOUNT_FAKE
+//config: bool "Support option -f"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable support for faking a file system mount.
+//config:
+//config:config FEATURE_MOUNT_VERBOSE
+//config: bool "Support option -v"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable multi-level -v[vv...] verbose messages. Useful if you
+//config: debug mount problems and want to see what is exactly passed
+//config: to the kernel.
+//config:
+//config:config FEATURE_MOUNT_HELPERS
+//config: bool "Support mount helpers"
+//config: default n
+//config: depends on MOUNT
+//config: help
+//config: Enable mounting of virtual file systems via external helpers.
+//config: E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
+//config: "obexfs -b00.11.22.33.44.55 /mnt"
+//config: Also "mount -t sometype [-o opts] fs /mnt" will try
+//config: "sometype [-o opts] fs /mnt" if simple mount syscall fails.
+//config: The idea is to use such virtual filesystems in /etc/fstab.
+//config:
+//config:config FEATURE_MOUNT_LABEL
+//config: bool "Support specifying devices by label or UUID"
+//config: default y
+//config: depends on MOUNT
+//config: select VOLUMEID
+//config: help
+//config: This allows for specifying a device by label or uuid, rather than by
+//config: name. This feature utilizes the same functionality as blkid/findfs.
+//config: This also enables label or uuid support for swapon.
+//config:
+//config:config FEATURE_MOUNT_NFS
+//config: bool "Support mounting NFS file systems on Linux < 2.6.23"
+//config: default n
+//config: depends on MOUNT
+//config: select FEATURE_HAVE_RPC
+//config: select FEATURE_SYSLOG
+//config: help
+//config: Enable mounting of NFS file systems on Linux kernels prior
+//config: to version 2.6.23. Note that in this case mounting of NFS
+//config: over IPv6 will not be possible.
+//config:
+//config: Note that this option links in RPC support from libc,
+//config: which is rather large (~10 kbytes on uclibc).
+//config:
+//config:config FEATURE_MOUNT_CIFS
+//config: bool "Support mounting CIFS/SMB file systems"
+//config: default y
+//config: depends on MOUNT
+//config: help
+//config: Enable support for samba mounts.
+//config:
+//config:config FEATURE_MOUNT_FLAGS
+//config: depends on MOUNT
+//config: bool "Support lots of -o flags in mount"
+//config: default y
+//config: help
+//config: Without this, mount only supports ro/rw/remount. With this, it
+//config: supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+//config: noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+//config: private, unbindable, rshared, rslave, rprivate, and runbindable.
+//config:
+//config:config FEATURE_MOUNT_FSTAB
+//config: depends on MOUNT
+//config: bool "Support /etc/fstab and -a"
+//config: default y
+//config: help
+//config: Support mount all and looking for files in /etc/fstab.
+//config:
+//config:config FEATURE_MOUNT_OTHERTAB
+//config: depends on FEATURE_MOUNT_FSTAB
+//config: bool "Support -T <alt_fstab>"
+//config: default y
+//config: help
+//config: Support mount -T (specifying an alternate fstab)
+
+/* On full-blown systems, requires suid for user mounts.
+ * But it's not unthinkable to have it available in non-suid flavor on some systems,
+ * for viewing mount table.
+ * Therefore we use BB_SUID_MAYBE instead of BB_SUID_REQUIRE: */
+//applet:IF_MOUNT(APPLET(mount, BB_DIR_BIN, IF_DESKTOP(BB_SUID_MAYBE) IF_NOT_DESKTOP(BB_SUID_DROP)))
+
+//kbuild:lib-$(CONFIG_MOUNT) += mount.o
+
//usage:#define mount_trivial_usage
-//usage: "[OPTIONS] [-o OPTS] DEVICE NODE"
+//usage: "[OPTIONS] [-o OPT] DEVICE NODE"
//usage:#define mount_full_usage "\n\n"
//usage: "Mount a filesystem. Filesystem autodetection requires /proc.\n"
//usage: "\n -a Mount all filesystems in fstab"
@@ -41,8 +144,11 @@
//usage: )
////usage: "\n -s Sloppy (ignored)"
//usage: "\n -r Read-only mount"
-//usage: "\n -w Read-write mount (default)"
+////usage: "\n -w Read-write mount (default)"
//usage: "\n -t FSTYPE[,...] Filesystem type(s)"
+//usage: IF_FEATURE_MOUNT_OTHERTAB(
+//usage: "\n -T FILE Read FILE instead of /etc/fstab"
+//usage: )
//usage: "\n -O OPT Mount only filesystems with option OPT (-a only)"
//usage: "\n-o OPT:"
//usage: IF_FEATURE_MOUNT_LOOP(
@@ -64,7 +170,7 @@
//usage: "\n move Relocate an existing mount point"
//usage: )
//usage: "\n remount Remount a mounted filesystem, changing flags"
-//usage: "\n ro/rw Same as -r/-w"
+//usage: "\n ro Same as -r"
//usage: "\n"
//usage: "\nThere are filesystem-specific -o flags."
//usage:
@@ -82,6 +188,8 @@
#include <mntent.h>
#include <syslog.h>
#include <sys/mount.h>
+#include <android.h>
+
// Grab more as needed from util-linux's mount/mount_constants.h
#ifndef MS_DIRSYNC
# define MS_DIRSYNC (1 << 7) // Directory modifications are synchronous
@@ -125,6 +233,7 @@
#define BB_MS_INVERTED_VALUE (1u << 31)
#include "libbb.h"
+#include "common_bufsiz.h"
#if ENABLE_FEATURE_MOUNT_LABEL
# include "volume_id.h"
#else
@@ -161,13 +270,15 @@ static struct mntent *getmntent_r(FILE* stream, struct mntent* result,
// Not real flags, but we want to be able to check for this.
enum {
- MOUNT_USERS = (1 << 28) * ENABLE_DESKTOP,
+ MOUNT_USERS = (1 << 27) * ENABLE_DESKTOP,
+ MOUNT_NOFAIL = (1 << 28) * ENABLE_DESKTOP,
MOUNT_NOAUTO = (1 << 29),
MOUNT_SWAP = (1 << 30),
+ MOUNT_FAKEFLAGS = MOUNT_USERS | MOUNT_NOFAIL | MOUNT_NOAUTO | MOUNT_SWAP
};
-#define OPTION_STR "o:t:rwanfvsiO:"
+#define OPTION_STR "o:*t:rwanfvsiO:" IF_FEATURE_MOUNT_OTHERTAB("T:")
enum {
OPT_o = (1 << 0),
OPT_t = (1 << 1),
@@ -180,6 +291,7 @@ enum {
OPT_s = (1 << 8),
OPT_i = (1 << 9),
OPT_O = (1 << 10),
+ OPT_T = (1 << 11),
};
#if ENABLE_FEATURE_MTAB_SUPPORT
@@ -227,6 +339,7 @@ static const int32_t mount_options[] = {
/* "swap" */ MOUNT_SWAP,
IF_DESKTOP(/* "user" */ MOUNT_USERS,)
IF_DESKTOP(/* "users" */ MOUNT_USERS,)
+ IF_DESKTOP(/* "nofail" */ MOUNT_NOFAIL,)
/* "_netdev" */ 0,
IF_DESKTOP(/* "comment=" */ 0,) /* systemd uses this in fstab */
)
@@ -274,7 +387,7 @@ static const int32_t mount_options[] = {
/* "remount" */ MS_REMOUNT // action flag
};
-static const char mount_option_str[] =
+static const char mount_option_str[] ALIGN1 =
IF_FEATURE_MOUNT_LOOP(
"loop\0"
)
@@ -286,6 +399,7 @@ static const char mount_option_str[] =
"swap\0"
IF_DESKTOP("user\0")
IF_DESKTOP("users\0")
+ IF_DESKTOP("nofail\0")
"_netdev\0"
IF_DESKTOP("comment=\0") /* systemd uses this in fstab */
)
@@ -344,7 +458,7 @@ struct globals {
char getmntent_buf[1];
} FIX_ALIASING;
enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define nfs_mount_version (G.nfs_mount_version)
#if ENABLE_FEATURE_MOUNT_VERBOSE
#define verbose (G.verbose )
@@ -353,7 +467,7 @@ enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_b
#endif
#define fslist (G.fslist )
#define getmntent_buf (G.getmntent_buf )
-#define INIT_G() do { } while (0)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
#if ENABLE_FEATURE_MTAB_SUPPORT
/*
@@ -542,7 +656,7 @@ static llist_t *get_block_backed_filesystems(void)
if (!f) continue;
while ((buf = xmalloc_fgetline(f)) != NULL) {
- if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
+ if (is_prefixed_with(buf, "nodev") && isspace(buf[5]))
goto next;
fs = skip_whitespace(buf);
if (*fs == '#' || *fs == '*' || !*fs)
@@ -573,6 +687,8 @@ static int mount_it_now(struct mntent *mp, unsigned long vfsflags, char *filtero
{
int rc = 0;
+ vfsflags &= ~(unsigned long)MOUNT_FAKEFLAGS;
+
if (FAKE_IT) {
if (verbose >= 2)
bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
@@ -897,7 +1013,7 @@ enum {
# define EDQUOT ENOSPC
#endif
/* Convert each NFSERR_BLAH into EBLAH */
-static const uint8_t nfs_err_stat[] = {
+static const uint8_t nfs_err_stat[] ALIGN1 = {
1, 2, 5, 6, 13, 17,
19, 20, 21, 22, 27, 28,
30, 63, 66, 69, 70, 71
@@ -910,7 +1026,7 @@ typedef uint8_t nfs_err_type;
#else
typedef uint16_t nfs_err_type;
#endif
-static const nfs_err_type nfs_err_errnum[] = {
+static const nfs_err_type nfs_err_errnum[] ALIGN2 = {
EPERM , ENOENT , EIO , ENXIO , EACCES, EEXIST,
ENODEV, ENOTDIR , EISDIR , EINVAL, EFBIG , ENOSPC,
EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
@@ -1265,9 +1381,9 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
strcspn(opteq, " \t\n\r,"));
continue;
case 18: // "proto"
- if (!strncmp(opteq, "tcp", 3))
+ if (is_prefixed_with(opteq, "tcp"))
tcp = 1;
- else if (!strncmp(opteq, "udp", 3))
+ else if (is_prefixed_with(opteq, "udp"))
tcp = 0;
else
bb_error_msg("warning: unrecognized proto= option");
@@ -1360,7 +1476,7 @@ static NOINLINE int nfsmount(struct mntent *mp, unsigned long vfsflags, char *fi
"rdirplus\0"
"acl\0";
int val = 1;
- if (!strncmp(opt, "no", 2)) {
+ if (is_prefixed_with(opt, "no")) {
val = 0;
opt += 2;
}
@@ -1825,7 +1941,6 @@ static int singlemount(struct mntent *mp, int ignore_busy)
int len;
char c;
char *hostname, *share;
- char *dotted, *ip;
len_and_sockaddr *lsa;
// Parse mp->mnt_fsname of the form "//hostname/share[/dir1/dir2]"
@@ -1865,13 +1980,26 @@ static int singlemount(struct mntent *mp, int ignore_busy)
if (!lsa)
goto report_error;
- // Insert "ip=..." option into options
- dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
- if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
- ip = xasprintf("ip=%s", dotted);
- if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
- parse_mount_options(ip, &filteropts);
- if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+ // If there is no "ip=..." option yet
+ if (!is_prefixed_with(filteropts, ",ip="+1)
+ && !strstr(filteropts, ",ip=")
+ ) {
+ char *dotted, *ip;
+ // Insert "ip=..." option into options
+ dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+ if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+ ip = xasprintf("ip=%s", dotted);
+ if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+// Note: IPv6 scoped addresses ("host%iface", see RFC 4007) should be
+// handled by libc in getnameinfo() (inside xmalloc_sockaddr2dotted_noport()).
+// Currently, glibc does not support that (has no NI_NUMERICSCOPE),
+// musl apparently does. This results in "ip=numericIPv6%iface_name"
+// (instead of _numeric_ iface_id) with glibc.
+// This probably should be fixed in glibc, not here.
+// The workaround is to manually specify correct "ip=ADDR%n" option.
+ parse_mount_options(ip, &filteropts);
+ if (ENABLE_FEATURE_CLEAN_UP) free(ip);
+ }
mp->mnt_type = (char*)"cifs";
rc = mount_it_now(mp, vfsflags, filteropts);
@@ -1880,7 +2008,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
}
// Might this be an NFS filesystem?
- if ((!mp->mnt_type || strncmp(mp->mnt_type, "nfs", 3) == 0)
+ if ((!mp->mnt_type || is_prefixed_with(mp->mnt_type, "nfs"))
&& strchr(mp->mnt_fsname, ':') != NULL
) {
if (!mp->mnt_type)
@@ -1952,7 +2080,7 @@ static int singlemount(struct mntent *mp, int ignore_busy)
del_loop(mp->mnt_fsname);
if (ENABLE_FEATURE_CLEAN_UP) {
free(loopFile);
- free(mp->mnt_fsname);
+ /* No, "rc != 0" needs it: free(mp->mnt_fsname); */
}
}
@@ -1962,6 +2090,8 @@ static int singlemount(struct mntent *mp, int ignore_busy)
if (errno == EBUSY && ignore_busy)
return 0;
+ if (errno == ENOENT && (vfsflags & MOUNT_NOFAIL))
+ return 0;
if (rc != 0)
bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
return rc;
@@ -2034,7 +2164,7 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
char *O_optmatch = NULL;
char *storage_path;
llist_t *lst_o = NULL;
- const char *fstabname;
+ const char *fstabname = "/etc/fstab";
FILE *fstab;
int i, j;
int rc = EXIT_SUCCESS;
@@ -2059,8 +2189,9 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
// Parse remaining options
// Max 2 params; -o is a list, -v is a counter
- opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
+ opt_complementary = "?2" IF_FEATURE_MOUNT_VERBOSE("vv");
opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
+ IF_FEATURE_MOUNT_OTHERTAB(, &fstabname)
IF_FEATURE_MOUNT_VERBOSE(, &verbose));
while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
@@ -2128,8 +2259,10 @@ int mount_main(int argc UNUSED_PARAM, char **argv)
return rc;
}
+ // A malicious user could overmount /usr without this.
+ if (ENABLE_FEATURE_MOUNT_OTHERTAB && nonroot)
+ fstabname = "/etc/fstab";
// Open either fstab or mtab
- fstabname = "/etc/fstab";
if (cmdopt_flags & MS_REMOUNT) {
// WARNING. I am not sure this matches util-linux's
// behavior. It's possible util-linux does not
diff --git a/util-linux/nsenter.c b/util-linux/nsenter.c
new file mode 100644
index 0000000..e4ade5d
--- a/dev/null
+++ b/util-linux/nsenter.c
@@ -0,0 +1,291 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nsenter implementation for busybox.
+ *
+ * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config NSENTER
+//config: bool "nsenter"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Run program with namespaces of other processes.
+//config:
+//config:config FEATURE_NSENTER_LONG_OPTS
+//config: bool "Enable long options"
+//config: default y
+//config: depends on NSENTER && LONG_OPTS
+//config: help
+//config: Support long options for the nsenter applet. This makes
+//config: the busybox implementation more compatible with upstream.
+
+//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NSENTER) += nsenter.o
+
+//usage:#define nsenter_trivial_usage
+//usage: "[OPTIONS] [PROG [ARGS]]"
+//usage:#if ENABLE_FEATURE_NSENTER_LONG_OPTS
+//usage:#define nsenter_full_usage "\n"
+//usage: "\n -t, --target=PID Target process to get namespaces from"
+//usage: "\n -m, --mount[=FILE] Enter mount namespace"
+//usage: "\n -u, --uts[=FILE] Enter UTS namespace (hostname etc)"
+//usage: "\n -i, --ipc[=FILE] Enter System V IPC namespace"
+//usage: "\n -n, --net[=FILE] Enter network namespace"
+//usage: "\n -p, --pid[=FILE] Enter pid namespace"
+//usage: "\n -U, --user[=FILE] Enter user namespace"
+//usage: "\n -S, --setuid=UID Set uid in entered namespace"
+//usage: "\n -G, --setgid=GID Set gid in entered namespace"
+//usage: "\n --preserve-credentials Don't touch uids or gids"
+//usage: "\n -r, --root[=DIR] Set root directory"
+//usage: "\n -w, --wd[=DIR] Set working directory"
+//usage: "\n -F, --no-fork Don't fork before exec'ing PROG"
+//usage:#else
+//usage:#define nsenter_full_usage "\n"
+//usage: "\n -t PID Target process to get namespaces from"
+//usage: "\n -m[FILE] Enter mount namespace"
+//usage: "\n -u[FILE] Enter UTS namespace (hostname etc)"
+//usage: "\n -i[FILE] Enter System V IPC namespace"
+//usage: "\n -n[FILE] Enter network namespace"
+//usage: "\n -p[FILE] Enter pid namespace"
+//usage: "\n -U[FILE] Enter user namespace"
+//usage: "\n -S UID Set uid in entered namespace"
+//usage: "\n -G GID Set gid in entered namespace"
+//usage: "\n -r[DIR] Set root directory"
+//usage: "\n -w[DIR] Set working directory"
+//usage: "\n -F Don't fork before exec'ing PROG"
+//usage:#endif
+
+#include <sched.h>
+#ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+#endif
+#ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWUSER
+# define CLONE_NEWUSER 0x10000000
+#endif
+#ifndef CLONE_NEWPID
+# define CLONE_NEWPID 0x20000000
+#endif
+#ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+#endif
+
+#include "libbb.h"
+
+struct namespace_descr {
+ int flag; /* value passed to setns() */
+ char ns_nsfile8[8]; /* "ns/" + namespace file in process' procfs entry */
+};
+
+struct namespace_ctx {
+ char *path; /* optional path to a custom ns file */
+ int fd; /* opened namespace file descriptor */
+};
+
+enum {
+ OPT_user = 1 << 0,
+ OPT_ipc = 1 << 1,
+ OPT_uts = 1 << 2,
+ OPT_network = 1 << 3,
+ OPT_pid = 1 << 4,
+ OPT_mount = 1 << 5,
+ OPT_target = 1 << 6,
+ OPT_setuid = 1 << 7,
+ OPT_setgid = 1 << 8,
+ OPT_root = 1 << 9,
+ OPT_wd = 1 << 10,
+ OPT_nofork = 1 << 11,
+ OPT_prescred = (1 << 12) * ENABLE_FEATURE_NSENTER_LONG_OPTS,
+};
+enum {
+ NS_USR_POS = 0,
+ NS_IPC_POS,
+ NS_UTS_POS,
+ NS_NET_POS,
+ NS_PID_POS,
+ NS_MNT_POS,
+ NS_COUNT,
+};
+/*
+ * The order is significant in nsenter.
+ * The user namespace comes first, so that it is entered first.
+ * This gives an unprivileged user the potential to enter other namespaces.
+ */
+static const struct namespace_descr ns_list[] = {
+ { CLONE_NEWUSER, "ns/user", },
+ { CLONE_NEWIPC, "ns/ipc", },
+ { CLONE_NEWUTS, "ns/uts", },
+ { CLONE_NEWNET, "ns/net", },
+ { CLONE_NEWPID, "ns/pid", },
+ { CLONE_NEWNS, "ns/mnt", },
+};
+/*
+ * Upstream nsenter doesn't support the short option for --preserve-credentials
+ */
+static const char opt_str[] ALIGN1 = "U::i::u::n::p::m::""t+S+G+r::w::F";
+
+#if ENABLE_FEATURE_NSENTER_LONG_OPTS
+static const char nsenter_longopts[] ALIGN1 =
+ "user\0" Optional_argument "U"
+ "ipc\0" Optional_argument "i"
+ "uts\0" Optional_argument "u"
+ "network\0" Optional_argument "n"
+ "pid\0" Optional_argument "p"
+ "mount\0" Optional_argument "m"
+ "target\0" Required_argument "t"
+ "setuid\0" Required_argument "S"
+ "setgid\0" Required_argument "G"
+ "root\0" Optional_argument "r"
+ "wd\0" Optional_argument "w"
+ "no-fork\0" No_argument "F"
+ "preserve-credentials\0" No_argument "\xff"
+ ;
+#endif
+
+/*
+ * Open a file and return the new descriptor. If a full path is provided in
+ * fs_path, then the file to which it points is opened. Otherwise (fd_path is
+ * NULL) the routine builds a path to a procfs file using the following
+ * template: '/proc/<target_pid>/<target_file>'.
+ */
+static int open_by_path_or_target(const char *path,
+ pid_t target_pid, const char *target_file)
+{
+ char proc_path_buf[sizeof("/proc/%u/1234567890") + sizeof(int)*3];
+
+ if (!path) {
+ if (target_pid == 0) {
+ /* Example:
+ * "nsenter -p PROG" - neither -pFILE nor -tPID given.
+ */
+ bb_show_usage();
+ }
+ snprintf(proc_path_buf, sizeof(proc_path_buf),
+ "/proc/%u/%s", (unsigned)target_pid, target_file);
+ path = proc_path_buf;
+ }
+
+ return xopen(path, O_RDONLY);
+}
+
+int nsenter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nsenter_main(int argc UNUSED_PARAM, char **argv)
+{
+ int i;
+ unsigned int opts;
+ const char *root_dir_str = NULL;
+ const char *wd_str = NULL;
+ struct namespace_ctx ns_ctx_list[NS_COUNT];
+ int setgroups_failed;
+ int root_fd, wd_fd;
+ int target_pid = 0;
+ int uid = 0;
+ int gid = 0;
+
+ memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
+
+ IF_FEATURE_NSENTER_LONG_OPTS(applet_long_options = nsenter_longopts);
+ opts = getopt32(argv, opt_str,
+ &ns_ctx_list[NS_USR_POS].path,
+ &ns_ctx_list[NS_IPC_POS].path,
+ &ns_ctx_list[NS_UTS_POS].path,
+ &ns_ctx_list[NS_NET_POS].path,
+ &ns_ctx_list[NS_PID_POS].path,
+ &ns_ctx_list[NS_MNT_POS].path,
+ &target_pid, &uid, &gid,
+ &root_dir_str, &wd_str
+ );
+ argv += optind;
+
+ root_fd = wd_fd = -1;
+ if (opts & OPT_root)
+ root_fd = open_by_path_or_target(root_dir_str,
+ target_pid, "root");
+ if (opts & OPT_wd)
+ wd_fd = open_by_path_or_target(wd_str, target_pid, "cwd");
+
+ for (i = 0; i < NS_COUNT; i++) {
+ const struct namespace_descr *ns = &ns_list[i];
+ struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
+
+ ns_ctx->fd = -1;
+ if (opts & (1 << i))
+ ns_ctx->fd = open_by_path_or_target(ns_ctx->path,
+ target_pid, ns->ns_nsfile8);
+ }
+
+ /*
+ * Entering the user namespace without --preserve-credentials implies
+ * --setuid & --setgid and clearing root's groups.
+ */
+ setgroups_failed = 0;
+ if ((opts & OPT_user) && !(opts & OPT_prescred)) {
+ opts |= (OPT_setuid | OPT_setgid);
+ /*
+ * We call setgroups() before and after setns() and only
+ * bail-out if it fails twice.
+ */
+ setgroups_failed = (setgroups(0, NULL) < 0);
+ }
+
+ for (i = 0; i < NS_COUNT; i++) {
+ const struct namespace_descr *ns = &ns_list[i];
+ struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
+
+ if (ns_ctx->fd < 0)
+ continue;
+ /*if (setns(ns_ctx->fd, ns->flag)) {
+ bb_perror_msg_and_die(
+ "setns(): can't reassociate to namespace '%s'",
+ ns->ns_nsfile8 + 3
+ );
+ }*/
+ close(ns_ctx->fd); /* should close fds, to not confuse exec'ed PROG */
+ /*ns_ctx->fd = -1;*/
+ }
+
+ if (root_fd >= 0) {
+ if (wd_fd < 0) {
+ /*
+ * Save the current working directory if we're not
+ * changing it.
+ */
+ wd_fd = xopen(".", O_RDONLY);
+ }
+ xfchdir(root_fd);
+ xchroot(".");
+ close(root_fd);
+ /*root_fd = -1;*/
+ }
+
+ if (wd_fd >= 0) {
+ xfchdir(wd_fd);
+ close(wd_fd);
+ /*wd_fd = -1;*/
+ }
+
+ /*
+ * Entering the pid namespace implies forking unless it's been
+ * explicitly requested by the user not to.
+ */
+ if (!(opts & OPT_nofork) && (opts & OPT_pid)) {
+ xvfork_parent_waits_and_exits();
+ /* Child continues */
+ }
+
+ if (opts & OPT_setgid) {
+ if (setgroups(0, NULL) < 0 && setgroups_failed)
+ bb_perror_msg_and_die("setgroups");
+ xsetgid(gid);
+ }
+ if (opts & OPT_setuid)
+ xsetuid(uid);
+
+ exec_prog_or_SHELL(argv);
+}
diff --git a/util-linux/pivot_root.c b/util-linux/pivot_root.c
index 83f01fa..9bdae50 100644
--- a/util-linux/pivot_root.c
+++ b/util-linux/pivot_root.c
@@ -8,6 +8,22 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config PIVOT_ROOT
+//config: bool "pivot_root"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The pivot_root utility swaps the mount points for the root filesystem
+//config: with some other mounted filesystem. This allows you to do all sorts
+//config: of wild and crazy things with your Linux system and is far more
+//config: powerful than 'chroot'.
+//config:
+//config: Note: This is for initrd in linux 2.4. Under initramfs (introduced
+//config: in linux 2.6) use switch_root instead.
+
+//applet:IF_PIVOT_ROOT(APPLET(pivot_root, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_PIVOT_ROOT) += pivot_root.o
//usage:#define pivot_root_trivial_usage
//usage: "NEW_ROOT PUT_OLD"
diff --git a/util-linux/rdate.c b/util-linux/rdate.c
index 6e35cd5..51d5eb6 100644
--- a/util-linux/rdate.c
+++ b/util-linux/rdate.c
@@ -6,16 +6,30 @@
* by Sterling Huxley <sterling@europa.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
-*/
+ */
+//config:config RDATE
+//config: bool "rdate"
+//config: default y
+//config: help
+//config: The rdate utility allows you to synchronize the date and time of your
+//config: system clock with the date and time of a remote networked system using
+//config: the RFC868 protocol, which is built into the inetd daemon on most
+//config: systems.
+
+//applet:IF_RDATE(APPLET(rdate, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RDATE) += rdate.o
//usage:#define rdate_trivial_usage
//usage: "[-sp] HOST"
//usage:#define rdate_full_usage "\n\n"
-//usage: "Get and possibly set the system date/time from a remote HOST\n"
-//usage: "\n -s Set the system date/time (default)"
-//usage: "\n -p Print the date/time"
+//usage: "Get and possibly set system time from a remote HOST\n"
+//usage: "\n -s Set system time (default)"
+//usage: "\n -p Print time"
#include "libbb.h"
+#include <android.h>
+
enum { RFC_868_BIAS = 2208988800UL };
@@ -36,7 +50,7 @@ static time_t askremotedate(const char *host)
fd = create_and_connect_stream_or_die(host, bb_lookup_port("time", "tcp", 37));
if (safe_read(fd, &nett, 4) != 4) /* read time from server */
- bb_error_msg_and_die("%s did not send the complete time", host);
+ bb_error_msg_and_die("%s: %s", host, "short read");
if (ENABLE_FEATURE_CLEAN_UP)
close(fd);
diff --git a/util-linux/rdev.c b/util-linux/rdev.c
index 4652817..0e1578e 100644
--- a/util-linux/rdev.c
+++ b/util-linux/rdev.c
@@ -8,6 +8,15 @@
* Licensed under GPLv2, see file LICENSE in this source tree.
*
*/
+//config:config RDEV
+//config: bool "rdev"
+//config: default y
+//config: help
+//config: Print the device node associated with the filesystem mounted at '/'.
+
+//applet:IF_RDEV(APPLET(rdev, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RDEV) += rdev.o
//usage:#define rdev_trivial_usage
//usage: ""
diff --git a/util-linux/readprofile.c b/util-linux/readprofile.c
index a645404..31abb6b 100644
--- a/util-linux/readprofile.c
+++ b/util-linux/readprofile.c
@@ -31,6 +31,16 @@
* Taken from util-linux and adapted for busybox by
* Paul Mundt <lethal@linux-sh.org>.
*/
+//config:config READPROFILE
+//config: bool "readprofile"
+//config: default y
+//config: #select PLATFORM_LINUX
+//config: help
+//config: This allows you to parse /proc/profile for basic profiling.
+
+//applet:IF_READPROFILE(APPLET(readprofile, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_READPROFILE) += readprofile.o
//usage:#define readprofile_trivial_usage
//usage: "[OPTIONS]"
@@ -99,8 +109,7 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
proFile = defaultpro;
mapFile = defaultmap;
- opt_complementary = "M+"; /* -M N */
- opt = getopt32(argv, "M:m:p:nabsirv", &multiplier, &mapFile, &proFile);
+ opt = getopt32(argv, "M:+m:p:nabsirv", &multiplier, &mapFile, &proFile);
if (opt & (OPT_M|OPT_r)) { /* mult or reset, or both */
int fd, to_write;
@@ -165,7 +174,7 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg_and_die("%s(%i): wrong map line",
mapFile, maplineno);
- if (!strcmp(fn_name, "_stext")) /* only elf works like this */ {
+ if (strcmp(fn_name, "_stext") == 0) /* only elf works like this */ {
add0 = fn_add;
break;
}
@@ -215,8 +224,9 @@ int readprofile_main(int argc UNUSED_PARAM, char **argv)
if (optBins) {
if (optVerbose || this > 0)
printf(" total\t\t\t\t%u\n", this);
- } else if ((this || optAll)
- && (fn_len = next_add-fn_add) != 0
+ } else
+ if ((this || optAll)
+ && (fn_len = next_add-fn_add) != 0
) {
if (optVerbose)
printf("%016llx %-40s %6u %8.4f\n", fn_add,
diff --git a/util-linux/rev.c b/util-linux/rev.c
index 3c1b22f..c225053 100644
--- a/util-linux/rev.c
+++ b/util-linux/rev.c
@@ -5,17 +5,16 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
-
-//applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_REV) += rev.o
-
//config:config REV
//config: bool "rev"
//config: default y
//config: help
//config: Reverse lines of a file or files.
+//applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_REV) += rev.o
+
//usage:#define rev_trivial_usage
//usage: "[FILE]..."
//usage:#define rev_full_usage "\n\n"
diff --git a/util-linux/rtcwake.c b/util-linux/rtcwake.c
index 735a298..54fc705 100644
--- a/util-linux/rtcwake.c
+++ b/util-linux/rtcwake.c
@@ -22,6 +22,16 @@
* RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
* That flag should not be needed on systems with adjtime support.
*/
+//config:config RTCWAKE
+//config: bool "rtcwake"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Enter a system sleep state until specified wakeup time.
+
+//applet:IF_RTCWAKE(APPLET(rtcwake, BB_DIR_USR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_RTCWAKE) += rtcwake.o
//usage:#define rtcwake_trivial_usage
//usage: "[-a | -l | -u] [-d DEV] [-m MODE] [-s SEC | -t TIME]"
@@ -32,18 +42,18 @@
//usage: "\n -l,--local Clock is set to local time"
//usage: "\n -u,--utc Clock is set to UTC time"
//usage: "\n -d,--device=DEV Specify the RTC device"
-//usage: "\n -m,--mode=MODE Set the sleep state (default: standby)"
-//usage: "\n -s,--seconds=SEC Set the timeout in SEC seconds from now"
-//usage: "\n -t,--time=TIME Set the timeout to TIME seconds from epoch"
+//usage: "\n -m,--mode=MODE Set sleep state (default: standby)"
+//usage: "\n -s,--seconds=SEC Set timeout in SEC seconds from now"
+//usage: "\n -t,--time=TIME Set timeout to TIME seconds from epoch"
//usage: )
//usage: IF_NOT_LONG_OPTS(
//usage: "\n -a Read clock mode from adjtime"
//usage: "\n -l Clock is set to local time"
//usage: "\n -u Clock is set to UTC time"
//usage: "\n -d DEV Specify the RTC device"
-//usage: "\n -m MODE Set the sleep state (default: standby)"
-//usage: "\n -s SEC Set the timeout in SEC seconds from now"
-//usage: "\n -t TIME Set the timeout to TIME seconds from epoch"
+//usage: "\n -m MODE Set sleep state (default: standby)"
+//usage: "\n -s SEC Set timeout in SEC seconds from now"
+//usage: "\n -t TIME Set timeout to TIME seconds from epoch"
//usage: )
#include "libbb.h"
@@ -51,7 +61,6 @@
#define SYS_RTC_PATH "/sys/class/rtc/%s/device/power/wakeup"
#define SYS_POWER_PATH "/sys/power/state"
-#define DEFAULT_MODE "standby"
static NOINLINE bool may_wakeup(const char *rtcname)
{
@@ -67,7 +76,7 @@ static NOINLINE bool may_wakeup(const char *rtcname)
return false;
/* wakeup events could be disabled or not supported */
- return strncmp(buf, "enabled\n", 8) == 0;
+ return is_prefixed_with(buf, "enabled\n") != NULL;
}
static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
@@ -122,17 +131,16 @@ static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int rtcwake_main(int argc UNUSED_PARAM, char **argv)
{
- time_t rtc_time;
-
unsigned opt;
const char *rtcname = NULL;
- const char *suspend;
+ const char *suspend = "standby";
const char *opt_seconds;
const char *opt_time;
+ time_t rtc_time;
time_t sys_time;
- time_t alarm_time = 0;
- unsigned seconds = 0;
+ time_t alarm_time = alarm_time;
+ unsigned seconds = seconds; /* for compiler */
int utc = -1;
int fd;
@@ -148,6 +156,8 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
;
applet_long_options = rtcwake_longopts;
#endif
+ /* Must have -s or -t, exclusive */
+ opt_complementary = "s:t:s--t:t--s";
opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time);
/* this is the default
@@ -156,17 +166,17 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
*/
if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
utc = opt & RTCWAKE_OPT_UTC;
- if (!(opt & RTCWAKE_OPT_SUSPEND_MODE))
- suspend = DEFAULT_MODE;
- if (opt & RTCWAKE_OPT_SECONDS)
+ if (opt & RTCWAKE_OPT_SECONDS) {
/* alarm time, seconds-to-sleep (relative) */
- seconds = xatoi(opt_seconds);
- if (opt & RTCWAKE_OPT_TIME)
+ seconds = xatou(opt_seconds);
+ } else {
+ /* RTCWAKE_OPT_TIME */
/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
- alarm_time = xatol(opt_time);
-
- if (!alarm_time && !seconds)
- bb_error_msg_and_die("must provide wake time");
+ if (sizeof(alarm_time) <= sizeof(long))
+ alarm_time = xatol(opt_time);
+ else
+ alarm_time = xatoll(opt_time);
+ }
if (utc == -1)
utc = rtc_adjtime_is_utc();
@@ -177,8 +187,9 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
fd = rtc_xopen(&rtcname, O_RDONLY);
- if (strcmp(suspend, "on") && !may_wakeup(rtcname))
- bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);
+ if (strcmp(suspend, "on") != 0)
+ if (!may_wakeup(rtcname))
+ bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);
/* relative or absolute alarm time, normalized to time_t */
sys_time = time(NULL);
@@ -188,21 +199,29 @@ int rtcwake_main(int argc UNUSED_PARAM, char **argv)
rtc_time = rtc_tm2time(&tm_time, utc);
}
-
- if (alarm_time) {
- if (alarm_time < sys_time)
+ if (opt & RTCWAKE_OPT_TIME) {
+ /* Correct for RTC<->system clock difference */
+ alarm_time += rtc_time - sys_time;
+ if (alarm_time < rtc_time)
+ /*
+ * Compat message text.
+ * I'd say "RTC time is already ahead of ..." instead.
+ */
bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
- alarm_time += sys_time - rtc_time;
} else
alarm_time = rtc_time + seconds + 1;
- setup_alarm(fd, &alarm_time, rtc_time);
+ setup_alarm(fd, &alarm_time, rtc_time);
sync();
+#if 0 /*debug*/
+ printf("sys_time: %s", ctime(&sys_time));
+ printf("rtc_time: %s", ctime(&rtc_time));
+#endif
printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
fflush_all();
usleep(10 * 1000);
- if (strcmp(suspend, "on"))
+ if (strcmp(suspend, "on") != 0)
xopen_xwrite_close(SYS_POWER_PATH, suspend);
else {
/* "fake" suspend ... we'll do the delay ourselves */
diff --git a/util-linux/script.c b/util-linux/script.c
index 8fb991d..c5063e8 100644
--- a/util-linux/script.c
+++ b/util-linux/script.c
@@ -10,6 +10,15 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SCRIPT
+//config: bool "script"
+//config: default y
+//config: help
+//config: The script makes typescript of terminal session.
+
+//applet:IF_SCRIPT(APPLET(script, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SCRIPT) += script.o
//usage:#define script_trivial_usage
//usage: "[-afq" IF_SCRIPTREPLAY("t") "] [-c PROG] [OUTFILE]"
@@ -23,6 +32,7 @@
//usage: )
#include "libbb.h"
+#include "common_bufsiz.h"
int script_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int script_main(int argc UNUSED_PARAM, char **argv)
@@ -77,8 +87,15 @@ int script_main(int argc UNUSED_PARAM, char **argv)
if (!(opt & OPT_q)) {
printf("Script started, file is %s\n", fname);
}
+
shell = get_shell_name();
+ /* Some people run "script ... 0>&-".
+ * Our code assumes that STDIN_FILENO != pty.
+ * Ensure STDIN_FILENO is not closed:
+ */
+ bb_sanitize_stdio();
+
pty = xgetpty(pty_line);
/* get current stdin's tty params */
@@ -101,11 +118,12 @@ int script_main(int argc UNUSED_PARAM, char **argv)
if (child_pid) {
/* parent */
-#define buf bb_common_bufsiz1
struct pollfd pfd[2];
int outfd, count, loop;
double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0;
smallint fd_count = 2;
+#define buf bb_common_bufsiz1
+ setup_common_bufsiz();
outfd = xopen(fname, mode);
pfd[0].fd = pty;
@@ -127,7 +145,7 @@ int script_main(int argc UNUSED_PARAM, char **argv)
}
if (pfd[0].revents) {
errno = 0;
- count = safe_read(pty, buf, sizeof(buf));
+ count = safe_read(pty, buf, COMMON_BUFSIZE);
if (count <= 0 && errno != EAGAIN) {
/* err/eof from pty: exit */
goto restore;
@@ -150,7 +168,7 @@ int script_main(int argc UNUSED_PARAM, char **argv)
}
}
if (pfd[1].revents) {
- count = safe_read(STDIN_FILENO, buf, sizeof(buf));
+ count = safe_read(STDIN_FILENO, buf, COMMON_BUFSIZE);
if (count <= 0) {
/* err/eof from stdin: don't read stdin anymore */
pfd[1].revents = 0;
@@ -169,7 +187,7 @@ int script_main(int argc UNUSED_PARAM, char **argv)
* (util-linux's script doesn't do this. buggy :) */
loop = 999;
/* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
- while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) {
+ while (--loop && (count = safe_read(pty, buf, COMMON_BUFSIZE)) > 0) {
full_write(STDOUT_FILENO, buf, count);
full_write(outfd, buf, count);
}
diff --git a/util-linux/scriptreplay.c b/util-linux/scriptreplay.c
index 382f56d..8a0c748 100644
--- a/util-linux/scriptreplay.c
+++ b/util-linux/scriptreplay.c
@@ -7,6 +7,16 @@
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*
*/
+//config:config SCRIPTREPLAY
+//config: bool "scriptreplay"
+//config: default y
+//config: help
+//config: This program replays a typescript, using timing information
+//config: given by script -t.
+
+//applet:IF_SCRIPTREPLAY(APPLET(scriptreplay, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SCRIPTREPLAY) += scriptreplay.o
//usage:#define scriptreplay_trivial_usage
//usage: "timingfile [typescript [divisor]]"
diff --git a/util-linux/setarch.c b/util-linux/setarch.c
index 7b9421a..ec473e9 100644
--- a/util-linux/setarch.c
+++ b/util-linux/setarch.c
@@ -6,13 +6,46 @@
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
+//config:config SETARCH
+//config: bool "setarch"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The linux32 utility is used to create a 32bit environment for the
+//config: specified program (usually a shell). It only makes sense to have
+//config: this util on a system that supports both 64bit and 32bit userland
+//config: (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
+//config:
+//config:config LINUX32
+//config: bool "linux32"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Alias to "setarch linux32".
+//config:
+//config:config LINUX64
+//config: bool "linux64"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: Alias to "setarch linux64".
+
+//applet:IF_SETARCH(APPLET(setarch, BB_DIR_BIN, BB_SUID_DROP))
+//applet:IF_LINUX32(APPLET_ODDNAME(linux32, setarch, BB_DIR_BIN, BB_SUID_DROP, linux32))
+//applet:IF_LINUX64(APPLET_ODDNAME(linux64, setarch, BB_DIR_BIN, BB_SUID_DROP, linux64))
+
+//kbuild:lib-$(CONFIG_SETARCH) += setarch.o
+//kbuild:lib-$(CONFIG_LINUX32) += setarch.o
+//kbuild:lib-$(CONFIG_LINUX64) += setarch.o
//usage:#define setarch_trivial_usage
-//usage: "personality PROG ARGS"
+//usage: "PERSONALITY [-R] PROG ARGS"
//usage:#define setarch_full_usage "\n\n"
-//usage: "Personality may be:\n"
-//usage: " linux32 Set 32bit uname emulation\n"
-//usage: " linux64 Set 64bit uname emulation"
+//usage: "PERSONALITY may be:"
+//usage: "\n"" linux32 Set 32bit uname emulation"
+//usage: "\n"" linux64 Set 64bit uname emulation"
+//usage: "\n"
+//usage: "\n"" -R Disable address space randomization"
//usage:
//usage:#define linux32_trivial_usage NOUSAGE_STR
//usage:#define linux32_full_usage ""
@@ -20,14 +53,18 @@
//usage:#define linux64_trivial_usage NOUSAGE_STR
//usage:#define linux64_full_usage ""
+#include "libbb.h"
#include <sys/personality.h>
-#include "libbb.h"
+#ifndef ADDR_NO_RANDOMIZE
+# define ADDR_NO_RANDOMIZE 0x0040000
+#endif
int setarch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int setarch_main(int argc UNUSED_PARAM, char **argv)
{
- int pers;
+ unsigned opts;
+ unsigned long pers;
/* Figure out what personality we are supposed to switch to ...
* we can be invoked as either:
@@ -35,27 +72,33 @@ int setarch_main(int argc UNUSED_PARAM, char **argv)
* argv[0] == "personality"
*/
if (ENABLE_SETARCH && applet_name[0] == 's'
- && argv[1] && strncpy(argv[1], "linux", 5)
+ && argv[1] && is_prefixed_with(argv[1], "linux")
) {
- applet_name = argv[1];
argv++;
+ applet_name = argv[0];
}
- if (applet_name[5] == '6') /* linux64 */
+ if ((!ENABLE_SETARCH && !ENABLE_LINUX32) || applet_name[5] == '6')
+ /* linux64 */
pers = PER_LINUX;
- else if (applet_name[5] == '3') /* linux32 */
+ else
+ if ((!ENABLE_SETARCH && !ENABLE_LINUX64) || applet_name[5] == '3')
+ /* linux32 */
pers = PER_LINUX32;
else
bb_show_usage();
- argv++;
- if (argv[0] == NULL)
- bb_show_usage();
+ opts = getopt32(argv, "+R"); /* '+': stop at first non-option */
+ if (opts)
+ pers |= ADDR_NO_RANDOMIZE;
/* Try to set personality */
- if (personality(pers) >= 0) {
- /* Try to execute the program */
- BB_EXECVP(argv[0], argv);
- }
+ if (personality(pers) < 0)
+ bb_perror_msg_and_die("personality(0x%lx)", pers);
+
+ argv += optind;
+ if (!argv[0])
+ (--argv)[0] = (char*)"/bin/sh";
- bb_simple_perror_msg_and_die(argv[0]);
+ /* Try to execute the program */
+ BB_EXECVP_or_die(argv);
}
diff --git a/util-linux/swaponoff.c b/util-linux/swaponoff.c
index 963139a..2a9b74d 100644
--- a/util-linux/swaponoff.c
+++ b/util-linux/swaponoff.c
@@ -6,12 +6,57 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SWAPON
+//config: bool "swapon"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This option enables the 'swapon' utility.
+//config: Once you have created some swap space using 'mkswap', you also need
+//config: to enable your swap space with the 'swapon' utility. The 'swapoff'
+//config: utility is used, typically at system shutdown, to disable any swap
+//config: space. If you are not using any swap space, you can leave this
+//config: option disabled.
+//config:
+//config:config FEATURE_SWAPON_DISCARD
+//config: bool "Support discard option -d"
+//config: default y
+//config: depends on SWAPON
+//config: help
+//config: Enable support for discarding swap area blocks at swapon and/or as
+//config: the kernel frees them. This option enables both the -d option on
+//config: 'swapon' and the 'discard' option for swap entries in /etc/fstab.
+//config:
+//config:config FEATURE_SWAPON_PRI
+//config: bool "Support priority option -p"
+//config: default y
+//config: depends on SWAPON
+//config: help
+//config: Enable support for setting swap device priority in swapon.
+//config:
+//config:config SWAPOFF
+//config: bool "swapoff"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: This option enables the 'swapoff' utility.
+
+//applet:IF_SWAPON(APPLET_ODDNAME(swapon, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapon))
+//applet:IF_SWAPOFF(APPLET_ODDNAME(swapoff, swap_on_off, BB_DIR_SBIN, BB_SUID_DROP, swapoff))
+
+//kbuild:lib-$(CONFIG_SWAPON) += swaponoff.o
+//kbuild:lib-$(CONFIG_SWAPOFF) += swaponoff.o
//usage:#define swapon_trivial_usage
-//usage: "[-a]" IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
+//usage: "[-a] [-e]" IF_FEATURE_SWAPON_DISCARD(" [-d[POL]]") IF_FEATURE_SWAPON_PRI(" [-p PRI]") " [DEVICE]"
//usage:#define swapon_full_usage "\n\n"
//usage: "Start swapping on DEVICE\n"
//usage: "\n -a Start swapping on all swap devices"
+//usage: IF_FEATURE_SWAPON_DISCARD(
+//usage: "\n -d[POL] Discard blocks at swapon (POL=once),"
+//usage: "\n as freed (POL=pages), or both (POL omitted)"
+//usage: )
+//usage: "\n -e Silently skip devices that do not exist"
//usage: IF_FEATURE_SWAPON_PRI(
//usage: "\n -p PRI Set swap device priority"
//usage: )
@@ -23,7 +68,9 @@
//usage: "\n -a Stop swapping on all swap devices"
#include "libbb.h"
+#include "common_bufsiz.h"
#include <mntent.h>
+#include <android.h>
#ifndef __BIONIC__
# include <sys/swap.h>
#endif
@@ -38,78 +85,176 @@
# define MNTTYPE_SWAP "swap"
#endif
-#if ENABLE_FEATURE_SWAPON_PRI
+#if ENABLE_FEATURE_SWAPON_DISCARD
+#ifndef SWAP_FLAG_DISCARD
+#define SWAP_FLAG_DISCARD 0x10000
+#endif
+#ifndef SWAP_FLAG_DISCARD_ONCE
+#define SWAP_FLAG_DISCARD_ONCE 0x20000
+#endif
+#ifndef SWAP_FLAG_DISCARD_PAGES
+#define SWAP_FLAG_DISCARD_PAGES 0x40000
+#endif
+#define SWAP_FLAG_DISCARD_MASK \
+ (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | SWAP_FLAG_DISCARD_PAGES)
+#endif
+
+
+#if ENABLE_FEATURE_SWAPON_DISCARD || ENABLE_FEATURE_SWAPON_PRI
struct globals {
int flags;
} FIX_ALIASING;
-#define G (*(struct globals*)&bb_common_bufsiz1)
+#define G (*(struct globals*)bb_common_bufsiz1)
#define g_flags (G.flags)
+#define save_g_flags() int save_g_flags = g_flags
+#define restore_g_flags() g_flags = save_g_flags
#else
#define g_flags 0
+#define save_g_flags() ((void)0)
+#define restore_g_flags() ((void)0)
+#endif
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
+
+#if ENABLE_SWAPOFF
+# if ENABLE_SWAPON
+# define do_swapoff (applet_name[5] == 'f')
+# else
+# define do_swapoff 1
+# endif
+#else
+# define do_swapoff 0
#endif
-#define INIT_G() do { } while (0)
+
+/* Command line options */
+enum {
+ OPTBIT_a, /* -a all */
+ OPTBIT_e, /* -e ifexists */
+ IF_FEATURE_SWAPON_DISCARD( OPTBIT_d ,) /* -d discard */
+ IF_FEATURE_SWAPON_PRI ( OPTBIT_p ,) /* -p priority */
+ OPT_a = 1 << OPTBIT_a,
+ OPT_e = 1 << OPTBIT_e,
+ OPT_d = IF_FEATURE_SWAPON_DISCARD((1 << OPTBIT_d)) + 0,
+ OPT_p = IF_FEATURE_SWAPON_PRI ((1 << OPTBIT_p)) + 0,
+};
+
+#define OPT_ALL (option_mask32 & OPT_a)
+#define OPT_DISCARD (option_mask32 & OPT_d)
+#define OPT_IFEXISTS (option_mask32 & OPT_e)
+#define OPT_PRIO (option_mask32 & OPT_p)
static int swap_enable_disable(char *device)
{
- int status;
- struct stat st;
+ int err = 0;
+ int quiet = 0;
resolve_mount_spec(&device);
- xstat(device, &st);
-#if ENABLE_DESKTOP
- /* test for holes */
- if (S_ISREG(st.st_mode))
- if (st.st_blocks * (off_t)512 < (uint64_t) st.st_size)
- bb_error_msg("warning: swap file has holes");
-#endif
-
- if (applet_name[5] == 'n')
- status = swapon(device, g_flags);
- else
- status = swapoff(device);
+ if (do_swapoff) {
+ err = swapoff(device);
+ /* Don't complain on OPT_ALL if not a swap device or if it doesn't exist */
+ quiet = (OPT_ALL && (errno == EINVAL || errno == ENOENT));
+ } else {
+ /* swapon */
+ struct stat st;
+ err = stat(device, &st);
+ if (!err) {
+ if (ENABLE_DESKTOP && S_ISREG(st.st_mode)) {
+ if (st.st_blocks * (off_t)512 < st.st_size) {
+ bb_error_msg("%s: file has holes", device);
+ return 1;
+ }
+ }
+ err = swapon(device, g_flags);
+ /* Don't complain on swapon -a if device is already in use */
+ quiet = (OPT_ALL && errno == EBUSY);
+ }
+ /* Don't complain if file does not exist with -e option */
+ if (err && OPT_IFEXISTS && errno == ENOENT)
+ err = 0;
+ }
- if (status != 0) {
+ if (err && !quiet) {
bb_simple_perror_msg(device);
return 1;
}
-
return 0;
}
-static int do_em_all(void)
+#if ENABLE_FEATURE_SWAPON_DISCARD
+static void set_discard_flag(char *s)
{
- struct mntent *m;
- FILE *f;
- int err;
+ /* Unset the flag first to allow fstab options to override */
+ /* options set on the command line */
+ g_flags = (g_flags & ~SWAP_FLAG_DISCARD_MASK) | SWAP_FLAG_DISCARD;
- f = setmntent("/etc/fstab", "r");
- if (f == NULL)
- bb_perror_msg_and_die("/etc/fstab");
+ if (!s) /* No optional policy value on the commandline */
+ return;
+ /* Skip prepended '=' */
+ if (*s == '=')
+ s++;
+ /* For fstab parsing: remove other appended options */
+ *strchrnul(s, ',') = '\0';
+
+ if (strcmp(s, "once") == 0)
+ g_flags |= SWAP_FLAG_DISCARD_ONCE;
+ if (strcmp(s, "pages") == 0)
+ g_flags |= SWAP_FLAG_DISCARD_PAGES;
+}
+#else
+#define set_discard_flag(s) ((void)0)
+#endif
+
+#if ENABLE_FEATURE_SWAPON_PRI
+static void set_priority_flag(char *s)
+{
+ unsigned prio;
+
+ /* For fstab parsing: remove other appended options */
+ *strchrnul(s, ',') = '\0';
+ /* Max allowed 32767 (== SWAP_FLAG_PRIO_MASK) */
+ prio = bb_strtou(s, NULL, 10);
+ if (!errno) {
+ /* Unset the flag first to allow fstab options to override */
+ /* options set on the command line */
+ g_flags = (g_flags & ~SWAP_FLAG_PRIO_MASK) | SWAP_FLAG_PREFER |
+ MIN(prio, SWAP_FLAG_PRIO_MASK);
+ }
+}
+#else
+#define set_priority_flag(s) ((void)0)
+#endif
+
+static int do_em_all_in_fstab(void)
+{
+ struct mntent *m;
+ int err = 0;
+ FILE *f = xfopen_for_read("/etc/fstab");
- err = 0;
while ((m = getmntent(f)) != NULL) {
if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) {
/* swapon -a should ignore entries with noauto,
- * but swapoff -a should process them */
- if (applet_name[5] != 'n'
- || hasmntopt(m, MNTOPT_NOAUTO) == NULL
- ) {
-#if ENABLE_FEATURE_SWAPON_PRI
- const char *p;
- g_flags = 0; /* each swap space might have different flags */
- p = hasmntopt(m, "pri");
- if (p) {
- /* Max allowed 32767 (==SWAP_FLAG_PRIO_MASK) */
- unsigned int swap_prio = MIN(bb_strtou(p + 4 , NULL, 10), SWAP_FLAG_PRIO_MASK);
- /* We want to allow "NNNN,foo", thus errno == EINVAL is allowed too */
- if (errno != ERANGE) {
- g_flags = SWAP_FLAG_PREFER |
- (swap_prio << SWAP_FLAG_PRIO_SHIFT);
+ * but swapoff -a should process them
+ */
+ if (do_swapoff || hasmntopt(m, MNTOPT_NOAUTO) == NULL) {
+ /* each swap space might have different flags */
+ /* save global flags for the next round */
+ save_g_flags();
+ if (ENABLE_FEATURE_SWAPON_DISCARD) {
+ char *p = hasmntopt(m, "discard");
+ if (p) {
+ /* move to '=' or to end of string */
+ p += 7;
+ set_discard_flag(p);
}
}
-#endif
- err += swap_enable_disable(m->mnt_fsname);
+ if (ENABLE_FEATURE_SWAPON_PRI) {
+ char *p = hasmntopt(m, "pri");
+ if (p) {
+ set_priority_flag(p + 4);
+ }
+ }
+ err |= swap_enable_disable(m->mnt_fsname);
+ restore_g_flags();
}
}
}
@@ -120,38 +265,68 @@ static int do_em_all(void)
return err;
}
+static int do_all_in_proc_swaps(void)
+{
+ char *line;
+ int err = 0;
+ FILE *f = fopen_for_read("/proc/swaps");
+ /* Don't complain if missing */
+ if (f) {
+ while ((line = xmalloc_fgetline(f)) != NULL) {
+ if (line[0] == '/') {
+ *strchrnul(line, ' ') = '\0';
+ err |= swap_enable_disable(line);
+ }
+ free(line);
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(f);
+ }
+
+ return err;
+}
+
+#define OPTSTR_SWAPON "ae" \
+ IF_FEATURE_SWAPON_DISCARD("d::") \
+ IF_FEATURE_SWAPON_PRI("p:")
+
int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
{
- int ret;
+ IF_FEATURE_SWAPON_PRI(char *prio;)
+ IF_FEATURE_SWAPON_DISCARD(char *discard = NULL;)
+ int ret = 0;
INIT_G();
-#if !ENABLE_FEATURE_SWAPON_PRI
- ret = getopt32(argv, "a");
-#else
- if (applet_name[5] == 'n')
- opt_complementary = "p+";
- ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags);
-
- if (ret & 2) { // -p
- g_flags = SWAP_FLAG_PREFER |
- ((g_flags & SWAP_FLAG_PRIO_MASK) << SWAP_FLAG_PRIO_SHIFT);
- ret &= 1;
- }
-#endif
-
- if (ret /* & 1: not needed */) // -a
- return do_em_all();
+ getopt32(argv, do_swapoff ? "ae" : OPTSTR_SWAPON
+ IF_FEATURE_SWAPON_DISCARD(, &discard)
+ IF_FEATURE_SWAPON_PRI(, &prio)
+ );
argv += optind;
- if (!*argv)
- bb_show_usage();
- /* ret = 0; redundant */
- do {
- ret += swap_enable_disable(*argv);
- } while (*++argv);
+ if (OPT_DISCARD) {
+ set_discard_flag(discard);
+ }
+ if (OPT_PRIO) {
+ set_priority_flag(prio);
+ }
+ if (OPT_ALL) {
+ /* swapoff -a does also /proc/swaps */
+ if (do_swapoff)
+ ret = do_all_in_proc_swaps();
+ ret |= do_em_all_in_fstab();
+ } else if (!*argv) {
+ /* if not -a we need at least one arg */
+ bb_show_usage();
+ }
+ /* Unset -a now to allow for more messages in swap_enable_disable */
+ option_mask32 = option_mask32 & ~OPT_a;
+ /* Now process devices on the commandline if any */
+ while (*argv) {
+ ret |= swap_enable_disable(*argv++);
+ }
return ret;
}
diff --git a/util-linux/switch_root.c b/util-linux/switch_root.c
index a301b36..6034485 100644
--- a/util-linux/switch_root.c
+++ b/util-linux/switch_root.c
@@ -5,6 +5,29 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config SWITCH_ROOT
+//config: bool "switch_root"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: The switch_root utility is used from initramfs to select a new
+//config: root device. Under initramfs, you have to use this instead of
+//config: pivot_root. (Stop reading here if you don't care why.)
+//config:
+//config: Booting with initramfs extracts a gzipped cpio archive into rootfs
+//config: (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
+//config: or unmounted*, pivot_root will not work from initramfs. Instead,
+//config: switch_root deletes everything out of rootfs (including itself),
+//config: does a mount --move that overmounts rootfs with the new root, and
+//config: then execs the specified init program.
+//config:
+//config: * Because the Linux kernel uses rootfs internally as the starting
+//config: and ending point for searching through the kernel's doubly linked
+//config: list of active mount points. That's why.
+
+//applet:IF_SWITCH_ROOT(APPLET(switch_root, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SWITCH_ROOT) += switch_root.o
//usage:#define switch_root_trivial_usage
//usage: "[-c /dev/console] NEW_ROOT NEW_INIT [ARGS]"
@@ -206,7 +229,7 @@ because they're what all paths your process uses would be relative to.
That's why the careful sequencing above: we cd into the new mount point before
we do the mount --move. Moving the mount point would otherwise make it
-totally inaccessible to is because cd-ing to the old path wouldn't give it to
+totally inaccessible to us because cd-ing to the old path wouldn't give it to
us anymore, and cd "/" just gives us the cached dentry from when the process
was created (in this case the old initramfs one). But the "." symlink gives
us the dentry of the filesystem we just moved, so we can then "chroot ." to
diff --git a/util-linux/uevent.c b/util-linux/uevent.c
new file mode 100644
index 0000000..c22216a
--- a/dev/null
+++ b/util-linux/uevent.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2015 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+//config:config UEVENT
+//config: bool "uevent"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: uevent is a netlink listener for kernel uevent notifications
+//config: sent via netlink. It is usually used for dynamic device creation.
+
+//applet:IF_UEVENT(APPLET(uevent, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UEVENT) += uevent.o
+
+//usage:#define uevent_trivial_usage
+//usage: "[PROG [ARGS]]"
+//usage:#define uevent_full_usage "\n\n"
+//usage: "uevent runs PROG for every netlink notification."
+//usage: "\n""PROG's environment contains data passed from the kernel."
+//usage: "\n""Typical usage (daemon for dynamic device node creation):"
+//usage: "\n"" # uevent mdev & mdev -s"
+
+#include "libbb.h"
+#include "common_bufsiz.h"
+#include <linux/netlink.h>
+
+#define BUFFER_SIZE 16*1024
+
+#define env ((char **)bb_common_bufsiz1)
+#define INIT_G() do { setup_common_bufsiz(); } while (0)
+enum {
+ MAX_ENV = COMMON_BUFSIZE / sizeof(env[0]) - 1,
+};
+
+#ifndef SO_RCVBUFFORCE
+#define SO_RCVBUFFORCE 33
+#endif
+enum { RCVBUF = 2 * 1024 * 1024 };
+
+int uevent_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uevent_main(int argc UNUSED_PARAM, char **argv)
+{
+ struct sockaddr_nl sa;
+ int fd;
+
+ INIT_G();
+
+ argv++;
+
+ // Subscribe for UEVENT kernel messages
+ sa.nl_family = AF_NETLINK;
+ sa.nl_pad = 0;
+ sa.nl_pid = getpid();
+ sa.nl_groups = 1 << 0;
+ fd = xsocket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
+ close_on_exec_on(fd);
+
+ // Without a sufficiently big RCVBUF, a ton of simultaneous events
+ // can trigger ENOBUFS on read, which is unrecoverable.
+ // Reproducer:
+ // uevent mdev &
+ // find /sys -name uevent -exec sh -c 'echo add >"{}"' ';'
+ //
+ // SO_RCVBUFFORCE (root only) can go above net.core.rmem_max sysctl
+ setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF, RCVBUF);
+ setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, RCVBUF);
+ if (0) {
+ int z;
+ socklen_t zl = sizeof(z);
+ getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl);
+ bb_error_msg("SO_RCVBUF:%d", z);
+ }
+
+ for (;;) {
+ char *netbuf;
+ char *s, *end;
+ ssize_t len;
+ int idx;
+
+ // In many cases, a system sits for *days* waiting
+ // for a new uevent notification to come in.
+ // We use a fresh mmap so that buffer is not allocated
+ // until kernel actually starts filling it.
+ netbuf = mmap(NULL, BUFFER_SIZE,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON,
+ /* ignored: */ -1, 0);
+ if (netbuf == MAP_FAILED)
+ bb_perror_msg_and_die("mmap");
+
+ // Here we block, possibly for a very long time
+ len = safe_read(fd, netbuf, BUFFER_SIZE - 1);
+ if (len < 0)
+ bb_perror_msg_and_die("read");
+ end = netbuf + len;
+ *end = '\0';
+
+ // Each netlink message starts with "ACTION@/path"
+ // (which we currently ignore),
+ // followed by environment variables.
+ if (!argv[0])
+ putchar('\n');
+ idx = 0;
+ s = netbuf;
+ while (s < end) {
+ if (!argv[0])
+ puts(s);
+ if (strchr(s, '=') && idx < MAX_ENV)
+ env[idx++] = s;
+ s += strlen(s) + 1;
+ }
+ env[idx] = NULL;
+
+ idx = 0;
+ while (env[idx])
+ putenv(env[idx++]);
+ if (argv[0])
+ spawn_and_wait(argv);
+ idx = 0;
+ while (env[idx])
+ bb_unsetenv(env[idx++]);
+ munmap(netbuf, BUFFER_SIZE);
+ }
+
+ return 0; // not reached
+}
diff --git a/util-linux/umount.c b/util-linux/umount.c
index c9d884e..9b8d48e 100644
--- a/util-linux/umount.c
+++ b/util-linux/umount.c
@@ -7,6 +7,26 @@
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
+//config:config UMOUNT
+//config: bool "umount"
+//config: default y
+//config: select PLATFORM_LINUX
+//config: help
+//config: When you want to remove a mounted filesystem from its current mount
+//config: point, for example when you are shutting down the system, the
+//config: 'umount' utility is the tool to use. If you enabled the 'mount'
+//config: utility, you almost certainly also want to enable 'umount'.
+//config:
+//config:config FEATURE_UMOUNT_ALL
+//config: bool "Support option -a"
+//config: default y
+//config: depends on UMOUNT
+//config: help
+//config: Support -a option to unmount all currently mounted filesystems.
+
+//applet:IF_UMOUNT(APPLET(umount, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UMOUNT) += umount.o
//usage:#define umount_trivial_usage
//usage: "[OPTIONS] FILESYSTEM|DIRECTORY"
@@ -30,7 +50,11 @@
#include <mntent.h>
#include <sys/mount.h>
+#ifndef MNT_DETACH
+# define MNT_DETACH 0x00000002
+#endif
#include "libbb.h"
+#include "common_bufsiz.h"
#if defined(__dietlibc__)
// TODO: This does not belong here.
@@ -81,8 +105,9 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
argv += optind;
// MNT_FORCE and MNT_DETACH (from linux/fs.h) must match
- // OPT_FORCE and OPT_LAZY, otherwise this trick won't work:
- doForce = MAX((opt & OPT_FORCE), (opt & OPT_LAZY));
+ // OPT_FORCE and OPT_LAZY.
+ BUILD_BUG_ON(OPT_FORCE != MNT_FORCE || OPT_LAZY != MNT_DETACH);
+ doForce = opt & (OPT_FORCE|OPT_LAZY);
/* Get a list of mount points from mtab. We read them all in now mostly
* for umount -a (so we don't have to worry about the list changing while
@@ -98,7 +123,8 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
if (opt & OPT_ALL)
bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file);
} else {
- while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) {
+ setup_common_bufsiz();
+ while (getmntent_r(fp, &me, bb_common_bufsiz1, COMMON_BUFSIZE)) {
/* Match fstype if passed */
if (!match_fstype(&me, fstype))
continue;
@@ -147,11 +173,18 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
// umount the directory even if we were given the block device.
if (m) zapit = m->dir;
+// umount from util-linux 2.22.2 does not do this:
+// umount -f uses umount2(MNT_FORCE) immediately,
+// not trying umount() first.
+// (Strangely, umount -fl ignores -f: it is equivalent to umount -l.
+// We do pass both flags in this case)
+#if 0
// Let's ask the thing nicely to unmount.
curstat = umount(zapit);
- // Force the unmount, if necessary.
+ // Unmount with force and/or lazy flags, if necessary.
if (curstat && doForce)
+#endif
curstat = umount2(zapit, doForce);
// If still can't umount, maybe remount read-only?
@@ -168,7 +201,7 @@ int umount_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg(msg, m->device);
} else {
status = EXIT_FAILURE;
- bb_perror_msg("can't %sumount %s", (doForce ? "forcibly " : ""), zapit);
+ bb_perror_msg("can't unmount %s", zapit);
}
} else {
// De-allocate the loop device. This ioctl should be ignored on
diff --git a/util-linux/unshare.c b/util-linux/unshare.c
new file mode 100644
index 0000000..bc32803
--- a/dev/null
+++ b/util-linux/unshare.c
@@ -0,0 +1,379 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini unshare implementation for busybox.
+ *
+ * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+//config:config UNSHARE
+//config: bool "unshare"
+//config: default y
+//config: depends on LONG_OPTS && !NOMMU
+//config: select PLATFORM_LINUX
+//config: help
+//config: Run program with some namespaces unshared from parent.
+
+// depends on LONG_OPTS: it is awkward to exclude code which handles --propagation
+// and --setgroups based on LONG_OPTS, so instead applet requires LONG_OPTS.
+// depends on !NOMMU: we need fork()
+
+//applet:IF_UNSHARE(APPLET(unshare, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_UNSHARE) += unshare.o
+
+//usage:#define unshare_trivial_usage
+//usage: "[OPTIONS] [PROG [ARGS]]"
+//usage:#define unshare_full_usage "\n"
+//usage: "\n -m, --mount[=FILE] Unshare mount namespace"
+//usage: "\n -u, --uts[=FILE] Unshare UTS namespace (hostname etc.)"
+//usage: "\n -i, --ipc[=FILE] Unshare System V IPC namespace"
+//usage: "\n -n, --net[=FILE] Unshare network namespace"
+//usage: "\n -p, --pid[=FILE] Unshare PID namespace"
+//usage: "\n -U, --user[=FILE} Unshare user namespace"
+//usage: "\n -f, --fork Fork before execing PROG"
+//usage: "\n -r, --map-root-user Map current user to root (implies -u)"
+//usage: "\n --mount-proc[=DIR] Mount /proc filesystem first (implies -m)"
+//usage: "\n --propagation slave|shared|private|unchanged"
+//usage: "\n Modify mount propagation in mount namespace"
+//usage: "\n --setgroups allow|deny Control the setgroups syscall in user namespaces"
+
+#include <sched.h>
+#ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+#endif
+#ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWUSER
+# define CLONE_NEWUSER 0x10000000
+#endif
+#ifndef CLONE_NEWPID
+# define CLONE_NEWPID 0x20000000
+#endif
+#ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+#endif
+
+#include <sys/mount.h>
+#ifndef MS_REC
+# define MS_REC (1 << 14)
+#endif
+#ifndef MS_PRIVATE
+# define MS_PRIVATE (1 << 18)
+#endif
+#ifndef MS_SLAVE
+# define MS_SLAVE (1 << 19)
+#endif
+#ifndef MS_SHARED
+# define MS_SHARED (1 << 20)
+#endif
+
+#include "libbb.h"
+
+static void mount_or_die(const char *source, const char *target,
+ const char *fstype, unsigned long mountflags)
+{
+ if (mount(source, target, fstype, mountflags, NULL)) {
+ bb_perror_msg_and_die("can't mount %s on %s (flags:0x%lx)",
+ source, target, mountflags);
+ /* fstype is always either NULL or "proc".
+ * "proc" is only used to mount /proc.
+ * No need to clutter up error message with fstype,
+ * it is easily deductible.
+ */
+ }
+}
+
+#define PATH_PROC_SETGROUPS "/proc/self/setgroups"
+#define PATH_PROC_UIDMAP "/proc/self/uid_map"
+#define PATH_PROC_GIDMAP "/proc/self/gid_map"
+
+struct namespace_descr {
+ int flag;
+ const char nsfile4[4];
+};
+
+struct namespace_ctx {
+ char *path;
+};
+
+enum {
+ OPT_mount = 1 << 0,
+ OPT_uts = 1 << 1,
+ OPT_ipc = 1 << 2,
+ OPT_net = 1 << 3,
+ OPT_pid = 1 << 4,
+ OPT_user = 1 << 5, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
+ OPT_fork = 1 << 6,
+ OPT_map_root = 1 << 7,
+ OPT_mount_proc = 1 << 8,
+ OPT_propagation = 1 << 9,
+ OPT_setgroups = 1 << 10,
+};
+enum {
+ NS_MNT_POS = 0,
+ NS_UTS_POS,
+ NS_IPC_POS,
+ NS_NET_POS,
+ NS_PID_POS,
+ NS_USR_POS, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
+ NS_COUNT,
+};
+static const struct namespace_descr ns_list[] = {
+ { CLONE_NEWNS, "mnt" },
+ { CLONE_NEWUTS, "uts" },
+ { CLONE_NEWIPC, "ipc" },
+ { CLONE_NEWNET, "net" },
+ { CLONE_NEWPID, "pid" },
+ { CLONE_NEWUSER, "user" }, /* OPT_user, NS_USR_POS, and ns_list[] index must match! */
+};
+
+/*
+ * Upstream unshare doesn't support short options for --mount-proc,
+ * --propagation, --setgroups.
+ * Optional arguments (namespace mountpoints) exist only for long opts,
+ * we are forced to use "fake" letters for them.
+ * '+': stop at first non-option.
+ */
+static const char opt_str[] ALIGN1 = "+muinpU""fr""\xfd::""\xfe:""\xff:";
+static const char unshare_longopts[] ALIGN1 =
+ "mount\0" Optional_argument "\xf0"
+ "uts\0" Optional_argument "\xf1"
+ "ipc\0" Optional_argument "\xf2"
+ "net\0" Optional_argument "\xf3"
+ "pid\0" Optional_argument "\xf4"
+ "user\0" Optional_argument "\xf5"
+ "fork\0" No_argument "f"
+ "map-root-user\0" No_argument "r"
+ "mount-proc\0" Optional_argument "\xfd"
+ "propagation\0" Required_argument "\xfe"
+ "setgroups\0" Required_argument "\xff"
+;
+
+/* Ugly-looking string reuse trick */
+#define PRIVATE_STR "private\0""unchanged\0""shared\0""slave\0"
+#define PRIVATE_UNCHANGED_SHARED_SLAVE PRIVATE_STR
+
+static unsigned long parse_propagation(const char *prop_str)
+{
+ int i = index_in_strings(PRIVATE_UNCHANGED_SHARED_SLAVE, prop_str);
+ if (i < 0)
+ bb_error_msg_and_die("unrecognized: --%s=%s", "propagation", prop_str);
+ if (i == 0)
+ return MS_REC | MS_PRIVATE;
+ if (i == 1)
+ return 0;
+ if (i == 2)
+ return MS_REC | MS_SHARED;
+ return MS_REC | MS_SLAVE;
+}
+
+static void mount_namespaces(pid_t pid, struct namespace_ctx *ns_ctx_list)
+{
+ const struct namespace_descr *ns;
+ struct namespace_ctx *ns_ctx;
+ int i;
+
+ for (i = 0; i < NS_COUNT; i++) {
+ char nsf[sizeof("/proc/%u/ns/AAAA") + sizeof(int)*3];
+
+ ns = &ns_list[i];
+ ns_ctx = &ns_ctx_list[i];
+ if (!ns_ctx->path)
+ continue;
+ sprintf(nsf, "/proc/%u/ns/%.4s", (unsigned)pid, ns->nsfile4);
+ mount_or_die(nsf, ns_ctx->path, NULL, MS_BIND);
+ }
+}
+
+int unshare_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int unshare_main(int argc UNUSED_PARAM, char **argv)
+{
+ int i;
+ unsigned int opts;
+ int unsflags;
+ uintptr_t need_mount;
+ const char *proc_mnt_target;
+ const char *prop_str;
+ const char *setgrp_str;
+ unsigned long prop_flags;
+ uid_t reuid = geteuid();
+ gid_t regid = getegid();
+ struct fd_pair fdp;
+ pid_t child = child; /* for compiler */
+ struct namespace_ctx ns_ctx_list[NS_COUNT];
+
+ memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
+ proc_mnt_target = "/proc";
+ prop_str = PRIVATE_STR;
+ setgrp_str = NULL;
+
+ opt_complementary =
+ "\xf0""m" /* long opts (via their "fake chars") imply short opts */
+ ":\xf1""u"
+ ":\xf2""i"
+ ":\xf3""n"
+ ":\xf4""p"
+ ":\xf5""U"
+ ":ru" /* --map-root-user or -r implies -u */
+ ":\xfd""m" /* --mount-proc implies -m */
+ ;
+ applet_long_options = unshare_longopts;
+ opts = getopt32(argv, opt_str,
+ &proc_mnt_target, &prop_str, &setgrp_str,
+ &ns_ctx_list[NS_MNT_POS].path,
+ &ns_ctx_list[NS_UTS_POS].path,
+ &ns_ctx_list[NS_IPC_POS].path,
+ &ns_ctx_list[NS_NET_POS].path,
+ &ns_ctx_list[NS_PID_POS].path,
+ &ns_ctx_list[NS_USR_POS].path
+ );
+ argv += optind;
+ //bb_error_msg("opts:0x%x", opts);
+ //bb_error_msg("mount:%s", ns_ctx_list[NS_MNT_POS].path);
+ //bb_error_msg("proc_mnt_target:%s", proc_mnt_target);
+ //bb_error_msg("prop_str:%s", prop_str);
+ //bb_error_msg("setgrp_str:%s", setgrp_str);
+ //exit(1);
+
+ if (setgrp_str) {
+ if (strcmp(setgrp_str, "allow") == 0) {
+ if (opts & OPT_map_root) {
+ bb_error_msg_and_die(
+ "--setgroups=allow and --map-root-user "
+ "are mutually exclusive"
+ );
+ }
+ } else {
+ /* It's not "allow", must be "deny" */
+ if (strcmp(setgrp_str, "deny") != 0)
+ bb_error_msg_and_die("unrecognized: --%s=%s",
+ "setgroups", setgrp_str);
+ }
+ }
+
+ unsflags = 0;
+ need_mount = 0;
+ for (i = 0; i < NS_COUNT; i++) {
+ const struct namespace_descr *ns = &ns_list[i];
+ struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
+
+ if (opts & (1 << i))
+ unsflags |= ns->flag;
+
+ need_mount |= (uintptr_t)(ns_ctx->path);
+ }
+ /* need_mount != 0 if at least one FILE was given */
+
+ prop_flags = MS_REC | MS_PRIVATE;
+ /* Silently ignore --propagation if --mount is not requested. */
+ if (opts & OPT_mount)
+ prop_flags = parse_propagation(prop_str);
+
+ /*
+ * Special case: if we were requested to unshare the mount namespace
+ * AND to make any namespace persistent (by bind mounting it) we need
+ * to spawn a child process which will wait for the parent to call
+ * unshare(), then mount parent's namespaces while still in the
+ * previous namespace.
+ */
+ fdp.wr = -1;
+ if (need_mount && (opts & OPT_mount)) {
+ /*
+ * Can't use getppid() in child, as we can be unsharing the
+ * pid namespace.
+ */
+ pid_t ppid = getpid();
+
+ xpiped_pair(fdp);
+
+ child = xfork();
+ if (child == 0) {
+ /* Child */
+ close(fdp.wr);
+
+ /* Wait until parent calls unshare() */
+ read(fdp.rd, ns_ctx_list, 1); /* ...using bogus buffer */
+ /*close(fdp.rd);*/
+
+ /* Mount parent's unshared namespaces. */
+ mount_namespaces(ppid, ns_ctx_list);
+ return EXIT_SUCCESS;
+ }
+ /* Parent continues */
+ }
+
+ //if (unshare(unsflags) != 0)
+ // bb_perror_msg_and_die("unshare(0x%x)", unsflags);
+
+ if (fdp.wr >= 0) {
+ close(fdp.wr); /* Release child */
+ close(fdp.rd); /* should close fd, to not confuse exec'ed PROG */
+ }
+
+ if (need_mount) {
+ /* Wait for the child to finish mounting the namespaces. */
+ if (opts & OPT_mount) {
+ int exit_status = wait_for_exitstatus(child);
+ if (WIFEXITED(exit_status) &&
+ WEXITSTATUS(exit_status) != EXIT_SUCCESS)
+ return WEXITSTATUS(exit_status);
+ } else {
+ /*
+ * Regular way - we were requested to mount some other
+ * namespaces: mount them after the call to unshare().
+ */
+ mount_namespaces(getpid(), ns_ctx_list);
+ }
+ }
+
+ /*
+ * When we're unsharing the pid namespace, it's not the process that
+ * calls unshare() that is put into the new namespace, but its first
+ * child. The user may want to use this option to spawn a new process
+ * that'll become PID 1 in this new namespace.
+ */
+ if (opts & OPT_fork) {
+ xvfork_parent_waits_and_exits();
+ /* Child continues */
+ }
+
+ if (opts & OPT_map_root) {
+ char uidmap_buf[sizeof("%u 0 1") + sizeof(int)*3];
+
+ /*
+ * Since Linux 3.19 unprivileged writing of /proc/self/gid_map
+ * has been disabled unless /proc/self/setgroups is written
+ * first to permanently disable the ability to call setgroups
+ * in that user namespace.
+ */
+ xopen_xwrite_close(PATH_PROC_SETGROUPS, "deny");
+ sprintf(uidmap_buf, "%u 0 1", (unsigned)reuid);
+ xopen_xwrite_close(PATH_PROC_UIDMAP, uidmap_buf);
+ sprintf(uidmap_buf, "%u 0 1", (unsigned)regid);
+ xopen_xwrite_close(PATH_PROC_GIDMAP, uidmap_buf);
+ } else
+ if (setgrp_str) {
+ /* Write "allow" or "deny" */
+ xopen_xwrite_close(PATH_PROC_SETGROUPS, setgrp_str);
+ }
+
+ if (opts & OPT_mount) {
+ mount_or_die("none", "/", NULL, prop_flags);
+ }
+
+ if (opts & OPT_mount_proc) {
+ /*
+ * When creating a new pid namespace, we might want the pid
+ * subdirectories in /proc to remain consistent with the new
+ * process IDs. Without --mount-proc the pids in /proc would
+ * still reflect the old pid namespace. This is why we make
+ * /proc private here and then do a fresh mount.
+ */
+ mount_or_die("none", proc_mnt_target, NULL, MS_PRIVATE | MS_REC);
+ mount_or_die("proc", proc_mnt_target, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV);
+ }
+
+ exec_prog_or_SHELL(argv);
+}
diff --git a/util-linux/volume_id/bcache.c b/util-linux/volume_id/bcache.c
new file mode 100644
index 0000000..fd40eb0
--- a/dev/null
+++ b/util-linux/volume_id/bcache.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Based on code fragments from bcache-tools by Kent Overstreet:
+ * http://evilpiepirate.org/git/bcache-tools.git
+ */
+
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_BCACHE) += bcache.o
+
+//config:
+//config:config FEATURE_VOLUMEID_BCACHE
+//config: bool "bcache filesystem"
+//config: default y
+//config: depends on VOLUMEID
+//config: help
+//config: TODO
+//config:
+
+#include "volume_id_internal.h"
+
+#define SB_LABEL_SIZE 32
+#define SB_JOURNAL_BUCKETS 256U
+
+static const char bcache_magic[] ALIGN1 = {
+ 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca,
+ 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81
+};
+
+struct bcache_super_block {
+ uint64_t csum;
+ uint64_t offset; /* sector where this sb was written */
+ uint64_t version;
+
+ uint8_t magic[16];
+
+ uint8_t uuid[16];
+ union {
+ uint8_t set_uuid[16];
+ uint64_t set_magic;
+ };
+ uint8_t label[SB_LABEL_SIZE];
+
+ uint64_t flags;
+ uint64_t seq;
+ uint64_t pad[8];
+
+ union {
+ struct {
+ /* Cache devices */
+ uint64_t nbuckets; /* device size */
+
+ uint16_t block_size; /* sectors */
+ uint16_t bucket_size; /* sectors */
+
+ uint16_t nr_in_set;
+ uint16_t nr_this_dev;
+ };
+ struct {
+ /* Backing devices */
+ uint64_t data_offset;
+
+ /*
+ * block_size from the cache device section is still used by
+ * backing devices, so don't add anything here until we fix
+ * things to not need it for backing devices anymore
+ */
+ };
+ };
+
+ uint32_t last_mount; /* time_t */
+
+ uint16_t first_bucket;
+ union {
+ uint16_t njournal_buckets;
+ uint16_t keys;
+ };
+ uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */
+};
+
+/* magic string */
+#define BCACHE_SB_MAGIC bcache_magic
+/* magic string len */
+#define BCACHE_SB_MAGIC_LEN sizeof (bcache_magic)
+/* super block offset */
+#define BCACHE_SB_OFF 0x1000
+/* supper block offset in kB */
+#define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10)
+/* magic string offset within super block */
+#define BCACHE_SB_MAGIC_OFF offsetof (struct bcache_super_block, magic)
+
+int FAST_FUNC volume_id_probe_bcache(struct volume_id *id /*,uint64_t off*/)
+{
+ struct bcache_super_block *sb;
+
+ sb = volume_id_get_buffer(id, BCACHE_SB_OFF, sizeof(*sb));
+ if (sb == NULL)
+ return -1;
+
+ if (memcmp(sb->magic, BCACHE_SB_MAGIC, BCACHE_SB_MAGIC_LEN) != 0)
+ return -1;
+
+ volume_id_set_label_string(id, sb->label, SB_LABEL_SIZE);
+ volume_id_set_uuid(id, sb->uuid, UUID_DCE);
+ IF_FEATURE_BLKID_TYPE(id->type = "bcache";)
+
+ return 0;
+}
diff --git a/util-linux/volume_id/get_devname.c b/util-linux/volume_id/get_devname.c
index 648dbc4..3789de4 100644
--- a/util-linux/volume_id/get_devname.c
+++ b/util-linux/volume_id/get_devname.c
@@ -107,7 +107,11 @@ uuidcache_check_device(const char *device,
int depth UNUSED_PARAM)
{
/* note: this check rejects links to devices, among other nodes */
- if (!S_ISBLK(statbuf->st_mode))
+ if (!S_ISBLK(statbuf->st_mode)
+#if ENABLE_FEATURE_VOLUMEID_UBIFS
+ && !(S_ISCHR(statbuf->st_mode) && strncmp(bb_basename(device), "ubi", 3) == 0)
+#endif
+ )
return TRUE;
/* Users report that mucking with floppies (especially non-present
@@ -302,9 +306,9 @@ int resolve_mount_spec(char **fsname)
{
char *tmp = *fsname;
- if (strncmp(*fsname, "UUID=", 5) == 0)
+ if (is_prefixed_with(*fsname, "UUID="))
tmp = get_devname_from_uuid(*fsname + 5);
- else if (strncmp(*fsname, "LABEL=", 6) == 0)
+ else if (is_prefixed_with(*fsname, "LABEL="))
tmp = get_devname_from_label(*fsname + 6);
if (tmp == *fsname)
diff --git a/util-linux/volume_id/luks.c b/util-linux/volume_id/luks.c
index 42bf876..21cb26f 100644
--- a/util-linux/volume_id/luks.c
+++ b/util-linux/volume_id/luks.c
@@ -40,7 +40,7 @@
#define LUKS_SALTSIZE 32
#define LUKS_NUMKEYS 8
-static const uint8_t LUKS_MAGIC[] = { 'L','U','K','S', 0xba, 0xbe };
+static const uint8_t LUKS_MAGIC[] ALIGN1 = { 'L','U','K','S', 0xba, 0xbe };
struct luks_phdr {
uint8_t magic[LUKS_MAGIC_L];
diff --git a/util-linux/volume_id/ubifs.c b/util-linux/volume_id/ubifs.c
new file mode 100644
index 0000000..13604ec
--- a/dev/null
+++ b/util-linux/volume_id/ubifs.c
@@ -0,0 +1,125 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_UBIFS) += ubifs.o
+
+//config:
+//config:config FEATURE_VOLUMEID_UBIFS
+//config: bool "UBIFS filesystem"
+//config: default y
+//config: depends on VOLUMEID
+//config: help
+//config: UBIFS (Unsorted Block Image File System) is a file
+//config: system for use with raw flash memory media.
+//config:
+
+#include "volume_id_internal.h"
+
+#define UBIFS_NODE_MAGIC 0x06101831
+
+/*
+ * struct ubifs_ch - common header node.
+ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
+ * @crc: CRC-32 checksum of the node header
+ * @sqnum: sequence number
+ * @len: full node length
+ * @node_type: node type
+ * @group_type: node group type
+ * @padding: reserved for future, zeroes
+ *
+ * Every UBIFS node starts with this common part. If the node has a key, the
+ * key always goes next.
+ */
+struct ubifs_ch {
+ uint32_t magic;
+ uint32_t crc;
+ uint64_t sqnum;
+ uint32_t len;
+ uint8_t node_type;
+ uint8_t group_type;
+ uint8_t padding[2];
+} PACKED;
+
+/*
+ * struct ubifs_sb_node - superblock node.
+ * @ch: common header
+ * @padding: reserved for future, zeroes
+ * @key_hash: type of hash function used in keys
+ * @key_fmt: format of the key
+ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
+ * @min_io_size: minimal input/output unit size
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_cnt: count of LEBs used by file-system
+ * @max_leb_cnt: maximum count of LEBs used by file-system
+ * @max_bud_bytes: maximum amount of data stored in buds
+ * @log_lebs: log size in logical eraseblocks
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @orph_lebs: number of LEBs used for recording orphans
+ * @jhead_cnt: count of journal heads
+ * @fanout: tree fanout (max. number of links per indexing node)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @fmt_version: UBIFS on-flash format version
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @padding1: reserved for future, zeroes
+ * @rp_uid: reserve pool UID
+ * @rp_gid: reserve pool GID
+ * @rp_size: size of the reserved pool in bytes
+ * @padding2: reserved for future, zeroes
+ * @time_gran: time granularity in nanoseconds
+ * @uuid: UUID generated when the file system image was created
+ * @ro_compat_version: UBIFS R/O compatibility version
+ */
+struct ubifs_sb_node {
+ struct ubifs_ch ch;
+ uint8_t padding[2];
+ uint8_t key_hash;
+ uint8_t key_fmt;
+ uint32_t flags;
+ uint32_t min_io_size;
+ uint32_t leb_size;
+ uint32_t leb_cnt;
+ uint32_t max_leb_cnt;
+ uint64_t max_bud_bytes;
+ uint32_t log_lebs;
+ uint32_t lpt_lebs;
+ uint32_t orph_lebs;
+ uint32_t jhead_cnt;
+ uint32_t fanout;
+ uint32_t lsave_cnt;
+ uint32_t fmt_version;
+ uint16_t default_compr;
+ uint8_t padding1[2];
+ uint32_t rp_uid;
+ uint32_t rp_gid;
+ uint64_t rp_size;
+ uint32_t time_gran;
+ uint8_t uuid[16];
+ uint32_t ro_compat_version;
+/*
+ uint8_t padding2[3968];
+*/
+} PACKED;
+
+int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+ struct ubifs_sb_node *sb;
+
+ dbg("UBIFS: probing at offset 0x%llx", (unsigned long long) off);
+ sb = volume_id_get_buffer(id, off, sizeof(struct ubifs_sb_node));
+ if (!sb)
+ return -1;
+
+ if (le32_to_cpu(sb->ch.magic) != UBIFS_NODE_MAGIC)
+ return -1;
+
+ IF_FEATURE_BLKID_TYPE(id->type = "ubifs";)
+ volume_id_set_uuid(id, sb->uuid, UUID_DCE);
+
+ return 0;
+}
diff --git a/util-linux/volume_id/volume_id.c b/util-linux/volume_id/volume_id.c
index 66690c2..fef7d53 100644
--- a/util-linux/volume_id/volume_id.c
+++ b/util-linux/volume_id/volume_id.c
@@ -107,6 +107,9 @@ static const probe_fptr fs1[] = {
#if ENABLE_FEATURE_VOLUMEID_XFS
volume_id_probe_xfs,
#endif
+#if ENABLE_FEATURE_VOLUMEID_BCACHE
+ volume_id_probe_bcache,
+#endif
};
/* fill buffer with maximum */
@@ -165,6 +168,9 @@ static const probe_fptr fs2[] = {
#if ENABLE_FEATURE_VOLUMEID_OCFS2
volume_id_probe_ocfs2,
#endif
+#if ENABLE_FEATURE_VOLUMEID_UBIFS
+ volume_id_probe_ubifs,
+#endif
};
int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size)
diff --git a/util-linux/volume_id/volume_id_internal.h b/util-linux/volume_id/volume_id_internal.h
index 6e2dbd7..759a832 100644
--- a/util-linux/volume_id/volume_id_internal.h
+++ b/util-linux/volume_id/volume_id_internal.h
@@ -169,6 +169,8 @@ int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/,
/* FS */
+int FAST_FUNC volume_id_probe_bcache(struct volume_id *id /*,uint64_t off*/);
+
int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/);
int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/);
@@ -219,4 +221,6 @@ int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/);
int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/);
+int FAST_FUNC volume_id_probe_ubifs(struct volume_id *id /*,uint64_t off*/);
+
POP_SAVED_FUNCTION_VISIBILITY